summaryrefslogtreecommitdiff
path: root/test/test-input-reader.cpp
blob: 000b23727ef8f10f334b0424b404d455f0b34280 (plain)
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-2023 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);
    }
}