def setUp(self):
        self.host = MockHost()
        self.host.port_factory = MockPortFactory(self.host)
        self.port = self.host.port_factory.get()

        # Create a testing manifest containing any test files that we
        # might interact with.
        self.host.filesystem.write_text_file(
            self.port.web_tests_dir() + '/external/' + BASE_MANIFEST_NAME,
            json.dumps({
                'items': {
                    'reftest': {
                        'reftest.html': [
                            'c3f2fb6f436da59d43aeda0a7e8a018084557033',
                            [None, [['reftest-ref.html', '==']], {}],
                        ]
                    },
                    'testharness': {
                        'test.html': [
                            'd933fd981d4a33ba82fb2b000234859bdda1494e',
                            [None, {}]
                        ],
                        'crash.html': [
                            'd933fd981d4a33ba82fb2b000234859bdda1494e',
                            [None, {}]
                        ],
                        'variant.html': [
                            'b8db5972284d1ac6bbda0da81621d9bca5d04ee7',
                            ['variant.html?foo=bar/abc', {}],
                            ['variant.html?foo=baz', {}],
                        ],
                        'dir': {
                            'multiglob.https.any.js': [
                                'd6498c3e388e0c637830fa080cca78b0ab0e5305',
                                ['dir/multiglob.https.any.window.html', {}],
                                ['dir/multiglob.https.any.worker.html', {}],
                            ],
                        },
                    },
                },
            }))
        self.host.filesystem.write_text_file(
            os.path.join(WEB_TESTS_DIR, "fast", "harness", "results.html"),
            "results-viewer-body")
        self.wpt_adapter = BaseWptScriptAdapter(self.host)
        self.wpt_adapter.wpt_output = OUTPUT_JSON_FILENAME
        self.wpt_adapter.sink = MockResultSink()
