[libcamera-devel] [PATCH v10 1/3] tests: Add IPADataSerializer test

Paul Elder paul.elder at ideasonboard.com
Tue Mar 2 10:33:16 CET 2021


Test the IPADataSerializer for controls, vectors, maps, and PODs of
built-in types.

Signed-off-by: Paul Elder <paul.elder at ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart at ideasonboard.com>

---
Changes in v10:
- add all std:: for real
- remove #include <string.h>

Changes in v9:
- convert C-style macros to C++ templates
- simplify (aka remove) the custom map/vector comparators
- remove dependency on the raspberrypi header, and create our own
  testing ControlInfoMap
- reduce the size of the string for testing serializing big objects

No change in v8

Changes in v7:
- remove printing values of vectors/maps
- simplify map and vector equality check
- return immediately on the first failure

Changes in v6:
- no longer need to initialize rpi ControlInfoMap
- no longer need to pass ControlInfoMap to the ControlList serializer

Changes in v5:
- use ControlInfoMap serializer instead of const ControlInfoMap
  serializer

Changes in v4:
- use RPi::controls instead RPi::Controls

Changes in v3:
- use re-namespaced RPi::Controls

New in v2
---
 .../ipa_data_serializer_test.cpp              | 439 ++++++++++++++++++
 test/serialization/meson.build                |   1 +
 2 files changed, 440 insertions(+)
 create mode 100644 test/serialization/ipa_data_serializer_test.cpp

