libcamera/test/serialization/ipa_data_serializer_test.cpp
Jacopo Mondi b48db3c489 libcamera: controls: Create ControlInfoMap with ControlIdMap
ControlInfoMap does not have a ControlId map associated, but rather
creates one with the generateIdMap() function at creation time.

As a consequence, when in the need to de-serialize a ControlInfoMap all
the ControlId it contains are created by the deserializer instance, not
being able to discern if the controls the ControlIdMap refers to are the
global libcamera controls (and properties) or instances local to the
V4L2 device that has first initialized the controls.

As a consequence the ControlId stored in a de-serialized map will always
be newly created entities, preventing lookup by ControlId reference on a
de-serialized ControlInfoMap.

In order to make it possible to use globally available ControlId
instances whenever possible, create ControlInfoMap with a reference to
an externally allocated ControlIdMap instead of generating one
internally.

As a consequence the class constructors take and additional argument,
which might be not pleasant to type in, but enforces the concepts that
ControlInfoMap should be created with controls part of the same id map.

As the ControlIdMap the ControlInfoMap refers to needs to be allocated
externally:
- Use the globally available controls::controls (or
  properties::properties) id map when referring to libcamera controls
- The V4L2 device that creates ControlInfoMap by parsing the device's
  controls has to allocate a ControlIdMap
- The ControlSerializer that de-serializes a ControlInfoMap has to
  create and store the ControlIdMap the de-serialized info map refers to

Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
Reviewed-by: Paul Elder <paul.elder@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2021-08-12 10:06:25 +02:00

440 lines
11 KiB
C++

/* 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/base/thread.h>
#include <libcamera/base/timer.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 "serialization_test.h"
#include "test.h"
using namespace std;
using namespace libcamera;
static const ControlInfoMap Controls = ControlInfoMap({
{ &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) },
}, controls::controls);
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)