blob: 0a4e32d7c37c61142459c0272093e8333b818fc3 [file] [log] [blame]
# 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()