[libcamera-devel] [PATCH v5 20/23] tests: Add IPADataSerializer test
Paul Elder
paul.elder at ideasonboard.com
Sat Dec 5 11:31:03 CET 2020
Test the IPADataSerializer for controls, vectors, maps, and PODs of
built-in types.
Signed-off-by: Paul Elder <paul.elder at ideasonboard.com>
---
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 | 465 ++++++++++++++++++
test/serialization/meson.build | 3 +-
2 files changed, 467 insertions(+), 1 deletion(-)
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..0cc1cb91
--- /dev/null
+++ b/test/serialization/ipa_data_serializer_test.cpp
@@ -0,0 +1,465 @@
+/* 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 <fcntl.h>
+#include <iostream>
+#include <limits>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <tuple>
+#include <unistd.h>
+#include <vector>
+
+#include <libcamera/ipa/raspberrypi.h>
+
+#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;
+
+template<typename T>
+bool isVectorEqual(vector<T> &vecA, vector<T> &vecB)
+{
+ if (vecA.size() != vecB.size())
+ return false;
+
+ size_t len = vecA.size();
+ for (unsigned int i = 0; i < len; i++)
+ if (vecA[i] != vecB[i])
+ goto nequal;
+
+ return true;
+
+nequal:
+ cerr << "lhs: { ";
+ for (const auto &value : vecA)
+ cerr << value << ", ";
+ cerr << "}" << endl;
+
+ cerr << "rhs: { ";
+ for (const auto &value : vecB)
+ cerr << value << ", ";
+ cerr << "}" << endl;
+
+ return false;
+}
+
+template<>
+bool isVectorEqual<ControlInfoMap>(vector<ControlInfoMap> &vecA,
+ vector<ControlInfoMap> &vecB)
+{
+ if (vecA.size() != vecB.size())
+ return false;
+
+ size_t len = vecA.size();
+ for (unsigned int i = 0; i < len; i++)
+ if (!SerializationTest::equals(vecA[i], vecB[i]))
+ return false;
+
+ return true;
+}
+
+template<typename K, typename V>
+bool isMapEqual(map<K, V> &mapA, map<K, V> &mapB)
+{
+ if (mapA == mapB)
+ return true;
+
+ cerr << "lhs: { ";
+ for (const auto &value : mapA)
+ cerr << value.first << " : " << value.second << ", ";
+ cerr << "}" << endl;
+
+ cerr << "rhs: { ";
+ for (const auto &value : mapB)
+ cerr << value.first << " : " << value.second << ", ";
+ cerr << "}" << endl;
+
+ return false;
+}
+
+template<typename K, typename V>
+bool isMapToCimEqual(map<K, V> &mapA, map<K, V> &mapB)
+{
+ bool isEqual = true;
+
+ auto itA = mapA.begin();
+ auto itB = mapB.begin();
+ while (true) {
+ bool endA = (itA == mapA.end());
+ bool endB = (itB == mapB.end());
+
+ if (endA and endB)
+ break;
+
+ if (!endA && (endB || itA->first < itB->first)) {
+ cerr << "key: " << itA->first << " not in mapB" << endl;
+ isEqual = false;
+ itA++;
+ continue;
+ }
+
+ if (endA || itB->first < itA->first) {
+ cerr << "key: " << itB->first << " not in mapA" << endl;
+ isEqual = false;
+ itB++;
+ continue;
+ }
+
+ if (!SerializationTest::equals(itA->second, itB->second)) {
+ cerr << "key " << itA->first
+ << " has different values" << endl;
+ isEqual = false;
+ }
+
+ itA++;
+ itB++;
+ }
+
+ return isEqual;
+}
+
+template<typename K, typename V>
+bool isMapToVecEqual(map<K, V> &mapA, map<K, V> &mapB)
+{
+ if (mapA == mapB)
+ return true;
+
+ cerr << "lhs: { ";
+ for (const auto &value : mapA) {
+ cerr << value.first << " : { ";
+ for (const auto &v : value.second)
+ cerr << v << ", ";
+ cerr << "}" << endl;
+ }
+ cerr << "}" << endl;
+
+ cerr << "rhs: { ";
+ for (const auto &value : mapB) {
+ cerr << value.first << " : { ";
+ for (const auto &v : value.second)
+ cerr << v << ", ";
+ cerr << "}" << endl;
+ }
+ cerr << "}" << endl;
+
+ return false;
+}
+
+class IPADataSerializerTest : public CameraTest, public Test
+{
+public:
+ IPADataSerializerTest()
+ : CameraTest("platform/vimc.0 Sensor B")
+ {
+ RPi::initializeRPiControls();
+ }
+
+protected:
+ int init() override
+ {
+ return status_;
+ }
+
+ int run() override
+ {
+ int finalRet = TestPass;
+ int ret;
+
+ ret = testControls();
+ if (ret != TestPass)
+ finalRet = ret;
+
+ ret = testVector();
+ if (ret != TestPass)
+ finalRet = ret;
+
+ ret = testMap();
+ if (ret != TestPass)
+ finalRet = ret;
+
+ ret = testPod();
+ if (ret != TestPass)
+ finalRet = ret;
+
+ return finalRet;
+ }
+
+private:
+ ControlList generateControlListA()
+ {
+ /* Create a control list with three controls. */
+ const ControlInfoMap &infoMap = camera_->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 = generateControlListA();
+
+ vector<uint8_t> infoMapBuf;
+ tie(infoMapBuf, ignore) =
+ IPADataSerializer<ControlInfoMap>::serialize(infoMap, &cs);
+
+ vector<uint8_t> listBuf;
+ tie(listBuf, ignore) =
+ IPADataSerializer<ControlList>::serialize(list, *list.infoMap(), &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()
+ {
+
+#define TEST_VEC_SERDES(type, vec, cs) \
+tie(buf, fds) = IPADataSerializer<vector<type>>::serialize(vec, cs); \
+vector<type> vec##Out = \
+ IPADataSerializer<vector<type>>::deserialize(buf, fds, cs); \
+ret = isVectorEqual<type>(vec, vec##Out); \
+if (!ret) { \
+ cerr << "Deserialized vector " << #vec << " doesn't match original" << endl;\
+ finalRet = TestFail; \
+}
+
+ ControlSerializer cs;
+
+ /*
+ * We don't test FileDescriptor serdes because it dup()s, so we
+ * can't check for equality.
+ */
+ vector<uint8_t> vecUint8 = { 1, 2, 3, 4, 5, 6 };
+ vector<uint16_t> vecUint16 = { 1, 2, 3, 4, 5, 6 };
+ vector<uint32_t> vecUint32 = { 1, 2, 3, 4, 5, 6 };
+ vector<uint64_t> vecUint64 = { 1, 2, 3, 4, 5, 6 };
+ vector<int8_t> vecInt8 = { 1, 2, 3, -4, 5, -6 };
+ vector<int16_t> vecInt16 = { 1, 2, 3, -4, 5, -6 };
+ vector<int32_t> vecInt32 = { 1, 2, 3, -4, 5, -6 };
+ vector<int64_t> vecInt64 = { 1, 2, 3, -4, 5, -6 };
+ vector<float> vecFloat = { 1.1, 2.2, 3.3, -4.4, 5.5, -6.6 };
+ vector<double> vecDouble = { 1.1, 2.2, 3.3, -4.4, 5.5, -6.6 };
+ vector<bool> vecBool = { true, true, false, false, true, false };
+ vector<string> vecString = { "foo", "bar", "baz" };
+ vector<ControlInfoMap> vecControlInfoMap = {
+ camera_->controls(),
+ RPi::controls,
+ };
+
+ vector<uint8_t> buf;
+ vector<int32_t> fds;
+ int finalRet = TestPass;
+ int ret;
+
+ TEST_VEC_SERDES(uint8_t, vecUint8, nullptr);
+ TEST_VEC_SERDES(uint16_t, vecUint16, nullptr);
+ TEST_VEC_SERDES(uint32_t, vecUint32, nullptr);
+ TEST_VEC_SERDES(uint64_t, vecUint64, nullptr);
+ TEST_VEC_SERDES(int8_t, vecInt8, nullptr);
+ TEST_VEC_SERDES(int16_t, vecInt16, nullptr);
+ TEST_VEC_SERDES(int32_t, vecInt32, nullptr);
+ TEST_VEC_SERDES(int64_t, vecInt64, nullptr);
+ TEST_VEC_SERDES(float, vecFloat, nullptr);
+ TEST_VEC_SERDES(double, vecDouble, nullptr);
+ TEST_VEC_SERDES(bool, vecBool, nullptr);
+ TEST_VEC_SERDES(string, vecString, nullptr);
+ TEST_VEC_SERDES(ControlInfoMap, vecControlInfoMap, &cs);
+
+ return finalRet;
+ }
+
+ int testMap()
+ {
+
+#define TEST_MAP_SERDES(ktype, vtype, m, cs) \
+tie(buf, fds) = IPADataSerializer<map<ktype, vtype>>::serialize(m, cs); \
+map<ktype, vtype> m##Out = \
+ IPADataSerializer<map<ktype, vtype>>::deserialize(buf, fds, cs);\
+ret = isMapEqual<ktype, vtype>(m, m##Out); \
+if (!ret) { \
+ cerr << "Deserialized map " << #m << " doesn't match original" << endl;\
+ finalRet = TestFail; \
+}
+
+#define TEST_MAP_CIM_SERDES(ktype, vtype, m, cs) \
+tie(buf, fds) = IPADataSerializer<map<ktype, vtype>>::serialize(m, cs); \
+map<ktype, vtype> m##Out = \
+ IPADataSerializer<map<ktype, vtype>>::deserialize(buf, fds, cs);\
+ret = isMapToCimEqual<ktype, vtype>(m, m##Out); \
+if (!ret) { \
+ cerr << "Deserialized map " << #m << " doesn't match original" << endl;\
+ finalRet = TestFail; \
+}
+
+#define TEST_MAP_VEC_SERDES(ktype, vtype, m, cs) \
+tie(buf, fds) = IPADataSerializer<map<ktype, vtype>>::serialize(m, cs); \
+map<ktype, vtype> m##Out = \
+ IPADataSerializer<map<ktype, vtype>>::deserialize(buf, fds, cs);\
+ret = isMapToVecEqual<ktype, vtype>(m, m##Out); \
+if (!ret) { \
+ cerr << "Deserialized map " << #m << " doesn't match original" << endl;\
+ finalRet = TestFail; \
+}
+
+ ControlSerializer cs;
+
+ /*
+ * Realistically, only string and integral keys.
+ * Test simple, complex, and nested compound value.
+ */
+ map<uint64_t, string> mapUintStr =
+ { {101, "foo"}, {102, "bar"}, {103, "baz"} };
+ map<int64_t, string> mapIntStr =
+ { {101, "foo"}, {-102, "bar"}, {-103, "baz"} };
+ map<string, string> mapStrStr =
+ { {"a", "foo"}, {"b", "bar"}, {"c", "baz"} };
+ map<uint64_t, ControlInfoMap> mapUintCIM =
+ { {201, camera_->controls()}, {202, RPi::controls} };
+ map<int64_t, ControlInfoMap> mapIntCIM =
+ { {201, camera_->controls()}, {-202, RPi::controls} };
+ map<string, ControlInfoMap> mapStrCIM =
+ { {"a", camera_->controls()}, {"b", RPi::controls} };
+ map<uint64_t, vector<uint8_t>> mapUintBVec =
+ { {301, { 1, 2, 3 }}, {302, {4, 5, 6}}, {303, {7, 8, 9}} };
+ map<int64_t, vector<uint8_t>> mapIntBVec =
+ { {301, { 1, 2, 3 }}, {-302, {4, 5, 6}}, {-303, {7, 8, 9}} };
+ map<string, vector<uint8_t>> mapStrBVec =
+ { {"a", { 1, 2, 3 }}, {"b", {4, 5, 6}}, {"c", {7, 8, 9}} };
+
+ vector<uint8_t> buf;
+ vector<int32_t> fds;
+ int finalRet = TestPass;
+ int ret;
+
+ TEST_MAP_SERDES(uint64_t, string, mapUintStr, nullptr);
+ TEST_MAP_SERDES(int64_t, string, mapIntStr, nullptr);
+ TEST_MAP_SERDES(string, string, mapStrStr, nullptr);
+ TEST_MAP_CIM_SERDES(uint64_t, ControlInfoMap, mapUintCIM, &cs);
+ TEST_MAP_CIM_SERDES(int64_t, ControlInfoMap, mapIntCIM, &cs);
+ TEST_MAP_CIM_SERDES(string, ControlInfoMap, mapStrCIM, &cs);
+ TEST_MAP_VEC_SERDES(uint64_t, vector<uint8_t>, mapUintBVec, nullptr);
+ TEST_MAP_VEC_SERDES(int64_t, vector<uint8_t>, mapIntBVec, nullptr);
+ TEST_MAP_VEC_SERDES(string, vector<uint8_t>, mapStrBVec, nullptr);
+
+ return finalRet;
+ }
+
+ int testPod()
+ {
+
+#define TEST_POD_SERDES(type, var) \
+tie(buf, fds) = IPADataSerializer<type>::serialize(var); \
+type var##Out = \
+ IPADataSerializer<type>::deserialize(buf, fds); \
+ret = (var == var##Out); \
+if (!ret) { \
+ cerr << "Deserialized " << #var << " as " << var##Out \
+ << ", expected " << var << endl; \
+ finalRet = TestFail; \
+}
+
+ uint32_t u32min = numeric_limits<uint32_t>::min();
+ uint32_t u32max = numeric_limits<uint32_t>::max();
+ uint32_t u32one = 1;
+ int32_t i32min = numeric_limits<int32_t>::min();
+ int32_t i32max = numeric_limits<int32_t>::max();
+ int32_t i32one = 1;
+
+ uint64_t u64min = numeric_limits<uint64_t>::min();
+ uint64_t u64max = numeric_limits<uint64_t>::max();
+ uint64_t u64one = 1;
+ int64_t i64min = numeric_limits<int64_t>::min();
+ int64_t i64max = numeric_limits<int64_t>::max();
+ int64_t i64one = 1;
+
+ float flow = numeric_limits<float>::lowest();
+ float fmin = numeric_limits<float>::min();
+ float fmax = numeric_limits<float>::max();
+ float falmostOne = 1 + 1.0e-37;
+ double dlow = numeric_limits<double>::lowest();
+ double dmin = numeric_limits<double>::min();
+ double dmax = numeric_limits<double>::max();
+ double dalmostOne = 1 + 1.0e-307;
+
+ bool t = true;
+ bool f = false;
+
+ stringstream ss;
+ for (unsigned int i = 0; i < (1 << 21); i++)
+ ss << "0123456789";
+
+ string strLong = ss.str();
+ string strEmpty = "";
+
+ vector<uint8_t> buf;
+ vector<int32_t> fds;
+ int finalRet = TestPass;
+ int ret;
+
+ TEST_POD_SERDES(uint32_t, u32min);
+ TEST_POD_SERDES(uint32_t, u32max);
+ TEST_POD_SERDES(uint32_t, u32one);
+ TEST_POD_SERDES(int32_t, i32min);
+ TEST_POD_SERDES(int32_t, i32max);
+ TEST_POD_SERDES(int32_t, i32one);
+ TEST_POD_SERDES(uint64_t, u64min);
+ TEST_POD_SERDES(uint64_t, u64max);
+ TEST_POD_SERDES(uint64_t, u64one);
+ TEST_POD_SERDES(int64_t, i64min);
+ TEST_POD_SERDES(int64_t, i64max);
+ TEST_POD_SERDES(int64_t, i64one);
+ TEST_POD_SERDES(float, flow);
+ TEST_POD_SERDES(float, fmin);
+ TEST_POD_SERDES(float, fmax);
+ TEST_POD_SERDES(float, falmostOne);
+ TEST_POD_SERDES(double, dlow);
+ TEST_POD_SERDES(double, dmin);
+ TEST_POD_SERDES(double, dmax);
+ TEST_POD_SERDES(double, dalmostOne);
+ TEST_POD_SERDES(bool, t);
+ TEST_POD_SERDES(bool, f);
+ TEST_POD_SERDES(string, strLong);
+ TEST_POD_SERDES(string, strEmpty);
+
+ return finalRet;
+ }
+};
+
+TEST_REGISTER(IPADataSerializerTest)
diff --git a/test/serialization/meson.build b/test/serialization/meson.build
index a9d9cbcb..c140a31c 100644
--- a/test/serialization/meson.build
+++ b/test/serialization/meson.build
@@ -1,7 +1,8 @@
# SPDX-License-Identifier: CC0-1.0
serialization_tests = [
- [ 'control_serialization', 'control_serialization.cpp' ],
+ [ '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