1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
|
/*
* Copyright (c) 2020-2021 Vasile Vilvoiu <vasi@vilvoiu.ro>
*
* 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-reader.hpp"
#include <fstream>
#include <random>
#include <string>
#include <thread>
#include <csignal>
std::vector<char> random_data(std::size_t size)
{
std::vector<char> data(size);
std::random_device rd;
std::default_random_engine re(rd());
std::uniform_int_distribution<std::int8_t> ud;
for (std::size_t i = 0; i < size; i ++) {
data[i] = ud(re);
}
return data;
}
void generate_file(const std::string& temp_file_name, const std::vector<char>& buffer)
{
/* dump */
std::ofstream file(temp_file_name, std::ios::out | std::ios::binary);
if (file.fail()) {
throw std::runtime_error("cannot open temp file " + temp_file_name);
}
file.write(buffer.data(), buffer.size());
file.close();
}
void check_same(const std::vector<char>& expected, const std::vector<char>& actual, std::size_t max_len_diff)
{
EXPECT_GE(expected.size(), actual.size());
EXPECT_LE(expected.size() - actual.size(), max_len_diff);
for (std::size_t i = 0; i<actual.size(); i++) {
EXPECT_EQ(expected[i], actual[i]);
}
}
TEST(TestInputReader, BadParameters)
{
EXPECT_THROW_MATCH(SyncInputReader(nullptr, 100),
std::runtime_error, "valid stream is required");
EXPECT_THROW_MATCH(AsyncInputReader(nullptr, 100),
std::runtime_error, "valid stream is required");
EXPECT_THROW_MATCH(SyncInputReader((std::istream *)1, 0),
std::runtime_error, "block size in bytes must be positive");
EXPECT_THROW_MATCH(AsyncInputReader((std::istream *)1, 0),
std::runtime_error, "block size in bytes must be positive");
}
TEST(TestInputReader, SyncInputReader)
{
constexpr std::size_t max_block_size = 4096;
constexpr std::size_t memory = 4096;
const std::string file_name = "/dev/shm/TestInputReader_SyncInputReader.data";
auto expected = random_data(memory);
generate_file(file_name, expected);
for (std::size_t block_size = 1; block_size < max_block_size; block_size++) {
std::ifstream file(file_name, std::ios::in | std::ios::binary);
EXPECT_FALSE(file.fail());
SyncInputReader reader(&file, block_size);
std::vector<char> output;
output.reserve(memory);
while (true) {
auto block = reader.GetBlock();
if (!block.has_value()) {
break;
}
EXPECT_EQ((*block).size(), block_size);
output.insert(output.end(), (*block).begin(), (*block).end());
}
EXPECT_TRUE(reader.ReachedEOF());
file.close();
check_same(expected, output, block_size);
}
}
TEST(TestInputReader, AsyncInputReader)
{
constexpr std::size_t max_block_size = 4096;
constexpr std::size_t memory = 4096;
const std::string file_name = "/dev/shm/TestInputReader_AsyncInputReader.data";
auto expected = random_data(memory);
generate_file(file_name, expected);
for (std::size_t block_size = 1; block_size < max_block_size; block_size++) {
std::ifstream file(file_name, std::ios::in | std::ios::binary);
EXPECT_FALSE(file.fail());
std::vector<char> output;
output.reserve(memory);
std::signal(SIGINT, [](int) { }); /* SIGINT is sent to the reader thread upon destruction of AsyncInputReader */
{ /* scope out the reader so it does not die when we close the file */
AsyncInputReader reader(&file, block_size);
while (output.size() < expected.size() - block_size) {
auto block = reader.GetBlock();
if (block.has_value()) {
EXPECT_EQ((*block).size(), block_size);
output.insert(output.end(), (*block).begin(), (*block).end());
}
}
EXPECT_FALSE(reader.ReachedEOF());
}
std::signal(SIGINT, nullptr);
file.close();
check_same(expected, output, block_size);
}
}
|