17db96d56Sopenharmony_ci"""
27db96d56Sopenharmony_ciUnit tests for refactor.py.
37db96d56Sopenharmony_ci"""
47db96d56Sopenharmony_ci
57db96d56Sopenharmony_ciimport sys
67db96d56Sopenharmony_ciimport os
77db96d56Sopenharmony_ciimport codecs
87db96d56Sopenharmony_ciimport io
97db96d56Sopenharmony_ciimport re
107db96d56Sopenharmony_ciimport tempfile
117db96d56Sopenharmony_ciimport shutil
127db96d56Sopenharmony_ciimport unittest
137db96d56Sopenharmony_ci
147db96d56Sopenharmony_cifrom lib2to3 import refactor, pygram, fixer_base
157db96d56Sopenharmony_cifrom lib2to3.pgen2 import token
167db96d56Sopenharmony_ci
177db96d56Sopenharmony_ci
187db96d56Sopenharmony_ciTEST_DATA_DIR = os.path.join(os.path.dirname(__file__), "data")
197db96d56Sopenharmony_ciFIXER_DIR = os.path.join(TEST_DATA_DIR, "fixers")
207db96d56Sopenharmony_ci
217db96d56Sopenharmony_cisys.path.append(FIXER_DIR)
227db96d56Sopenharmony_citry:
237db96d56Sopenharmony_ci    _DEFAULT_FIXERS = refactor.get_fixers_from_package("myfixes")
247db96d56Sopenharmony_cifinally:
257db96d56Sopenharmony_ci    sys.path.pop()
267db96d56Sopenharmony_ci
277db96d56Sopenharmony_ci_2TO3_FIXERS = refactor.get_fixers_from_package("lib2to3.fixes")
287db96d56Sopenharmony_ci
297db96d56Sopenharmony_ciclass TestRefactoringTool(unittest.TestCase):
307db96d56Sopenharmony_ci
317db96d56Sopenharmony_ci    def setUp(self):
327db96d56Sopenharmony_ci        sys.path.append(FIXER_DIR)
337db96d56Sopenharmony_ci
347db96d56Sopenharmony_ci    def tearDown(self):
357db96d56Sopenharmony_ci        sys.path.pop()
367db96d56Sopenharmony_ci
377db96d56Sopenharmony_ci    def check_instances(self, instances, classes):
387db96d56Sopenharmony_ci        for inst, cls in zip(instances, classes):
397db96d56Sopenharmony_ci            if not isinstance(inst, cls):
407db96d56Sopenharmony_ci                self.fail("%s are not instances of %s" % instances, classes)
417db96d56Sopenharmony_ci
427db96d56Sopenharmony_ci    def rt(self, options=None, fixers=_DEFAULT_FIXERS, explicit=None):
437db96d56Sopenharmony_ci        return refactor.RefactoringTool(fixers, options, explicit)
447db96d56Sopenharmony_ci
457db96d56Sopenharmony_ci    def test_print_function_option(self):
467db96d56Sopenharmony_ci        rt = self.rt({"print_function" : True})
477db96d56Sopenharmony_ci        self.assertNotIn("print", rt.grammar.keywords)
487db96d56Sopenharmony_ci        self.assertNotIn("print", rt.driver.grammar.keywords)
497db96d56Sopenharmony_ci
507db96d56Sopenharmony_ci    def test_exec_function_option(self):
517db96d56Sopenharmony_ci        rt = self.rt({"exec_function" : True})
527db96d56Sopenharmony_ci        self.assertNotIn("exec", rt.grammar.keywords)
537db96d56Sopenharmony_ci        self.assertNotIn("exec", rt.driver.grammar.keywords)
547db96d56Sopenharmony_ci
557db96d56Sopenharmony_ci    def test_write_unchanged_files_option(self):
567db96d56Sopenharmony_ci        rt = self.rt()
577db96d56Sopenharmony_ci        self.assertFalse(rt.write_unchanged_files)
587db96d56Sopenharmony_ci        rt = self.rt({"write_unchanged_files" : True})
597db96d56Sopenharmony_ci        self.assertTrue(rt.write_unchanged_files)
607db96d56Sopenharmony_ci
617db96d56Sopenharmony_ci    def test_fixer_loading_helpers(self):
627db96d56Sopenharmony_ci        contents = ["explicit", "first", "last", "parrot", "preorder"]
637db96d56Sopenharmony_ci        non_prefixed = refactor.get_all_fix_names("myfixes")
647db96d56Sopenharmony_ci        prefixed = refactor.get_all_fix_names("myfixes", False)
657db96d56Sopenharmony_ci        full_names = refactor.get_fixers_from_package("myfixes")
667db96d56Sopenharmony_ci        self.assertEqual(prefixed, ["fix_" + name for name in contents])
677db96d56Sopenharmony_ci        self.assertEqual(non_prefixed, contents)
687db96d56Sopenharmony_ci        self.assertEqual(full_names,
697db96d56Sopenharmony_ci                         ["myfixes.fix_" + name for name in contents])
707db96d56Sopenharmony_ci
717db96d56Sopenharmony_ci    def test_detect_future_features(self):
727db96d56Sopenharmony_ci        run = refactor._detect_future_features
737db96d56Sopenharmony_ci        fs = frozenset
747db96d56Sopenharmony_ci        empty = fs()
757db96d56Sopenharmony_ci        self.assertEqual(run(""), empty)
767db96d56Sopenharmony_ci        self.assertEqual(run("from __future__ import print_function"),
777db96d56Sopenharmony_ci                         fs(("print_function",)))
787db96d56Sopenharmony_ci        self.assertEqual(run("from __future__ import generators"),
797db96d56Sopenharmony_ci                         fs(("generators",)))
807db96d56Sopenharmony_ci        self.assertEqual(run("from __future__ import generators, feature"),
817db96d56Sopenharmony_ci                         fs(("generators", "feature")))
827db96d56Sopenharmony_ci        inp = "from __future__ import generators, print_function"
837db96d56Sopenharmony_ci        self.assertEqual(run(inp), fs(("generators", "print_function")))
847db96d56Sopenharmony_ci        inp ="from __future__ import print_function, generators"
857db96d56Sopenharmony_ci        self.assertEqual(run(inp), fs(("print_function", "generators")))
867db96d56Sopenharmony_ci        inp = "from __future__ import (print_function,)"
877db96d56Sopenharmony_ci        self.assertEqual(run(inp), fs(("print_function",)))
887db96d56Sopenharmony_ci        inp = "from __future__ import (generators, print_function)"
897db96d56Sopenharmony_ci        self.assertEqual(run(inp), fs(("generators", "print_function")))
907db96d56Sopenharmony_ci        inp = "from __future__ import (generators, nested_scopes)"
917db96d56Sopenharmony_ci        self.assertEqual(run(inp), fs(("generators", "nested_scopes")))
927db96d56Sopenharmony_ci        inp = """from __future__ import generators
937db96d56Sopenharmony_cifrom __future__ import print_function"""
947db96d56Sopenharmony_ci        self.assertEqual(run(inp), fs(("generators", "print_function")))
957db96d56Sopenharmony_ci        invalid = ("from",
967db96d56Sopenharmony_ci                   "from 4",
977db96d56Sopenharmony_ci                   "from x",
987db96d56Sopenharmony_ci                   "from x 5",
997db96d56Sopenharmony_ci                   "from x im",
1007db96d56Sopenharmony_ci                   "from x import",
1017db96d56Sopenharmony_ci                   "from x import 4",
1027db96d56Sopenharmony_ci                   )
1037db96d56Sopenharmony_ci        for inp in invalid:
1047db96d56Sopenharmony_ci            self.assertEqual(run(inp), empty)
1057db96d56Sopenharmony_ci        inp = "'docstring'\nfrom __future__ import print_function"
1067db96d56Sopenharmony_ci        self.assertEqual(run(inp), fs(("print_function",)))
1077db96d56Sopenharmony_ci        inp = "'docstring'\n'somng'\nfrom __future__ import print_function"
1087db96d56Sopenharmony_ci        self.assertEqual(run(inp), empty)
1097db96d56Sopenharmony_ci        inp = "# comment\nfrom __future__ import print_function"
1107db96d56Sopenharmony_ci        self.assertEqual(run(inp), fs(("print_function",)))
1117db96d56Sopenharmony_ci        inp = "# comment\n'doc'\nfrom __future__ import print_function"
1127db96d56Sopenharmony_ci        self.assertEqual(run(inp), fs(("print_function",)))
1137db96d56Sopenharmony_ci        inp = "class x: pass\nfrom __future__ import print_function"
1147db96d56Sopenharmony_ci        self.assertEqual(run(inp), empty)
1157db96d56Sopenharmony_ci
1167db96d56Sopenharmony_ci    def test_get_headnode_dict(self):
1177db96d56Sopenharmony_ci        class NoneFix(fixer_base.BaseFix):
1187db96d56Sopenharmony_ci            pass
1197db96d56Sopenharmony_ci
1207db96d56Sopenharmony_ci        class FileInputFix(fixer_base.BaseFix):
1217db96d56Sopenharmony_ci            PATTERN = "file_input< any * >"
1227db96d56Sopenharmony_ci
1237db96d56Sopenharmony_ci        class SimpleFix(fixer_base.BaseFix):
1247db96d56Sopenharmony_ci            PATTERN = "'name'"
1257db96d56Sopenharmony_ci
1267db96d56Sopenharmony_ci        no_head = NoneFix({}, [])
1277db96d56Sopenharmony_ci        with_head = FileInputFix({}, [])
1287db96d56Sopenharmony_ci        simple = SimpleFix({}, [])
1297db96d56Sopenharmony_ci        d = refactor._get_headnode_dict([no_head, with_head, simple])
1307db96d56Sopenharmony_ci        top_fixes = d.pop(pygram.python_symbols.file_input)
1317db96d56Sopenharmony_ci        self.assertEqual(top_fixes, [with_head, no_head])
1327db96d56Sopenharmony_ci        name_fixes = d.pop(token.NAME)
1337db96d56Sopenharmony_ci        self.assertEqual(name_fixes, [simple, no_head])
1347db96d56Sopenharmony_ci        for fixes in d.values():
1357db96d56Sopenharmony_ci            self.assertEqual(fixes, [no_head])
1367db96d56Sopenharmony_ci
1377db96d56Sopenharmony_ci    def test_fixer_loading(self):
1387db96d56Sopenharmony_ci        from myfixes.fix_first import FixFirst
1397db96d56Sopenharmony_ci        from myfixes.fix_last import FixLast
1407db96d56Sopenharmony_ci        from myfixes.fix_parrot import FixParrot
1417db96d56Sopenharmony_ci        from myfixes.fix_preorder import FixPreorder
1427db96d56Sopenharmony_ci
1437db96d56Sopenharmony_ci        rt = self.rt()
1447db96d56Sopenharmony_ci        pre, post = rt.get_fixers()
1457db96d56Sopenharmony_ci
1467db96d56Sopenharmony_ci        self.check_instances(pre, [FixPreorder])
1477db96d56Sopenharmony_ci        self.check_instances(post, [FixFirst, FixParrot, FixLast])
1487db96d56Sopenharmony_ci
1497db96d56Sopenharmony_ci    def test_naughty_fixers(self):
1507db96d56Sopenharmony_ci        self.assertRaises(ImportError, self.rt, fixers=["not_here"])
1517db96d56Sopenharmony_ci        self.assertRaises(refactor.FixerError, self.rt, fixers=["no_fixer_cls"])
1527db96d56Sopenharmony_ci        self.assertRaises(refactor.FixerError, self.rt, fixers=["bad_order"])
1537db96d56Sopenharmony_ci
1547db96d56Sopenharmony_ci    def test_refactor_string(self):
1557db96d56Sopenharmony_ci        rt = self.rt()
1567db96d56Sopenharmony_ci        input = "def parrot(): pass\n\n"
1577db96d56Sopenharmony_ci        tree = rt.refactor_string(input, "<test>")
1587db96d56Sopenharmony_ci        self.assertNotEqual(str(tree), input)
1597db96d56Sopenharmony_ci
1607db96d56Sopenharmony_ci        input = "def f(): pass\n\n"
1617db96d56Sopenharmony_ci        tree = rt.refactor_string(input, "<test>")
1627db96d56Sopenharmony_ci        self.assertEqual(str(tree), input)
1637db96d56Sopenharmony_ci
1647db96d56Sopenharmony_ci    def test_refactor_stdin(self):
1657db96d56Sopenharmony_ci
1667db96d56Sopenharmony_ci        class MyRT(refactor.RefactoringTool):
1677db96d56Sopenharmony_ci
1687db96d56Sopenharmony_ci            def print_output(self, old_text, new_text, filename, equal):
1697db96d56Sopenharmony_ci                results.extend([old_text, new_text, filename, equal])
1707db96d56Sopenharmony_ci
1717db96d56Sopenharmony_ci        results = []
1727db96d56Sopenharmony_ci        rt = MyRT(_DEFAULT_FIXERS)
1737db96d56Sopenharmony_ci        save = sys.stdin
1747db96d56Sopenharmony_ci        sys.stdin = io.StringIO("def parrot(): pass\n\n")
1757db96d56Sopenharmony_ci        try:
1767db96d56Sopenharmony_ci            rt.refactor_stdin()
1777db96d56Sopenharmony_ci        finally:
1787db96d56Sopenharmony_ci            sys.stdin = save
1797db96d56Sopenharmony_ci        expected = ["def parrot(): pass\n\n",
1807db96d56Sopenharmony_ci                    "def cheese(): pass\n\n",
1817db96d56Sopenharmony_ci                    "<stdin>", False]
1827db96d56Sopenharmony_ci        self.assertEqual(results, expected)
1837db96d56Sopenharmony_ci
1847db96d56Sopenharmony_ci    def check_file_refactoring(self, test_file, fixers=_2TO3_FIXERS,
1857db96d56Sopenharmony_ci                               options=None, mock_log_debug=None,
1867db96d56Sopenharmony_ci                               actually_write=True):
1877db96d56Sopenharmony_ci        test_file = self.init_test_file(test_file)
1887db96d56Sopenharmony_ci        old_contents = self.read_file(test_file)
1897db96d56Sopenharmony_ci        rt = self.rt(fixers=fixers, options=options)
1907db96d56Sopenharmony_ci        if mock_log_debug:
1917db96d56Sopenharmony_ci            rt.log_debug = mock_log_debug
1927db96d56Sopenharmony_ci
1937db96d56Sopenharmony_ci        rt.refactor_file(test_file)
1947db96d56Sopenharmony_ci        self.assertEqual(old_contents, self.read_file(test_file))
1957db96d56Sopenharmony_ci
1967db96d56Sopenharmony_ci        if not actually_write:
1977db96d56Sopenharmony_ci            return
1987db96d56Sopenharmony_ci        rt.refactor_file(test_file, True)
1997db96d56Sopenharmony_ci        new_contents = self.read_file(test_file)
2007db96d56Sopenharmony_ci        self.assertNotEqual(old_contents, new_contents)
2017db96d56Sopenharmony_ci        return new_contents
2027db96d56Sopenharmony_ci
2037db96d56Sopenharmony_ci    def init_test_file(self, test_file):
2047db96d56Sopenharmony_ci        tmpdir = tempfile.mkdtemp(prefix="2to3-test_refactor")
2057db96d56Sopenharmony_ci        self.addCleanup(shutil.rmtree, tmpdir)
2067db96d56Sopenharmony_ci        shutil.copy(test_file, tmpdir)
2077db96d56Sopenharmony_ci        test_file = os.path.join(tmpdir, os.path.basename(test_file))
2087db96d56Sopenharmony_ci        os.chmod(test_file, 0o644)
2097db96d56Sopenharmony_ci        return test_file
2107db96d56Sopenharmony_ci
2117db96d56Sopenharmony_ci    def read_file(self, test_file):
2127db96d56Sopenharmony_ci        with open(test_file, "rb") as fp:
2137db96d56Sopenharmony_ci            return fp.read()
2147db96d56Sopenharmony_ci
2157db96d56Sopenharmony_ci    def refactor_file(self, test_file, fixers=_2TO3_FIXERS):
2167db96d56Sopenharmony_ci        test_file = self.init_test_file(test_file)
2177db96d56Sopenharmony_ci        old_contents = self.read_file(test_file)
2187db96d56Sopenharmony_ci        rt = self.rt(fixers=fixers)
2197db96d56Sopenharmony_ci        rt.refactor_file(test_file, True)
2207db96d56Sopenharmony_ci        new_contents = self.read_file(test_file)
2217db96d56Sopenharmony_ci        return old_contents, new_contents
2227db96d56Sopenharmony_ci
2237db96d56Sopenharmony_ci    def test_refactor_file(self):
2247db96d56Sopenharmony_ci        test_file = os.path.join(FIXER_DIR, "parrot_example.py")
2257db96d56Sopenharmony_ci        self.check_file_refactoring(test_file, _DEFAULT_FIXERS)
2267db96d56Sopenharmony_ci
2277db96d56Sopenharmony_ci    def test_refactor_file_write_unchanged_file(self):
2287db96d56Sopenharmony_ci        test_file = os.path.join(FIXER_DIR, "parrot_example.py")
2297db96d56Sopenharmony_ci        debug_messages = []
2307db96d56Sopenharmony_ci        def recording_log_debug(msg, *args):
2317db96d56Sopenharmony_ci            debug_messages.append(msg % args)
2327db96d56Sopenharmony_ci        self.check_file_refactoring(test_file, fixers=(),
2337db96d56Sopenharmony_ci                                    options={"write_unchanged_files": True},
2347db96d56Sopenharmony_ci                                    mock_log_debug=recording_log_debug,
2357db96d56Sopenharmony_ci                                    actually_write=False)
2367db96d56Sopenharmony_ci        # Testing that it logged this message when write=False was passed is
2377db96d56Sopenharmony_ci        # sufficient to see that it did not bail early after "No changes".
2387db96d56Sopenharmony_ci        message_regex = r"Not writing changes to .*%s" % \
2397db96d56Sopenharmony_ci                re.escape(os.sep + os.path.basename(test_file))
2407db96d56Sopenharmony_ci        for message in debug_messages:
2417db96d56Sopenharmony_ci            if "Not writing changes" in message:
2427db96d56Sopenharmony_ci                self.assertRegex(message, message_regex)
2437db96d56Sopenharmony_ci                break
2447db96d56Sopenharmony_ci        else:
2457db96d56Sopenharmony_ci            self.fail("%r not matched in %r" % (message_regex, debug_messages))
2467db96d56Sopenharmony_ci
2477db96d56Sopenharmony_ci    def test_refactor_dir(self):
2487db96d56Sopenharmony_ci        def check(structure, expected):
2497db96d56Sopenharmony_ci            def mock_refactor_file(self, f, *args):
2507db96d56Sopenharmony_ci                got.append(f)
2517db96d56Sopenharmony_ci            save_func = refactor.RefactoringTool.refactor_file
2527db96d56Sopenharmony_ci            refactor.RefactoringTool.refactor_file = mock_refactor_file
2537db96d56Sopenharmony_ci            rt = self.rt()
2547db96d56Sopenharmony_ci            got = []
2557db96d56Sopenharmony_ci            dir = tempfile.mkdtemp(prefix="2to3-test_refactor")
2567db96d56Sopenharmony_ci            try:
2577db96d56Sopenharmony_ci                os.mkdir(os.path.join(dir, "a_dir"))
2587db96d56Sopenharmony_ci                for fn in structure:
2597db96d56Sopenharmony_ci                    open(os.path.join(dir, fn), "wb").close()
2607db96d56Sopenharmony_ci                rt.refactor_dir(dir)
2617db96d56Sopenharmony_ci            finally:
2627db96d56Sopenharmony_ci                refactor.RefactoringTool.refactor_file = save_func
2637db96d56Sopenharmony_ci                shutil.rmtree(dir)
2647db96d56Sopenharmony_ci            self.assertEqual(got,
2657db96d56Sopenharmony_ci                             [os.path.join(dir, path) for path in expected])
2667db96d56Sopenharmony_ci        check([], [])
2677db96d56Sopenharmony_ci        tree = ["nothing",
2687db96d56Sopenharmony_ci                "hi.py",
2697db96d56Sopenharmony_ci                ".dumb",
2707db96d56Sopenharmony_ci                ".after.py",
2717db96d56Sopenharmony_ci                "notpy.npy",
2727db96d56Sopenharmony_ci                "sappy"]
2737db96d56Sopenharmony_ci        expected = ["hi.py"]
2747db96d56Sopenharmony_ci        check(tree, expected)
2757db96d56Sopenharmony_ci        tree = ["hi.py",
2767db96d56Sopenharmony_ci                os.path.join("a_dir", "stuff.py")]
2777db96d56Sopenharmony_ci        check(tree, tree)
2787db96d56Sopenharmony_ci
2797db96d56Sopenharmony_ci    def test_file_encoding(self):
2807db96d56Sopenharmony_ci        fn = os.path.join(TEST_DATA_DIR, "different_encoding.py")
2817db96d56Sopenharmony_ci        self.check_file_refactoring(fn)
2827db96d56Sopenharmony_ci
2837db96d56Sopenharmony_ci    def test_false_file_encoding(self):
2847db96d56Sopenharmony_ci        fn = os.path.join(TEST_DATA_DIR, "false_encoding.py")
2857db96d56Sopenharmony_ci        data = self.check_file_refactoring(fn)
2867db96d56Sopenharmony_ci
2877db96d56Sopenharmony_ci    def test_bom(self):
2887db96d56Sopenharmony_ci        fn = os.path.join(TEST_DATA_DIR, "bom.py")
2897db96d56Sopenharmony_ci        data = self.check_file_refactoring(fn)
2907db96d56Sopenharmony_ci        self.assertTrue(data.startswith(codecs.BOM_UTF8))
2917db96d56Sopenharmony_ci
2927db96d56Sopenharmony_ci    def test_crlf_newlines(self):
2937db96d56Sopenharmony_ci        old_sep = os.linesep
2947db96d56Sopenharmony_ci        os.linesep = "\r\n"
2957db96d56Sopenharmony_ci        try:
2967db96d56Sopenharmony_ci            fn = os.path.join(TEST_DATA_DIR, "crlf.py")
2977db96d56Sopenharmony_ci            fixes = refactor.get_fixers_from_package("lib2to3.fixes")
2987db96d56Sopenharmony_ci            self.check_file_refactoring(fn, fixes)
2997db96d56Sopenharmony_ci        finally:
3007db96d56Sopenharmony_ci            os.linesep = old_sep
3017db96d56Sopenharmony_ci
3027db96d56Sopenharmony_ci    def test_crlf_unchanged(self):
3037db96d56Sopenharmony_ci        fn = os.path.join(TEST_DATA_DIR, "crlf.py")
3047db96d56Sopenharmony_ci        old, new = self.refactor_file(fn)
3057db96d56Sopenharmony_ci        self.assertIn(b"\r\n", old)
3067db96d56Sopenharmony_ci        self.assertIn(b"\r\n", new)
3077db96d56Sopenharmony_ci        self.assertNotIn(b"\r\r\n", new)
3087db96d56Sopenharmony_ci
3097db96d56Sopenharmony_ci    def test_refactor_docstring(self):
3107db96d56Sopenharmony_ci        rt = self.rt()
3117db96d56Sopenharmony_ci
3127db96d56Sopenharmony_ci        doc = """
3137db96d56Sopenharmony_ci>>> example()
3147db96d56Sopenharmony_ci42
3157db96d56Sopenharmony_ci"""
3167db96d56Sopenharmony_ci        out = rt.refactor_docstring(doc, "<test>")
3177db96d56Sopenharmony_ci        self.assertEqual(out, doc)
3187db96d56Sopenharmony_ci
3197db96d56Sopenharmony_ci        doc = """
3207db96d56Sopenharmony_ci>>> def parrot():
3217db96d56Sopenharmony_ci...      return 43
3227db96d56Sopenharmony_ci"""
3237db96d56Sopenharmony_ci        out = rt.refactor_docstring(doc, "<test>")
3247db96d56Sopenharmony_ci        self.assertNotEqual(out, doc)
3257db96d56Sopenharmony_ci
3267db96d56Sopenharmony_ci    def test_explicit(self):
3277db96d56Sopenharmony_ci        from myfixes.fix_explicit import FixExplicit
3287db96d56Sopenharmony_ci
3297db96d56Sopenharmony_ci        rt = self.rt(fixers=["myfixes.fix_explicit"])
3307db96d56Sopenharmony_ci        self.assertEqual(len(rt.post_order), 0)
3317db96d56Sopenharmony_ci
3327db96d56Sopenharmony_ci        rt = self.rt(explicit=["myfixes.fix_explicit"])
3337db96d56Sopenharmony_ci        for fix in rt.post_order:
3347db96d56Sopenharmony_ci            if isinstance(fix, FixExplicit):
3357db96d56Sopenharmony_ci                break
3367db96d56Sopenharmony_ci        else:
3377db96d56Sopenharmony_ci            self.fail("explicit fixer not loaded")
338