1import errno
2import importlib
3import io
4import os
5import shutil
6import socket
7import stat
8import subprocess
9import sys
10import tempfile
11import textwrap
12import time
13import unittest
14import warnings
15
16from test import support
17from test.support import import_helper
18from test.support import os_helper
19from test.support import script_helper
20from test.support import socket_helper
21from test.support import warnings_helper
22
23TESTFN = os_helper.TESTFN
24
25
26class TestSupport(unittest.TestCase):
27    @classmethod
28    def setUpClass(cls):
29        orig_filter_len = len(warnings.filters)
30        cls._warnings_helper_token = support.ignore_deprecations_from(
31            "test.support.warnings_helper", like=".*used in test_support.*"
32        )
33        cls._test_support_token = support.ignore_deprecations_from(
34            "test.test_support", like=".*You should NOT be seeing this.*"
35        )
36        assert len(warnings.filters) == orig_filter_len + 2
37
38    @classmethod
39    def tearDownClass(cls):
40        orig_filter_len = len(warnings.filters)
41        support.clear_ignored_deprecations(
42            cls._warnings_helper_token,
43            cls._test_support_token,
44        )
45        assert len(warnings.filters) == orig_filter_len - 2
46
47    def test_ignored_deprecations_are_silent(self):
48        """Test support.ignore_deprecations_from() silences warnings"""
49        with warnings.catch_warnings(record=True) as warning_objs:
50            warnings_helper._warn_about_deprecation()
51            warnings.warn("You should NOT be seeing this.", DeprecationWarning)
52            messages = [str(w.message) for w in warning_objs]
53        self.assertEqual(len(messages), 0, messages)
54
55    def test_import_module(self):
56        import_helper.import_module("ftplib")
57        self.assertRaises(unittest.SkipTest,
58                          import_helper.import_module, "foo")
59
60    def test_import_fresh_module(self):
61        import_helper.import_fresh_module("ftplib")
62
63    def test_get_attribute(self):
64        self.assertEqual(support.get_attribute(self, "test_get_attribute"),
65                        self.test_get_attribute)
66        self.assertRaises(unittest.SkipTest, support.get_attribute, self, "foo")
67
68    @unittest.skip("failing buildbots")
69    def test_get_original_stdout(self):
70        self.assertEqual(support.get_original_stdout(), sys.stdout)
71
72    def test_unload(self):
73        import sched
74        self.assertIn("sched", sys.modules)
75        import_helper.unload("sched")
76        self.assertNotIn("sched", sys.modules)
77
78    def test_unlink(self):
79        with open(TESTFN, "w", encoding="utf-8") as f:
80            pass
81        os_helper.unlink(TESTFN)
82        self.assertFalse(os.path.exists(TESTFN))
83        os_helper.unlink(TESTFN)
84
85    def test_rmtree(self):
86        dirpath = os_helper.TESTFN + 'd'
87        subdirpath = os.path.join(dirpath, 'subdir')
88        os.mkdir(dirpath)
89        os.mkdir(subdirpath)
90        os_helper.rmtree(dirpath)
91        self.assertFalse(os.path.exists(dirpath))
92        with support.swap_attr(support, 'verbose', 0):
93            os_helper.rmtree(dirpath)
94
95        os.mkdir(dirpath)
96        os.mkdir(subdirpath)
97        os.chmod(dirpath, stat.S_IRUSR|stat.S_IXUSR)
98        with support.swap_attr(support, 'verbose', 0):
99            os_helper.rmtree(dirpath)
100        self.assertFalse(os.path.exists(dirpath))
101
102        os.mkdir(dirpath)
103        os.mkdir(subdirpath)
104        os.chmod(dirpath, 0)
105        with support.swap_attr(support, 'verbose', 0):
106            os_helper.rmtree(dirpath)
107        self.assertFalse(os.path.exists(dirpath))
108
109    def test_forget(self):
110        mod_filename = TESTFN + '.py'
111        with open(mod_filename, 'w', encoding="utf-8") as f:
112            print('foo = 1', file=f)
113        sys.path.insert(0, os.curdir)
114        importlib.invalidate_caches()
115        try:
116            mod = __import__(TESTFN)
117            self.assertIn(TESTFN, sys.modules)
118
119            import_helper.forget(TESTFN)
120            self.assertNotIn(TESTFN, sys.modules)
121        finally:
122            del sys.path[0]
123            os_helper.unlink(mod_filename)
124            os_helper.rmtree('__pycache__')
125
126    @support.requires_working_socket()
127    def test_HOST(self):
128        s = socket.create_server((socket_helper.HOST, 0))
129        s.close()
130
131    @support.requires_working_socket()
132    def test_find_unused_port(self):
133        port = socket_helper.find_unused_port()
134        s = socket.create_server((socket_helper.HOST, port))
135        s.close()
136
137    @support.requires_working_socket()
138    def test_bind_port(self):
139        s = socket.socket()
140        socket_helper.bind_port(s)
141        s.listen()
142        s.close()
143
144    # Tests for temp_dir()
145
146    def test_temp_dir(self):
147        """Test that temp_dir() creates and destroys its directory."""
148        parent_dir = tempfile.mkdtemp()
149        parent_dir = os.path.realpath(parent_dir)
150
151        try:
152            path = os.path.join(parent_dir, 'temp')
153            self.assertFalse(os.path.isdir(path))
154            with os_helper.temp_dir(path) as temp_path:
155                self.assertEqual(temp_path, path)
156                self.assertTrue(os.path.isdir(path))
157            self.assertFalse(os.path.isdir(path))
158        finally:
159            os_helper.rmtree(parent_dir)
160
161    def test_temp_dir__path_none(self):
162        """Test passing no path."""
163        with os_helper.temp_dir() as temp_path:
164            self.assertTrue(os.path.isdir(temp_path))
165        self.assertFalse(os.path.isdir(temp_path))
166
167    def test_temp_dir__existing_dir__quiet_default(self):
168        """Test passing a directory that already exists."""
169        def call_temp_dir(path):
170            with os_helper.temp_dir(path) as temp_path:
171                raise Exception("should not get here")
172
173        path = tempfile.mkdtemp()
174        path = os.path.realpath(path)
175        try:
176            self.assertTrue(os.path.isdir(path))
177            self.assertRaises(FileExistsError, call_temp_dir, path)
178            # Make sure temp_dir did not delete the original directory.
179            self.assertTrue(os.path.isdir(path))
180        finally:
181            shutil.rmtree(path)
182
183    def test_temp_dir__existing_dir__quiet_true(self):
184        """Test passing a directory that already exists with quiet=True."""
185        path = tempfile.mkdtemp()
186        path = os.path.realpath(path)
187
188        try:
189            with warnings_helper.check_warnings() as recorder:
190                with os_helper.temp_dir(path, quiet=True) as temp_path:
191                    self.assertEqual(path, temp_path)
192                warnings = [str(w.message) for w in recorder.warnings]
193            # Make sure temp_dir did not delete the original directory.
194            self.assertTrue(os.path.isdir(path))
195        finally:
196            shutil.rmtree(path)
197
198        self.assertEqual(len(warnings), 1, warnings)
199        warn = warnings[0]
200        self.assertTrue(warn.startswith(f'tests may fail, unable to create '
201                                        f'temporary directory {path!r}: '),
202                        warn)
203
204    @support.requires_fork()
205    def test_temp_dir__forked_child(self):
206        """Test that a forked child process does not remove the directory."""
207        # See bpo-30028 for details.
208        # Run the test as an external script, because it uses fork.
209        script_helper.assert_python_ok("-c", textwrap.dedent("""
210            import os
211            from test import support
212            from test.support import os_helper
213            with os_helper.temp_cwd() as temp_path:
214                pid = os.fork()
215                if pid != 0:
216                    # parent process
217
218                    # wait for the child to terminate
219                    support.wait_process(pid, exitcode=0)
220
221                    # Make sure that temp_path is still present. When the child
222                    # process leaves the 'temp_cwd'-context, the __exit__()-
223                    # method of the context must not remove the temporary
224                    # directory.
225                    if not os.path.isdir(temp_path):
226                        raise AssertionError("Child removed temp_path.")
227        """))
228
229    # Tests for change_cwd()
230
231    def test_change_cwd(self):
232        original_cwd = os.getcwd()
233
234        with os_helper.temp_dir() as temp_path:
235            with os_helper.change_cwd(temp_path) as new_cwd:
236                self.assertEqual(new_cwd, temp_path)
237                self.assertEqual(os.getcwd(), new_cwd)
238
239        self.assertEqual(os.getcwd(), original_cwd)
240
241    def test_change_cwd__non_existent_dir(self):
242        """Test passing a non-existent directory."""
243        original_cwd = os.getcwd()
244
245        def call_change_cwd(path):
246            with os_helper.change_cwd(path) as new_cwd:
247                raise Exception("should not get here")
248
249        with os_helper.temp_dir() as parent_dir:
250            non_existent_dir = os.path.join(parent_dir, 'does_not_exist')
251            self.assertRaises(FileNotFoundError, call_change_cwd,
252                              non_existent_dir)
253
254        self.assertEqual(os.getcwd(), original_cwd)
255
256    def test_change_cwd__non_existent_dir__quiet_true(self):
257        """Test passing a non-existent directory with quiet=True."""
258        original_cwd = os.getcwd()
259
260        with os_helper.temp_dir() as parent_dir:
261            bad_dir = os.path.join(parent_dir, 'does_not_exist')
262            with warnings_helper.check_warnings() as recorder:
263                with os_helper.change_cwd(bad_dir, quiet=True) as new_cwd:
264                    self.assertEqual(new_cwd, original_cwd)
265                    self.assertEqual(os.getcwd(), new_cwd)
266                warnings = [str(w.message) for w in recorder.warnings]
267
268        self.assertEqual(len(warnings), 1, warnings)
269        warn = warnings[0]
270        self.assertTrue(warn.startswith(f'tests may fail, unable to change '
271                                        f'the current working directory '
272                                        f'to {bad_dir!r}: '),
273                        warn)
274
275    # Tests for change_cwd()
276
277    def test_change_cwd__chdir_warning(self):
278        """Check the warning message when os.chdir() fails."""
279        path = TESTFN + '_does_not_exist'
280        with warnings_helper.check_warnings() as recorder:
281            with os_helper.change_cwd(path=path, quiet=True):
282                pass
283            messages = [str(w.message) for w in recorder.warnings]
284
285        self.assertEqual(len(messages), 1, messages)
286        msg = messages[0]
287        self.assertTrue(msg.startswith(f'tests may fail, unable to change '
288                                       f'the current working directory '
289                                       f'to {path!r}: '),
290                        msg)
291
292    # Tests for temp_cwd()
293
294    def test_temp_cwd(self):
295        here = os.getcwd()
296        with os_helper.temp_cwd(name=TESTFN):
297            self.assertEqual(os.path.basename(os.getcwd()), TESTFN)
298        self.assertFalse(os.path.exists(TESTFN))
299        self.assertEqual(os.getcwd(), here)
300
301
302    def test_temp_cwd__name_none(self):
303        """Test passing None to temp_cwd()."""
304        original_cwd = os.getcwd()
305        with os_helper.temp_cwd(name=None) as new_cwd:
306            self.assertNotEqual(new_cwd, original_cwd)
307            self.assertTrue(os.path.isdir(new_cwd))
308            self.assertEqual(os.getcwd(), new_cwd)
309        self.assertEqual(os.getcwd(), original_cwd)
310
311    def test_sortdict(self):
312        self.assertEqual(support.sortdict({3:3, 2:2, 1:1}), "{1: 1, 2: 2, 3: 3}")
313
314    def test_make_bad_fd(self):
315        fd = os_helper.make_bad_fd()
316        with self.assertRaises(OSError) as cm:
317            os.write(fd, b"foo")
318        self.assertEqual(cm.exception.errno, errno.EBADF)
319
320    def test_check_syntax_error(self):
321        support.check_syntax_error(self, "def class", lineno=1, offset=5)
322        with self.assertRaises(AssertionError):
323            support.check_syntax_error(self, "x=1")
324
325    def test_CleanImport(self):
326        import importlib
327        with import_helper.CleanImport("pprint"):
328            importlib.import_module("pprint")
329
330    def test_DirsOnSysPath(self):
331        with import_helper.DirsOnSysPath('foo', 'bar'):
332            self.assertIn("foo", sys.path)
333            self.assertIn("bar", sys.path)
334        self.assertNotIn("foo", sys.path)
335        self.assertNotIn("bar", sys.path)
336
337    def test_captured_stdout(self):
338        with support.captured_stdout() as stdout:
339            print("hello")
340        self.assertEqual(stdout.getvalue(), "hello\n")
341
342    def test_captured_stderr(self):
343        with support.captured_stderr() as stderr:
344            print("hello", file=sys.stderr)
345        self.assertEqual(stderr.getvalue(), "hello\n")
346
347    def test_captured_stdin(self):
348        with support.captured_stdin() as stdin:
349            stdin.write('hello\n')
350            stdin.seek(0)
351            # call test code that consumes from sys.stdin
352            captured = input()
353        self.assertEqual(captured, "hello")
354
355    def test_gc_collect(self):
356        support.gc_collect()
357
358    def test_python_is_optimized(self):
359        self.assertIsInstance(support.python_is_optimized(), bool)
360
361    def test_swap_attr(self):
362        class Obj:
363            pass
364        obj = Obj()
365        obj.x = 1
366        with support.swap_attr(obj, "x", 5) as x:
367            self.assertEqual(obj.x, 5)
368            self.assertEqual(x, 1)
369        self.assertEqual(obj.x, 1)
370        with support.swap_attr(obj, "y", 5) as y:
371            self.assertEqual(obj.y, 5)
372            self.assertIsNone(y)
373        self.assertFalse(hasattr(obj, 'y'))
374        with support.swap_attr(obj, "y", 5):
375            del obj.y
376        self.assertFalse(hasattr(obj, 'y'))
377
378    def test_swap_item(self):
379        D = {"x":1}
380        with support.swap_item(D, "x", 5) as x:
381            self.assertEqual(D["x"], 5)
382            self.assertEqual(x, 1)
383        self.assertEqual(D["x"], 1)
384        with support.swap_item(D, "y", 5) as y:
385            self.assertEqual(D["y"], 5)
386            self.assertIsNone(y)
387        self.assertNotIn("y", D)
388        with support.swap_item(D, "y", 5):
389            del D["y"]
390        self.assertNotIn("y", D)
391
392    class RefClass:
393        attribute1 = None
394        attribute2 = None
395        _hidden_attribute1 = None
396        __magic_1__ = None
397
398    class OtherClass:
399        attribute2 = None
400        attribute3 = None
401        __magic_1__ = None
402        __magic_2__ = None
403
404    def test_detect_api_mismatch(self):
405        missing_items = support.detect_api_mismatch(self.RefClass,
406                                                    self.OtherClass)
407        self.assertEqual({'attribute1'}, missing_items)
408
409        missing_items = support.detect_api_mismatch(self.OtherClass,
410                                                    self.RefClass)
411        self.assertEqual({'attribute3', '__magic_2__'}, missing_items)
412
413    def test_detect_api_mismatch__ignore(self):
414        ignore = ['attribute1', 'attribute3', '__magic_2__', 'not_in_either']
415
416        missing_items = support.detect_api_mismatch(
417                self.RefClass, self.OtherClass, ignore=ignore)
418        self.assertEqual(set(), missing_items)
419
420        missing_items = support.detect_api_mismatch(
421                self.OtherClass, self.RefClass, ignore=ignore)
422        self.assertEqual(set(), missing_items)
423
424    def test_check__all__(self):
425        extra = {'tempdir'}
426        not_exported = {'template'}
427        support.check__all__(self,
428                             tempfile,
429                             extra=extra,
430                             not_exported=not_exported)
431
432        extra = {
433            'TextTestResult',
434            'findTestCases',
435            'getTestCaseNames',
436            'installHandler',
437            'makeSuite',
438        }
439        not_exported = {'load_tests', "TestProgram", "BaseTestSuite"}
440        support.check__all__(self,
441                             unittest,
442                             ("unittest.result", "unittest.case",
443                              "unittest.suite", "unittest.loader",
444                              "unittest.main", "unittest.runner",
445                              "unittest.signals", "unittest.async_case"),
446                             extra=extra,
447                             not_exported=not_exported)
448
449        self.assertRaises(AssertionError, support.check__all__, self, unittest)
450
451    @unittest.skipUnless(hasattr(os, 'waitpid') and hasattr(os, 'WNOHANG'),
452                         'need os.waitpid() and os.WNOHANG')
453    @support.requires_fork()
454    def test_reap_children(self):
455        # Make sure that there is no other pending child process
456        support.reap_children()
457
458        # Create a child process
459        pid = os.fork()
460        if pid == 0:
461            # child process: do nothing, just exit
462            os._exit(0)
463
464        t0 = time.monotonic()
465        deadline = time.monotonic() + support.SHORT_TIMEOUT
466
467        was_altered = support.environment_altered
468        try:
469            support.environment_altered = False
470            stderr = io.StringIO()
471
472            while True:
473                if time.monotonic() > deadline:
474                    self.fail("timeout")
475
476                with support.swap_attr(support.print_warning, 'orig_stderr', stderr):
477                    support.reap_children()
478
479                # Use environment_altered to check if reap_children() found
480                # the child process
481                if support.environment_altered:
482                    break
483
484                # loop until the child process completed
485                time.sleep(0.100)
486
487            msg = "Warning -- reap_children() reaped child process %s" % pid
488            self.assertIn(msg, stderr.getvalue())
489            self.assertTrue(support.environment_altered)
490        finally:
491            support.environment_altered = was_altered
492
493        # Just in case, check again that there is no other
494        # pending child process
495        support.reap_children()
496
497    @support.requires_subprocess()
498    def check_options(self, args, func, expected=None):
499        code = f'from test.support import {func}; print(repr({func}()))'
500        cmd = [sys.executable, *args, '-c', code]
501        env = {key: value for key, value in os.environ.items()
502               if not key.startswith('PYTHON')}
503        proc = subprocess.run(cmd,
504                              stdout=subprocess.PIPE,
505                              stderr=subprocess.DEVNULL,
506                              universal_newlines=True,
507                              env=env)
508        if expected is None:
509            expected = args
510        self.assertEqual(proc.stdout.rstrip(), repr(expected))
511        self.assertEqual(proc.returncode, 0)
512
513    def test_args_from_interpreter_flags(self):
514        # Test test.support.args_from_interpreter_flags()
515        for opts in (
516            # no option
517            [],
518            # single option
519            ['-B'],
520            ['-s'],
521            ['-S'],
522            ['-E'],
523            ['-v'],
524            ['-b'],
525            ['-P'],
526            ['-q'],
527            ['-I'],
528            # same option multiple times
529            ['-bb'],
530            ['-vvv'],
531            # -W options
532            ['-Wignore'],
533            # -X options
534            ['-X', 'dev'],
535            ['-Wignore', '-X', 'dev'],
536            ['-X', 'faulthandler'],
537            ['-X', 'importtime'],
538            ['-X', 'showrefcount'],
539            ['-X', 'tracemalloc'],
540            ['-X', 'tracemalloc=3'],
541        ):
542            with self.subTest(opts=opts):
543                self.check_options(opts, 'args_from_interpreter_flags')
544
545        self.check_options(['-I', '-E', '-s', '-P'],
546                           'args_from_interpreter_flags',
547                           ['-I'])
548
549    def test_optim_args_from_interpreter_flags(self):
550        # Test test.support.optim_args_from_interpreter_flags()
551        for opts in (
552            # no option
553            [],
554            ['-O'],
555            ['-OO'],
556            ['-OOOO'],
557        ):
558            with self.subTest(opts=opts):
559                self.check_options(opts, 'optim_args_from_interpreter_flags')
560
561    def test_match_test(self):
562        class Test:
563            def __init__(self, test_id):
564                self.test_id = test_id
565
566            def id(self):
567                return self.test_id
568
569        test_access = Test('test.test_os.FileTests.test_access')
570        test_chdir = Test('test.test_os.Win32ErrorTests.test_chdir')
571
572        # Test acceptance
573        with support.swap_attr(support, '_match_test_func', None):
574            # match all
575            support.set_match_tests([])
576            self.assertTrue(support.match_test(test_access))
577            self.assertTrue(support.match_test(test_chdir))
578
579            # match all using None
580            support.set_match_tests(None, None)
581            self.assertTrue(support.match_test(test_access))
582            self.assertTrue(support.match_test(test_chdir))
583
584            # match the full test identifier
585            support.set_match_tests([test_access.id()], None)
586            self.assertTrue(support.match_test(test_access))
587            self.assertFalse(support.match_test(test_chdir))
588
589            # match the module name
590            support.set_match_tests(['test_os'], None)
591            self.assertTrue(support.match_test(test_access))
592            self.assertTrue(support.match_test(test_chdir))
593
594            # Test '*' pattern
595            support.set_match_tests(['test_*'], None)
596            self.assertTrue(support.match_test(test_access))
597            self.assertTrue(support.match_test(test_chdir))
598
599            # Test case sensitivity
600            support.set_match_tests(['filetests'], None)
601            self.assertFalse(support.match_test(test_access))
602            support.set_match_tests(['FileTests'], None)
603            self.assertTrue(support.match_test(test_access))
604
605            # Test pattern containing '.' and a '*' metacharacter
606            support.set_match_tests(['*test_os.*.test_*'], None)
607            self.assertTrue(support.match_test(test_access))
608            self.assertTrue(support.match_test(test_chdir))
609
610            # Multiple patterns
611            support.set_match_tests([test_access.id(), test_chdir.id()], None)
612            self.assertTrue(support.match_test(test_access))
613            self.assertTrue(support.match_test(test_chdir))
614
615            support.set_match_tests(['test_access', 'DONTMATCH'], None)
616            self.assertTrue(support.match_test(test_access))
617            self.assertFalse(support.match_test(test_chdir))
618
619        # Test rejection
620        with support.swap_attr(support, '_match_test_func', None):
621            # match all
622            support.set_match_tests(ignore_patterns=[])
623            self.assertTrue(support.match_test(test_access))
624            self.assertTrue(support.match_test(test_chdir))
625
626            # match all using None
627            support.set_match_tests(None, None)
628            self.assertTrue(support.match_test(test_access))
629            self.assertTrue(support.match_test(test_chdir))
630
631            # match the full test identifier
632            support.set_match_tests(None, [test_access.id()])
633            self.assertFalse(support.match_test(test_access))
634            self.assertTrue(support.match_test(test_chdir))
635
636            # match the module name
637            support.set_match_tests(None, ['test_os'])
638            self.assertFalse(support.match_test(test_access))
639            self.assertFalse(support.match_test(test_chdir))
640
641            # Test '*' pattern
642            support.set_match_tests(None, ['test_*'])
643            self.assertFalse(support.match_test(test_access))
644            self.assertFalse(support.match_test(test_chdir))
645
646            # Test case sensitivity
647            support.set_match_tests(None, ['filetests'])
648            self.assertTrue(support.match_test(test_access))
649            support.set_match_tests(None, ['FileTests'])
650            self.assertFalse(support.match_test(test_access))
651
652            # Test pattern containing '.' and a '*' metacharacter
653            support.set_match_tests(None, ['*test_os.*.test_*'])
654            self.assertFalse(support.match_test(test_access))
655            self.assertFalse(support.match_test(test_chdir))
656
657            # Multiple patterns
658            support.set_match_tests(None, [test_access.id(), test_chdir.id()])
659            self.assertFalse(support.match_test(test_access))
660            self.assertFalse(support.match_test(test_chdir))
661
662            support.set_match_tests(None, ['test_access', 'DONTMATCH'])
663            self.assertFalse(support.match_test(test_access))
664            self.assertTrue(support.match_test(test_chdir))
665
666    @unittest.skipIf(support.is_emscripten, "Unstable in Emscripten")
667    @unittest.skipIf(support.is_wasi, "Unavailable on WASI")
668    def test_fd_count(self):
669        # We cannot test the absolute value of fd_count(): on old Linux
670        # kernel or glibc versions, os.urandom() keeps a FD open on
671        # /dev/urandom device and Python has 4 FD opens instead of 3.
672        # Test is unstable on Emscripten. The platform starts and stops
673        # background threads that use pipes and epoll fds.
674        start = os_helper.fd_count()
675        fd = os.open(__file__, os.O_RDONLY)
676        try:
677            more = os_helper.fd_count()
678        finally:
679            os.close(fd)
680        self.assertEqual(more - start, 1)
681
682    def check_print_warning(self, msg, expected):
683        stderr = io.StringIO()
684        with support.swap_attr(support.print_warning, 'orig_stderr', stderr):
685            support.print_warning(msg)
686        self.assertEqual(stderr.getvalue(), expected)
687
688    def test_print_warning(self):
689        self.check_print_warning("msg",
690                                 "Warning -- msg\n")
691        self.check_print_warning("a\nb",
692                                 'Warning -- a\nWarning -- b\n')
693
694    def test_has_strftime_extensions(self):
695        if support.is_emscripten or sys.platform == "win32":
696            self.assertFalse(support.has_strftime_extensions)
697        else:
698            self.assertTrue(support.has_strftime_extensions)
699
700    # XXX -follows a list of untested API
701    # make_legacy_pyc
702    # is_resource_enabled
703    # requires
704    # fcmp
705    # umaks
706    # findfile
707    # check_warnings
708    # EnvironmentVarGuard
709    # transient_internet
710    # run_with_locale
711    # set_memlimit
712    # bigmemtest
713    # precisionbigmemtest
714    # bigaddrspacetest
715    # requires_resource
716    # run_doctest
717    # threading_cleanup
718    # reap_threads
719    # can_symlink
720    # skip_unless_symlink
721    # SuppressCrashReport
722
723
724if __name__ == '__main__':
725    unittest.main()
726