validate:launcher: Allow specifying expected tests errors

In the future instead of blacklisting tests we should define
what error is expected, and this way when the bug is closed,
we will notice, also, it will allow us to check GstValidate
error reporting itself.
This commit is contained in:
Thibault Saunier 2016-09-02 17:37:24 -03:00
parent 2fff14e469
commit 03548cd63d
2 changed files with 131 additions and 32 deletions

View file

@ -225,12 +225,14 @@ class GstValidatePipelineTestsGenerator(GstValidateTestsGenerator):
fname = self.get_fname(scenario, protocol=mediainfo.get_protocol(), name=name) fname = self.get_fname(scenario, protocol=mediainfo.get_protocol(), name=name)
expected_failures = extra_datas.get("expected-failures")
self.add_test(GstValidateLaunchTest(fname, self.add_test(GstValidateLaunchTest(fname,
self.test_manager.options, self.test_manager.options,
self.test_manager.reporter, self.test_manager.reporter,
pipeline_desc, pipeline_desc,
scenario=scenario, scenario=scenario,
media_descriptor=mediainfo) media_descriptor=mediainfo,
expected_failures=expected_failures)
) )
@ -373,7 +375,7 @@ class GstValidateLaunchTest(GstValidateTest):
def __init__(self, classname, options, reporter, pipeline_desc, def __init__(self, classname, options, reporter, pipeline_desc,
timeout=DEFAULT_TIMEOUT, scenario=None, timeout=DEFAULT_TIMEOUT, scenario=None,
media_descriptor=None, duration=0, hard_timeout=None, media_descriptor=None, duration=0, hard_timeout=None,
extra_env_variables=None): extra_env_variables=None, expected_failures=None):
extra_env_variables = extra_env_variables or {} extra_env_variables = extra_env_variables or {}
@ -398,7 +400,8 @@ class GstValidateLaunchTest(GstValidateTest):
timeout=timeout, timeout=timeout,
hard_timeout=hard_timeout, hard_timeout=hard_timeout,
media_descriptor=media_descriptor, media_descriptor=media_descriptor,
extra_env_variables=extra_env_variables) extra_env_variables=extra_env_variables,
expected_failures=expected_failures)
self.pipeline_desc = pipeline_desc self.pipeline_desc = pipeline_desc
self.media_descriptor = media_descriptor self.media_descriptor = media_descriptor
@ -415,7 +418,8 @@ class GstValidateMediaCheckTest(GstValidateTest):
def __init__(self, classname, options, reporter, media_descriptor, def __init__(self, classname, options, reporter, media_descriptor,
uri, minfo_path, timeout=DEFAULT_TIMEOUT, uri, minfo_path, timeout=DEFAULT_TIMEOUT,
extra_env_variables=None): extra_env_variables=None,
expected_failures=None):
extra_env_variables = extra_env_variables or {} extra_env_variables = extra_env_variables or {}
super( super(
@ -423,7 +427,8 @@ class GstValidateMediaCheckTest(GstValidateTest):
options, reporter, options, reporter,
timeout=timeout, timeout=timeout,
media_descriptor=media_descriptor, media_descriptor=media_descriptor,
extra_env_variables=extra_env_variables) extra_env_variables=extra_env_variables,
expected_failures=expected_failures)
self._uri = uri self._uri = uri
self._media_info_path = minfo_path self._media_info_path = minfo_path
@ -440,7 +445,8 @@ class GstValidateTranscodingTest(GstValidateTest, GstValidateEncodingTestInterfa
combination, uri, media_descriptor, combination, uri, media_descriptor,
timeout=DEFAULT_TIMEOUT, timeout=DEFAULT_TIMEOUT,
scenario=None, scenario=None,
extra_env_variables=None): extra_env_variables=None,
expected_failures=None):
Loggable.__init__(self) Loggable.__init__(self)
extra_env_variables = extra_env_variables or {} extra_env_variables = extra_env_variables or {}
@ -468,7 +474,8 @@ class GstValidateTranscodingTest(GstValidateTest, GstValidateEncodingTestInterfa
timeout=timeout, timeout=timeout,
scenario=scenario, scenario=scenario,
media_descriptor=media_descriptor, media_descriptor=media_descriptor,
extra_env_variables=None) extra_env_variables=None,
expected_failures=expected_failures)
extra_env_variables = extra_env_variables or {} extra_env_variables = extra_env_variables or {}
GstValidateEncodingTestInterface.__init__( GstValidateEncodingTestInterface.__init__(

View file

@ -23,6 +23,7 @@ import json
import os import os
import sys import sys
import re import re
import copy
import SocketServer import SocketServer
import struct import struct
import time import time
@ -56,7 +57,8 @@ class Test(Loggable):
def __init__(self, application_name, classname, options, def __init__(self, application_name, classname, options,
reporter, duration=0, timeout=DEFAULT_TIMEOUT, reporter, duration=0, timeout=DEFAULT_TIMEOUT,
hard_timeout=None, extra_env_variables=None): hard_timeout=None, extra_env_variables=None,
expected_failures=None):
""" """
@timeout: The timeout during which the value return by get_current_value @timeout: The timeout during which the value return by get_current_value
keeps being exactly equal keeps being exactly equal
@ -75,6 +77,12 @@ class Test(Loggable):
self.thread = None self.thread = None
self.queue = None self.queue = None
self.duration = duration self.duration = duration
if expected_failures is None:
self.expected_failures = []
elif not isinstance(expected_failures, list):
self.expected_failures = [expected_failures]
else:
self.expected_failures = expected_failures
extra_env_variables = extra_env_variables or {} extra_env_variables = extra_env_variables or {}
self.extra_env_variables = extra_env_variables self.extra_env_variables = extra_env_variables
@ -82,6 +90,7 @@ class Test(Loggable):
self.clean() self.clean()
def clean(self): def clean(self):
self.kill_subprocess()
self.message = "" self.message = ""
self.error_str = "" self.error_str = ""
self.time_taken = 0.0 self.time_taken = 0.0
@ -484,7 +493,8 @@ class GstValidateTest(Test):
def __init__(self, application_name, classname, def __init__(self, application_name, classname,
options, reporter, duration=0, options, reporter, duration=0,
timeout=DEFAULT_TIMEOUT, scenario=None, hard_timeout=None, timeout=DEFAULT_TIMEOUT, scenario=None, hard_timeout=None,
media_descriptor=None, extra_env_variables=None): media_descriptor=None, extra_env_variables=None,
expected_failures=None):
extra_env_variables = extra_env_variables or {} extra_env_variables = extra_env_variables or {}
@ -506,7 +516,7 @@ class GstValidateTest(Test):
self.reports = [] self.reports = []
self.position = -1 self.position = -1
self.duration = -1 self.media_duration = -1
self.speed = 1.0 self.speed = 1.0
self.actions_infos = [] self.actions_infos = []
self.media_descriptor = media_descriptor self.media_descriptor = media_descriptor
@ -524,7 +534,8 @@ class GstValidateTest(Test):
duration=duration, duration=duration,
timeout=timeout, timeout=timeout,
hard_timeout=hard_timeout, hard_timeout=hard_timeout,
extra_env_variables=extra_env_variables) extra_env_variables=extra_env_variables,
expected_failures=expected_failures)
# defines how much the process can be outside of the configured # defines how much the process can be outside of the configured
# segment / seek # segment / seek
@ -553,7 +564,7 @@ class GstValidateTest(Test):
def set_position(self, position, duration, speed=None): def set_position(self, position, duration, speed=None):
self.position = position self.position = position
self.duration = duration self.media_duration = duration
if speed: if speed:
self.speed = speed self.speed = speed
@ -584,9 +595,11 @@ class GstValidateTest(Test):
Test.test_start(self, queue) Test.test_start(self, queue)
def test_end(self): def test_end(self):
Test.test_end(self) res = Test.test_end(self)
self.stop_server() self.stop_server()
return res
def get_override_file(self, media_descriptor): def get_override_file(self, media_descriptor):
if media_descriptor: if media_descriptor:
if media_descriptor.get_path(): if media_descriptor.get_path():
@ -665,6 +678,11 @@ class GstValidateTest(Test):
def clean(self): def clean(self):
Test.clean(self) Test.clean(self)
self._sent_eos_pos = None self._sent_eos_pos = None
self.reports = []
self.position = -1
self.media_duration = -1
self.speed = 1.0
self.actions_infos = []
def build_arguments(self): def build_arguments(self):
super(GstValidateTest, self).build_arguments() super(GstValidateTest, self).build_arguments()
@ -686,16 +704,44 @@ class GstValidateTest(Test):
return value return value
def get_validate_criticals_errors(self): def report_matches_expected_failure(self, report, expected_failure):
for key in ['bug', 'sometimes']:
if key in expected_failure:
del expected_failure[key]
for key, value in report.items():
if key in expected_failure:
if not re.findall(expected_failure[key], value):
return False
expected_failure.pop(key)
return not bool(expected_failure)
def check_reported_issues(self):
ret = [] ret = []
expected_failures = copy.deepcopy(self.expected_failures)
expected_retcode = [0]
for report in self.reports: for report in self.reports:
found = None
for expected_failure in expected_failures:
if self.report_matches_expected_failure(report,
expected_failure.copy()):
found = expected_failure
break
if found is not None:
expected_failures.remove(found)
if report['level'] == 'critical': if report['level'] == 'critical':
if found.get('sometimes') and isinstance(expected_retcode, list):
expected_retcode.append(18)
else:
expected_retcode = [18]
elif report['level'] == 'critical':
ret.append(report['summary']) ret.append(report['summary'])
if not ret: if not ret:
return None return None, expected_failures, expected_retcode
return str(ret) return ret, expected_failures, expected_retcode
def check_results(self): def check_results(self):
if self.result is Result.FAILED or self.result is Result.PASSED or self.result is Result.TIMEOUT: if self.result is Result.FAILED or self.result is Result.PASSED or self.result is Result.TIMEOUT:
@ -703,23 +749,53 @@ class GstValidateTest(Test):
self.debug("%s returncode: %s", self, self.process.returncode) self.debug("%s returncode: %s", self, self.process.returncode)
criticals = self.get_validate_criticals_errors() criticals, not_found_expected_failures, expected_returncode = self.check_reported_issues()
returncode_index = None
for i, f in enumerate(not_found_expected_failures):
if len(f) == 1 and f.get("returncode"):
returncode = f['returncode']
if not isinstance(expected_returncode, list):
returncode = [expected_returncode]
if 'sometimes' in f:
returncode.append(0)
returncode_index = i
break
not_found_expected_failures = [f for f in not_found_expected_failures
if not f.get('returncode')]
msg = ""
result = Result.PASSED
if self.process.returncode == 139: if self.process.returncode == 139:
# FIXME Reimplement something like that if needed result = Result.FAILED
# self.get_backtrace("SEGFAULT") msg = "Application segfaulted "
self.set_result(Result.FAILED,
"Application segfaulted",
"segfault")
elif self.process.returncode == VALGRIND_ERROR_CODE: elif self.process.returncode == VALGRIND_ERROR_CODE:
self.set_result(Result.FAILED, "Valgrind reported errors") msg = "Valgrind reported errors "
elif criticals or self.process.returncode != 0: result = Result.FAILED
if criticals is None: elif self.process.returncode not in expected_returncode:
criticals = "No criticals" msg = "Application returned %s " % self.process.returncode
self.set_result(Result.FAILED, if expected_returncode != 0:
"Application returned %s (issues: %s)" msg += "(expected %s) " % expected_returncode
% (self.process.returncode, criticals)) result = Result.FAILED
else:
self.set_result(Result.PASSED) if criticals:
msg += "(critical errors: [%s]) " % ', '.join(criticals)
result = Result.FAILED
if not_found_expected_failures:
mandatory_failures = [f for f in not_found_expected_failures
if not f.get('sometimes')]
if mandatory_failures:
msg += "(Expected errors not found: %s) " % mandatory_failures
result = Result.FAILED
elif self.expected_failures:
msg += '%s(Expected errors occured: %s)%s' % (Colors.OKBLUE,
self.expected_failures,
Colors.ENDC)
self.set_result(result, msg.strip())
def get_valgrind_suppression_file(self, subdir, name): def get_valgrind_suppression_file(self, subdir, name):
p = get_data_file(subdir, name) p = get_data_file(subdir, name)
@ -892,6 +968,7 @@ class TestsManager(Loggable):
self.starting_test_num = 0 self.starting_test_num = 0
self.check_testslist = True self.check_testslist = True
self.all_tests = None self.all_tests = None
self.expected_failures = {}
def init(self): def init(self):
return False return False
@ -899,7 +976,22 @@ class TestsManager(Loggable):
def list_tests(self): def list_tests(self):
return sorted(list(self.tests)) return sorted(list(self.tests))
def add_expected_issues(self, expected_failures):
expected_failures_re = {}
for test_name_regex, failures in expected_failures.items():
regex = re.compile(test_name_regex)
expected_failures_re[regex] = failures
for test in self.tests:
if regex.findall(test.classname):
test.expected_failures.extend(failures)
self.expected_failures.update(expected_failures_re)
def add_test(self, test): def add_test(self, test):
for regex, failures in self.expected_failures.items():
if regex.findall(test.classname):
test.expected_failures.extend(failures)
if self._is_test_wanted(test): if self._is_test_wanted(test):
if test not in self.tests: if test not in self.tests:
self.tests.append(test) self.tests.append(test)