Skip to content

Commit 82cabe6

Browse files
committed
Initial Commit
Signed-off-by: U. Artie Eoff <[email protected]>
0 parents  commit 82cabe6

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

128 files changed

+17095
-0
lines changed

Diff for: .gitignore

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
*.pyc
2+
__pycache_/*
3+
results/*
4+
slash-logs/*
5+
assets/*

Diff for: .slashrc

+236
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
###
2+
### Copyright (C) 2018-2019 Intel Corporation
3+
###
4+
### SPDX-License-Identifier: BSD-3-Clause
5+
###
6+
7+
from datetime import datetime as dt
8+
import itertools
9+
from lib.baseline import Baseline
10+
import lib.system
11+
import os
12+
import re
13+
import slash
14+
from slash.utils.traceback_utils import get_traceback_string
15+
import sys
16+
import xml.etree.cElementTree as et
17+
18+
__SCRIPT_DIR__ = os.path.abspath(os.path.dirname(__file__))
19+
20+
slash.config.root.log.root = os.path.join(__SCRIPT_DIR__, "results")
21+
slash.config.root.log.last_session_symlink = "session.latest.log"
22+
slash.config.root.log.last_session_dir_symlink = "session.latest"
23+
slash.config.root.log.highlights_subpath = "highlights.latest.log"
24+
slash.config.root.log.colorize = False
25+
slash.config.root.log.unified_session_log = True
26+
slash.config.root.log.truncate_console_lines = False
27+
slash.config.root.run.dump_variation = True
28+
slash.config.root.run.default_sources = ["test"]
29+
slash.config.root.log.subpath = os.path.join(
30+
"{context.session.id}",
31+
"{context.test.__slash__.module_name}",
32+
"{context.test.__slash__.function_name}({context.test.__slash__.variation.safe_repr}).log")
33+
34+
def validate_unique_cases(tree):
35+
for e in tree.findall(".//testcase"):
36+
occurrences = tree.findall(".//testcase[@name='{}'][@classname='{}']".format(
37+
e.get("name"), e.get("classname")))
38+
if len(occurrences) > 1:
39+
slash.logger.warn("{} occurrences of testcase found: {} {}".format(
40+
len(occurrences), e.get("classname"), e.get("name")))
41+
42+
def ansi_escape(text):
43+
return ansi_escape.prog.sub('', text)
44+
ansi_escape.prog = re.compile(r'(\x9B|\x1B\[)[0-?]*[ -/]*[@-~]')
45+
46+
class MediaPlugin(slash.plugins.PluginInterface):
47+
testspec = dict()
48+
49+
suite = os.path.basename(sys.argv[0])
50+
mypath = __SCRIPT_DIR__
51+
52+
RETENTION_NONE = 0;
53+
RETENTION_FAIL = 1;
54+
RETENTION_ALL = 2;
55+
56+
def get_name(self):
57+
return "media"
58+
59+
def configure_argument_parser(self, parser):
60+
parser.add_argument("--rebase", action = "store_true")
61+
parser.add_argument("--baseline-file",
62+
default = os.path.abspath(os.path.join(self.mypath, "baseline", "default")))
63+
parser.add_argument("--artifact-retention", default = self.RETENTION_NONE,
64+
type = int,
65+
help = "{} = Keep None; {} = Keep Failed; {} = Keep All".format(
66+
self.RETENTION_NONE, self.RETENTION_FAIL, self.RETENTION_ALL))
67+
parser.add_argument("--call-timeout", default = 300, type = int,
68+
help = "call timeout in seconds")
69+
parser.add_argument("--parallel-metrics", action = "store_true")
70+
71+
def configure_from_parsed_args(self, args):
72+
self.baseline = Baseline(args.baseline_file, args.rebase)
73+
self.retention = args.artifact_retention
74+
self.call_timeout = args.call_timeout
75+
self.parallel_metrics = args.parallel_metrics
76+
77+
assert not (args.rebase and slash.config.root.parallel.num_workers > 0), "rebase in parallel mode is not supported"
78+
79+
def _set_test_details(self, **kwargs):
80+
for k, v in kwargs.iteritems():
81+
slash.context.result.details.set(k, v)
82+
slash.logger.notice("DETAIL: {} = {}".format(k, v))
83+
84+
def _test_artifact(self, filename):
85+
tstfile = os.path.join(slash.context.result.get_log_dir(), filename)
86+
slash.context.result.data.setdefault("artifacts", list()).append(tstfile)
87+
if os.path.exists(tstfile):
88+
os.remove(tstfile)
89+
return tstfile
90+
91+
def _get_test_spec(self, *args):
92+
spec = self.testspec
93+
for key in args:
94+
spec = spec.setdefault(key, dict())
95+
return spec.setdefault("--spec--", dict())
96+
97+
def _get_driver_name(self):
98+
# TODO: query vaapi for driver name (i.e. use ctypes to call vaapi)
99+
return os.environ.get("LIBVA_DRIVER_NAME", None) or "i965"
100+
101+
def test_start(self):
102+
test = slash.context.test
103+
result = slash.context.result
104+
variation = test.get_variation().values
105+
self._set_test_details(**variation)
106+
result.data.update(test_start = dt.now())
107+
108+
# Begin system capture for test (i.e. dmesg).
109+
# NOTE: syscapture is not accurate for parallel runs
110+
if slash.config.root.parallel.worker_id is None:
111+
self.syscapture.checkpoint()
112+
113+
def test_end(self):
114+
test = slash.context.test
115+
result = slash.context.result
116+
117+
# Cleanup test artifacts?
118+
if self.retention != self.RETENTION_ALL:
119+
if self.retention == self.RETENTION_FAIL and not result.is_success():
120+
pass # Keep failed test artifacts
121+
else:
122+
for tstfile in result.data.get("artifacts", list()):
123+
if os.path.exists(tstfile):
124+
os.remove(tstfile)
125+
126+
# Process system capture result (i.e. dmesg)
127+
# NOTE: syscapture is not accurate for parallel runs
128+
if slash.config.root.parallel.worker_id is None:
129+
capture = self.syscapture.checkpoint()
130+
hangmsgs = [
131+
"\[.*\] i915 0000:00:02.0: Resetting .* after gpu hang",
132+
"\[.*\] i915 0000:00:02.0: Resetting .* for hang on .*",
133+
]
134+
for msg in hangmsgs:
135+
if re.search(msg, capture, re.MULTILINE) is not None:
136+
slash.logger.error("GPU HANG DETECTED!")
137+
for line in capture.split('\n'):
138+
if len(line):
139+
slash.logger.info(line)
140+
141+
# Finally, calculate test execution time
142+
result.data.update(test_end = dt.now())
143+
time = (result.data["test_end"] - result.data["test_start"]).total_seconds()
144+
result.data.update(time = str(time))
145+
self._set_test_details(time = "{} seconds".format(time))
146+
147+
def session_start(self):
148+
self.session_start = dt.now()
149+
150+
self.syscapture = lib.system.Capture()
151+
152+
self.metrics_pool = None
153+
154+
if self.parallel_metrics:
155+
import multiprocessing, signal
156+
handler = signal.signal(signal.SIGINT, signal.SIG_IGN)
157+
self.metrics_pool = multiprocessing.Pool()
158+
signal.signal(signal.SIGINT, handler)
159+
160+
def session_end(self):
161+
if self.metrics_pool is not None:
162+
self.metrics_pool.close()
163+
self.metrics_pool.join()
164+
165+
if slash.config.root.parallel.worker_id is not None:
166+
return
167+
168+
self.baseline.finalize()
169+
170+
time = (dt.now() - self.session_start).total_seconds()
171+
tests = slash.context.session.results.get_num_results()
172+
errors = slash.context.session.results.get_num_errors()
173+
failures = slash.context.session.results.get_num_failures()
174+
skipped = slash.context.session.results.get_num_skipped()
175+
176+
suite = et.Element(
177+
"testsuite", name = self.suite, disabled = "0", tests = str(tests),
178+
errors = str(errors), failures = str(failures), skipped = str(skipped),
179+
time = str(time), timestamp = self.session_start.isoformat())
180+
181+
for result in slash.context.session.results.iter_test_results():
182+
suitename, casename = result.test_metadata.address.split(':')
183+
classname = suitename.rstrip(".py").replace(os.sep, '.').strip('.')
184+
classname = "{}.{}".format(self.suite, classname)
185+
186+
case = et.SubElement(
187+
suite, "testcase", name = casename, classname = classname,
188+
time = result.data.get("time") or "0")
189+
190+
outfile = result.get_log_path()
191+
if os.path.exists(outfile):
192+
with open(outfile, 'rb', 0) as out:
193+
value = "".join(out.readlines())
194+
et.SubElement(case, "system-out").text = ansi_escape(value)
195+
196+
for error in itertools.chain(result.get_errors(), result.get_failures()):
197+
exc_type, exc_value, _ = exc_info = sys.exc_info()
198+
tag = "failure" if error.is_failure() else "error"
199+
et.SubElement(
200+
case, tag, message = error.message,
201+
type = exc_type.__name__ if exc_type else tag).text = ansi_escape(
202+
get_traceback_string(exc_info) if exc_value is not None else "")
203+
204+
for skip in result.get_skips():
205+
case.set("skipped", "1")
206+
et.SubElement(case, "skipped", type = skip or '')
207+
208+
for name, value in result.details.all().items():
209+
et.SubElement(case, "detail", name = name, value = str(value))
210+
211+
tree = et.ElementTree(suite)
212+
213+
validate_unique_cases(tree)
214+
215+
filename = os.path.join(
216+
slash.context.session.results.global_result.get_log_dir(), "results.xml")
217+
tree.write(filename)
218+
219+
media = MediaPlugin()
220+
slash.plugins.manager.install(media, activate = True, is_internal = True)
221+
222+
# Allow user to override test config file via environment variable.
223+
# NOTE: It would be nice to use the configure_argument_parser mechanism in our
224+
# media plugin instead. However, it does not work since
225+
# configure_argument_parser does not get called for "slash list"... it only gets
226+
# invoked for "slash run". Hence, we use an environment var so that we can
227+
# always load the config file when slash loads this file.
228+
config = os.environ.get(
229+
"VAAPI_FITS_CONFIG_FILE",
230+
os.path.abspath(os.path.join(media.mypath, "config", "default")))
231+
assert os.path.exists(config)
232+
execfile(config)
233+
234+
###
235+
### kate: syntax python;
236+
###

Diff for: LICENSE.md

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
Copyright (c) 2018-2019 Intel Corporation. All rights reserved.
2+
3+
Redistribution and use in source and binary forms, with or without modification,
4+
are permitted provided that the following conditions are met:
5+
6+
1. Redistributions of source code must retain the above copyright notice, this
7+
list of conditions and the following disclaimer.
8+
9+
2. Redistributions in binary form must reproduce the above copyright notice,
10+
this list of conditions and the following disclaimer in the documentation
11+
and/or other materials provided with the distribution.
12+
13+
3. Neither the name of the copyright holder nor the names of its contributors
14+
may be used to endorse or promote products derived from this software without
15+
specific prior written permission.
16+
17+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
21+
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
24+
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Diff for: README.md

+87
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# VA-API Functional Integration Test Suite
2+
3+
The VA-API Functional Integration Test Suite is a configurable test suite for VA-API-based media software/middleware. It is written in Python and currently provides tests for VA-API hardware accelerated encode, decode and vpp video pipelines for gstreamer and ffmpeg. It can be easily extended to support additional VA-API-based software/middleware. The tests execute the video pipelines and validate their outputs using common image and video metrics such as checksum, SSIM and PSNR.
4+
5+
## License
6+
7+
See [LICENSE.md](LICENSE.md) file
8+
9+
## Overview
10+
11+
A vaapi-fits bootstrap script is provided at the top-level directory. By default, it loads a test config (spec) file from `config/default` and a baseline (references) file from `baseline/default`. These default files provide a minimal set of example test cases and can be used as a reference guide to create your own custom config and baseline files.
12+
13+
To have vaapi-fits load a custom config file, you can use the `VAAPI_FITS_CONFIG_FILE` environment variable. To use a custom baseline file, use `--baseline-file` command-line option.
14+
15+
## Cloning the Repository
16+
17+
This project uses Git Large File Storage (Git LFS) to track the [assets.tbz2](assets.tbz2) file. Therefore, you will need to install [Git LFS](https://help.github.com/articles/versioning-large-files/) before cloning this repository to your local system.
18+
19+
distro|command
20+
------|-------
21+
Fedora | `sudo dnf install git-lfs` (NOTE: may not be available on older versions)
22+
Ubuntu | `sudo apt install git-lfs && git lfs install` (NOTE: may not be available on older versions)
23+
Other | please follow the instructions [here](https://help.github.com/articles/installing-git-large-file-storage/#platform-linux)
24+
25+
After Git LFS is installed, you can clone and interact with this repository using the same standard Git workflow as usual.
26+
27+
## Requirements
28+
29+
* Python Slash library.
30+
31+
`sudo pip install slash==1.5.1`
32+
33+
* Python NumPy library.
34+
35+
`sudo pip install numpy`
36+
37+
* Python scikit-image library.
38+
39+
`sudo pip install scikit-image`
40+
41+
## Examples
42+
43+
1. Run all available test cases
44+
45+
`vaapi-fits run test`
46+
47+
1. Run only test cases that are supported by SKL platform
48+
49+
`vaapi-fits run test -k tag:SKL`
50+
51+
1. Run only gst-vaapi test cases on iHD driver
52+
53+
`LIBVA_DRIVER_NAME=iHD GST_VAAPI_ALL_DRIVERS=1 vaapi-fits run test/gst-vaapi`
54+
55+
## Some Useful Options
56+
57+
#### _run_ sub-command
58+
59+
option|description
60+
------|-----------
61+
<nobr>`-v`</nobr> | Make console more verbose (can be specified multiple times)
62+
<nobr>`--artifact-retention NUM`</nobr> | Retention policy for test artifacts (e.g. encoded or decoded output files) 0 = Keep None; 1 = Keep Failed; 2 = Keep All
63+
<nobr>`--parallel-metrics`</nobr> | SSIM and PSNR calculations will be processed in parallel mode
64+
<nobr>`--parallel NUM`</nobr> | Run test cases in parallel using NUM worker processes
65+
<nobr>`--call-timeout SECONDS`</nobr> | The maximum amount of time that any execution of external programs (e.g. ffmpeg, gst-launch-1.0, etc.) is allowed before being terminated/killed
66+
<nobr>`-l DIR`</nobr> | Specify root directory to store logs
67+
68+
## Contributing
69+
70+
Patches should be submitted via GitHub pull-requests.
71+
72+
Patches should comply with the following format (note: summary and body must be separated by a blank line):
73+
74+
```
75+
<component>: Change summary (limit to 50 characters)
76+
77+
More detailed explanation of your changes: Why and how.
78+
Wrap it to 72 characters.
79+
80+
Signed-off-by: <[email protected]>
81+
```
82+
83+
See [here](http://chris.beams.io/posts/git-commit/) for some more good advice about writing commit messages.
84+
85+
## Reporting a Security Issue:
86+
87+
Please send email to [email protected] for security issue.

0 commit comments

Comments
 (0)