@@ -224,6 +224,67 @@ def test_crcrcrlf2(self):
224
224
out = self .check_script_output (src , br"'\n\n\n'" )
225
225
226
226
227
+ class UTF8ValidatorTest (unittest .TestCase ):
228
+ @unittest .skipIf (not sys .platform .startswith ("linux" ),
229
+ "Too slow to run on non-Linux platforms" )
230
+ def test_invalid_utf8 (self ):
231
+ # This is a port of test_utf8_decode_invalid_sequences in
232
+ # test_unicode.py to exercise the separate utf8 validator in
233
+ # Parser/tokenizer.c used when reading source files.
234
+
235
+ # That file is written using low-level C file I/O, so the only way to
236
+ # test it is to write actual files to disk.
237
+
238
+ # Each example is put inside a string at the top of the file so
239
+ # it's an otherwise valid Python source file.
240
+ template = b'"%s"\n '
241
+
242
+ with tempfile .TemporaryDirectory () as tmpd :
243
+ fn = os .path .join (tmpd , 'test.py' )
244
+
245
+ def check (content ):
246
+ with open (fn , 'wb' ) as fp :
247
+ fp .write (template % content )
248
+ script_helper .assert_python_failure (fn )
249
+
250
+ # continuation bytes in a sequence of 2, 3, or 4 bytes
251
+ continuation_bytes = [bytes ([x ]) for x in range (0x80 , 0xC0 )]
252
+ # start bytes of a 2-byte sequence equivalent to code points < 0x7F
253
+ invalid_2B_seq_start_bytes = [bytes ([x ]) for x in range (0xC0 , 0xC2 )]
254
+ # start bytes of a 4-byte sequence equivalent to code points > 0x10FFFF
255
+ invalid_4B_seq_start_bytes = [bytes ([x ]) for x in range (0xF5 , 0xF8 )]
256
+ invalid_start_bytes = (
257
+ continuation_bytes + invalid_2B_seq_start_bytes +
258
+ invalid_4B_seq_start_bytes + [bytes ([x ]) for x in range (0xF7 , 0x100 )]
259
+ )
260
+
261
+ for byte in invalid_start_bytes :
262
+ check (byte )
263
+
264
+ for sb in invalid_2B_seq_start_bytes :
265
+ for cb in continuation_bytes :
266
+ check (sb + cb )
267
+
268
+ for sb in invalid_4B_seq_start_bytes :
269
+ for cb1 in continuation_bytes [:3 ]:
270
+ for cb3 in continuation_bytes [:3 ]:
271
+ check (sb + cb1 + b'\x80 ' + cb3 )
272
+
273
+ for cb in [bytes ([x ]) for x in range (0x80 , 0xA0 )]:
274
+ check (b'\xE0 ' + cb + b'\x80 ' )
275
+ check (b'\xE0 ' + cb + b'\xBF ' )
276
+ # surrogates
277
+ for cb in [bytes ([x ]) for x in range (0xA0 , 0xC0 )]:
278
+ check (b'\xED ' + cb + b'\x80 ' )
279
+ check (b'\xED ' + cb + b'\xBF ' )
280
+ for cb in [bytes ([x ]) for x in range (0x80 , 0x90 )]:
281
+ check (b'\xF0 ' + cb + b'\x80 \x80 ' )
282
+ check (b'\xF0 ' + cb + b'\xBF \xBF ' )
283
+ for cb in [bytes ([x ]) for x in range (0x90 , 0xC0 )]:
284
+ check (b'\xF4 ' + cb + b'\x80 \x80 ' )
285
+ check (b'\xF4 ' + cb + b'\xBF \xBF ' )
286
+
287
+
227
288
class BytesSourceEncodingTest (AbstractSourceEncodingTest , unittest .TestCase ):
228
289
229
290
def check_script_output (self , src , expected ):
0 commit comments