[libcamera-devel] [RFC PATCH] tests: v4l2_compat: Add test for v4l2_compat
Paul Elder
paul.elder at ideasonboard.com
Wed Jun 24 16:07:52 CEST 2020
Test the V4L2 compatibility layer by running v4l2-compliance -s on every
/dev/video* device.
Signed-off-by: Paul Elder <paul.elder at ideasonboard.com>
---
test/meson.build | 1 +
test/v4l2_compat/meson.build | 10 +++
test/v4l2_compat/v4l2_compat_test.py | 125 +++++++++++++++++++++++++++
3 files changed, 136 insertions(+)
create mode 100644 test/v4l2_compat/meson.build
create mode 100755 test/v4l2_compat/v4l2_compat_test.py
diff --git a/test/meson.build b/test/meson.build
index a868813..591920f 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -12,6 +12,7 @@ subdir('pipeline')
subdir('process')
subdir('serialization')
subdir('stream')
+subdir('v4l2_compat')
subdir('v4l2_subdevice')
subdir('v4l2_videodevice')
diff --git a/test/v4l2_compat/meson.build b/test/v4l2_compat/meson.build
new file mode 100644
index 0000000..a67aac4
--- /dev/null
+++ b/test/v4l2_compat/meson.build
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: CC0-1.0
+
+pymod = import('python')
+py3 = pymod.find_installation('python3')
+
+v4l2_compat_test = files('v4l2_compat_test.py')
+
+test('v4l2_compat_test', py3,
+ args : v4l2_compat_test,
+ suite : 'v4l2_compat')
diff --git a/test/v4l2_compat/v4l2_compat_test.py b/test/v4l2_compat/v4l2_compat_test.py
new file mode 100755
index 0000000..f56db4e
--- /dev/null
+++ b/test/v4l2_compat/v4l2_compat_test.py
@@ -0,0 +1,125 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (C) 2019, Google Inc.
+#
+# Author: Paul Elder <paul.elder at ideasonboard.com>
+#
+# v4l2_compat_test.py - Test the V4L2 compatibility layer
+
+import os
+import re
+from shutil import which
+import subprocess
+import sys
+
+TestPass = 0
+TestFail = -1
+TestSkip = 77
+
+
+supported_pipelines = [
+ "uvcvideo",
+ "vimc",
+]
+
+
+def find_file(name, path):
+ for root, dirs, files in os.walk(path):
+ if name in files:
+ return os.path.join(root, name)
+
+
+def grep(exp, arr):
+ return [s for s in arr if re.search(exp, s)]
+
+
+def run_with_stdout(cmd, args="", env={}):
+ try:
+ with open(os.devnull, 'w') as devnull:
+ output = subprocess.check_output(f"{cmd} {args}", shell=True, env=env, stderr=devnull)
+ except subprocess.CalledProcessError as err:
+ output = err.output
+ return output.decode("utf-8").split("\n")
+
+
+def extract_result(result):
+ res = result.split(", ")
+ ret = {}
+ ret["total"] = int(res[0].split(": ")[-1])
+ ret["succeeded"] = int(res[1].split(": ")[-1])
+ ret["failed"] = int(res[2].split(": ")[-1])
+ ret["warnings"] = int(res[3].split(": ")[-1])
+ ret["device"] = res[0].split()[4].strip(":")
+ ret["driver"] = res[0].split()[2]
+ return ret
+
+
+def print_output_arr(output_arr):
+ print("\n".join(output_arr))
+
+
+def test_v4l2_compliance(v4l2_compliance, v4l2_compat, device, base_driver):
+ output = run_with_stdout(v4l2_compliance, f"-s -d {device}", {"LD_PRELOAD": v4l2_compat})
+ result = extract_result(output[-2])
+ if result["driver"] != "libcamera":
+ return TestSkip
+
+ if result['failed'] == 0:
+ return TestPass
+
+ # vimc will fail s_fmt because it only supports framesizes that are
+ # multiples of 3
+ if base_driver == "vimc" and result["failed"] <= 1:
+ failures = grep("fail", output)
+ if re.search("S_FMT cannot handle an invalid format", failures[0]) is None:
+ print_output_arr(output)
+ return TestFail
+ return TestPass
+
+ print_output_arr(output)
+ return TestFail
+
+
+def main():
+ v4l2_compliance = which("v4l2-compliance")
+ if v4l2_compliance is None:
+ print("v4l2-compliance is not available")
+ return TestSkip
+
+ v4l2_ctl = which("v4l2-ctl")
+ if v4l2_ctl is None:
+ print("v4l2-ctl is not available")
+ return TestSkip
+
+ v4l2_compat = find_file("v4l2-compat.so", ".")
+ if v4l2_compat is None:
+ print("v4l2-compat.so is not built")
+ return TestSkip
+
+ dev_nodes = grep("video", os.listdir("/dev"))
+ dev_nodes = list(map(lambda s: f"/dev/{s}", dev_nodes))
+ if len(dev_nodes) == 0:
+ print("no video nodes available to test with")
+ return TestSkip
+
+ failed = []
+ for device in dev_nodes:
+ out = run_with_stdout(v4l2_ctl, f"-D -d {device}")
+ driver = grep("Driver name", out)[0].split(":")[-1].strip()
+ if driver not in supported_pipelines:
+ continue
+
+ ret = test_v4l2_compliance(v4l2_compliance, v4l2_compat, device, driver)
+ if ret == TestFail:
+ failed.append(device)
+
+ if len(failed) > 0:
+ print(f"Failed {len(failed)} tests:")
+ for device in failed:
+ print(f"- {device}")
+
+ return TestPass if not failed else TestFail
+
+
+if __name__ == '__main__':
+ sys.exit(main())
--
2.27.0
More information about the libcamera-devel
mailing list