blob: c1005a57078a7c5f93aec55f3f8b7d277b7f8dc2 [file] [log] [blame]
#!/usr/bin/env vpython3
# Copyright 2020 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
from __future__ import print_function
import copy
import sys
import typing
from typing import Dict, List
import unittest
if sys.version_info[0] == 2:
import mock
else:
import unittest.mock as mock
from unexpected_passes_common import constants
from unexpected_passes_common import data_types
from unexpected_passes_common import unittest_utils as uu
GENERIC_EXPECTATION = data_types.Expectation('test', ['tag1', 'tag2'], ['Pass'])
GENERIC_RESULT = data_types.Result('test', ['tag1', 'tag2'], 'Pass',
'pixel_tests', 'build_id')
class CustomImplementationUnittest(unittest.TestCase):
def testCustomExpectation(self) -> None:
class CustomExpectation(data_types.BaseExpectation):
pass
data_types.SetExpectationImplementation(CustomExpectation)
expectation = data_types.Expectation('test', ['tag1', 'tag2'], 'Pass')
self.assertIsInstance(expectation, CustomExpectation)
def testCustomResult(self) -> None:
class CustomResult(data_types.BaseResult):
pass
data_types.SetResultImplementation(CustomResult)
result = data_types.Result('test', ['tag1', 'tag2'], 'Pass', 'pixel_tests',
'build_id')
self.assertIsInstance(result, CustomResult)
def testCustomBuildStats(self) -> None:
class CustomBuildStats(data_types.BaseBuildStats):
pass
data_types.SetBuildStatsImplementation(CustomBuildStats)
build_stats = data_types.BuildStats()
self.assertIsInstance(build_stats, CustomBuildStats)
def testCustomTestExpectationMap(self) -> None:
class CustomTestExpectationMap(data_types.BaseTestExpectationMap):
pass
data_types.SetTestExpectationMapImplementation(CustomTestExpectationMap)
expectation_map = data_types.TestExpectationMap()
self.assertIsInstance(expectation_map, CustomTestExpectationMap)
class ExpectationUnittest(unittest.TestCase):
def testEquality(self) -> None:
e = GENERIC_EXPECTATION
other = data_types.Expectation('test', ['tag1', 'tag2'], 'Pass')
self.assertEqual(e, other)
other = data_types.Expectation('test2', ['tag1', 'tag2'], 'Pass')
self.assertNotEqual(e, other)
other = data_types.Expectation('test', ['tag1'], 'Pass')
self.assertNotEqual(e, other)
other = data_types.Expectation('test', ['tag1', 'tag2'], 'Failure')
self.assertNotEqual(e, other)
other = data_types.Expectation('test', ['tag1', 'tag2'], 'Pass', 'bug')
self.assertNotEqual(e, other)
other = data_types.Result('test', ['tag1', 'tag2'], 'Pass', 'pixel_tests',
'build_id')
self.assertNotEqual(e, other)
def testHashability(self) -> None:
e = GENERIC_EXPECTATION
_ = {e}
def testAppliesToResultNonResult(self) -> None:
e = GENERIC_EXPECTATION
with self.assertRaises(AssertionError):
e.AppliesToResult(typing.cast(data_types.Result, e))
def testAppliesToResultApplies(self) -> None:
r = data_types.Result('test', ['tag1', 'tag2'], 'Pass', 'pixel_tests',
'build_id')
# Exact name match, exact tag match.
e = GENERIC_EXPECTATION
self.assertTrue(e.AppliesToResult(r))
# Glob name match, exact tag match.
e = data_types.Expectation('te*', ['tag1', 'tag2'], 'Pass')
self.assertTrue(e.AppliesToResult(r))
# Exact name match, tag subset match.
e = data_types.Expectation('test', ['tag1'], 'Pass')
self.assertTrue(e.AppliesToResult(r))
# Expected result subset match.
r = data_types.Result('test', ['tag1', 'tag2'], 'Pass', 'pixel_tests',
'build_id')
e = GENERIC_EXPECTATION
self.assertTrue(e.AppliesToResult(r))
e = data_types.Expectation('test', ['tag1', 'tag2'], ['RetryOnFailure'])
self.assertTrue(e.AppliesToResult(r))
def testAppliesToResultDoesNotApply(self) -> None:
r = data_types.Result('test', ['tag1', 'tag2'], 'Pass', 'pixel_tests',
'build_id')
# Exact name mismatch.
e = data_types.Expectation('te', ['tag1', 'tag2'], 'Pass')
self.assertFalse(e.AppliesToResult(r))
# Glob name mismatch.
e = data_types.Expectation('ta*', ['tag1', 'tag2'], 'Pass')
self.assertFalse(e.AppliesToResult(r))
# Tags subset mismatch.
e = data_types.Expectation('test', ['tag3'], 'Pass')
self.assertFalse(e.AppliesToResult(r))
def testAppliesToResultResultHasAsterisk(self) -> None:
r = data_types.Result('foo.html?include=*', ['tag1', 'tag2'], 'Pass',
'pixel_tests', 'build_id')
e = data_types.Expectation('*', ['tag1', 'tag2'], 'Pass')
self.assertTrue(e.AppliesToResult(r))
e = data_types.Expectation('foo.html?include=*', ['tag1', 'tag2'], 'Pass')
self.assertTrue(e.AppliesToResult(r))
e = data_types.Expectation('foo.html?include=bar*', ['tag1', 'tag2'],
'Pass')
self.assertFalse(e.AppliesToResult(r))
class ResultUnittest(unittest.TestCase):
def testEquality(self) -> None:
r = GENERIC_RESULT
other = data_types.Result('test', ['tag1', 'tag2'], 'Pass', 'pixel_tests',
'build_id')
self.assertEqual(r, other)
other = data_types.Result('test2', ['tag1', 'tag2'], 'Pass', 'pixel_tests',
'build_id')
self.assertNotEqual(r, other)
other = data_types.Result('test', ['tag1'], 'Pass', 'pixel_tests',
'build_id')
self.assertNotEqual(r, other)
other = data_types.Result('test', ['tag1', 'tag2'], 'Failure',
'pixel_tests', 'build_id')
self.assertNotEqual(r, other)
other = data_types.Result('test', ['tag1', 'tag2'], 'Pass', 'webgl_tests',
'build_id')
self.assertNotEqual(r, other)
other = data_types.Result('test', ['tag1', 'tag2'], 'Pass', 'pixel_tests',
'other_build_id')
self.assertNotEqual(r, other)
other = data_types.Expectation('test', ['tag1', 'tag2'], 'Pass')
self.assertNotEqual(r, other)
def testHashability(self) -> None:
r = GENERIC_RESULT
_ = {r}
class BuildStatsUnittest(unittest.TestCase):
def CreateGenericBuildStats(self) -> data_types.BuildStats:
stats = data_types.BuildStats()
stats.AddPassedBuild()
stats.AddFailedBuild('')
return stats
def testEquality(self) -> None:
s = self.CreateGenericBuildStats()
other = self.CreateGenericBuildStats()
self.assertEqual(s, other)
other.passed_builds = 0
self.assertNotEqual(s, other)
other = self.CreateGenericBuildStats()
other.total_builds = 0
self.assertNotEqual(s, other)
other = self.CreateGenericBuildStats()
other.failure_links = frozenset()
self.assertNotEqual(s, other)
def testAddFailedBuild(self) -> None:
s = data_types.BuildStats()
s.AddFailedBuild('build_id')
self.assertEqual(s.total_builds, 1)
self.assertEqual(s.failed_builds, 1)
self.assertEqual(s.failure_links,
frozenset(['http://ci.chromium.org/b/build_id']))
def testGetStatsAsString(self) -> None:
s = self.CreateGenericBuildStats()
expected_str = '(1/2 passed)'
self.assertEqual(s.GetStatsAsString(), expected_str)
class MapTypeUnittest(unittest.TestCase):
def testMapConstructor(self) -> None:
"""Tests that constructors enforce type."""
# We only use one map type since they all share the same implementation for
# this logic.
with self.assertRaises(AssertionError):
data_types.StepBuildStatsMap({1: 2})
m = data_types.StepBuildStatsMap({'step': data_types.BuildStats()})
self.assertEqual(m, {'step': data_types.BuildStats()})
def testMapUpdate(self) -> None:
"""Tests that update() enforces type."""
# We only use one map type since they all share the same implementation for
# this logic.
m = data_types.StepBuildStatsMap({'step': data_types.BuildStats()})
with self.assertRaises(AssertionError):
m.update({1: 2})
with self.assertRaises(AssertionError):
m.update(step2=1)
m.update(step=data_types.BuildStats())
self.assertEqual(m, {'step': data_types.BuildStats()})
def testMapSetdefault(self) -> None:
"""Tests that setdefault() enforces type."""
# We only use one map type since they all share the same implementation for
# this logic.
m = data_types.StepBuildStatsMap()
with self.assertRaises(AssertionError):
m.setdefault(1, data_types.BuildStats())
with self.assertRaises(AssertionError):
m.setdefault('1', 2)
m.setdefault('1', data_types.BuildStats())
self.assertEqual(m, {'1': data_types.BuildStats()})
def _StringToMapHelper(self, map_type: type, value_type: type) -> None:
"""Helper function for testing string type -> map type enforcement."""
m = map_type()
with self.assertRaises(AssertionError):
m[1] = value_type()
with self.assertRaises(AssertionError):
m['1'] = 2
m['1'] = value_type()
self.assertEqual(m, {'1': value_type()})
m[u'2'] = value_type()
self.assertEqual(m, {'1': value_type(), u'2': value_type()})
def testStepBuildStatsMap(self) -> None:
"""Tests StepBuildStats' type enforcement."""
self._StringToMapHelper(data_types.StepBuildStatsMap, data_types.BuildStats)
def testBuilderStepMap(self) -> None:
"""Tests BuilderStepMap's type enforcement."""
self._StringToMapHelper(data_types.BuilderStepMap,
data_types.StepBuildStatsMap)
def testExpectationBuilderMap(self) -> None:
"""Tests ExpectationBuilderMap's type enforcement."""
m = data_types.ExpectationBuilderMap()
e = data_types.Expectation('test', ['tag'], 'Failure')
with self.assertRaises(AssertionError):
m[typing.cast(data_types.BaseExpectation,
1)] = data_types.BuilderStepMap()
with self.assertRaises(AssertionError):
m[e] = typing.cast(data_types.BuilderStepMap, 2)
m[e] = data_types.BuilderStepMap()
self.assertEqual(m, {e: data_types.BuilderStepMap()})
def testTestExpectationMap(self) -> None:
"""Tests TestExpectationMap's type enforcement."""
self._StringToMapHelper(data_types.TestExpectationMap,
data_types.ExpectationBuilderMap)
def _GetSampleBuildStats(self) -> List[data_types.BuildStats]:
build_stats = []
for i in range(8):
bs = data_types.BuildStats()
for _ in range(i):
bs.AddPassedBuild()
build_stats.append(bs)
return build_stats
def _GetSampleTestExpectationMap(self) -> data_types.TestExpectationMap:
build_stats = self._GetSampleBuildStats()
return data_types.TestExpectationMap({
'foo':
data_types.ExpectationBuilderMap({
data_types.Expectation('foo', ['tag'], ['Failure']):
data_types.BuilderStepMap({
'builder1':
data_types.StepBuildStatsMap({
'step1': build_stats[0],
'step2': build_stats[1],
}),
'builder2':
data_types.StepBuildStatsMap({
'step3': build_stats[2],
'step4': build_stats[3],
}),
}),
data_types.Expectation('foo', ['tag2'], ['Failure']):
data_types.BuilderStepMap({
'builder3':
data_types.StepBuildStatsMap({
'step5': build_stats[4],
'step6': build_stats[5],
}),
'builder4':
data_types.StepBuildStatsMap({
'step7': build_stats[6],
'step8': build_stats[7],
}),
}),
}),
})
def testIterBuilderStepMaps(self) -> None:
"""Tests that iterating to BuilderStepMap works as expected."""
test_expectation_map = self._GetSampleTestExpectationMap()
expected_values = []
for test_name, expectation_map in test_expectation_map.items():
for expectation, builder_map in expectation_map.items():
expected_values.append((test_name, expectation, builder_map))
returned_values = []
for (test_name, expectation,
builder_map) in test_expectation_map.IterBuilderStepMaps():
returned_values.append((test_name, expectation, builder_map))
self.assertEqual(len(returned_values), len(expected_values))
for rv in returned_values:
self.assertIn(rv, expected_values)
self.assertIsInstance(rv[-1], data_types.BuilderStepMap)
def testIterToNoSuchValue(self) -> None:
"""Tests that iterating to a type that has no data works as expected."""
test_expectation_map = data_types.TestExpectationMap()
# This should neither break nor return any data.
for _, __, ___ in test_expectation_map.IterBuilderStepMaps():
self.fail()
# TODO(bsheedy): Test is temporarily disabled because no AttributeError is
# raised when an object of the correct type is used for test_expectation_map.
#
# def testIterToNoSuchType(self):
# """Tests that an error is raised if no such type is found when
# iterating."""
# test_expectation_map = self._GetSampleTestExpectationMap()
# with self.assertRaises(AttributeError):
# test_expectation_map.IterToValueType(int)
class TypedMapMergeUnittest(unittest.TestCase):
def testEmptyBaseMap(self) -> None:
"""Tests that a merge with an empty base map copies the merge map."""
base_map = data_types.TestExpectationMap()
merge_map = data_types.TestExpectationMap({
'foo':
data_types.ExpectationBuilderMap({
data_types.Expectation('foo', ['win'], 'Failure'):
data_types.BuilderStepMap({
'builder':
data_types.StepBuildStatsMap({
'step': data_types.BuildStats(),
}),
}),
}),
})
original_merge_map = copy.deepcopy(merge_map)
base_map.Merge(merge_map)
self.assertEqual(base_map, merge_map)
self.assertEqual(merge_map, original_merge_map)
def testEmptyMergeMap(self) -> None:
"""Tests that a merge with an empty merge map is a no-op."""
base_map = data_types.TestExpectationMap({
'foo':
data_types.ExpectationBuilderMap({
data_types.Expectation('foo', ['win'], 'Failure'):
data_types.BuilderStepMap({
'builder':
data_types.StepBuildStatsMap({
'step': data_types.BuildStats(),
}),
}),
}),
})
merge_map = data_types.TestExpectationMap()
original_base_map = copy.deepcopy(base_map)
base_map.Merge(merge_map)
self.assertEqual(base_map, original_base_map)
self.assertEqual(merge_map, {})
def testMissingKeys(self) -> None:
"""Tests that missing keys are properly copied to the base map."""
base_map = data_types.TestExpectationMap({
'foo':
data_types.ExpectationBuilderMap({
data_types.Expectation('foo', ['win'], 'Failure'):
data_types.BuilderStepMap({
'builder':
data_types.StepBuildStatsMap({
'step': data_types.BuildStats(),
}),
}),
}),
})
merge_map = data_types.TestExpectationMap({
'foo':
data_types.ExpectationBuilderMap({
data_types.Expectation('foo', ['win'], 'Failure'):
data_types.BuilderStepMap({
'builder':
data_types.StepBuildStatsMap({
'step2': data_types.BuildStats(),
}),
'builder2':
data_types.StepBuildStatsMap({
'step': data_types.BuildStats(),
}),
}),
data_types.Expectation('foo', ['mac'], 'Failure'):
data_types.BuilderStepMap({
'builder':
data_types.StepBuildStatsMap({
'step': data_types.BuildStats(),
})
})
}),
'bar':
data_types.ExpectationBuilderMap({
data_types.Expectation('bar', ['win'], 'Failure'):
data_types.BuilderStepMap({
'builder':
data_types.StepBuildStatsMap({
'step': data_types.BuildStats(),
}),
}),
}),
})
expected_base_map = {
'foo': {
data_types.Expectation('foo', ['win'], 'Failure'): {
'builder': {
'step': data_types.BuildStats(),
'step2': data_types.BuildStats(),
},
'builder2': {
'step': data_types.BuildStats(),
},
},
data_types.Expectation('foo', ['mac'], 'Failure'): {
'builder': {
'step': data_types.BuildStats(),
}
}
},
'bar': {
data_types.Expectation('bar', ['win'], 'Failure'): {
'builder': {
'step': data_types.BuildStats(),
},
},
},
}
base_map.Merge(merge_map)
self.assertEqual(base_map, expected_base_map)
def testMergeBuildStats(self) -> None:
"""Tests that BuildStats for the same step are merged properly."""
base_map = data_types.TestExpectationMap({
'foo':
data_types.ExpectationBuilderMap({
data_types.Expectation('foo', ['win'], 'Failure'):
data_types.BuilderStepMap({
'builder':
data_types.StepBuildStatsMap({
'step': data_types.BuildStats(),
}),
}),
}),
})
merge_stats = data_types.BuildStats()
merge_stats.AddFailedBuild('1')
merge_map = data_types.TestExpectationMap({
'foo':
data_types.ExpectationBuilderMap({
data_types.Expectation('foo', ['win'], 'Failure'):
data_types.BuilderStepMap({
'builder':
data_types.StepBuildStatsMap({
'step': merge_stats,
}),
}),
}),
})
expected_stats = data_types.BuildStats()
expected_stats.AddFailedBuild('1')
expected_base_map = {
'foo': {
data_types.Expectation('foo', ['win'], 'Failure'): {
'builder': {
'step': expected_stats,
},
},
},
}
base_map.Merge(merge_map)
self.assertEqual(base_map, expected_base_map)
def testInvalidMerge(self) -> None:
"""Tests that updating a BuildStats instance twice is an error."""
base_map = data_types.TestExpectationMap({
'foo':
data_types.ExpectationBuilderMap({
data_types.Expectation('foo', ['win'], 'Failure'):
data_types.BuilderStepMap({
'builder':
data_types.StepBuildStatsMap({
'step': data_types.BuildStats(),
}),
}),
}),
})
merge_stats = data_types.BuildStats()
merge_stats.AddFailedBuild('1')
merge_map = data_types.TestExpectationMap({
'foo':
data_types.ExpectationBuilderMap({
data_types.Expectation('foo', ['win'], 'Failure'):
data_types.BuilderStepMap({
'builder':
data_types.StepBuildStatsMap({
'step': merge_stats,
}),
}),
}),
})
original_base_map = copy.deepcopy(base_map)
base_map.Merge(merge_map, original_base_map)
with self.assertRaises(AssertionError):
base_map.Merge(merge_map, original_base_map)
class TestExpectationMapAddResultListUnittest(unittest.TestCase):
def GetGenericRetryExpectation(self) -> data_types.Expectation:
return data_types.Expectation('foo/test', ['win10'], 'RetryOnFailure')
def GetGenericFailureExpectation(self) -> data_types.Expectation:
return data_types.Expectation('foo/test', ['win10'], 'Failure')
def GetEmptyMapForGenericRetryExpectation(self
) -> data_types.TestExpectationMap:
foo_expectation = self.GetGenericRetryExpectation()
return data_types.TestExpectationMap({
'expectation_file':
data_types.ExpectationBuilderMap({
foo_expectation:
data_types.BuilderStepMap(),
}),
})
def GetEmptyMapForGenericFailureExpectation(
self) -> data_types.TestExpectationMap:
foo_expectation = self.GetGenericFailureExpectation()
return data_types.TestExpectationMap({
'expectation_file':
data_types.ExpectationBuilderMap({
foo_expectation:
data_types.BuilderStepMap(),
}),
})
def GetPassedMapForExpectation(self, expectation: data_types.Expectation
) -> data_types.TestExpectationMap:
stats = data_types.BuildStats()
stats.AddPassedBuild()
return self.GetMapForExpectationAndStats(expectation, stats)
def GetFailedMapForExpectation(self, expectation: data_types.Expectation
) -> data_types.TestExpectationMap:
stats = data_types.BuildStats()
stats.AddFailedBuild('build_id')
return self.GetMapForExpectationAndStats(expectation, stats)
def GetMapForExpectationAndStats(self, expectation: data_types.Expectation,
stats: data_types.BuildStats
) -> data_types.TestExpectationMap:
return data_types.TestExpectationMap({
'expectation_file':
data_types.ExpectationBuilderMap({
expectation:
data_types.BuilderStepMap({
'builder':
data_types.StepBuildStatsMap({
'pixel_tests': stats,
}),
}),
}),
})
def testRetryOnlyPassMatching(self) -> None:
"""Tests when the only tests are retry expectations that pass and match."""
foo_result = data_types.Result('foo/test', ['win10'], 'Pass', 'pixel_tests',
'build_id')
expectation_map = self.GetEmptyMapForGenericRetryExpectation()
unmatched_results = expectation_map.AddResultList('builder', [foo_result])
self.assertEqual(unmatched_results, [])
expected_expectation_map = self.GetPassedMapForExpectation(
self.GetGenericRetryExpectation())
self.assertEqual(expectation_map, expected_expectation_map)
def testRetryOnlyFailMatching(self) -> None:
"""Tests when the only tests are retry expectations that fail and match."""
foo_result = data_types.Result('foo/test', ['win10'], 'Failure',
'pixel_tests', 'build_id')
expectation_map = self.GetEmptyMapForGenericRetryExpectation()
unmatched_results = expectation_map.AddResultList('builder', [foo_result])
self.assertEqual(unmatched_results, [])
expected_expectation_map = self.GetFailedMapForExpectation(
self.GetGenericRetryExpectation())
self.assertEqual(expectation_map, expected_expectation_map)
def testRetryFailThenPassMatching(self) -> None:
"""Tests when there are pass and fail results for retry expectations."""
foo_fail_result = data_types.Result('foo/test', ['win10'], 'Failure',
'pixel_tests', 'build_id')
foo_pass_result = data_types.Result('foo/test', ['win10'], 'Pass',
'pixel_tests', 'build_id')
expectation_map = self.GetEmptyMapForGenericRetryExpectation()
unmatched_results = expectation_map.AddResultList(
'builder', [foo_fail_result, foo_pass_result])
self.assertEqual(unmatched_results, [])
expected_expectation_map = self.GetFailedMapForExpectation(
self.GetGenericRetryExpectation())
self.assertEqual(expectation_map, expected_expectation_map)
def testFailurePassMatching(self) -> None:
"""Tests when there are pass results for failure expectations."""
foo_result = data_types.Result('foo/test', ['win10'], 'Pass', 'pixel_tests',
'build_id')
expectation_map = self.GetEmptyMapForGenericFailureExpectation()
unmatched_results = expectation_map.AddResultList('builder', [foo_result])
self.assertEqual(unmatched_results, [])
expected_expectation_map = self.GetPassedMapForExpectation(
self.GetGenericFailureExpectation())
self.assertEqual(expectation_map, expected_expectation_map)
def testFailureFailureMatching(self) -> None:
"""Tests when there are failure results for failure expectations."""
foo_result = data_types.Result('foo/test', ['win10'], 'Failure',
'pixel_tests', 'build_id')
expectation_map = self.GetEmptyMapForGenericFailureExpectation()
unmatched_results = expectation_map.AddResultList('builder', [foo_result])
self.assertEqual(unmatched_results, [])
expected_expectation_map = self.GetFailedMapForExpectation(
self.GetGenericFailureExpectation())
self.assertEqual(expectation_map, expected_expectation_map)
def testMismatches(self) -> None:
"""Tests that unmatched results get returned."""
foo_match_result = data_types.Result('foo/test', ['win10'], 'Pass',
'pixel_tests', 'build_id')
foo_mismatch_result = data_types.Result('foo/not_a_test', ['win10'],
'Failure', 'pixel_tests',
'build_id')
bar_result = data_types.Result('bar/test', ['win10'], 'Pass', 'pixel_tests',
'build_id')
expectation_map = self.GetEmptyMapForGenericFailureExpectation()
unmatched_results = expectation_map.AddResultList(
'builder', [foo_match_result, foo_mismatch_result, bar_result])
self.assertEqual(len(set(unmatched_results)), 2)
self.assertEqual(set(unmatched_results),
set([foo_mismatch_result, bar_result]))
expected_expectation_map = self.GetPassedMapForExpectation(
self.GetGenericFailureExpectation())
self.assertEqual(expectation_map, expected_expectation_map)
class TestExpectationMapAddGroupedResultsUnittest(unittest.TestCase):
def testResultMatchPassingNew(self) -> None:
"""Test adding a passing result when no results for a builder exist."""
r = data_types.Result('some/test/case', ['win', 'win10'], 'Pass',
'pixel_tests', 'build_id')
e = data_types.Expectation('some/test/*', ['win10'], 'Failure')
expectation_map = data_types.TestExpectationMap({
'expectation_file':
data_types.ExpectationBuilderMap({
e: data_types.BuilderStepMap(),
}),
})
grouped_results = {
'some/test/case': [r],
}
matched_results = expectation_map._AddGroupedResults(
grouped_results, 'builder', None)
self.assertEqual(matched_results, set([r]))
stats = data_types.BuildStats()
stats.AddPassedBuild()
expected_expectation_map = {
'expectation_file': {
e: {
'builder': {
'pixel_tests': stats,
},
},
},
}
self.assertEqual(expectation_map, expected_expectation_map)
def testResultMatchFailingNew(self) -> None:
"""Test adding a failing result when no results for a builder exist."""
r = data_types.Result('some/test/case', ['win', 'win10'], 'Failure',
'pixel_tests', 'build_id')
e = data_types.Expectation('some/test/*', ['win10'], 'Failure')
expectation_map = data_types.TestExpectationMap({
'expectation_file':
data_types.ExpectationBuilderMap({
e: data_types.BuilderStepMap(),
}),
})
grouped_results = {
'some/test/case': [r],
}
matched_results = expectation_map._AddGroupedResults(
grouped_results, 'builder', None)
self.assertEqual(matched_results, set([r]))
stats = data_types.BuildStats()
stats.AddFailedBuild('build_id')
expected_expectation_map = {
'expectation_file': {
e: {
'builder': {
'pixel_tests': stats,
},
}
}
}
self.assertEqual(expectation_map, expected_expectation_map)
def testResultMatchPassingExisting(self) -> None:
"""Test adding a passing result when results for a builder exist."""
r = data_types.Result('some/test/case', ['win', 'win10'], 'Pass',
'pixel_tests', 'build_id')
e = data_types.Expectation('some/test/*', ['win10'], 'Failure')
stats = data_types.BuildStats()
stats.AddFailedBuild('build_id')
expectation_map = data_types.TestExpectationMap({
'expectation_file':
data_types.ExpectationBuilderMap({
e:
data_types.BuilderStepMap({
'builder':
data_types.StepBuildStatsMap({
'pixel_tests': stats,
}),
}),
}),
})
grouped_results = {
'some/test/case': [r],
}
matched_results = expectation_map._AddGroupedResults(
grouped_results, 'builder', None)
self.assertEqual(matched_results, set([r]))
stats = data_types.BuildStats()
stats.AddFailedBuild('build_id')
stats.AddPassedBuild()
expected_expectation_map = {
'expectation_file': {
e: {
'builder': {
'pixel_tests': stats,
},
},
},
}
self.assertEqual(expectation_map, expected_expectation_map)
def testResultMatchFailingExisting(self) -> None:
"""Test adding a failing result when results for a builder exist."""
r = data_types.Result('some/test/case', ['win', 'win10'], 'Failure',
'pixel_tests', 'build_id')
e = data_types.Expectation('some/test/*', ['win10'], 'Failure')
stats = data_types.BuildStats()
stats.AddPassedBuild()
expectation_map = data_types.TestExpectationMap({
'expectation_file':
data_types.ExpectationBuilderMap({
e:
data_types.BuilderStepMap({
'builder':
data_types.StepBuildStatsMap({
'pixel_tests': stats,
}),
}),
}),
})
grouped_results = {
'some/test/case': [r],
}
matched_results = expectation_map._AddGroupedResults(
grouped_results, 'builder', None)
self.assertEqual(matched_results, set([r]))
stats = data_types.BuildStats()
stats.AddFailedBuild('build_id')
stats.AddPassedBuild()
expected_expectation_map = {
'expectation_file': {
e: {
'builder': {
'pixel_tests': stats,
},
},
},
}
self.assertEqual(expectation_map, expected_expectation_map)
def testResultMatchMultiMatch(self) -> None:
"""Test adding a passing result when multiple expectations match."""
r = data_types.Result('some/test/case', ['win', 'win10'], 'Pass',
'pixel_tests', 'build_id')
e = data_types.Expectation('some/test/*', ['win10'], 'Failure')
e2 = data_types.Expectation('some/test/case', ['win10'], 'Failure')
expectation_map = data_types.TestExpectationMap({
'expectation_file':
data_types.ExpectationBuilderMap({
e: data_types.BuilderStepMap(),
e2: data_types.BuilderStepMap(),
}),
})
grouped_results = {
'some/test/case': [r],
}
matched_results = expectation_map._AddGroupedResults(
grouped_results, 'builder', None)
self.assertEqual(matched_results, set([r]))
stats = data_types.BuildStats()
stats.AddPassedBuild()
expected_expectation_map = {
'expectation_file': {
e: {
'builder': {
'pixel_tests': stats,
},
},
e2: {
'builder': {
'pixel_tests': stats,
},
}
}
}
self.assertEqual(expectation_map, expected_expectation_map)
def testResultNoMatch(self) -> None:
"""Tests that a result is not added if no match is found."""
r = data_types.Result('some/test/case', ['win', 'win10'], 'Failure',
'pixel_tests', 'build_id')
e = data_types.Expectation('some/test/*', ['win10', 'foo'], 'Failure')
expectation_map = data_types.TestExpectationMap({
'expectation_file':
data_types.ExpectationBuilderMap({
e: data_types.BuilderStepMap(),
})
})
grouped_results = {
'some/test/case': [r],
}
matched_results = expectation_map._AddGroupedResults(
grouped_results, 'builder', None)
self.assertEqual(matched_results, set())
expected_expectation_map = {'expectation_file': {e: {}}}
self.assertEqual(expectation_map, expected_expectation_map)
def testResultMatchSpecificExpectationFiles(self) -> None:
"""Tests that a match can be found when specifying expectation files."""
r = data_types.Result('some/test/case', ['win'], 'Pass', 'pixel_tests',
'build_id')
e = data_types.Expectation('some/test/case', ['win'], 'Failure')
expectation_map = data_types.TestExpectationMap({
'foo_expectations':
data_types.ExpectationBuilderMap({e: data_types.BuilderStepMap()}),
'bar_expectations':
data_types.ExpectationBuilderMap({e: data_types.BuilderStepMap()}),
})
grouped_results = {
'some/test/case': [r],
}
matched_results = expectation_map._AddGroupedResults(
grouped_results, 'builder', ['bar_expectations'])
self.assertEqual(matched_results, set([r]))
stats = data_types.BuildStats()
stats.AddPassedBuild()
expected_expectation_map = {
'foo_expectations': {
e: {},
},
'bar_expectations': {
e: {
'builder': {
'pixel_tests': stats,
}
}
}
}
self.assertEqual(expectation_map, expected_expectation_map)
def testMultipleResults(self) -> None:
"""Tests that behavior is as expected when multiple results are given."""
r1 = data_types.Result('some/test/case', ['win'], 'Pass', 'pixel_tests',
'build_id')
r2 = data_types.Result('some/test/case', ['linux'], 'Pass', 'pixel_tests',
'build_id')
r3 = data_types.Result('some/other/test', ['win'], 'Pass', 'pixel_tests',
'build_id')
r4 = data_types.Result('some/other/test', ['linux'], 'Pass', 'pixel_tests',
'build_id')
r5 = data_types.Result('some/other/other/test', ['win'], 'Pass',
'pixel_tests', 'build_id')
r6 = data_types.Result('some/other/other/test', ['linux'], 'Pass',
'pixel_tests', 'build_id')
e1 = data_types.Expectation('some/test/case', [], 'Failure')
e2 = data_types.Expectation('some/other/test', ['win'], 'Failure')
e3 = data_types.Expectation('some/other/other/test', ['mac'], 'Failure')
expectation_map = data_types.TestExpectationMap({
'expectation_file':
data_types.ExpectationBuilderMap({
e1: data_types.BuilderStepMap(),
e2: data_types.BuilderStepMap(),
e3: data_types.BuilderStepMap(),
})
})
grouped_results = {
'some/test/case': [r1, r2],
'some/other/test': [r3, r4],
'some/other/other/test': [r5, r6],
}
matched_results = expectation_map._AddGroupedResults(
grouped_results, 'builder', None)
self.assertEqual(matched_results, set([r1, r2, r3]))
stats1 = data_types.BuildStats()
stats1.AddPassedBuild()
stats1.AddPassedBuild()
stats2 = data_types.BuildStats()
stats2.AddPassedBuild()
expected_expectation_map = {
'expectation_file': {
e1: {
'builder': {
'pixel_tests': stats1,
},
},
e2: {
'builder': {
'pixel_tests': stats2,
},
},
e3: {},
}
}
self.assertEqual(expectation_map, expected_expectation_map)
class TestExpectationMapSplitByStalenessUnittest(unittest.TestCase):
def testEmptyInput(self) -> None:
"""Tests that nothing blows up with empty input."""
stale_dict, semi_stale_dict, active_dict =\
data_types.TestExpectationMap().SplitByStaleness()
self.assertEqual(stale_dict, {})
self.assertEqual(semi_stale_dict, {})
self.assertEqual(active_dict, {})
self.assertIsInstance(stale_dict, data_types.TestExpectationMap)
self.assertIsInstance(semi_stale_dict, data_types.TestExpectationMap)
self.assertIsInstance(active_dict, data_types.TestExpectationMap)
def testStaleExpectations(self) -> None:
"""Tests output when only stale expectations are provided."""
expectation_map = data_types.TestExpectationMap({
'foo':
data_types.ExpectationBuilderMap({
data_types.Expectation('foo', ['win'], ['Failure']):
data_types.BuilderStepMap({
'foo_builder':
data_types.StepBuildStatsMap({
'step1':
uu.CreateStatsWithPassFails(1, 0),
'step2':
uu.CreateStatsWithPassFails(2, 0),
}),
'bar_builder':
data_types.StepBuildStatsMap({
'step1':
uu.CreateStatsWithPassFails(3, 0),
'step2':
uu.CreateStatsWithPassFails(4, 0)
}),
}),
data_types.Expectation('foo', ['linux'], ['RetryOnFailure']):
data_types.BuilderStepMap({
'foo_builder':
data_types.StepBuildStatsMap({
'step1':
uu.CreateStatsWithPassFails(5, 0),
'step2':
uu.CreateStatsWithPassFails(6, 0),
}),
}),
}),
'bar':
data_types.ExpectationBuilderMap({
data_types.Expectation('bar', ['win'], ['Failure']):
data_types.BuilderStepMap({
'foo_builder':
data_types.StepBuildStatsMap({
'step1':
uu.CreateStatsWithPassFails(7, 0),
}),
}),
}),
})
expected_stale_dict = copy.deepcopy(expectation_map)
stale_dict, semi_stale_dict, active_dict =\
expectation_map.SplitByStaleness()
self.assertEqual(stale_dict, expected_stale_dict)
self.assertEqual(semi_stale_dict, {})
self.assertEqual(active_dict, {})
def testActiveExpectations(self) -> None:
"""Tests output when only active expectations are provided."""
expectation_map = data_types.TestExpectationMap({
'foo':
data_types.ExpectationBuilderMap({
data_types.Expectation('foo', ['win'], ['Failure']):
data_types.BuilderStepMap({
'foo_builder':
data_types.StepBuildStatsMap({
'step1':
uu.CreateStatsWithPassFails(0, 1),
'step2':
uu.CreateStatsWithPassFails(0, 2),
}),
'bar_builder':
data_types.StepBuildStatsMap({
'step1':
uu.CreateStatsWithPassFails(0, 3),
'step2':
uu.CreateStatsWithPassFails(0, 4)
}),
}),
data_types.Expectation('foo', ['linux'], ['RetryOnFailure']):
data_types.BuilderStepMap({
'foo_builder':
data_types.StepBuildStatsMap({
'step1':
uu.CreateStatsWithPassFails(0, 5),
'step2':
uu.CreateStatsWithPassFails(0, 6),
}),
}),
}),
'bar':
data_types.ExpectationBuilderMap({
data_types.Expectation('bar', ['win'], ['Failure']):
data_types.BuilderStepMap({
'foo_builder':
data_types.StepBuildStatsMap({
'step1':
uu.CreateStatsWithPassFails(0, 7),
}),
}),
}),
})
expected_active_dict = copy.deepcopy(expectation_map)
stale_dict, semi_stale_dict, active_dict =\
expectation_map.SplitByStaleness()
self.assertEqual(stale_dict, {})
self.assertEqual(semi_stale_dict, {})
self.assertEqual(active_dict, expected_active_dict)
def testSemiStaleExpectations(self) -> None:
"""Tests output when only semi-stale expectations are provided."""
expectation_map = data_types.TestExpectationMap({
'foo':
data_types.ExpectationBuilderMap({
data_types.Expectation('foo', ['win'], ['Failure']):
data_types.BuilderStepMap({
'foo_builder':
data_types.StepBuildStatsMap({
'step1':
uu.CreateStatsWithPassFails(1, 0),
'step2':
uu.CreateStatsWithPassFails(2, 2),
}),
'bar_builder':
data_types.StepBuildStatsMap({
'step1':
uu.CreateStatsWithPassFails(3, 0),
'step2':
uu.CreateStatsWithPassFails(0, 4)
}),
}),
data_types.Expectation('foo', ['linux'], ['RetryOnFailure']):
data_types.BuilderStepMap({
'foo_builder':
data_types.StepBuildStatsMap({
'step1':
uu.CreateStatsWithPassFails(5, 0),
'step2':
uu.CreateStatsWithPassFails(6, 6),
}),
}),
}),
'bar':
data_types.ExpectationBuilderMap({
data_types.Expectation('bar', ['win'], ['Failure']):
data_types.BuilderStepMap({
'foo_builder':
data_types.StepBuildStatsMap({
'step1':
uu.CreateStatsWithPassFails(7, 0),
}),
'bar_builder':
data_types.StepBuildStatsMap({
'step1':
uu.CreateStatsWithPassFails(0, 8),
}),
}),
}),
})
expected_semi_stale_dict = copy.deepcopy(expectation_map)
stale_dict, semi_stale_dict, active_dict =\
expectation_map.SplitByStaleness()
self.assertEqual(stale_dict, {})
self.assertEqual(semi_stale_dict, expected_semi_stale_dict)
self.assertEqual(active_dict, {})
def testSemiStaleTreatedAsActive(self) -> None:
"""Tests output when semi-stale expectations are considered active."""
expectation_map = data_types.TestExpectationMap({
'foo':
data_types.ExpectationBuilderMap({
data_types.Expectation('foo', ['win'], ['Failure']):
data_types.BuilderStepMap({
'foo_builder':
data_types.StepBuildStatsMap({
'step1':
uu.CreateStatsWithPassFails(1, 0),
'step2':
uu.CreateStatsWithPassFails(2, 2),
}),
'bar_builder':
data_types.StepBuildStatsMap({
'step1':
uu.CreateStatsWithPassFails(3, 0),
'step2':
uu.CreateStatsWithPassFails(0, 4)
}),
}),
data_types.Expectation('foo', ['linux'], ['RetryOnFailure']):
data_types.BuilderStepMap({
'foo_builder':
data_types.StepBuildStatsMap({
'step1':
uu.CreateStatsWithPassFails(5, 0),
'step2':
uu.CreateStatsWithPassFails(6, 6),
}),
}),
}),
'bar':
data_types.ExpectationBuilderMap({
data_types.Expectation('bar', ['win'], ['Failure']):
data_types.BuilderStepMap({
'foo_builder':
data_types.StepBuildStatsMap({
'step1':
uu.CreateStatsWithPassFails(7, 0),
}),
'bar_builder':
data_types.StepBuildStatsMap({
'step1':
uu.CreateStatsWithPassFails(0, 8),
}),
}),
}),
})
expected_semi_stale_dict = data_types.TestExpectationMap({
'foo':
data_types.ExpectationBuilderMap({
data_types.Expectation('foo', ['linux'], ['RetryOnFailure']):
data_types.BuilderStepMap({
'foo_builder':
data_types.StepBuildStatsMap({
'step1':
uu.CreateStatsWithPassFails(5, 0),
'step2':
uu.CreateStatsWithPassFails(6, 6),
}),
}),
}),
'bar':
data_types.ExpectationBuilderMap({
data_types.Expectation('bar', ['win'], ['Failure']):
data_types.BuilderStepMap({
'foo_builder':
data_types.StepBuildStatsMap({
'step1':
uu.CreateStatsWithPassFails(7, 0),
}),
'bar_builder':
data_types.StepBuildStatsMap({
'step1':
uu.CreateStatsWithPassFails(0, 8),
}),
}),
}),
})
expected_active_dict = data_types.TestExpectationMap({
'foo':
data_types.ExpectationBuilderMap({
data_types.Expectation('foo', ['win'], ['Failure']):
data_types.BuilderStepMap({
'foo_builder':
data_types.StepBuildStatsMap({
'step1':
uu.CreateStatsWithPassFails(1, 0),
'step2':
uu.CreateStatsWithPassFails(2, 2),
}),
'bar_builder':
data_types.StepBuildStatsMap({
'step1':
uu.CreateStatsWithPassFails(3, 0),
'step2':
uu.CreateStatsWithPassFails(0, 4)
}),
}),
}),
})
def SideEffect(pass_map: Dict[int, data_types.BuilderStepMap]) -> bool:
return pass_map[data_types.FULL_PASS]['foo_builder'][
'step1'] == uu.CreateStatsWithPassFails(1, 0)
with mock.patch.object(expectation_map,
'_ShouldTreatSemiStaleAsActive',
side_effect=SideEffect):
stale_dict, semi_stale_dict, active_dict =\
expectation_map.SplitByStaleness()
self.assertEqual(stale_dict, {})
self.assertEqual(semi_stale_dict, expected_semi_stale_dict)
self.assertEqual(active_dict, expected_active_dict)
def testAllExpectations(self) -> None:
"""Tests output when all three types of expectations are provided."""
expectation_map = data_types.TestExpectationMap({
'foo':
data_types.ExpectationBuilderMap({
data_types.Expectation('foo', ['stale'], 'Failure'):
data_types.BuilderStepMap({
'foo_builder':
data_types.StepBuildStatsMap({
'step1':
uu.CreateStatsWithPassFails(1, 0),
'step2':
uu.CreateStatsWithPassFails(2, 0),
}),
'bar_builder':
data_types.StepBuildStatsMap({
'step1':
uu.CreateStatsWithPassFails(3, 0),
'step2':
uu.CreateStatsWithPassFails(4, 0)
}),
}),
data_types.Expectation('foo', ['semistale'], 'Failure'):
data_types.BuilderStepMap({
'foo_builder':
data_types.StepBuildStatsMap({
'step1':
uu.CreateStatsWithPassFails(1, 0),
'step2':
uu.CreateStatsWithPassFails(2, 2),
}),
'bar_builder':
data_types.StepBuildStatsMap({
'step1':
uu.CreateStatsWithPassFails(3, 0),
'step2':
uu.CreateStatsWithPassFails(0, 4)
}),
}),
data_types.Expectation('foo', ['active'], 'Failure'):
data_types.BuilderStepMap({
'foo_builder':
data_types.StepBuildStatsMap({
'step1':
uu.CreateStatsWithPassFails(1, 1),
'step2':
uu.CreateStatsWithPassFails(2, 2),
}),
'bar_builder':
data_types.StepBuildStatsMap({
'step1':
uu.CreateStatsWithPassFails(3, 3),
'step2':
uu.CreateStatsWithPassFails(0, 4)
}),
}),
}),
})
expected_stale = {
'foo': {
data_types.Expectation('foo', ['stale'], 'Failure'): {
'foo_builder': {
'step1': uu.CreateStatsWithPassFails(1, 0),
'step2': uu.CreateStatsWithPassFails(2, 0),
},
'bar_builder': {
'step1': uu.CreateStatsWithPassFails(3, 0),
'step2': uu.CreateStatsWithPassFails(4, 0)
},
},
},
}
expected_semi_stale = {
'foo': {
data_types.Expectation('foo', ['semistale'], 'Failure'): {
'foo_builder': {
'step1': uu.CreateStatsWithPassFails(1, 0),
'step2': uu.CreateStatsWithPassFails(2, 2),
},
'bar_builder': {
'step1': uu.CreateStatsWithPassFails(3, 0),
'step2': uu.CreateStatsWithPassFails(0, 4)
},
},
},
}
expected_active = {
'foo': {
data_types.Expectation('foo', ['active'], 'Failure'): {
'foo_builder': {
'step1': uu.CreateStatsWithPassFails(1, 1),
'step2': uu.CreateStatsWithPassFails(2, 2),
},
'bar_builder': {
'step1': uu.CreateStatsWithPassFails(3, 3),
'step2': uu.CreateStatsWithPassFails(0, 4)
},
},
},
}
stale_dict, semi_stale_dict, active_dict =\
expectation_map.SplitByStaleness()
self.assertEqual(stale_dict, expected_stale)
self.assertEqual(semi_stale_dict, expected_semi_stale)
self.assertEqual(active_dict, expected_active)
class TestExpectationMapFilterOutUnusedExpectationsUnittest(unittest.TestCase):
def testNoUnused(self) -> None:
"""Tests that filtering is a no-op if there are no unused expectations."""
expectation_map = data_types.TestExpectationMap({
'expectation_file':
data_types.ExpectationBuilderMap({
data_types.Expectation('foo/test', ['win'], ['Failure']):
data_types.BuilderStepMap({
'SomeBuilder':
data_types.StepBuildStatsMap(),
}),
})
})
expected_expectation_map = copy.deepcopy(expectation_map)
unused_expectations = expectation_map.FilterOutUnusedExpectations()
self.assertEqual(len(unused_expectations), 0)
self.assertEqual(expectation_map, expected_expectation_map)
def testUnusedButNotEmpty(self) -> None:
"""Tests filtering if there is an unused expectation but no empty tests."""
expectation_map = data_types.TestExpectationMap({
'expectation_file':
data_types.ExpectationBuilderMap({
data_types.Expectation('foo/test', ['win'], ['Failure']):
data_types.BuilderStepMap({
'SomeBuilder':
data_types.StepBuildStatsMap(),
}),
data_types.Expectation('foo/test', ['linux'], ['Failure']):
data_types.BuilderStepMap(),
})
})
expected_expectation_map = data_types.TestExpectationMap({
'expectation_file':
data_types.ExpectationBuilderMap({
data_types.Expectation('foo/test', ['win'], ['Failure']):
data_types.BuilderStepMap({
'SomeBuilder':
data_types.StepBuildStatsMap(),
}),
}),
})
expected_unused = {
'expectation_file':
[data_types.Expectation('foo/test', ['linux'], ['Failure'])]
}
unused_expectations = expectation_map.FilterOutUnusedExpectations()
self.assertEqual(unused_expectations, expected_unused)
self.assertEqual(expectation_map, expected_expectation_map)
def testUnusedAndEmpty(self) -> None:
"""Tests filtering if there is an expectation that causes an empty test."""
expectation_map = data_types.TestExpectationMap({
'expectation_file':
data_types.ExpectationBuilderMap({
data_types.Expectation('foo/test', ['win'], ['Failure']):
data_types.BuilderStepMap(),
}),
})
expected_unused = {
'expectation_file':
[data_types.Expectation('foo/test', ['win'], ['Failure'])]
}
unused_expectations = expectation_map.FilterOutUnusedExpectations()
self.assertEqual(unused_expectations, expected_unused)
self.assertEqual(expectation_map, {})
class BuilderEntryUnittest(unittest.TestCase):
def testProject(self) -> None:
"""Tests that the project property functions as expected."""
be = data_types.BuilderEntry('', constants.BuilderTypes.CI, False)
self.assertEqual(be.project, 'chromium')
be = data_types.BuilderEntry('', constants.BuilderTypes.CI, True)
self.assertEqual(be.project, 'chrome')
def testEquality(self) -> None:
"""Tests equality between two BuilderEntry instances."""
be = data_types.BuilderEntry('builder', constants.BuilderTypes.CI, False)
other = data_types.BuilderEntry('builder', constants.BuilderTypes.CI, False)
self.assertEqual(be, other)
other = data_types.BuilderEntry('builder', constants.BuilderTypes.TRY,
False)
self.assertNotEqual(be, other)
other = data_types.BuilderEntry('builder', constants.BuilderTypes.CI, True)
self.assertNotEqual(be, other)
other = data_types.BuilderEntry('not_builder', constants.BuilderTypes.CI,
False)
self.assertNotEqual(be, other)
self.assertNotEqual(be, 'builder')
def testHashability(self) -> None:
"""Tests the hashability of the BuilderEntry class."""
be = data_types.BuilderEntry('builder', constants.BuilderTypes.CI, False)
_ = {be}
other = data_types.BuilderEntry('builder', constants.BuilderTypes.CI, False)
self.assertEqual(be.__hash__(), other.__hash__())
other = data_types.BuilderEntry('builder', constants.BuilderTypes.TRY,
False)
self.assertNotEqual(be.__hash__(), other.__hash__())
other = data_types.BuilderEntry('builder', constants.BuilderTypes.CI, True)
self.assertNotEqual(be.__hash__(), other.__hash__())
other = data_types.BuilderEntry('not_builder', constants.BuilderTypes.CI,
False)
self.assertNotEqual(be.__hash__(), other.__hash__())
if __name__ == '__main__':
unittest.main(verbosity=2)