| #!/usr/bin/env python3 |
| |
| # Copyright 2021 The Chromium OS Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| # Ignore indention messages, since legacy scripts use 2 spaces instead of 4. |
| # pylint: disable=bad-indentation,docstring-section-indent |
| # pylint: disable=docstring-trailing-quotes |
| |
| # Ignore line too long errors as many lines are large byte-arrays over 80 char |
| # pylint: disable=line-too-long |
| |
| # Ignore protected member access as these are unit tests explicitly accessing |
| # pylint: disable=protected-access |
| """Unit tests for generate_gtest_metadata.py script""" |
| |
| from unittest import TestCase, main, mock |
| import collections |
| import yaml |
| |
| import jsonschema |
| from chromiumos.test.api import test_case_metadata_pb2 as tc_metadata_pb |
| from chromiumos.test.api import test_harness_pb2 as th_pb |
| from chromiumos.test.api import test_case_pb2 as tc_pb |
| |
| import gtest_unittest_const as gtc |
| import generate_gtest_metadata |
| |
| |
| class Generate_Gtest_Metadata_Test(TestCase): |
| """Main test class for these unit tests""" |
| @classmethod |
| def setUpClass(cls) -> None: |
| """Setup our YAML/parse json as it only needs to be done once""" |
| Yaml_Proto = collections.namedtuple('Yaml_Proto', ['yaml', 'proto']) |
| cls._yaml_proto_data = [ |
| Yaml_Proto(gtc.VALID_YAML_SINGLE_CASE_NO_TAG, |
| gtc.VALID_YAML_SINGLE_CASE_NO_TAG_PROTOBUF), |
| Yaml_Proto(gtc.VALID_YAML_SINGLE_CASE_ONE_TAG, |
| gtc.VALID_YAML_SINGLE_CASE_ONE_TAG_PROTOBUF), |
| Yaml_Proto(gtc.VALID_YAML_SINGLE_CASE_MULTIPLE_TAGS, |
| gtc.VALID_YAML_SINGLE_CASE_MULTIPLE_TAGS_PROTOBUF), |
| Yaml_Proto(gtc.VALID_YAML_MULTIPLE_CASE_NO_TAG, |
| gtc.VALID_YAML_MULTIPLE_CASE_NO_TAG_PROTOBUF), |
| Yaml_Proto(gtc.VALID_YAML_MULTIPLE_CASE_ONE_TAG, |
| gtc.VALID_YAML_MULTIPLE_CASE_ONE_TAG_PROTOBUF), |
| Yaml_Proto(gtc.VALID_YAML_MULTIPLE_CASE_MULTIPLE_TAGS, |
| gtc.VALID_YAML_MULTIPLE_CASE_MULTIPLE_TAGS_PROTOBUF) |
| ] |
| |
| with open('gtest_schema.yaml', 'r') as f: |
| cls._yaml_schema = f.read() |
| |
| Bad_Yaml = collections.namedtuple('Bad_Yaml', ['yaml', 'msg']) |
| cls._bad_yaml_data = [ |
| Bad_Yaml(gtc.INVALID_YAML_NO_AUTHOR, |
| 'Expected a failure, no "author" field in yaml!'), |
| Bad_Yaml(gtc.INVALID_YAML_NO_NAME, |
| 'Expected a failure, no "name" field in yaml!'), |
| Bad_Yaml(gtc.INVALID_YAML_NO_HARNESS, |
| 'Expected a failure, no "harness" field in yaml!'), |
| Bad_Yaml(gtc.INVALID_YAML_NO_CATEGORY, |
| 'Expected a failure, no "category" field in yaml!'), |
| Bad_Yaml(gtc.INVALID_YAML_NO_OWNERS, |
| 'Expected a failure, no "owners" field in yaml!'), |
| Bad_Yaml(gtc.INVALID_YAML_NO_EMAIL, |
| 'Expected a failure, no "owners/email" field in yaml!'), |
| Bad_Yaml(gtc.INVALID_YAML_NO_CASES, |
| 'Expected a failure, no "cases" field in yaml!'), |
| Bad_Yaml(gtc.INVALID_YAML_NO_CASE_ID, |
| 'Expected a failure, no "cases/id" field in yaml!'), |
| Bad_Yaml(gtc.INVALID_YAML_NO_CASE_TAGS, |
| 'Expected a failure, no "cases/tags" field in yaml!'), |
| Bad_Yaml(gtc.INVALID_YAML_NO_CASE_CRITERIA, |
| 'Expected a failure, no "cases/criteria" field in yaml!'), |
| Bad_Yaml( |
| gtc.INVALID_YAML_NO_CASE_TARGET_LOC, |
| 'Expected a failure, no "cases/target_bin_location" field in yaml!' |
| ) |
| ] |
| |
| def _get_protobuf_bytes(self, input_files: mock.MagicMock, |
| schema_file: mock.MagicMock, |
| output_file: mock.MagicMock) -> bytearray: |
| generate_gtest_metadata.main(input_files=input_files, |
| output_file=output_file, |
| yaml_schema_file=schema_file) |
| return output_file.write_bytes.call_args[0][0] |
| |
| def test_single_file_protobuf_generation(self) -> None: |
| """Test that the protobuf generated bytes match expected for a given file""" |
| for yp in self._yaml_proto_data: |
| schema_file = mock.MagicMock() |
| schema_file.read_text.return_value = self._yaml_schema |
| |
| input_file = mock.MagicMock() |
| input_file.read_text.return_value = yp.yaml |
| |
| output_file = mock.MagicMock() |
| |
| bytes_from_main = self._get_protobuf_bytes([input_file], |
| schema_file, |
| output_file) |
| |
| self.assertEqual(yp.proto, bytes_from_main) |
| |
| def test_multi_file_protobuf_generation(self) -> None: |
| """Test that protobuf generated bytes match expected for a given file""" |
| schema_file = mock.MagicMock() |
| schema_file.read_text.return_value = self._yaml_schema |
| input_files = [] |
| for yp in self._yaml_proto_data: |
| input_file = mock.MagicMock() |
| input_file.read_text.return_value = yp.yaml |
| input_files.append(input_file) |
| output_file = mock.MagicMock() |
| |
| bytes_from_main = self._get_protobuf_bytes(input_files, schema_file, |
| output_file) |
| self.assertEqual(gtc.VALID_YAML_ALL_FILES_PROTOBUF, bytes_from_main) |
| |
| def test_invalid_yaml(self) -> None: |
| """Test yaml schema validation""" |
| for ym in self._bad_yaml_data: |
| schema_file = mock.MagicMock() |
| schema_file.read_text.return_value = self._yaml_schema |
| |
| input_file = mock.MagicMock() |
| input_file.read_text.return_value = ym.yaml |
| |
| output_file = mock.MagicMock() |
| |
| with self.assertRaises(jsonschema.exceptions.ValidationError, |
| msg=ym.msg): |
| self._get_protobuf_bytes([input_file], schema_file, |
| output_file) |
| |
| def _build_testcase_metadata_objects( |
| self, input_data: list) -> tc_metadata_pb.TestCaseMetadataList: |
| cases = [] |
| for f in input_data: |
| s_name = f['name'] |
| |
| tce = tc_metadata_pb.TestCaseExec(test_harness=th_pb.TestHarness( |
| gtest=th_pb.TestHarness.Gtest())) |
| tci = tc_metadata_pb.TestCaseInfo(owners=[ |
| tc_metadata_pb.Contact(email=x['email']) for x in f['owners'] |
| ]) |
| |
| for c in f['cases']: |
| tags = [tc_pb.TestCase.Tag(value=t) for t in c['tags']] |
| tc_id = tc_pb.TestCase.Id(value=f'gtest.{s_name}.{c["id"]}') |
| c_name = f'{s_name}.{c["id"]}' |
| case = tc_pb.TestCase(id=tc_id, name=c_name, tags=tags) |
| cases.append( |
| tc_metadata_pb.TestCaseMetadata(test_case=case, |
| test_case_exec=tce, |
| test_case_info=tci)) |
| |
| return tc_metadata_pb.TestCaseMetadataList(values=cases) |
| |
| def test_proto_objects_single_file(self) -> None: |
| """Test proto object building prior to serialization""" |
| input_yaml = yaml.safe_load(gtc.VALID_YAML_MULTIPLE_CASE_MULTIPLE_TAGS) |
| |
| actual_test_case_list = generate_gtest_metadata._test_case_list_factory( |
| input_data=input_yaml) |
| actual_testcase_metadata_list = tc_metadata_pb.TestCaseMetadataList( |
| values=actual_test_case_list) |
| |
| # Make sure we got our 3 cases |
| self.assertEqual(len(input_yaml['cases']), |
| len(actual_testcase_metadata_list.values)) |
| |
| # Check the object values to make sure they match |
| for i, tc in enumerate(actual_testcase_metadata_list.values): |
| # Check test harness |
| self.assertIsInstance(tc.test_case_exec.test_harness.gtest, |
| th_pb.TestHarness.Gtest) |
| |
| # Check owner/email |
| actual_owner_emails = [c.email for c in tc.test_case_info.owners] |
| expected_owner_emails = [e['email'] for e in input_yaml['owners']] |
| self.assertListEqual(expected_owner_emails, actual_owner_emails) |
| |
| # Check case tags, id, name |
| actual_tags = [t.value for t in tc.test_case.tags] |
| expected_tags = [t for t in input_yaml['cases'][i]['tags']] |
| self.assertListEqual(expected_tags, actual_tags) |
| |
| actual_id = tc.test_case.id.value |
| expected_id = f'gtest.{input_yaml["name"]}.{input_yaml["cases"][i]["id"]}' |
| self.assertEqual(expected_id, actual_id) |
| |
| actual_name = tc.test_case.name |
| expected_name = f'{input_yaml["name"]}.{input_yaml["cases"][i]["id"]}' |
| self.assertEqual(expected_name, actual_name) |
| |
| # Check it all for good measure |
| expected_testcase_metadata_list = self._build_testcase_metadata_objects( |
| [input_yaml]) |
| self.assertEqual(expected_testcase_metadata_list, |
| actual_testcase_metadata_list) |
| |
| def test_proto_objects_multiple_file(self) -> None: |
| """Test multiple proto object building prior to serialization""" |
| input_yaml = [yaml.safe_load(x.yaml) for x in self._yaml_proto_data] |
| |
| actual_test_case_list = [] |
| for y in input_yaml: |
| actual_test_case_list.extend( |
| generate_gtest_metadata._test_case_list_factory(input_data=y)) |
| |
| actual_testcase_metadata_list = tc_metadata_pb.TestCaseMetadataList( |
| values=actual_test_case_list) |
| |
| # Check it all for good measure |
| expected_testcase_metadata_list = self._build_testcase_metadata_objects( |
| input_yaml) |
| self.assertEqual(expected_testcase_metadata_list, |
| actual_testcase_metadata_list) |
| |
| |
| if __name__ == '__main__': |
| main(verbosity=2) |