class BaseWptScriptAdapterTest(unittest.TestCase):
    def setUp(self):
        self.host = MockHost()
        self.host.port_factory = MockPortFactory(self.host)
        self.port = self.host.port_factory.get()

        # Create a testing manifest containing any test files that we
        # might interact with.
        self.host.filesystem.write_text_file(
            self.port.web_tests_dir() + '/external/' + BASE_MANIFEST_NAME,
            json.dumps({
                'items': {
                    'reftest': {
                        'reftest.html': [
                            'c3f2fb6f436da59d43aeda0a7e8a018084557033',
                            [None, [['reftest-ref.html', '==']], {}],
                        ]
                    },
                    'testharness': {
                        'test.html': [
                            'd933fd981d4a33ba82fb2b000234859bdda1494e',
                            [None, {}]
                        ],
                        'crash.html': [
                            'd933fd981d4a33ba82fb2b000234859bdda1494e',
                            [None, {}]
                        ],
                        'variant.html': [
                            'b8db5972284d1ac6bbda0da81621d9bca5d04ee7',
                            ['variant.html?foo=bar/abc', {}],
                            ['variant.html?foo=baz', {}],
                        ],
                        'dir': {
                            'multiglob.https.any.js': [
                                'd6498c3e388e0c637830fa080cca78b0ab0e5305',
                                ['dir/multiglob.https.any.window.html', {}],
                                ['dir/multiglob.https.any.worker.html', {}],
                            ],
                        },
                    },
                },
            }))
        self.host.filesystem.write_text_file(
            os.path.join(WEB_TESTS_DIR, "fast", "harness", "results.html"),
            "results-viewer-body")
        self.wpt_adapter = BaseWptScriptAdapter(self.host)
        self.wpt_adapter.wpt_output = OUTPUT_JSON_FILENAME
        self.wpt_adapter.sink = MockResultSink()

    def _create_json_output(self, json_dict):
        """Writing some json output for processing."""
        self.host.filesystem.write_text_file(OUTPUT_JSON_FILENAME,
                                             json.dumps(json_dict))

    def _load_json_output(self, filename=OUTPUT_JSON_FILENAME):
        """Loads the json output after post-processing."""
        return json.loads(self.host.filesystem.read_text_file(filename))

    def test_result_sink_for_test_expected_result(self):
        json_dict = {
            'tests': {
                'fail': {
                    'test.html?variant1': {
                        'expected': 'PASS FAIL',
                        'actual': 'FAIL',
                        'artifacts': {
                            'wpt_actual_status': ['OK'],
                            'wpt_actual_metadata': ['test.html actual text'],
                        },
                    },
                },
            },
            'path_delimiter': os.path.sep,
        }
        test_abs_path = os.path.join(WEB_TESTS_DIR, 'external', 'wpt', 'fail',
                                     'test.html')
        self._create_json_output(json_dict)
        self.wpt_adapter.do_post_test_run_tasks()

        baseline_artifacts = {
            'wpt_actual_status': ['OK'],
            'actual_text':
            [(os.path.join('layout-test-results', 'external', 'wpt', 'fail',
                           'test_variant1-actual.txt'))]
        }
        self.assertEquals(self.wpt_adapter.sink.sink_requests, [{
            'test':
            os.path.join('external', 'wpt', 'fail', 'test.html?variant1'),
            'test_path':
            test_abs_path,
            'result': {
                'actual': 'FAIL',
                'expected': set(['PASS', 'FAIL']),
                'unexpected': False,
                'artifacts': baseline_artifacts
            }
        }])

    def test_result_sink_for_test_variant(self):
        json_dict = {
            'tests': {
                'fail': {
                    'test.html?variant1': {
                        'expected': 'PASS',
                        'actual': 'FAIL',
                        'artifacts': {
                            'wpt_actual_status': ['OK'],
                            'wpt_actual_metadata': ['test.html actual text'],
                        },
                    },
                },
            },
            'path_delimiter': os.path.sep,
        }
        test_abs_path = os.path.join(WEB_TESTS_DIR, 'external', 'wpt', 'fail',
                                     'test.html')
        self._create_json_output(json_dict)
        self.wpt_adapter.do_post_test_run_tasks()
        baseline_artifacts = {
            'wpt_actual_status': ['OK'],
            'actual_text':
            [(os.path.join('layout-test-results', 'external', 'wpt', 'fail',
                           'test_variant1-actual.txt'))]
        }
        self.assertEquals(self.wpt_adapter.sink.sink_requests, [{
            'test':
            os.path.join('external', 'wpt', 'fail', 'test.html?variant1'),
            'test_path':
            test_abs_path,
            'result': {
                'actual': 'FAIL',
                'expected': set(['PASS']),
                'unexpected': True,
                'artifacts': baseline_artifacts
            }
        }])

    def test_result_sink_artifacts(self):
        json_dict = {
            'tests': {
                'fail': {
                    'test.html': {
                        'expected': 'PASS',
                        'actual': 'FAIL',
                        'artifacts': {
                            'wpt_actual_status': ['OK'],
                            'wpt_actual_metadata': ['test.html actual text'],
                        },
                    },
                },
            },
            'path_delimiter': os.path.sep,
        }
        self._create_json_output(json_dict)
        self.wpt_adapter.do_post_test_run_tasks()
        test_abs_path = os.path.join(WEB_TESTS_DIR, 'external', 'wpt', 'fail',
                                     'test.html')
        baseline_artifacts = {
            'wpt_actual_status': ['OK'],
            'actual_text': [
                os.path.join('layout-test-results', 'external', 'wpt', 'fail',
                             'test-actual.txt')
            ]
        }
        self.assertEquals(
            self.wpt_adapter.sink.sink_requests,
            [{
                'test': os.path.join('external', 'wpt', 'fail', 'test.html'),
                'test_path': test_abs_path,
                'result': {
                    'actual': 'FAIL',
                    'expected': set(['PASS']),
                    'unexpected': True,
                    'artifacts': baseline_artifacts
                }
            }])

    def test_write_jsons(self):
        # Ensure that various JSONs are written to the correct location.

        json_dict = {
            'tests': {
                'pass': {
                    'test.html': {
                        'expected': 'PASS',
                        'actual': 'PASS',
                        'artifacts': {
                            'wpt_actual_status': ['OK'],
                        },
                    },
                },
                'unexpected_pass.html': {
                    'expected': 'FAIL',
                    'actual': 'PASS',
                    'artifacts': {
                        'wpt_actual_status': ['OK'],
                    },
                    'is_unexpected': True,
                },
                'fail.html': {
                    'expected': 'PASS',
                    'actual': 'FAIL',
                    'artifacts': {
                        'wpt_actual_status': ['ERROR'],
                    },
                    'is_unexpected': True,
                    'is_regression': True,
                },
            },
            'path_delimiter': os.path.sep,
        }
        self._create_json_output(json_dict)
        self.wpt_adapter.do_post_test_run_tasks()
        # The correctness of the output JSON is verified by other tests.
        written_files = self.wpt_adapter.fs.written_files
        self.assertEqual(
            written_files[OUTPUT_JSON_FILENAME],
            written_files[os.path.join(LAYOUT_TEST_RESULTS_SUBDIR,
                                       'full_results.json')])
        # Verify JSONP
        full_results_jsonp = written_files[os.path.join(
            LAYOUT_TEST_RESULTS_SUBDIR, 'full_results_jsonp.js')]
        match = re.match(r'ADD_FULL_RESULTS\((.*)\);$', full_results_jsonp)
        self.assertIsNotNone(match)
        self.assertEqual(
            match.group(1),
            written_files[OUTPUT_JSON_FILENAME].decode(encoding='utf-8'))
        failing_results_jsonp = written_files[os.path.join(
            LAYOUT_TEST_RESULTS_SUBDIR, 'failing_results.json')]
        match = re.match(r'ADD_RESULTS\((.*)\);$', failing_results_jsonp)
        self.assertIsNotNone(match)
        failing_results = json.loads(match.group(1))
        # Verify filtering of failing_results.json
        self.assertIn('fail.html', failing_results['tests']['external']['wpt'])
        # We shouldn't have unexpected passes or empty dirs after filtering.
        self.assertNotIn('unexpected_pass.html',
                         failing_results['tests']['external']['wpt'])
        self.assertNotIn('pass', failing_results['tests']['external']['wpt'])

    def test_write_text_outputs(self):
        # Ensure that text outputs are written to the correct location.

        # We only generate an actual.txt if our actual wasn't PASS, so in this
        # case we shouldn't write anything.
        json_dict = {
            'tests': {
                'test.html': {
                    'expected': 'PASS',
                    'actual': 'PASS',
                    'artifacts': {
                        'wpt_actual_status': ['OK'],
                        'wpt_actual_metadata': ['test.html actual text'],
                    },
                },
            },
            'path_delimiter': os.path.sep,
        }
        self._create_json_output(json_dict)
        self.wpt_adapter.do_post_test_run_tasks()
        written_files = self.wpt_adapter.fs.written_files
        artifact_subdir = os.path.join(LAYOUT_TEST_RESULTS_SUBDIR, "external",
                                       "wpt")
        actual_path = os.path.join(artifact_subdir, "test-actual.txt")
        diff_path = os.path.join(artifact_subdir, "test-diff.txt")
        pretty_diff_path = os.path.join(artifact_subdir,
                                        "test-pretty-diff.html")
        self.assertNotIn(actual_path, written_files)
        self.assertNotIn(diff_path, written_files)
        self.assertNotIn(pretty_diff_path, written_files)

        # Now we change the outcome to be a FAIL, which should generate an
        # actual.txt
        json_dict['tests']['test.html']['actual'] = 'FAIL'
        self._create_json_output(json_dict)
        self.wpt_adapter.do_post_test_run_tasks()
        written_files = self.wpt_adapter.fs.written_files
        actual_path = os.path.join(artifact_subdir, "test-actual.txt")
        self.assertEqual("test.html actual text",
                         written_files[actual_path].decode(encoding='utf-8'))
        # Ensure the artifact in the json was replaced with the location of
        # the newly-created file.
        updated_json = self._load_json_output()
        self.assertFalse("wpt_actual_metadata" in updated_json["tests"]
                         ["external"]["wpt"]["test.html"]["artifacts"])
        self.assertEqual([actual_path], updated_json["tests"]["external"]
                         ["wpt"]["test.html"]["artifacts"]["actual_text"])

        self.assertIn(actual_path, written_files)
        self.assertNotIn(diff_path, written_files)
        self.assertNotIn(pretty_diff_path, written_files)

    def test_write_log_artifact(self):
        # Ensure that crash log artifacts are written to the correct location.
        json_dict = {
            'tests': {
                'test.html': {
                    'expected': 'PASS',
                    'actual': 'FAIL',
                    'artifacts': {
                        'wpt_actual_status': ['ERROR'],
                        'wpt_log': ['test.html exceptions'],
                    },
                },
            },
            'path_delimiter': os.path.sep,
        }
        self._create_json_output(json_dict)
        self.wpt_adapter.do_post_test_run_tasks()
        written_files = self.wpt_adapter.fs.written_files
        artifact_subdir = os.path.join(LAYOUT_TEST_RESULTS_SUBDIR, "external",
                                       "wpt")
        stderr_path = os.path.join(artifact_subdir, "test-stderr.txt")
        self.assertEqual("test.html exceptions",
                         written_files[stderr_path].decode(encoding='utf-8'))
        # Ensure the artifact in the json was replaced with the location of
        # the newly-created file.
        updated_json = self._load_json_output()
        self.assertFalse("wpt_log" in updated_json["tests"]["external"]["wpt"]
                         ["test.html"]["artifacts"])
        self.assertEqual([stderr_path], updated_json["tests"]["external"]
                         ["wpt"]["test.html"]["artifacts"]["stderr"])
        self.assertTrue(updated_json["tests"]["external"]["wpt"]["test.html"]
                        ["has_stderr"])

    def test_write_crashlog_artifact(self):
        # Ensure that crash log artifacts are written to the correct location.
        json_dict = {
            'tests': {
                'test.html': {
                    'expected': 'PASS',
                    'actual': 'CRASH',
                    'artifacts': {
                        'wpt_actual_status': ['CRASH'],
                        'wpt_crash_log': ['test.html crashed!'],
                    },
                },
            },
            'path_delimiter': os.path.sep,
        }
        self._create_json_output(json_dict)
        self.wpt_adapter.do_post_test_run_tasks()
        written_files = self.wpt_adapter.fs.written_files
        artifact_subdir = os.path.join(LAYOUT_TEST_RESULTS_SUBDIR, "external",
                                       "wpt")
        crash_log_path = os.path.join(artifact_subdir, "test-crash-log.txt")
        self.assertEqual(
            "test.html crashed!",
            written_files[crash_log_path].decode(encoding='utf-8'))
        # Ensure the artifact in the json was replaced with the location of
        # the newly-created file.
        updated_json = self._load_json_output()
        self.assertFalse("wpt_crash_log" in updated_json["tests"]["external"]
                         ["wpt"]["test.html"]["artifacts"])
        self.assertEqual([crash_log_path], updated_json["tests"]["external"]
                         ["wpt"]["test.html"]["artifacts"]["crash_log"])

    def test_write_screenshot_artifacts(self):
        # Ensure that screenshots are written to the correct filenames and
        # their bytes are base64 decoded. The image diff should also be created.
        json_dict = {
            'tests': {
                'reftest.html': {
                    'expected': 'PASS',
                    'actual': 'PASS',
                    'artifacts': {
                        'wpt_actual_status': ['PASS'],
                        'screenshots': [
                            'reftest.html:abcd',
                            'reftest-ref.html:bcde',
                        ],
                    },
                },
            },
            'path_delimiter': os.path.sep,
        }
        self._create_json_output(json_dict)
        self.wpt_adapter.do_post_test_run_tasks()
        written_files = self.wpt_adapter.fs.written_files
        artifact_subdir = os.path.join(LAYOUT_TEST_RESULTS_SUBDIR, "external",
                                       "wpt")
        actual_image_path = os.path.join(artifact_subdir, "reftest-actual.png")
        self.assertEqual(base64.b64decode('abcd'),
                         written_files[actual_image_path])
        expected_image_path = os.path.join(artifact_subdir,
                                           "reftest-expected.png")
        self.assertEqual(base64.b64decode('bcde'),
                         written_files[expected_image_path])
        diff_image_path = os.path.join(artifact_subdir, "reftest-diff.png")
        # Make sure the diff image was written but don't check the contents,
        # assume that diff_image does the right thing.
        self.assertIsNotNone(written_files[diff_image_path])
        # Ensure the artifacts in the json were replaced with the location of
        # the newly-created files.
        updated_json = self._load_json_output()
        self.assertFalse("screenshots" in updated_json["tests"]["external"]
                         ["wpt"]["reftest.html"]["artifacts"])
        self.assertEqual([actual_image_path], updated_json["tests"]["external"]
                         ["wpt"]["reftest.html"]["artifacts"]["actual_image"])
        self.assertEqual([expected_image_path],
                         updated_json["tests"]["external"]["wpt"]
                         ["reftest.html"]["artifacts"]["expected_image"])
        self.assertEqual([diff_image_path], updated_json["tests"]["external"]
                         ["wpt"]["reftest.html"]["artifacts"]["image_diff"])

    def test_copy_expected_output(self):
        # Check that an -expected.txt file is created from a checked-in metadata
        # ini file if it exists for a test
        json_dict = {
            'tests': {
                'test.html': {
                    'expected': 'PASS',
                    'actual': 'FAIL',
                    'artifacts': {
                        'wpt_actual_status': ['OK'],
                        'wpt_actual_metadata': ['test.html actual text'],
                    },
                },
            },
            'path_delimiter': os.path.sep,
        }
        self._create_json_output(json_dict)
        # Also create a checked-in metadata file for this test
        self.host.filesystem.write_text_file(
            os.path.join(EXTERNAL_WPT_TESTS_DIR, "test.html.ini"),
            "test.html checked-in metadata")
        self.wpt_adapter.do_post_test_run_tasks()
        written_files = self.wpt_adapter.fs.written_files
        artifact_subdir = os.path.join(LAYOUT_TEST_RESULTS_SUBDIR, "external",
                                       "wpt")
        actual_path = os.path.join(artifact_subdir, "test-actual.txt")
        self.assertEqual("test.html actual text",
                         written_files[actual_path].decode(encoding='utf-8'))
        # The checked-in metadata file gets renamed from .ini to -expected.txt
        expected_path = os.path.join(artifact_subdir, "test-expected.txt")
        self.assertEqual("test.html checked-in metadata",
                         written_files[expected_path].decode(encoding='utf-8'))
        # Ensure the artifacts in the json were replaced with the locations of
        # the newly-created files.
        updated_json = self._load_json_output()
        self.assertFalse("wpt_actual_metadata" in updated_json["tests"]
                         ["external"]["wpt"]["test.html"]["artifacts"])
        self.assertEqual([actual_path], updated_json["tests"]["external"]
                         ["wpt"]["test.html"]["artifacts"]["actual_text"])
        self.assertEqual([expected_path], updated_json["tests"]["external"]
                         ["wpt"]["test.html"]["artifacts"]["expected_text"])

        # Ensure that a diff was also generated. There should be both additions
        # and deletions for this test since we have expected output. We don't
        # validate the entire diff files to avoid checking line numbers/markup.
        diff_path = os.path.join(artifact_subdir, "test-diff.txt")
        self.assertIn("-test.html checked-in metadata",
                      written_files[diff_path].decode(encoding='utf-8'))
        self.assertIn("+test.html actual text",
                      written_files[diff_path].decode(encoding='utf-8'))
        self.assertEqual([diff_path], updated_json["tests"]["external"]["wpt"]
                         ["test.html"]["artifacts"]["text_diff"])
        pretty_diff_path = os.path.join(artifact_subdir,
                                        "test-pretty-diff.html")
        self.assertIn("test.html checked-in metadata",
                      written_files[pretty_diff_path].decode(encoding='utf-8'))
        self.assertIn("test.html actual text",
                      written_files[pretty_diff_path].decode(encoding='utf-8'))
        self.assertEqual([pretty_diff_path], updated_json["tests"]["external"]
                         ["wpt"]["test.html"]["artifacts"]["pretty_text_diff"])

    def test_expected_output_for_variant(self):
        # Check that an -expected.txt file is created from a checked-in metadata
        # ini file for a variant test. Variants are a little different because
        # we have to use the manifest to map a test name to the test file, and
        # determine the associated metadata from the test file.
        # Check that an -expected.txt file is created from a checked-in metadata
        # ini file if it exists for a test
        json_dict = {
            'tests': {
                'variant.html?foo=bar/abc': {
                    'expected': 'PASS',
                    'actual': 'FAIL',
                    'artifacts': {
                        'wpt_actual_status': ['OK'],
                        'wpt_actual_metadata': ['variant bar/abc actual text'],
                    },
                },
            },
            'path_delimiter': os.path.sep,
        }
        self._create_json_output(json_dict)
        # Also create a checked-in metadata file for this test. This filename
        # matches the test *file* name, not the test name (which includes the
        # variant).
        self.host.filesystem.write_text_file(
            os.path.join(EXTERNAL_WPT_TESTS_DIR, "variant.html.ini"),
            "variant.html checked-in metadata")
        self.wpt_adapter.do_post_test_run_tasks()
        written_files = self.wpt_adapter.fs.written_files
        artifact_subdir = os.path.join(LAYOUT_TEST_RESULTS_SUBDIR, "external",
                                       "wpt")
        actual_path = os.path.join(artifact_subdir,
                                   "variant_foo=bar_abc-actual.txt")
        self.assertEqual("variant bar/abc actual text",
                         written_files[actual_path].decode(encoding='utf-8'))
        # The checked-in metadata file gets renamed from .ini to -expected.txt
        expected_path = os.path.join(artifact_subdir,
                                     "variant_foo=bar_abc-expected.txt")
        self.assertEqual("variant.html checked-in metadata",
                         written_files[expected_path].decode(encoding='utf-8'))
        # Ensure the artifacts in the json were replaced with the locations of
        # the newly-created files.
        updated_json = self._load_json_output()
        self.assertFalse(
            "wpt_actual_metadata" in updated_json["tests"]["external"]["wpt"]
            ["variant.html?foo=bar/abc"]["artifacts"])
        self.assertEqual(
            [actual_path], updated_json["tests"]["external"]["wpt"]
            ["variant.html?foo=bar/abc"]["artifacts"]["actual_text"])
        self.assertEqual(
            [expected_path], updated_json["tests"]["external"]["wpt"]
            ["variant.html?foo=bar/abc"]["artifacts"]["expected_text"])

    def test_expected_output_for_multiglob(self):
        # Check that an -expected.txt file is created from a checked-in metadata
        # ini file for a multiglobal test. Multi-globals are a little different
        # because we have to use the manifest to map a test name to the test
        # file, and determine the associated metadata from the test file.
        #
        # Also note that the "dir" is both a directory and a part of the test
        # name, so the path delimiter remains a / (ie: dir/multiglob) even on
        # Windows.
        json_dict = {
            'tests': {
                'dir/multiglob.https.any.worker.html': {
                    'expected': 'PASS',
                    'actual': 'FAIL',
                    'artifacts': {
                        'wpt_actual_status': ['OK'],
                        'wpt_actual_metadata':
                        ['dir/multiglob worker actual text'],
                    },
                },
            },
            'path_delimiter': os.path.sep,
        }
        self._create_json_output(json_dict)
        # Also create a checked-in metadata file for this test. This filename
        # matches the test *file* name, not the test name (which includes test
        # scope).
        self.host.filesystem.write_text_file(
            os.path.join(EXTERNAL_WPT_TESTS_DIR,
                         "dir/multiglob.https.any.js.ini"),
            "dir/multiglob checked-in metadata")
        self.wpt_adapter.do_post_test_run_tasks()
        written_files = self.wpt_adapter.fs.written_files
        artifact_subdir = os.path.join(LAYOUT_TEST_RESULTS_SUBDIR, "external",
                                       "wpt")
        actual_path = os.path.join(
            artifact_subdir, "dir/multiglob.https.any.worker-actual.txt")
        self.assertEqual("dir/multiglob worker actual text",
                         written_files[actual_path].decode(encoding='utf-8'))
        # The checked-in metadata file gets renamed from .ini to -expected.txt
        expected_path = os.path.join(
            artifact_subdir, "dir/multiglob.https.any.worker-expected.txt")
        self.assertEqual("dir/multiglob checked-in metadata",
                         written_files[expected_path].decode(encoding='utf-8'))
        # Ensure the artifacts in the json were replaced with the locations of
        # the newly-created files.
        updated_json = self._load_json_output()
        self.assertFalse(
            "wpt_actual_metadata" in updated_json["tests"]["external"]["wpt"]
            ["dir/multiglob.https.any.worker.html"]["artifacts"])
        self.assertEqual([actual_path], updated_json["tests"]["external"]
                         ["wpt"]["dir/multiglob.https.any.worker.html"]
                         ["artifacts"]["actual_text"])
        self.assertEqual([expected_path], updated_json["tests"]["external"]
                         ["wpt"]["dir/multiglob.https.any.worker.html"]
                         ["artifacts"]["expected_text"])
