blob: 035af5059e147cee2c3737940958c58ebd8b07a7 [file] [log] [blame]
# Copyright 2019 The LUCI Authors. All rights reserved.
# Use of this source code is governed under the Apache License, Version 2.0
# that can be found in the LICENSE file.
import os
# pylint: disable=import-error
from PB.recipe_engine.test_result import TestResult
class TestFailure(object):
"""Base class for different kinds of test failures."""
def format(self):
"""Returns a human-readable description of the failure."""
raise NotImplementedError()
def as_proto(self):
"""Returns a machine-readable description of the failure as proto.
The returned message should be an instance of TestResult.TestFailure
(see test_result.proto).
"""
raise NotImplementedError()
class DiffFailure(TestFailure):
"""Failure when simulated recipe commands don't match recorded expectations.
"""
def __init__(self, diff):
self.diff = diff
def format(self):
return self.diff
def as_proto(self):
proto = TestResult.TestFailure()
proto.diff_failure.MergeFrom(TestResult.DiffFailure())
return proto
class CheckFailure(TestFailure):
"""Failure when any of the post-process checks fails."""
def __init__(self, check):
self.check = check
def format(self):
return self.check.format(indent=4)
def as_proto(self):
return self.check.as_proto()
class BadTestFailure(TestFailure):
"""Failure when the test itself was bad somehow (e.g. provides mock data
for steps which never ran)."""
def __init__(self, error):
self.error = error
def format(self):
return str(self.error)
def as_proto(self):
proto = TestResult.TestFailure()
proto.bad_test_failure.error = self.error
return proto
class CrashFailure(TestFailure):
"""Failure when the recipe run crashes with an uncaught exception."""
def __init__(self, error):
self.error = error
def format(self):
return str(self.error)
def as_proto(self):
proto = TestResult.TestFailure()
proto.crash_failure.error = self.error
return proto
class TestResult_(object):
"""Result of running a test."""
def __init__(self, test_description, failures, coverage_data,
generates_expectation):
self.test_description = test_description
self.failures = failures
self.coverage_data = coverage_data
self.generates_expectation = generates_expectation
class TestDescription(object):
"""Identifies a specific test.
Deliberately small and picklable for use with multiprocessing."""
def __init__(self, recipe_name, test_name, expect_dir, covers):
self.recipe_name = recipe_name
self.test_name = test_name
self.expect_dir = expect_dir
self.covers = covers
@staticmethod
def filesystem_safe(name):
return ''.join('_' if c in '<>:"\\/|?*\0' else c for c in name)
@staticmethod
def test_case_full_name(recipe_name, test_name):
return '%s.%s' % (recipe_name, test_name)
@property
def full_name(self):
return self.test_case_full_name(self.recipe_name, self.test_name)
@property
def expectation_path(self):
name = self.filesystem_safe(self.test_name)
return os.path.join(self.expect_dir, name + '.json')