/* * Copyright (c) 2020-2021 Vasile Vilvoiu * * specgram is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include "test.hpp" #include "../src/input-parser.hpp" #include const std::vector ALL_DATA_TYPES { DataType::kSignedInt8, DataType::kSignedInt16, DataType::kSignedInt32, DataType::kSignedInt64, DataType::kUnsignedInt8, DataType::kUnsignedInt16, DataType::kUnsignedInt32, DataType::kUnsignedInt64, DataType::kFloat32, DataType::kFloat64 }; TEST(TestInputParser, IsSigned) { for (bool is_complex : { false, true }) { /* direct instantiate */ EXPECT_FALSE(IntegerInputParser(1.0, is_complex).IsSigned()); EXPECT_FALSE(IntegerInputParser(1.0, is_complex).IsSigned()); EXPECT_FALSE(IntegerInputParser(1.0, is_complex).IsSigned()); EXPECT_FALSE(IntegerInputParser(1.0, is_complex).IsSigned()); EXPECT_TRUE(IntegerInputParser(1.0, is_complex).IsSigned()); EXPECT_TRUE(IntegerInputParser(1.0, is_complex).IsSigned()); EXPECT_TRUE(IntegerInputParser(1.0, is_complex).IsSigned()); EXPECT_TRUE(IntegerInputParser(1.0, is_complex).IsSigned()); EXPECT_TRUE(FloatInputParser(1.0, is_complex).IsSigned()); EXPECT_TRUE(FloatInputParser(1.0, is_complex).IsSigned()); /* factory */ EXPECT_FALSE(InputParser::Build(DataType::kUnsignedInt8, 1.0, is_complex)->IsSigned()); EXPECT_FALSE(InputParser::Build(DataType::kUnsignedInt16, 1.0, is_complex)->IsSigned()); EXPECT_FALSE(InputParser::Build(DataType::kUnsignedInt32, 1.0, is_complex)->IsSigned()); EXPECT_FALSE(InputParser::Build(DataType::kUnsignedInt64, 1.0, is_complex)->IsSigned()); EXPECT_TRUE(InputParser::Build(DataType::kSignedInt8, 1.0, is_complex)->IsSigned()); EXPECT_TRUE(InputParser::Build(DataType::kSignedInt16, 1.0, is_complex)->IsSigned()); EXPECT_TRUE(InputParser::Build(DataType::kSignedInt32, 1.0, is_complex)->IsSigned()); EXPECT_TRUE(InputParser::Build(DataType::kSignedInt64, 1.0, is_complex)->IsSigned()); EXPECT_TRUE(InputParser::Build(DataType::kFloat32, 1.0, is_complex)->IsSigned()); EXPECT_TRUE(InputParser::Build(DataType::kFloat64, 1.0, is_complex)->IsSigned()); } } TEST(TestInputParser, IsFloatingPoint) { for (bool is_complex : { false, true }) { /* direct instantiate */ EXPECT_FALSE(IntegerInputParser(1.0, is_complex).IsFloatingPoint()); EXPECT_FALSE(IntegerInputParser(1.0, is_complex).IsFloatingPoint()); EXPECT_FALSE(IntegerInputParser(1.0, is_complex).IsFloatingPoint()); EXPECT_FALSE(IntegerInputParser(1.0, is_complex).IsFloatingPoint()); EXPECT_FALSE(IntegerInputParser(1.0, is_complex).IsFloatingPoint()); EXPECT_FALSE(IntegerInputParser(1.0, is_complex).IsFloatingPoint()); EXPECT_FALSE(IntegerInputParser(1.0, is_complex).IsFloatingPoint()); EXPECT_FALSE(IntegerInputParser(1.0, is_complex).IsFloatingPoint()); EXPECT_TRUE(FloatInputParser(1.0, is_complex).IsFloatingPoint()); EXPECT_TRUE(FloatInputParser(1.0, is_complex).IsFloatingPoint()); /* factory */ EXPECT_FALSE(InputParser::Build(DataType::kUnsignedInt8, 1.0, is_complex)->IsFloatingPoint()); EXPECT_FALSE(InputParser::Build(DataType::kUnsignedInt16, 1.0, is_complex)->IsFloatingPoint()); EXPECT_FALSE(InputParser::Build(DataType::kUnsignedInt32, 1.0, is_complex)->IsFloatingPoint()); EXPECT_FALSE(InputParser::Build(DataType::kUnsignedInt64, 1.0, is_complex)->IsFloatingPoint()); EXPECT_FALSE(InputParser::Build(DataType::kSignedInt8, 1.0, is_complex)->IsFloatingPoint()); EXPECT_FALSE(InputParser::Build(DataType::kSignedInt16, 1.0, is_complex)->IsFloatingPoint()); EXPECT_FALSE(InputParser::Build(DataType::kSignedInt32, 1.0, is_complex)->IsFloatingPoint()); EXPECT_FALSE(InputParser::Build(DataType::kSignedInt64, 1.0, is_complex)->IsFloatingPoint()); EXPECT_TRUE(InputParser::Build(DataType::kFloat32, 1.0, is_complex)->IsFloatingPoint()); EXPECT_TRUE(InputParser::Build(DataType::kFloat64, 1.0, is_complex)->IsFloatingPoint()); } } TEST(TestInputParser, IsComplex) { for (bool is_complex : { false, true }) { /* direct instantiate */ EXPECT_EQ(is_complex, IntegerInputParser(1.0, is_complex).IsComplex()); EXPECT_EQ(is_complex, IntegerInputParser(1.0, is_complex).IsComplex()); EXPECT_EQ(is_complex, IntegerInputParser(1.0, is_complex).IsComplex()); EXPECT_EQ(is_complex, IntegerInputParser(1.0, is_complex).IsComplex()); EXPECT_EQ(is_complex, IntegerInputParser(1.0, is_complex).IsComplex()); EXPECT_EQ(is_complex, IntegerInputParser(1.0, is_complex).IsComplex()); EXPECT_EQ(is_complex, IntegerInputParser(1.0, is_complex).IsComplex()); EXPECT_EQ(is_complex, IntegerInputParser(1.0, is_complex).IsComplex()); EXPECT_EQ(is_complex, FloatInputParser(1.0, is_complex).IsComplex()); EXPECT_EQ(is_complex, FloatInputParser(1.0, is_complex).IsComplex()); /* factory */ EXPECT_EQ(is_complex, InputParser::Build(DataType::kUnsignedInt8, 1.0, is_complex)->IsComplex()); EXPECT_EQ(is_complex, InputParser::Build(DataType::kUnsignedInt16, 1.0, is_complex)->IsComplex()); EXPECT_EQ(is_complex, InputParser::Build(DataType::kUnsignedInt32, 1.0, is_complex)->IsComplex()); EXPECT_EQ(is_complex, InputParser::Build(DataType::kUnsignedInt64, 1.0, is_complex)->IsComplex()); EXPECT_EQ(is_complex, InputParser::Build(DataType::kSignedInt8, 1.0, is_complex)->IsComplex()); EXPECT_EQ(is_complex, InputParser::Build(DataType::kSignedInt16, 1.0, is_complex)->IsComplex()); EXPECT_EQ(is_complex, InputParser::Build(DataType::kSignedInt32, 1.0, is_complex)->IsComplex()); EXPECT_EQ(is_complex, InputParser::Build(DataType::kSignedInt64, 1.0, is_complex)->IsComplex()); EXPECT_EQ(is_complex, InputParser::Build(DataType::kFloat32, 1.0, is_complex)->IsComplex()); EXPECT_EQ(is_complex, InputParser::Build(DataType::kFloat64, 1.0, is_complex)->IsComplex()); } } TEST(TestInputParser, GetDataTypeSize) { for (bool is_complex : { false, true }) { /* direct instantiate */ EXPECT_EQ(1 * (is_complex ? 2 : 1), IntegerInputParser(1.0, is_complex).GetDataTypeSize()); EXPECT_EQ(2 * (is_complex ? 2 : 1), IntegerInputParser(1.0, is_complex).GetDataTypeSize()); EXPECT_EQ(4 * (is_complex ? 2 : 1), IntegerInputParser(1.0, is_complex).GetDataTypeSize()); EXPECT_EQ(8 * (is_complex ? 2 : 1), IntegerInputParser(1.0, is_complex).GetDataTypeSize()); EXPECT_EQ(1 * (is_complex ? 2 : 1), IntegerInputParser(1.0, is_complex).GetDataTypeSize()); EXPECT_EQ(2 * (is_complex ? 2 : 1), IntegerInputParser(1.0, is_complex).GetDataTypeSize()); EXPECT_EQ(4 * (is_complex ? 2 : 1), IntegerInputParser(1.0, is_complex).GetDataTypeSize()); EXPECT_EQ(8 * (is_complex ? 2 : 1), IntegerInputParser(1.0, is_complex).GetDataTypeSize()); EXPECT_EQ(4 * (is_complex ? 2 : 1), FloatInputParser(1.0, is_complex).GetDataTypeSize()); EXPECT_EQ(8 * (is_complex ? 2 : 1), FloatInputParser(1.0, is_complex).GetDataTypeSize()); /* factory */ EXPECT_EQ(1 * (is_complex ? 2 : 1), InputParser::Build(DataType::kUnsignedInt8, 1.0, is_complex)->GetDataTypeSize()); EXPECT_EQ(2 * (is_complex ? 2 : 1), InputParser::Build(DataType::kUnsignedInt16, 1.0, is_complex)->GetDataTypeSize()); EXPECT_EQ(4 * (is_complex ? 2 : 1), InputParser::Build(DataType::kUnsignedInt32, 1.0, is_complex)->GetDataTypeSize()); EXPECT_EQ(8 * (is_complex ? 2 : 1), InputParser::Build(DataType::kUnsignedInt64, 1.0, is_complex)->GetDataTypeSize()); EXPECT_EQ(1 * (is_complex ? 2 : 1), InputParser::Build(DataType::kSignedInt8, 1.0, is_complex)->GetDataTypeSize()); EXPECT_EQ(2 * (is_complex ? 2 : 1), InputParser::Build(DataType::kSignedInt16, 1.0, is_complex)->GetDataTypeSize()); EXPECT_EQ(4 * (is_complex ? 2 : 1), InputParser::Build(DataType::kSignedInt32, 1.0, is_complex)->GetDataTypeSize()); EXPECT_EQ(8 * (is_complex ? 2 : 1), InputParser::Build(DataType::kSignedInt64, 1.0, is_complex)->GetDataTypeSize()); EXPECT_EQ(4 * (is_complex ? 2 : 1), InputParser::Build(DataType::kFloat32, 1.0, is_complex)->GetDataTypeSize()); EXPECT_EQ(8 * (is_complex ? 2 : 1), InputParser::Build(DataType::kFloat64, 1.0, is_complex)->GetDataTypeSize()); } } TEST(TestInputParser, ParseBlockWrongSize) { for (bool is_complex : { false, true }) { for (auto dt : ALL_DATA_TYPES) { auto parser = InputParser::Build(dt, 1.0, is_complex); for (size_t bs = 0; bs < 512; bs++) { if ((bs % parser->GetDataTypeSize()) == 0) { EXPECT_NO_THROW(parser->ParseBlock(std::vector(bs))); } else { EXPECT_THROW_MATCH(parser->ParseBlock(std::vector(bs)), std::runtime_error, "block size must be a multiple of sizeof(datatype)"); } } } } } using Buffer = std::vector; using Result = std::vector; std::tuple make_test(DataType dt, bool is_complex, size_t length = 20480) { Buffer buf; Result res; res.resize(length); size_t factor = length * (is_complex ? 2 : 1); std::random_device rd; std::default_random_engine re(rd()); switch (dt) { case DataType::kSignedInt8: { buf.resize(factor * 1); std::uniform_int_distribution ud; auto mem = (int8_t *) buf.data(); for (size_t i = 0; i < length; i++) { if (is_complex) { mem[i*2+0] = ud(re); mem[i*2+1] = ud(re); res[i] = std::complex((double)mem[i*2+0] / (double)std::numeric_limits::max(), (double)mem[i*2+1] / (double)std::numeric_limits::max()); } else { mem[i] = ud(re); res[i] = std::complex((double)mem[i] / (double)std::numeric_limits::max()); } } } break; case DataType::kUnsignedInt8: { buf.resize(factor * 1); std::uniform_int_distribution ud; auto mem = (uint8_t *) buf.data(); for (size_t i = 0; i < length; i++) { if (is_complex) { mem[i*2+0] = ud(re); mem[i*2+1] = ud(re); res[i] = std::complex((double)mem[i*2+0] / (double)std::numeric_limits::max(), (double)mem[i*2+1] / (double)std::numeric_limits::max()); } else { mem[i] = ud(re); res[i] = std::complex((double)mem[i] / (double)std::numeric_limits::max()); } } } break; case DataType::kSignedInt16: { buf.resize(factor * 2); std::uniform_int_distribution ud; auto mem = (int16_t *) buf.data(); for (std::size_t i = 0; i < length; i++) { if (is_complex) { mem[i*2+0] = ud(re); mem[i*2+1] = ud(re); res[i] = std::complex((double)mem[i*2+0] / (double)std::numeric_limits::max(), (double)mem[i*2+1] / (double)std::numeric_limits::max()); } else { mem[i] = ud(re); res[i] = std::complex((double)mem[i] / (double)std::numeric_limits::max()); } } } break; case DataType::kUnsignedInt16: { buf.resize(factor * 2); std::uniform_int_distribution ud; auto mem = (uint16_t *) buf.data(); for (std::size_t i = 0; i < length; i++) { if (is_complex) { mem[i*2+0] = ud(re); mem[i*2+1] = ud(re); res[i] = std::complex((double)mem[i*2+0] / (double)std::numeric_limits::max(), (double)mem[i*2+1] / (double)std::numeric_limits::max()); } else { mem[i] = ud(re); res[i] = std::complex((double)mem[i] / (double)std::numeric_limits::max()); } } } break; case DataType::kSignedInt32: { buf.resize(factor * 4); std::uniform_int_distribution ud; auto mem = (int32_t *) buf.data(); for (std::size_t i = 0; i < length; i++) { if (is_complex) { mem[i*2+0] = ud(re); mem[i*2+1] = ud(re); res[i] = std::complex((double)mem[i*2+0] / (double)std::numeric_limits::max(), (double)mem[i*2+1] / (double)std::numeric_limits::max()); } else { mem[i] = ud(re); res[i] = std::complex((double)mem[i] / (double)std::numeric_limits::max()); } } } break; case DataType::kUnsignedInt32: { buf.resize(factor * 4); std::uniform_int_distribution ud; auto mem = (uint32_t *) buf.data(); for (std::size_t i = 0; i < length; i++) { if (is_complex) { mem[i*2+0] = ud(re); mem[i*2+1] = ud(re); res[i] = std::complex((double)mem[i*2+0] / (double)std::numeric_limits::max(), (double)mem[i*2+1] / (double)std::numeric_limits::max()); } else { mem[i] = ud(re); res[i] = std::complex((double)mem[i] / (double)std::numeric_limits::max()); } } } break; case DataType::kSignedInt64: { buf.resize(factor * 8); std::uniform_int_distribution ud; auto mem = (int64_t *) buf.data(); for (std::size_t i = 0; i < length; i++) { if (is_complex) { mem[i*2+0] = ud(re); mem[i*2+1] = ud(re); res[i] = std::complex((double)mem[i*2+0] / (double)std::numeric_limits::max(), (double)mem[i*2+1] / (double)std::numeric_limits::max()); } else { mem[i] = ud(re); res[i] = std::complex((double)mem[i] / (double)std::numeric_limits::max()); } } } break; case DataType::kUnsignedInt64: { buf.resize(factor * 8); std::uniform_int_distribution ud; auto mem = (uint64_t *) buf.data(); for (std::size_t i = 0; i < length; i++) { if (is_complex) { mem[i*2+0] = ud(re); mem[i*2+1] = ud(re); res[i] = std::complex((double)mem[i*2+0] / (double)std::numeric_limits::max(), (double)mem[i*2+1] / (double)std::numeric_limits::max()); } else { mem[i] = ud(re); res[i] = std::complex((double)mem[i] / (double)std::numeric_limits::max()); } } } break; case DataType::kFloat32: { buf.resize(factor * 4); std::uniform_int_distribution ud; auto mem = (float *) buf.data(); for (std::size_t i = 0; i < length; i++) { if (is_complex) { mem[i*2+0] = (float)ud(re) / (float)std::numeric_limits::max(); mem[i*2+1] = (float)ud(re) / (float)std::numeric_limits::max(); res[i] = std::complex(mem[i*2+0], mem[i*2+1]); } else { mem[i] = (float)ud(re) / (float)std::numeric_limits::max(); res[i] = std::complex(mem[i]); } } } break; case DataType::kFloat64: { buf.resize(factor * 8); std::uniform_int_distribution ud; auto mem = (double *) buf.data(); for (std::size_t i = 0; i < length; i++) { if (is_complex) { mem[i*2+0] = (double)ud(re) / (double)std::numeric_limits::max(); mem[i*2+1] = (double)ud(re) / (double)std::numeric_limits::max(); res[i] = std::complex(mem[i*2+0], mem[i*2+1]); } else { mem[i] = (double)ud(re) / (double)std::numeric_limits::max(); res[i] = std::complex(mem[i]); } } } break; default: throw std::runtime_error("unknown data type"); } return std::make_tuple(buf, res); } TEST(TestInputParser, ParseBlock) { constexpr double epsilon = 1e-9; std::vector block_sizes { 312, 123, 874, 2048, 1024, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 921, 1, 432, 1, 225, 1, 231, 1 }; for (bool is_complex : { false, true }) { for (auto dt : ALL_DATA_TYPES) { for (double prescale : {1.0, 33.49}) { auto parser = InputParser::Build(dt, prescale, is_complex); auto[buf, res] = make_test(dt, is_complex); std::size_t bs_idx = 0; std::size_t buf_idx = 0; std::size_t res_idx = 0; while (buf_idx < buf.size()) { /* get next block size in round robin fashion */ auto bs = block_sizes[bs_idx % block_sizes.size()] * parser->GetDataTypeSize(); bs_idx++; /* build block */ bs = std::min(bs, buf.size() - buf_idx); std::vector block(bs); ::memcpy(block.data(), buf.data() + buf_idx, bs); buf_idx += bs; /* parse */ std::size_t parsed_count = 0; EXPECT_NO_THROW(parsed_count = parser->ParseBlock(block)); /* retrieve parsed */ if (parsed_count > 0) { std::vector values; EXPECT_NO_THROW(values = parser->PeekValues(parsed_count)); EXPECT_EQ(values.size(), parsed_count); EXPECT_NO_THROW(parser->RemoveValues(parsed_count)); for (std::size_t i = 0; i < parsed_count; i++, res_idx++) { EXPECT_TRUE(res_idx < res.size()); EXPECT_LE(std::abs(res[res_idx] * prescale - values[i]), epsilon); } } } } } } } TEST(TestInputParser, PeekValues) { constexpr std::size_t memory = 1024; std::vector block(memory); for (bool is_complex : { false, true }) { for (auto dt : ALL_DATA_TYPES) { auto parser = InputParser::Build(dt, 1.0, is_complex); std::size_t parsed_count = 0; EXPECT_NO_THROW(parsed_count = parser->ParseBlock(block)); for (std::size_t i = 0; i < parsed_count + 100; i ++) { std::vector result; EXPECT_NO_THROW(result = parser->PeekValues(i)); EXPECT_TRUE((result.size() == i) || (result.size() == memory / parser->GetDataTypeSize())); } } } } TEST(TestInputParser, RemoveValues) { constexpr std::size_t memory = 1024; std::vector block(memory); for (bool is_complex : { false, true }) { for (auto dt : ALL_DATA_TYPES) { for (std::size_t i = 0; i < (memory / InputParser::Build(dt, 1.0, is_complex)->GetDataTypeSize()) + 100; i++) { auto parser = InputParser::Build(dt, 1.0, is_complex); std::size_t parsed_count = 0; EXPECT_NO_THROW(parsed_count = parser->ParseBlock(block)); std::vector result; EXPECT_NO_THROW(parser->RemoveValues(i)); EXPECT_NO_THROW(result = parser->PeekValues(1000000)); EXPECT_EQ(result.size(), std::max(parsed_count - i, 0)); } } } }