示例#3
0
class BaseWptScriptAdapterTest(unittest.TestCase):
    def setUp(self):
        self.host = MockHost()
        self.host.port_factory = MockPortFactory(self.host)
        self.port = self.host.port_factory.get()

        # Create a testing manifest containing any test files that we
        # might interact with.
        self.host.filesystem.write_text_file(
            self.port.web_tests_dir() + '/external/' + BASE_MANIFEST_NAME,
            json.dumps({
                'items': {
                    'reftest': {
                        'reftest.html': [
                            'c3f2fb6f436da59d43aeda0a7e8a018084557033',
                            [None, [['reftest-ref.html', '==']], {}],
                        ]
                    },
                    'testharness': {
                        'test.html': [
                            'd933fd981d4a33ba82fb2b000234859bdda1494e',
                            [None, {}]
                        ],
                        'crash.html': [
                            'd933fd981d4a33ba82fb2b000234859bdda1494e',
                            [None, {}]
                        ],
                        'variant.html': [
                            'b8db5972284d1ac6bbda0da81621d9bca5d04ee7',
                            ['variant.html?foo=bar/abc', {}],
                            ['variant.html?foo=baz', {}],
                        ],
                        'dir': {
                            'multiglob.https.any.js': [
                                'd6498c3e388e0c637830fa080cca78b0ab0e5305',
                                ['dir/multiglob.https.any.window.html', {}],
                                ['dir/multiglob.https.any.worker.html', {}],
                            ],
                        },
                    },
                },
            }))
        self.host.filesystem.write_text_file(
            os.path.join(WEB_TESTS_DIR, "fast", "harness", "results.html"),
            "results-viewer-body")
        self.wpt_adapter = BaseWptScriptAdapter(self.host)
        self.wpt_adapter.wpt_output = OUTPUT_JSON_FILENAME

    def _create_json_output(self, json_dict):
        """Writing some json output for processing."""
        self.host.filesystem.write_text_file(OUTPUT_JSON_FILENAME,
                                             json.dumps(json_dict))

    def _load_json_output(self):
        """Loads the json output after post-processing."""
        return json.loads(
            self.host.filesystem.read_text_file(OUTPUT_JSON_FILENAME))

    def test_write_log_artifact(self):
        # Ensure that log artifacts are written to the correct location.

        # We only generate an actual.txt if our actual wasn't PASS, so in this
        # case we shouldn't write anything.
        json_dict = {
            'tests': {
                'test.html': {
                    'expected': 'PASS',
                    'actual': 'PASS',
                    'artifacts': {
                        'wpt_actual_status': ['OK'],
                        'log': ['test.html actual text'],
                    },
                },
            },
            'path_delimiter': '/',
        }
        self._create_json_output(json_dict)
        self.wpt_adapter.do_post_test_run_tasks()
        written_files = self.wpt_adapter.fs.written_files
        actual_path = os.path.join("layout-test-results", "test-actual.txt")
        diff_path = os.path.join("layout-test-results", "test-diff.txt")
        pretty_diff_path = os.path.join("layout-test-results",
                                        "test-pretty-diff.html")
        assert actual_path not in written_files
        assert diff_path not in written_files
        assert pretty_diff_path not in written_files

        # Now we change the outcome to be a FAIL, which should generate an
        # actual.txt
        json_dict['tests']['test.html']['actual'] = 'FAIL'
        self._create_json_output(json_dict)
        self.wpt_adapter.do_post_test_run_tasks()
        written_files = self.wpt_adapter.fs.written_files
        actual_path = os.path.join("layout-test-results", "test-actual.txt")
        self.assertEqual("test.html actual text", written_files[actual_path])
        # Ensure the artifact in the json was replaced with the location of
        # the newly-created file.
        updated_json = self._load_json_output()
        self.assertFalse(
            "log" in updated_json["tests"]["test.html"]["artifacts"])
        self.assertEqual(
            [actual_path],
            updated_json["tests"]["test.html"]["artifacts"]["actual_text"])

        # Ensure that a diff was also generated. Since there's no expected
        # output, the actual text is all new. We don't validate the entire diff
        # files to avoid checking line numbers/markup.
        diff_path = os.path.join("layout-test-results", "test-diff.txt")
        self.assertIn("+test.html actual text", written_files[diff_path])
        self.assertEqual(
            [diff_path],
            updated_json["tests"]["test.html"]["artifacts"]["text_diff"])
        pretty_diff_path = os.path.join("layout-test-results",
                                        "test-pretty-diff.html")
        self.assertIn("test.html actual text", written_files[pretty_diff_path])
        self.assertEqual([
            pretty_diff_path
        ], updated_json["tests"]["test.html"]["artifacts"]["pretty_text_diff"])

    def test_write_crashlog_artifact(self):
        # Ensure that crash log artifacts are written to the correct location.
        json_dict = {
            'tests': {
                'test.html': {
                    'expected': 'PASS',
                    'actual': 'CRASH',
                    'artifacts': {
                        'wpt_actual_status': ['CRASH'],
                        'wpt_crash_log': ['test.html crashed!'],
                    },
                },
            },
            'path_delimiter': '/',
        }
        self._create_json_output(json_dict)
        self.wpt_adapter.do_post_test_run_tasks()
        written_files = self.wpt_adapter.fs.written_files
        crash_log_path = os.path.join("layout-test-results",
                                      "test-crash-log.txt")
        self.assertEqual("test.html crashed!", written_files[crash_log_path])
        # Ensure the artifact in the json was replaced with the location of
        # the newly-created file.
        updated_json = self._load_json_output()
        self.assertFalse(
            "wpt_crash_log" in updated_json["tests"]["test.html"]["artifacts"])
        self.assertEqual(
            [crash_log_path],
            updated_json["tests"]["test.html"]["artifacts"]["crash_log"])

    def test_write_screenshot_artifacts(self):
        # Ensure that screenshots are written to the correct filenames and
        # their bytes are base64 decoded.
        json_dict = {
            'tests': {
                'reftest.html': {
                    'expected': 'PASS',
                    'actual': 'PASS',
                    'artifacts': {
                        'wpt_actual_status': ['PASS'],
                        'screenshots': [
                            'reftest.html:abcd',
                            'reftest-ref.html:bcde',
                        ],
                    },
                },
            },
            'path_delimiter': '/',
        }
        self._create_json_output(json_dict)
        self.wpt_adapter.do_post_test_run_tasks()
        written_files = self.wpt_adapter.fs.written_files
        actual_image_path = os.path.join("layout-test-results",
                                         "reftest-actual.png")
        self.assertEqual(base64.b64decode('abcd'),
                         written_files[actual_image_path])
        expected_image_path = os.path.join("layout-test-results",
                                           "reftest-expected.png")
        self.assertEqual(base64.b64decode('bcde'),
                         written_files[expected_image_path])
        # Ensure the artifacts in the json were replaced with the location of
        # the newly-created files.
        updated_json = self._load_json_output()
        self.assertFalse("screenshots" in updated_json["tests"]["reftest.html"]
                         ["artifacts"])
        self.assertEqual(
            [actual_image_path],
            updated_json["tests"]["reftest.html"]["artifacts"]["actual_image"])
        self.assertEqual([expected_image_path], updated_json["tests"]
                         ["reftest.html"]["artifacts"]["expected_image"])

    def test_copy_expected_output(self):
        # Check that an -expected.txt file is created from a checked-in metadata
        # ini file if it exists for a test
        json_dict = {
            'tests': {
                'test.html': {
                    'expected': 'PASS',
                    'actual': 'FAIL',
                    'artifacts': {
                        'wpt_actual_status': ['OK'],
                        'log': ['test.html actual text'],
                    },
                },
            },
            'path_delimiter': '/',
        }
        self._create_json_output(json_dict)
        # Also create a checked-in metadata file for this test
        self.host.filesystem.write_text_file(
            os.path.join(EXTERNAL_WPT_TESTS_DIR, "test.html.ini"),
            "test.html checked-in metadata")
        self.wpt_adapter.do_post_test_run_tasks()
        written_files = self.wpt_adapter.fs.written_files
        actual_path = os.path.join("layout-test-results", "test-actual.txt")
        self.assertEqual("test.html actual text", written_files[actual_path])
        # The checked-in metadata file gets renamed from .ini to -expected.txt
        expected_path = os.path.join("layout-test-results",
                                     "test-expected.txt")
        self.assertEqual("test.html checked-in metadata",
                         written_files[expected_path])
        # Ensure the artifacts in the json were replaced with the locations of
        # the newly-created files.
        updated_json = self._load_json_output()
        self.assertFalse(
            "log" in updated_json["tests"]["test.html"]["artifacts"])
        self.assertEqual(
            [actual_path],
            updated_json["tests"]["test.html"]["artifacts"]["actual_text"])
        self.assertEqual(
            [expected_path],
            updated_json["tests"]["test.html"]["artifacts"]["expected_text"])

        # Ensure that a diff was also generated. There should be both additions
        # and deletions for this test since we have expected output. We don't
        # validate the entire diff files to avoid checking line numbers/markup.
        diff_path = os.path.join("layout-test-results", "test-diff.txt")
        self.assertIn("-test.html checked-in metadata",
                      written_files[diff_path])
        self.assertIn("+test.html actual text", written_files[diff_path])
        self.assertEqual(
            [diff_path],
            updated_json["tests"]["test.html"]["artifacts"]["text_diff"])
        pretty_diff_path = os.path.join("layout-test-results",
                                        "test-pretty-diff.html")
        self.assertIn("test.html checked-in metadata",
                      written_files[pretty_diff_path])
        self.assertIn("test.html actual text", written_files[pretty_diff_path])
        self.assertEqual([
            pretty_diff_path
        ], updated_json["tests"]["test.html"]["artifacts"]["pretty_text_diff"])

    def test_expected_output_for_variant(self):
        # Check that an -expected.txt file is created from a checked-in metadata
        # ini file for a variant test. Variants are a little different because
        # we have to use the manifest to map a test name to the test file, and
        # determine the associated metadata from the test file.
        # Check that an -expected.txt file is created from a checked-in metadata
        # ini file if it exists for a test
        json_dict = {
            'tests': {
                'variant.html?foo=bar/abc': {
                    'expected': 'PASS',
                    'actual': 'FAIL',
                    'artifacts': {
                        'wpt_actual_status': ['OK'],
                        'log': ['variant bar/abc actual text'],
                    },
                },
            },
            'path_delimiter': '/',
        }
        self._create_json_output(json_dict)
        # Also create a checked-in metadata file for this test. This filename
        # matches the test *file* name, not the test name (which includes the
        # variant).
        self.host.filesystem.write_text_file(
            os.path.join(EXTERNAL_WPT_TESTS_DIR, "variant.html.ini"),
            "variant.html checked-in metadata")
        self.wpt_adapter.do_post_test_run_tasks()
        written_files = self.wpt_adapter.fs.written_files
        actual_path = os.path.join("layout-test-results",
                                   "variant_foo=bar_abc-actual.txt")
        self.assertEqual("variant bar/abc actual text",
                         written_files[actual_path])
        # The checked-in metadata file gets renamed from .ini to -expected.txt
        expected_path = os.path.join("layout-test-results",
                                     "variant_foo=bar_abc-expected.txt")
        self.assertEqual("variant.html checked-in metadata",
                         written_files[expected_path])
        # Ensure the artifacts in the json were replaced with the locations of
        # the newly-created files.
        updated_json = self._load_json_output()
        self.assertFalse("log" in updated_json["tests"]
                         ["variant.html?foo=bar/abc"]["artifacts"])
        self.assertEqual([actual_path],
                         updated_json["tests"]["variant.html?foo=bar/abc"]
                         ["artifacts"]["actual_text"])
        self.assertEqual([expected_path],
                         updated_json["tests"]["variant.html?foo=bar/abc"]
                         ["artifacts"]["expected_text"])

    def test_expected_output_for_multiglob(self):
        # Check that an -expected.txt file is created from a checked-in metadata
        # ini file for a multiglobal test. Multi-globals are a little different
        # because we have to use the manifest to map a test name to the test
        # file, and determine the associated metadata from the test file.
        #
        # Also note that the "dir" is both a directory and a part of the test
        # name, so the path delimiter remains a / (ie: dir/multiglob) even on
        # Windows.
        json_dict = {
            'tests': {
                'dir/multiglob.https.any.worker.html': {
                    'expected': 'PASS',
                    'actual': 'FAIL',
                    'artifacts': {
                        'wpt_actual_status': ['OK'],
                        'log': ['dir/multiglob worker actual text'],
                    },
                },
            },
            'path_delimiter': '/',
        }
        self._create_json_output(json_dict)
        # Also create a checked-in metadata file for this test. This filename
        # matches the test *file* name, not the test name (which includes test
        # scope).
        self.host.filesystem.write_text_file(
            os.path.join(EXTERNAL_WPT_TESTS_DIR,
                         "dir/multiglob.https.any.js.ini"),
            "dir/multiglob checked-in metadata")
        self.wpt_adapter.do_post_test_run_tasks()
        written_files = self.wpt_adapter.fs.written_files
        actual_path = os.path.join(
            "layout-test-results", "dir/multiglob.https.any.worker-actual.txt")
        self.assertEqual("dir/multiglob worker actual text",
                         written_files[actual_path])
        # The checked-in metadata file gets renamed from .ini to -expected.txt
        expected_path = os.path.join(
            "layout-test-results",
            "dir/multiglob.https.any.worker-expected.txt")
        self.assertEqual("dir/multiglob checked-in metadata",
                         written_files[expected_path])
        # Ensure the artifacts in the json were replaced with the locations of
        # the newly-created files.
        updated_json = self._load_json_output()
        self.assertFalse("log" in updated_json["tests"]
                         ["dir/multiglob.https.any.worker.html"]["artifacts"])
        self.assertEqual(
            [actual_path],
            updated_json["tests"]["dir/multiglob.https.any.worker.html"]
            ["artifacts"]["actual_text"])
        self.assertEqual(
            [expected_path],
            updated_json["tests"]["dir/multiglob.https.any.worker.html"]
            ["artifacts"]["expected_text"])