diff --git a/test/serialization/ipa_data_serializer_test.cpp b/test/serialization/ipa_data_serializer_test.cpp
new file mode 100644
index 00000000..dbbba891
--- /dev/null
+++ b/test/serialization/ipa_data_serializer_test.cpp
@@ -0,0 +1,439 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2020, Google Inc.
+ *
+ * ipa_data_serializer_test.cpp - Test serializing/deserializing with IPADataSerializer
+ */
+
+#include <algorithm>
+#include <cxxabi.h>
+#include <fcntl.h>
+#include <iostream>
+#include <limits>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <tuple>
+#include <unistd.h>
+#include <vector>
+
+#include "libcamera/internal/device_enumerator.h"
+#include "libcamera/internal/ipa_data_serializer.h"
+#include "libcamera/internal/ipa_manager.h"
+#include "libcamera/internal/ipa_module.h"
+#include "libcamera/internal/pipeline_handler.h"
+#include "libcamera/internal/thread.h"
+#include "libcamera/internal/timer.h"
+
+#include "serialization_test.h"
+#include "test.h"
+
+using namespace std;
+using namespace libcamera;
+
+static const ControlInfoMap Controls = {
+	{ &controls::AeEnable, ControlInfo(false, true) },
+	{ &controls::ExposureTime, ControlInfo(0, 999999) },
+	{ &controls::AnalogueGain, ControlInfo(1.0f, 32.0f) },
+	{ &controls::ColourGains, ControlInfo(0.0f, 32.0f) },
+	{ &controls::Brightness, ControlInfo(-1.0f, 1.0f) },
+};
+
+namespace libcamera {
+
+static bool operator==(const ControlInfoMap &lhs, const ControlInfoMap &rhs)
+{
+	return SerializationTest::equals(lhs, rhs);
+}
+
+} /* namespace libcamera */
+
+template<typename T>
+int testPodSerdes(T in)
+{
+	std::vector<uint8_t> buf;
+	std::vector<int32_t> fds;
+
+	std::tie(buf, fds) = IPADataSerializer<T>::serialize(in);
+	T out = IPADataSerializer<T>::deserialize(buf, fds);
+	if (in == out)
+		return TestPass;
+
+	char *name = abi::__cxa_demangle(typeid(T).name(), nullptr,
+					 nullptr, nullptr);
+	cerr << "Deserialized " << name << " doesn't match original" << endl;
+	free(name);
+	return TestFail;
+}
+
+template<typename T>
+int testVectorSerdes(const std::vector<T> &in,
+		     ControlSerializer *cs = nullptr)
+{
+	std::vector<uint8_t> buf;
+	std::vector<int32_t> fds;
+
+	std::tie(buf, fds) = IPADataSerializer<std::vector<T>>::serialize(in, cs);
+	std::vector<T> out = IPADataSerializer<std::vector<T>>::deserialize(buf, fds, cs);
+	if (in == out)
+		return TestPass;
+
+	char *name = abi::__cxa_demangle(typeid(T).name(), nullptr,
+					 nullptr, nullptr);
+	cerr << "Deserialized std::vector<" << name
+	     << "> doesn't match original" << endl;
+	free(name);
+	return TestFail;
+}
+
+template<typename K, typename V>
+int testMapSerdes(const std::map<K, V> &in,
+		  ControlSerializer *cs = nullptr)
+{
+	std::vector<uint8_t> buf;
+	std::vector<int32_t> fds;
+
+	std::tie(buf, fds) = IPADataSerializer<std::map<K, V>>::serialize(in, cs);
+	std::map<K, V> out = IPADataSerializer<std::map<K, V>>::deserialize(buf, fds, cs);
+	if (in == out)
+		return TestPass;
+
+	char *nameK = abi::__cxa_demangle(typeid(K).name(), nullptr,
+					  nullptr, nullptr);
+	char *nameV = abi::__cxa_demangle(typeid(V).name(), nullptr,
+					  nullptr, nullptr);
+	cerr << "Deserialized std::map<" << nameK << ", " << nameV
+	     << "> doesn't match original" << endl;
+	free(nameK);
+	free(nameV);
+	return TestFail;
+}
+
+class IPADataSerializerTest : public CameraTest, public Test
+{
+public:
+	IPADataSerializerTest()
+		: CameraTest("platform/vimc.0 Sensor B")
+	{
+	}
+
+protected:
+	int init() override
+	{
+		return status_;
+	}
+
+	int run() override
+	{
+		int ret;
+
+		ret = testControls();
+		if (ret != TestPass)
+			return ret;
+
+		ret = testVector();
+		if (ret != TestPass)
+			return ret;
+
+		ret = testMap();
+		if (ret != TestPass)
+			return ret;
+
+		ret = testPod();
+		if (ret != TestPass)
+			return ret;
+
+		return TestPass;
+	}
+
+private:
+	ControlList generateControlList(const ControlInfoMap &infoMap)
+	{
+		/* Create a control list with three controls. */
+		ControlList list(infoMap);
+
+		list.set(controls::Brightness, 0.5f);
+		list.set(controls::Contrast, 1.2f);
+		list.set(controls::Saturation, 0.2f);
+
+		return list;
+	}
+
+	int testControls()
+	{
+		ControlSerializer cs;
+
+		const ControlInfoMap &infoMap = camera_->controls();
+		ControlList list = generateControlList(infoMap);
+
+		std::vector<uint8_t> infoMapBuf;
+		std::tie(infoMapBuf, std::ignore) =
+			IPADataSerializer<ControlInfoMap>::serialize(infoMap, &cs);
+
+		std::vector<uint8_t> listBuf;
+		std::tie(listBuf, std::ignore) =
+			IPADataSerializer<ControlList>::serialize(list, &cs);
+
+		const ControlInfoMap infoMapOut =
+			IPADataSerializer<ControlInfoMap>::deserialize(infoMapBuf, &cs);
+
+		ControlList listOut = IPADataSerializer<ControlList>::deserialize(listBuf, &cs);
+
+		if (!SerializationTest::equals(infoMap, infoMapOut)) {
+			cerr << "Deserialized map doesn't match original" << endl;
+			return TestFail;
+		}
+
+		if (!SerializationTest::equals(list, listOut)) {
+			cerr << "Deserialized list doesn't match original" << endl;
+			return TestFail;
+		}
+
+		return TestPass;
+	}
+
+	int testVector()
+	{
+		ControlSerializer cs;
+
+		/*
+		 * We don't test FileDescriptor serdes because it dup()s, so we
+		 * can't check for equality.
+		 */
+		std::vector<uint8_t>  vecUint8  = { 1, 2, 3, 4, 5, 6 };
+		std::vector<uint16_t> vecUint16 = { 1, 2, 3, 4, 5, 6 };
+		std::vector<uint32_t> vecUint32 = { 1, 2, 3, 4, 5, 6 };
+		std::vector<uint64_t> vecUint64 = { 1, 2, 3, 4, 5, 6 };
+		std::vector<int8_t>   vecInt8   = { 1, 2, 3, -4, 5, -6 };
+		std::vector<int16_t>  vecInt16  = { 1, 2, 3, -4, 5, -6 };
+		std::vector<int32_t>  vecInt32  = { 1, 2, 3, -4, 5, -6 };
+		std::vector<int64_t>  vecInt64  = { 1, 2, 3, -4, 5, -6 };
+		std::vector<float>    vecFloat  = { 1.1, 2.2, 3.3, -4.4, 5.5, -6.6 };
+		std::vector<double>   vecDouble = { 1.1, 2.2, 3.3, -4.4, 5.5, -6.6 };
+		std::vector<bool>     vecBool   = { true, true, false, false, true, false };
+		std::vector<std::string>   vecString = { "foo", "bar", "baz" };
+		std::vector<ControlInfoMap> vecControlInfoMap = {
+			camera_->controls(),
+			Controls,
+		};
+
+		std::vector<uint8_t> buf;
+		std::vector<int32_t> fds;
+
+		if (testVectorSerdes(vecUint8) != TestPass)
+			return TestFail;
+
+		if (testVectorSerdes(vecUint16) != TestPass)
+			return TestFail;
+
+		if (testVectorSerdes(vecUint32) != TestPass)
+			return TestFail;
+
+		if (testVectorSerdes(vecUint64) != TestPass)
+			return TestFail;
+
+		if (testVectorSerdes(vecInt8) != TestPass)
+			return TestFail;
+
+		if (testVectorSerdes(vecInt16) != TestPass)
+			return TestFail;
+
+		if (testVectorSerdes(vecInt32) != TestPass)
+			return TestFail;
+
+		if (testVectorSerdes(vecInt64) != TestPass)
+			return TestFail;
+
+		if (testVectorSerdes(vecFloat) != TestPass)
+			return TestFail;
+
+		if (testVectorSerdes(vecDouble) != TestPass)
+			return TestFail;
+
+		if (testVectorSerdes(vecBool) != TestPass)
+			return TestFail;
+
+		if (testVectorSerdes(vecString) != TestPass)
+			return TestFail;
+
+		if (testVectorSerdes(vecControlInfoMap, &cs) != TestPass)
+			return TestFail;
+
+		return TestPass;
+	}
+
+	int testMap()
+	{
+		ControlSerializer cs;
+
+		/*
+		 * Realistically, only string and integral keys.
+		 * Test simple, complex, and nested compound value.
+		 */
+		std::map<uint64_t, std::string> mapUintStr =
+			{ { 101, "foo" }, { 102, "bar" }, { 103, "baz" } };
+		std::map<int64_t, std::string> mapIntStr =
+			{ { 101, "foo" }, { -102, "bar" }, { -103, "baz" } };
+		std::map<std::string, std::string> mapStrStr =
+			{ { "a", "foo" }, { "b", "bar" }, { "c", "baz" } };
+		std::map<uint64_t, ControlInfoMap> mapUintCIM =
+			{ { 201, camera_->controls() }, { 202, Controls } };
+		std::map<int64_t, ControlInfoMap> mapIntCIM =
+			{ { 201, camera_->controls() }, { -202, Controls } };
+		std::map<std::string, ControlInfoMap> mapStrCIM =
+			{ { "a", camera_->controls() }, { "b", Controls } };
+		std::map<uint64_t, std::vector<uint8_t>> mapUintBVec =
+			{ { 301, { 1, 2, 3 } }, { 302, { 4, 5, 6 } }, { 303, { 7, 8, 9 } } };
+		std::map<int64_t, std::vector<uint8_t>> mapIntBVec =
+			{ { 301, { 1, 2, 3 } }, { -302, { 4, 5, 6} }, { -303, { 7, 8, 9 } } };
+		std::map<std::string, std::vector<uint8_t>> mapStrBVec =
+			{ { "a", { 1, 2, 3 } }, { "b", { 4, 5, 6 } }, { "c", { 7, 8, 9 } } };
+
+		std::vector<uint8_t> buf;
+		std::vector<int32_t> fds;
+
+		if (testMapSerdes(mapUintStr) != TestPass)
+			return TestFail;
+
+		if (testMapSerdes(mapIntStr) != TestPass)
+			return TestFail;
+
+		if (testMapSerdes(mapStrStr) != TestPass)
+			return TestFail;
+
+		if (testMapSerdes(mapUintCIM, &cs) != TestPass)
+			return TestFail;
+
+		if (testMapSerdes(mapIntCIM,  &cs) != TestPass)
+			return TestFail;
+
+		if (testMapSerdes(mapStrCIM,  &cs) != TestPass)
+			return TestFail;
+
+		if (testMapSerdes(mapUintBVec) != TestPass)
+			return TestFail;
+
+		if (testMapSerdes(mapIntBVec) != TestPass)
+			return TestFail;
+
+		if (testMapSerdes(mapStrBVec) != TestPass)
+			return TestFail;
+
+		return TestPass;
+	}
+
+	int testPod()
+	{
+		uint32_t u32min = std::numeric_limits<uint32_t>::min();
+		uint32_t u32max = std::numeric_limits<uint32_t>::max();
+		uint32_t u32one = 1;
+		int32_t  i32min = std::numeric_limits<int32_t>::min();
+		int32_t  i32max = std::numeric_limits<int32_t>::max();
+		int32_t  i32one = 1;
+
+		uint64_t u64min = std::numeric_limits<uint64_t>::min();
+		uint64_t u64max = std::numeric_limits<uint64_t>::max();
+		uint64_t u64one = 1;
+		int64_t  i64min = std::numeric_limits<int64_t>::min();
+		int64_t  i64max = std::numeric_limits<int64_t>::max();
+		int64_t  i64one = 1;
+
+		float  flow = std::numeric_limits<float>::lowest();
+		float  fmin = std::numeric_limits<float>::min();
+		float  fmax = std::numeric_limits<float>::max();
+		float  falmostOne = 1 + 1.0e-37;
+		double dlow = std::numeric_limits<double>::lowest();
+		double dmin = std::numeric_limits<double>::min();
+		double dmax = std::numeric_limits<double>::max();
+		double dalmostOne = 1 + 1.0e-307;
+
+		bool t = true;
+		bool f = false;
+
+		std::stringstream ss;
+		for (unsigned int i = 0; i < (1 << 11); i++)
+			ss << "0123456789";
+
+		std::string strLong = ss.str();
+		std::string strEmpty = "";
+
+		std::vector<uint8_t> buf;
+		std::vector<int32_t> fds;
+
+		if (testPodSerdes(u32min) != TestPass)
+			return TestFail;
+
+		if (testPodSerdes(u32max) != TestPass)
+			return TestFail;
+
+		if (testPodSerdes(u32one) != TestPass)
+			return TestFail;
+
+		if (testPodSerdes(i32min) != TestPass)
+			return TestFail;
+
+		if (testPodSerdes(i32max) != TestPass)
+			return TestFail;
+
+		if (testPodSerdes(i32one) != TestPass)
+			return TestFail;
+
+		if (testPodSerdes(u64min) != TestPass)
+			return TestFail;
+
+		if (testPodSerdes(u64max) != TestPass)
+			return TestFail;
+
+		if (testPodSerdes(u64one) != TestPass)
+			return TestFail;
+
+		if (testPodSerdes(i64min) != TestPass)
+			return TestFail;
+
+		if (testPodSerdes(i64max) != TestPass)
+			return TestFail;
+
+		if (testPodSerdes(i64one) != TestPass)
+			return TestFail;
+
+		if (testPodSerdes(flow) != TestPass)
+			return TestFail;
+
+		if (testPodSerdes(fmin) != TestPass)
+			return TestFail;
+
+		if (testPodSerdes(fmax) != TestPass)
+			return TestFail;
+
+		if (testPodSerdes(falmostOne) != TestPass)
+			return TestFail;
+
+		if (testPodSerdes(dlow) != TestPass)
+			return TestFail;
+
+		if (testPodSerdes(dmin) != TestPass)
+			return TestFail;
+
+		if (testPodSerdes(dmax) != TestPass)
+			return TestFail;
+
+		if (testPodSerdes(dalmostOne) != TestPass)
+			return TestFail;
+
+		if (testPodSerdes(t) != TestPass)
+			return TestFail;
+
+		if (testPodSerdes(f) != TestPass)
+			return TestFail;
+
+		if (testPodSerdes(strLong) != TestPass)
+			return TestFail;
+
+		if (testPodSerdes(strEmpty) != TestPass)
+			return TestFail;
+
+		return TestPass;
+	}
+};
+
+TEST_REGISTER(IPADataSerializerTest)
diff --git a/test/serialization/meson.build b/test/serialization/meson.build
index 6fc54f6b..a4636337 100644
--- a/test/serialization/meson.build
+++ b/test/serialization/meson.build
@@ -2,6 +2,7 @@
 
 serialization_tests = [
     ['control_serialization',     'control_serialization.cpp'],
+    ['ipa_data_serializer_test',  'ipa_data_serializer_test.cpp'],
 ]
 
 foreach t : serialization_tests
-- 
2.27.0



More information about the libcamera-devel mailing list