| # Copyright 2017 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. |
| |
| """Unittests for cl_feature_extractor.py.""" |
| |
| # pylint: disable=g-bad-import-order |
| import unittest |
| |
| from lib import constants |
| from lib import cl_feature_extractor |
| |
| |
| FILE_MSG_KEYS = ('filename', 'lines_inserted', 'lines_deleted') |
| REVISION_MSG_KEYS = ('kind', 'commit_message', 'files') |
| GERRIT_INFO_MSG_KEYS = ('unresolved_comment_count', 'subject', 'project', |
| 'status', 'revision') |
| FAILURE_MSG_KEYS = ('exception_type', 'exception_message', 'exception_category', |
| 'stage_name', 'builder_name', 'important') |
| CL_INFO_MSG_KEYS = ('failure_msgs', 'gerrit_msg', 'is_bad_cl', 'change_number') |
| |
| |
| class FakeObject(object): |
| """Fake CL object class.""" |
| |
| def __init__(self, object_keys, object_values): |
| failure_dict = {} |
| for key, arg in zip(object_keys, object_values): |
| failure_dict[key] = arg |
| self.__dict__ = failure_dict |
| |
| def __eq__(self, other): |
| for (_, value1), (_, value2) in zip(self.__dict__.iteritems(), |
| other.__dict__.iteritems()): |
| if isinstance(value1, FakeObject): |
| return value1.__eq__(value2) |
| elif value1 != value2: |
| return False |
| return True |
| |
| |
| class FakeFileMsg(FakeObject): |
| """Fake File message class. |
| |
| Simulate functionality of File message defined in CLsForCQ.proto. |
| """ |
| |
| def __init__(self, *args): |
| FakeObject.__init__(self, FILE_MSG_KEYS, (args)) |
| |
| |
| class FakeRevisionMsg(FakeObject): |
| """Fake Revision message class. |
| |
| Simulate functionality of Revision message defined in CLsForCQ.proto. |
| """ |
| |
| def __init__(self, *args): |
| FakeObject.__init__(self, REVISION_MSG_KEYS, (args)) |
| |
| |
| class FakeGerritInfoMsg(FakeObject): |
| """Fake GerritInfo message class. |
| |
| Simulate functionality of GerritInfo message defined in CLsForCQ.proto. |
| """ |
| |
| def __init__(self, *args): |
| FakeObject.__init__(self, GERRIT_INFO_MSG_KEYS, (args)) |
| |
| |
| class FakeFailureInfoMsg(FakeObject): |
| """Fake FailureInfo message class. |
| |
| Simulate functionality of FailureInfo message and StageFailure classes defined |
| in CLsForCQ proto and cidb.failure_message_lib. |
| """ |
| |
| def __init__(self, *args): |
| FakeObject.__init__(self, FAILURE_MSG_KEYS, (args)) |
| |
| |
| class FakeCLInfoMsg(FakeObject): |
| """Fake CLInfo message class. |
| |
| Simulate functionality of CLInfo message defined in CLsForCQ.proto. |
| """ |
| |
| def __init__(self, *args): |
| FakeObject.__init__(self, CL_INFO_MSG_KEYS, (args)) |
| |
| |
| class TestCLFeatureExtractorFunctions(unittest.TestCase): |
| """Ensures that cl_feature_extractor functions are doing the right jobs.""" |
| |
| def testExtractFileFeatures(self): |
| """Test _ExtractFileFeatures() function.""" |
| input_files = [FakeFileMsg('test_file1', 111, 222), |
| FakeFileMsg('test_file2', 333, -222)] |
| result = cl_feature_extractor._ExtractFileFeatures(input_files) |
| |
| # need two possible expected results because a set is used in the function, |
| # so the order in 'files' field can not be guaranteed. |
| expected_result1 = {'files': 'test_file1 test_file2', 'lines_inserted': 444, |
| 'lines_deleted': 0} |
| expected_result2 = {'files': 'test_file2 test_file1', 'lines_inserted': 444, |
| 'lines_deleted': 0} |
| |
| self.assertIn(result, (expected_result1, expected_result2)) |
| |
| def testExtractRevisionFeatures(self): |
| """Test _ExtractRevisionFeatures() function.""" |
| revision_input = FakeRevisionMsg('some kind', |
| 'some message change id 999', |
| [FakeFileMsg('test_file', 111, 222)]) |
| |
| result = cl_feature_extractor._ExtractRevisionFeatures(revision_input) |
| |
| expected_result = {'kind': 'some kind', |
| 'commit': 'some message ', |
| 'files': 'test_file', |
| 'lines_inserted': 111, |
| 'lines_deleted': 222} |
| |
| self.assertEqual(result, expected_result) |
| |
| def testExtractGerritInfoFeatures(self): |
| """Test _ExtractGerritInfoFeatures() function.""" |
| revision_msg = FakeRevisionMsg('some kind', |
| 'some message change id 999', |
| [FakeFileMsg('test_file', 111, 222)]) |
| |
| gerrit_info_msg = FakeGerritInfoMsg(0, 'some subject', 'some project', |
| 'being tested', revision_msg) |
| |
| result = cl_feature_extractor._ExtractGerritInfoFeatures(gerrit_info_msg) |
| |
| expected_result = {'unresolved_comments': 0, |
| 'subject': 'some subject', |
| 'project': 'some project', |
| 'status': 'being tested', |
| 'kind': 'some kind', |
| 'commit': 'some message ', |
| 'files': 'test_file', |
| 'lines_inserted': 111, |
| 'lines_deleted': 222} |
| |
| self.assertEqual(result, expected_result) |
| |
| def testExtractFailureInfoFeatures(self): |
| """Test _ExtractFailureInfoFeatures() function.""" |
| failure_msgs = [FakeFailureInfoMsg('exception type 1', 'sth is wrong', |
| 'test category', 'stage x', 'builder y', |
| constants.TRUE), |
| FakeFailureInfoMsg('exception type 2', 'sth is wrong', |
| 'test category', 'stage x', 'builder y', |
| constants.FALSE)] |
| |
| result = cl_feature_extractor._ExtractFailureInfoFeatures(failure_msgs) |
| |
| # need two possible expected results because a set is used in the function, |
| # so the order in 'exception_type' field can not be guaranteed. |
| expected_result_1 = {'exception_type': 'exception type 1 exception type 2', |
| 'exception_msg': 'sth is wrong', |
| 'exception_category': 'test category', |
| 'stages': 'stage x', |
| 'builders': 'builder y', |
| 'important': constants.TRUE, |
| 'failures_count': 2} |
| expected_result_2 = {'exception_type': 'exception type 2 exception type 1', |
| 'exception_msg': 'sth is wrong', |
| 'exception_category': 'test category', |
| 'stages': 'stage x', |
| 'builders': 'builder y', |
| 'important': constants.TRUE, |
| 'failures_count': 2} |
| |
| self.assertIn(result, (expected_result_1, expected_result_2)) |
| |
| def testExtractFeaturesForSingleCL(self): |
| """Test _ExtractFeaturesForSingleCL() function.""" |
| failure_msgs = [FakeFailureInfoMsg('exception type', 'sth is wrong', |
| 'test category', 'stage x', 'builder y', |
| constants.TRUE)] |
| |
| revision_msg = FakeRevisionMsg('some kind', |
| 'some message change id 999', |
| [FakeFileMsg('test_file', 111, 222)]) |
| |
| gerrit_info_message = FakeGerritInfoMsg(0, 'some subject', 'some project', |
| 'being tested', revision_msg) |
| |
| cl_info_msg = FakeCLInfoMsg(failure_msgs, gerrit_info_message, False, 123) |
| result = cl_feature_extractor._ExtractFeaturesForSingleCL(cl_info_msg) |
| |
| expected_result = {'is_bad_cl': 0, |
| 'exception_type': 'exception type', |
| 'exception_msg': 'sth is wrong', |
| 'exception_category': 'test category', |
| 'stages': 'stage x', |
| 'builders': 'builder y', |
| 'important': constants.TRUE, |
| 'unresolved_comments': 0, |
| 'subject': 'some subject', |
| 'project': 'some project', |
| 'status': 'being tested', |
| 'kind': 'some kind', |
| 'commit': 'some message ', |
| 'files': 'test_file', |
| 'lines_inserted': 111, |
| 'lines_deleted': 222, |
| 'failures_count': 1, |
| 'change_number': 123} |
| |
| self.assertEqual(result, expected_result) |
| |
| |
| if __name__ == '__main__': |
| unittest.main() |