From 8f34c3b362cdd5dda8166d24233f9e1349188603 Mon Sep 17 00:00:00 2001 From: Joachim Metz Date: Fri, 29 Mar 2024 09:46:02 +0100 Subject: [PATCH] Changes to make year-less log helper support full dates --- plaso/containers/events.py | 95 +++++++++++++++++++++--- plaso/engine/timeliner.py | 32 ++++---- plaso/lib/dateless_helper.py | 75 ++++++++++--------- plaso/multi_process/extraction_engine.py | 3 +- plaso/parsers/text_parser.py | 4 +- plaso/serializer/json_serializer.py | 4 +- plaso/storage/serializers.py | 12 +-- plaso/storage/sqlite/sqlite_file.py | 42 ++++++++--- tests/containers/events.py | 6 +- tests/engine/timeliner.py | 4 +- tests/lib/dateless_helper.py | 46 ++++++------ 11 files changed, 210 insertions(+), 113 deletions(-) diff --git a/plaso/containers/events.py b/plaso/containers/events.py index bdf576e3a1..01902ec21e 100644 --- a/plaso/containers/events.py +++ b/plaso/containers/events.py @@ -86,20 +86,22 @@ class DateLessLogHelper(interface.AttributeContainer): """Attribute container to assist with logs without full dates. Attributes: - earliest_year (int): earliest possible year the event data stream was - created. - last_relative_year (int): last relative year determined by the date-less - log helper. - latest_year (int): latest possible year the event data stream was created. + earliest_date (list[int, int, int]): earliest possible date the event data + stream was created. The date is a tuple of year, month and day of month. + last_relative_date (list[int, int, int]): last relative date determined by + the date-less log helper. The date is a tuple of year, month and day of + month. + latest_date (List[int]): latest possible date the event data stream was + created. The date is a tuple of year, month and day of month. """ CONTAINER_TYPE = 'date_less_log_helper' SCHEMA = { '_event_data_stream_identifier': 'AttributeContainerIdentifier', - 'earliest_year': 'int', - 'last_relative_year': 'int', - 'latest_year': 'int'} + 'earliest_date': 'List[int]', + 'last_relative_date': 'List[int]', + 'latest_date': 'List[int]'} _SERIALIZABLE_PROTECTED_ATTRIBUTES = [ '_event_data_stream_identifier'] @@ -108,9 +110,21 @@ def __init__(self): """Initializes a date-less log helper attribute container.""" super(DateLessLogHelper, self).__init__() self._event_data_stream_identifier = None - self.earliest_year = None - self.last_relative_year = None - self.latest_year = None + self.earliest_date = None + self.last_relative_date = None + self.latest_date = None + + # TODO: the YearLessLogHelper attribute container is kept for backwards + # compatibility remove once storage format 20230327 is obsolete. + def CopyFromYearLessLogHelper(self, year_less_log_helper): + """Copy the values of a year-less log helper. + + Args: + year_less_log_helper (YearLessLogHelper): year-less log helper. + """ + self.earliest_date = (year_less_log_helper.earliest_year, 1, 1) + self.last_relative_date = (year_less_log_helper.last_relative_year, 0, 0) + self.latest_date = (year_less_log_helper.latest_year, 1, 1) def GetEventDataStreamIdentifier(self): """Retrieves the identifier of the associated event data stream. @@ -438,5 +452,62 @@ def SetEventIdentifier(self, event_identifier): self._event_identifier = event_identifier +# TODO: the YearLessLogHelper attribute container is kept for backwards +# compatibility remove once storage format 20230327 is obsolete. +class YearLessLogHelper(interface.AttributeContainer): + """Year-less log helper attribute container. + + Attributes: + earliest_year (int): earliest possible year the event data stream was + created. + last_relative_year (int): last relative year determined by the year-less + log helper. + latest_year (int): latest possible year the event data stream was created. + """ + + CONTAINER_TYPE = 'year_less_log_helper' + + SCHEMA = { + '_event_data_stream_identifier': 'AttributeContainerIdentifier', + 'earliest_year': 'int', + 'last_relative_year': 'int', + 'latest_year': 'int'} + + _SERIALIZABLE_PROTECTED_ATTRIBUTES = [ + '_event_data_stream_identifier'] + + def __init__(self): + """Initializes a year-less log helper attribute container.""" + super(YearLessLogHelper, self).__init__() + self._event_data_stream_identifier = None + self.earliest_year = None + self.last_relative_year = None + self.latest_year = None + + def GetEventDataStreamIdentifier(self): + """Retrieves the identifier of the associated event data stream. + + The event data stream identifier is a storage specific value that requires + special handling during serialization. + + Returns: + AttributeContainerIdentifier: event data stream or None when not set. + """ + return self._event_data_stream_identifier + + def SetEventDataStreamIdentifier(self, event_data_stream_identifier): + """Sets the identifier of the associated event data stream. + + The event data stream identifier is a storage specific value that requires + special handling during serialization. + + Args: + event_data_stream_identifier (AttributeContainerIdentifier): event data + stream identifier. + """ + self._event_data_stream_identifier = event_data_stream_identifier + + manager.AttributeContainersManager.RegisterAttributeContainers([ - DateLessLogHelper, EventData, EventDataStream, EventObject, EventTag]) + DateLessLogHelper, EventData, EventDataStream, EventObject, EventTag, + YearLessLogHelper]) diff --git a/plaso/engine/timeliner.py b/plaso/engine/timeliner.py index c329828bb4..d0ee3029f4 100644 --- a/plaso/engine/timeliner.py +++ b/plaso/engine/timeliner.py @@ -115,11 +115,11 @@ def _GetBaseYear(self, storage_writer, event_data): base_year = self._current_year else: - earliest_year = date_less_log_helpers[0].earliest_year - last_relative_year = date_less_log_helpers[0].last_relative_year - latest_year = date_less_log_helpers[0].latest_year + earliest_date = date_less_log_helpers[0].earliest_date + last_relative_date = date_less_log_helpers[0].last_relative_date + latest_date = date_less_log_helpers[0].latest_date - if earliest_year is None and latest_year is None: + if earliest_date is None and latest_date is None: message = ( f'missing earliest and latest year in date-less log helper, ' f'defaulting to current year: {self._current_year:d}') @@ -127,27 +127,27 @@ def _GetBaseYear(self, storage_writer, event_data): base_year = self._current_year - elif earliest_year + last_relative_year < self._current_year: - base_year = earliest_year + elif earliest_date[0] + last_relative_date[0] < self._current_year: + base_year = earliest_date[0] - elif latest_year < self._current_year: + elif latest_date[0] < self._current_year: message = ( - f'earliest year: {earliest_year:d} as base year would exceed ' - f'current year: {self._current_year:d} + {last_relative_year:d}, ' - f'using latest year: {latest_year:d}') + f'earliest year: {earliest_date[0]:d} as base year would exceed ' + f'current year: {self._current_year:d} + ' + f'{last_relative_date[0]:d}, using latest year: {latest_date[0]:d}') self._ProduceTimeliningWarning(storage_writer, event_data, message) - base_year = latest_year - last_relative_year + base_year = latest_date[0] - last_relative_date[0] else: message = ( - f'earliest year: {earliest_year:d} and latest: year: ' - f'{latest_year:d} as base year would exceed current year: ' - f'{self._current_year:d} + {last_relative_year:d}, using current ' - f'year') + f'earliest year: {earliest_date[0]:d} and latest: year: ' + f'{latest_date[0]:d} as base year would exceed current year: ' + f'{self._current_year:d} + {last_relative_date[0]:d}, using ' + f'current year') self._ProduceTimeliningWarning(storage_writer, event_data, message) - base_year = self._current_year - last_relative_year + base_year = self._current_year - last_relative_date[0] self._base_years[lookup_key] = base_year diff --git a/plaso/lib/dateless_helper.py b/plaso/lib/dateless_helper.py index f2c9e75463..a7d82b1af7 100644 --- a/plaso/lib/dateless_helper.py +++ b/plaso/lib/dateless_helper.py @@ -29,42 +29,43 @@ class DateLessLogFormatHelper(object): def __init__(self): """Initializes the date-less log format helper mix-in.""" super(DateLessLogFormatHelper, self).__init__() - self._base_year = None - self._maximum_year = None + self._base_date = None + self._maximum_date = None self._month = None - self._relative_year = 0 + self._relative_date = (0, 0, 0) self._year = 0 - def _GetYearsFromFileEntry(self, file_entry): - """Retrieves the years from the file entry date and time values. + def _GetDatesFromFileEntry(self, file_entry): + """Retrieves the dates from the file entry date and time values. Args: file_entry (dfvfs.FileEntry): file entry. Returns: - set[int]: years of the file entry. + set[tuple[int, int, int]]: dates, as tuple of year, month, day, of the + file entry. """ if file_entry.type_indicator == dfvfs_definitions.TYPE_INDICATOR_GZIP: # Ignore a gzip file that contains a modification timestamp of 0. if (file_entry.modification_time and file_entry.modification_time.timestamp > 0): - year, _, _ = file_entry.modification_time.GetDate() - return set([year]) + date_tuple = file_entry.modification_time.GetDate() + return set([date_tuple]) - years = set() + dates = set() for attribute_name in ('change_time', 'creation_time', 'modification_time'): date_time = getattr(file_entry, attribute_name, None) if date_time: - year, _, _ = date_time.GetDate() + date_tuple = date_time.GetDate() - if year == 1970 and file_entry.type_indicator == ( - dfvfs_definitions.TYPE_INDICATOR_GZIP): + if (date_tuple == (1970, 1, 1) and + file_entry.type_indicator == dfvfs_definitions.TYPE_INDICATOR_GZIP): continue - years.add(year) + dates.add(date_tuple) - return years + return dates def _GetMonthFromString(self, month_string): """Retrieves a numeric month value from a string. @@ -84,7 +85,7 @@ def _GetRelativeYear(self): Returns: int: relative year. """ - return self._relative_year + return self._relative_date[0] def _GetYear(self): """Retrieves the year. @@ -101,19 +102,19 @@ def _SetEstimatedYear(self, parser_mediator): parser_mediator (ParserMediator): mediates interactions between parsers and other components, such as storage and dfVFS. """ - self._base_year = None - self._maximum_year = None + self._base_date = None + self._maximum_date = None self._month = None - self._relative_year = 0 + self._relative_date = (0, 0, 0) self._year = 0 - years = set() + dates = set() file_entry = parser_mediator.GetFileEntry() if file_entry: - years = self._GetYearsFromFileEntry(file_entry) + dates = self._GetDatesFromFileEntry(file_entry) - if not years and file_entry.type_indicator in ( + if not dates and file_entry.type_indicator in ( dfvfs_definitions.TYPE_INDICATOR_COMPRESSED_STREAM, dfvfs_definitions.TYPE_INDICATOR_GZIP): @@ -121,12 +122,12 @@ def _SetEstimatedYear(self, parser_mediator): file_entry.path_spec.parent, resolver_context=parser_mediator.resolver_context) if parent_file_entry: - years = self._GetYearsFromFileEntry(parent_file_entry) + dates = self._GetDatesFromFileEntry(parent_file_entry) - if years: - self._base_year = min(years) - self._maximum_year = max(years) - self._year = self._base_year + if dates: + self._base_date = min(dates) + self._maximum_date = max(dates) + self._year = self._base_date[0] def _SetMonthAndYear(self, month, year): """Sets the month and year. @@ -142,7 +143,7 @@ def _SetMonthAndYear(self, month, year): raise ValueError('Invalid month: {0!s}'.format(month)) self._month = month - self._relative_year = 0 + self._relative_date = (0, 0, 0) self._year = year def _UpdateYear(self, month): @@ -158,17 +159,21 @@ def _UpdateYear(self, month): raise ValueError('Invalid month: {0!s}'.format(month)) if self._month: + relative_year, relative_month, relative_day_of_month = self._relative_date + # Account for log formats that allow out-of-order date and time values # (Apr->May->Apr) such as rsyslog with the RepeatedMsgReduction setting # enabled. if month + 1 < self._month: - self._relative_year += 1 + self._relative_date = ( + relative_year + 1, relative_month, relative_day_of_month) self._year += 1 # Account for out-of-order Jan->Dec->Jan with the exception of the start # of the log file. - elif self._relative_year > 0 and self._month == 1 and month == 12: - self._relative_year -= 1 + elif relative_year > 0 and self._month == 1 and month == 12: + self._relative_date = ( + relative_year - 1, relative_month, relative_day_of_month) self._year -= 1 self._month = month @@ -179,9 +184,9 @@ def GetDateLessLogHelper(self): Returns: DateLessLogHelper: date-less log helper. """ - year_less_log_helper = events.DateLessLogHelper() - year_less_log_helper.earliest_year = self._base_year - year_less_log_helper.last_relative_year = self._relative_year - year_less_log_helper.latest_year = self._maximum_year + date_less_log_helper = events.DateLessLogHelper() + date_less_log_helper.earliest_date = self._base_date + date_less_log_helper.last_relative_date = self._relative_date + date_less_log_helper.latest_date = self._maximum_date - return year_less_log_helper + return date_less_log_helper diff --git a/plaso/multi_process/extraction_engine.py b/plaso/multi_process/extraction_engine.py index 71f58b1fdc..70ae8081f1 100644 --- a/plaso/multi_process/extraction_engine.py +++ b/plaso/multi_process/extraction_engine.py @@ -757,9 +757,10 @@ def _ProcessEventSources(self, storage_writer, session_identifier): # All exceptions need to be caught here to prevent the foreman # from being killed by an uncaught exception. except Exception as exception: # pylint: disable=broad-except + path_spec = getattr(event_source, 'path_spec', None) or 'N/A' self._ProduceExtractionWarning(storage_writer, ( f'unable to process path specification with error: ' - f'{exception!s}'), event_source.path_spec) + f'{exception!s}'), path_spec) event_source = None for task in self._task_manager.GetFailedTasks(): diff --git a/plaso/parsers/text_parser.py b/plaso/parsers/text_parser.py index b096014c86..28ebbd286e 100644 --- a/plaso/parsers/text_parser.py +++ b/plaso/parsers/text_parser.py @@ -317,8 +317,8 @@ def ParseFileObject(self, parser_mediator, file_object): parser_mediator.SampleStopTiming(profiling_name) if hasattr(plugin, 'GetDateLessLogHelper'): - year_less_log_helper = plugin.GetDateLessLogHelper() - parser_mediator.AddDateLessLogHelper(year_less_log_helper) + date_less_log_helper = plugin.GetDateLessLogHelper() + parser_mediator.AddDateLessLogHelper(date_less_log_helper) break diff --git a/plaso/serializer/json_serializer.py b/plaso/serializer/json_serializer.py index 0d5f955088..b4afbce266 100644 --- a/plaso/serializer/json_serializer.py +++ b/plaso/serializer/json_serializer.py @@ -29,8 +29,10 @@ 'json': serializers.JSONDateTimeAttributeSerializer()}, 'dfvfs.PathSpec': { 'json': serializers.JSONPathSpecAttributeSerializer()}, + 'List[int]': { + 'json': serializers.JSONValueListAttributeSerializer()}, 'List[str]': { - 'json': serializers.JSONStringsListAttributeSerializer()}}) + 'json': serializers.JSONValueListAttributeSerializer()}}) class JSONAttributeContainerSerializer( diff --git a/plaso/storage/serializers.py b/plaso/storage/serializers.py index 6a8f4682f9..91363d4460 100644 --- a/plaso/storage/serializers.py +++ b/plaso/storage/serializers.py @@ -93,17 +93,17 @@ def SerializeValue(self, value): return json_dict -class JSONStringsListAttributeSerializer(acstore_interface.AttributeSerializer): - """JSON strings list attribute serializer.""" +class JSONValueListAttributeSerializer(acstore_interface.AttributeSerializer): + """JSON value list attribute serializer.""" def DeserializeValue(self, value): """Deserializes a value. Args: - value (list[str]): serialized value. + value (list[int|str]): serialized value. Returns: - list[str]: runtime value. + list[int|str]: runtime value. """ return value @@ -111,9 +111,9 @@ def SerializeValue(self, value): """Serializes a value. Args: - value (list[str]): runtime value. + value (list[int|str]): runtime value. Returns: - list[str]: serialized value. + list[int|str]: serialized value. """ return value diff --git a/plaso/storage/sqlite/sqlite_file.py b/plaso/storage/sqlite/sqlite_file.py index af89667883..b3b1f7d8cb 100644 --- a/plaso/storage/sqlite/sqlite_file.py +++ b/plaso/storage/sqlite/sqlite_file.py @@ -388,9 +388,18 @@ def GetAttributeContainerByIndex(self, container_type, index): """ schema = self._GetAttributeContainerSchema(container_type) if schema: - return super(SQLiteStorageFile, self).GetAttributeContainerByIndex( + container = super(SQLiteStorageFile, self).GetAttributeContainerByIndex( container_type, index) + # TODO: the YearLessLogHelper attribute container is kept for backwards + # compatibility remove once storage format 20230327 is obsolete. + if container_type == 'year_less_log_helper': + year_less_log_helper = container + container = events.DateLessLogHelper() + container.CopyFromYearLessLogHelper(year_less_log_helper) + + return container + container = self._GetCachedAttributeContainer(container_type, index) if container: return container @@ -443,8 +452,8 @@ def GetAttributeContainers(self, container_type, filter_expression=None): filter_expression (Optional[str]): expression to filter the resulting attribute containers by. - Returns: - generator(AttributeContainer): attribute container generator. + Yields: + AttributeContainer: attribute container. Raises: IOError: when there is an error querying the storage file. @@ -452,17 +461,26 @@ def GetAttributeContainers(self, container_type, filter_expression=None): """ schema = self._GetAttributeContainerSchema(container_type) if schema: - return super(SQLiteStorageFile, self).GetAttributeContainers( - container_type, filter_expression=filter_expression) + for container in super(SQLiteStorageFile, self).GetAttributeContainers( + container_type, filter_expression=filter_expression): + # TODO: the YearLessLogHelper attribute container is kept for backwards + # compatibility remove once storage format 20230327 is obsolete. + if container_type == 'year_less_log_helper': + year_less_log_helper = container + container = events.DateLessLogHelper() + container.CopyFromYearLessLogHelper(year_less_log_helper) - sql_filter_expression = None - if filter_expression: - expression_ast = ast.parse(filter_expression, mode='eval') - sql_filter_expression = sqlite_store.PythonAST2SQL(expression_ast.body) + yield container - return self._GetAttributeContainersWithFilter( - container_type, column_names=['_data'], - filter_expression=sql_filter_expression) + else: + sql_filter_expression = None + if filter_expression: + expression_ast = ast.parse(filter_expression, mode='eval') + sql_filter_expression = sqlite_store.PythonAST2SQL(expression_ast.body) + + yield from self._GetAttributeContainersWithFilter( + container_type, column_names=['_data'], + filter_expression=sql_filter_expression) def GetSortedEvents(self, time_range=None): """Retrieves the events in increasing chronological order. diff --git a/tests/containers/events.py b/tests/containers/events.py index 42f796d9c2..6ed0f09822 100644 --- a/tests/containers/events.py +++ b/tests/containers/events.py @@ -162,9 +162,9 @@ def testGetAttributeNames(self): expected_attribute_names = [ '_event_data_stream_identifier', - 'earliest_year', - 'last_relative_year', - 'latest_year'] + 'earliest_date', + 'last_relative_date', + 'latest_date'] attribute_names = sorted(attribute_container.GetAttributeNames()) diff --git a/tests/engine/timeliner.py b/tests/engine/timeliner.py index 184521b50a..675e1bd151 100644 --- a/tests/engine/timeliner.py +++ b/tests/engine/timeliner.py @@ -75,8 +75,8 @@ def _CreateStorageWriter(self, event_data, base_year=None): if base_year: date_less_log_helper = events.DateLessLogHelper() - date_less_log_helper.earliest_year = base_year - date_less_log_helper.last_relative_year = 0 + date_less_log_helper.earliest_date = (base_year, 1, 1) + date_less_log_helper.last_relative_date = (0, 0, 0) date_less_log_helper.SetEventDataStreamIdentifier( event_data_stream_identifier) diff --git a/tests/lib/dateless_helper.py b/tests/lib/dateless_helper.py index d1499fc8db..ca05d6c5f3 100644 --- a/tests/lib/dateless_helper.py +++ b/tests/lib/dateless_helper.py @@ -18,8 +18,8 @@ class DateLessLogFormatHelperTest(shared_test_lib.BaseTestCase): # pylint: disable=protected-access - def testGetYearsFromFileEntry(self): - """Tests the _GetYearsFromFileEntry function.""" + def testGetDatesFromFileEntry(self): + """Tests the _GetDatesFromFileEntry function.""" test_path = self._GetTestFilePath(['syslog.gz']) os_path_spec = path_spec_factory.Factory.NewPathSpec( dfvfs_definitions.TYPE_INDICATOR_OS, location=test_path) @@ -29,8 +29,8 @@ def testGetYearsFromFileEntry(self): test_helper = dateless_helper.DateLessLogFormatHelper() - years = test_helper._GetYearsFromFileEntry(file_entry) - self.assertEqual(years, set([2012])) + dates = test_helper._GetDatesFromFileEntry(file_entry) + self.assertEqual(dates, set([tuple([2012, 7, 28])])) def testGetMonthFromString(self): """Tests the _GetMonthFromString function.""" @@ -75,13 +75,13 @@ def testSetEstimatedYear(self): test_helper = dateless_helper.DateLessLogFormatHelper() - self.assertEqual(test_helper._relative_year, 0) + self.assertEqual(test_helper._relative_date, (0, 0, 0)) self.assertEqual(test_helper._year, 0) self.assertIsNone(test_helper._month) test_helper._SetEstimatedYear(parser_mediator) - self.assertEqual(test_helper._relative_year, 0) + self.assertEqual(test_helper._relative_date, (0, 0, 0)) self.assertEqual(test_helper._year, 2012) self.assertIsNone(test_helper._month) @@ -89,7 +89,7 @@ def testSetEstimatedYear(self): os_path_spec = path_spec_factory.Factory.NewPathSpec( dfvfs_definitions.TYPE_INDICATOR_OS, location=test_path) file_entry = path_spec_resolver.Resolver.OpenFileEntry(os_path_spec) - years = test_helper._GetYearsFromFileEntry(file_entry) + dates = test_helper._GetDatesFromFileEntry(file_entry) compressed_stream_path_spec = path_spec_factory.Factory.NewPathSpec( dfvfs_definitions.TYPE_INDICATOR_COMPRESSED_STREAM, @@ -102,29 +102,29 @@ def testSetEstimatedYear(self): test_helper = dateless_helper.DateLessLogFormatHelper() - self.assertEqual(test_helper._relative_year, 0) + self.assertEqual(test_helper._relative_date, (0, 0, 0)) self.assertEqual(test_helper._year, 0) self.assertIsNone(test_helper._month) test_helper._SetEstimatedYear(parser_mediator) - expected_year = min(years) + expected_date = min(dates) - self.assertEqual(test_helper._relative_year, 0) - self.assertEqual(test_helper._year, expected_year) + self.assertEqual(test_helper._relative_date, (0, 0, 0)) + self.assertEqual(test_helper._year, expected_date[0]) self.assertIsNone(test_helper._month) def testSetMonthAndYear(self): """Tests the _SetMonthAndYear function.""" test_helper = dateless_helper.DateLessLogFormatHelper() - self.assertEqual(test_helper._relative_year, 0) + self.assertEqual(test_helper._relative_date, (0, 0, 0)) self.assertEqual(test_helper._year, 0) self.assertIsNone(test_helper._month) test_helper._SetMonthAndYear(11, 2022) - self.assertEqual(test_helper._relative_year, 0) + self.assertEqual(test_helper._relative_date, (0, 0, 0)) self.assertEqual(test_helper._year, 2022) self.assertEqual(test_helper._month, 11) @@ -132,49 +132,49 @@ def testUpdateYear(self): """Tests the _UpdateYear function.""" test_helper = dateless_helper.DateLessLogFormatHelper() - self.assertEqual(test_helper._relative_year, 0) + self.assertEqual(test_helper._relative_date, (0, 0, 0)) self.assertEqual(test_helper._year, 0) self.assertIsNone(test_helper._month) test_helper._UpdateYear(1) - self.assertEqual(test_helper._relative_year, 0) + self.assertEqual(test_helper._relative_date, (0, 0, 0)) self.assertEqual(test_helper._year, 0) self.assertEqual(test_helper._month, 1) test_helper._UpdateYear(5) - self.assertEqual(test_helper._relative_year, 0) + self.assertEqual(test_helper._relative_date, (0, 0, 0)) self.assertEqual(test_helper._year, 0) self.assertEqual(test_helper._month, 5) test_helper._UpdateYear(12) - self.assertEqual(test_helper._relative_year, 0) + self.assertEqual(test_helper._relative_date, (0, 0, 0)) self.assertEqual(test_helper._year, 0) self.assertEqual(test_helper._month, 12) test_helper._UpdateYear(1) - self.assertEqual(test_helper._relative_year, 1) + self.assertEqual(test_helper._relative_date, (1, 0, 0)) self.assertEqual(test_helper._year, 1) self.assertEqual(test_helper._month, 1) test_helper._UpdateYear(12) - self.assertEqual(test_helper._relative_year, 0) + self.assertEqual(test_helper._relative_date, (0, 0, 0)) self.assertEqual(test_helper._year, 0) self.assertEqual(test_helper._month, 12) test_helper._UpdateYear(5) - self.assertEqual(test_helper._relative_year, 1) + self.assertEqual(test_helper._relative_date, (1, 0, 0)) self.assertEqual(test_helper._year, 1) self.assertEqual(test_helper._month, 5) test_helper._UpdateYear(1) - self.assertEqual(test_helper._relative_year, 2) + self.assertEqual(test_helper._relative_date, (2, 0, 0)) self.assertEqual(test_helper._year, 2) self.assertEqual(test_helper._month, 1) @@ -182,8 +182,8 @@ def testGetDateLessLogHelper(self): """Tests the GetDateLessLogHelper function.""" test_helper = dateless_helper.DateLessLogFormatHelper() - year_less_log_helper = test_helper.GetDateLessLogHelper() - self.assertIsNotNone(year_less_log_helper) + date_less_log_helper = test_helper.GetDateLessLogHelper() + self.assertIsNotNone(date_less_log_helper) if __name__ == '__main__':