util: Add stub unit tests for the call types in the m5 utility.
[gem5.git] / util / m5 / src / command / readfile.test.cc
1 /*
2 * Copyright 2020 Google Inc.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met: redistributions of source code must retain the above copyright
7 * notice, this list of conditions and the following disclaimer;
8 * redistributions in binary form must reproduce the above copyright
9 * notice, this list of conditions and the following disclaimer in the
10 * documentation and/or other materials provided with the distribution;
11 * neither the name of the copyright holders nor the names of its
12 * contributors may be used to endorse or promote products derived from
13 * this software without specific prior written permission.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 #include <gtest/gtest.h>
29
30 #include <sstream>
31
32 #include "args.hh"
33 #include "command.hh"
34 #include "dispatch_table.hh"
35
36 uint64_t test_read_file_size;
37 uint64_t test_max_buf_size;
38
39 uint64_t test_total_read;
40
41 uint64_t
42 test_m5_read_file(void *buffer, uint64_t len, uint64_t offset)
43 {
44 // The "file" we're reading is just a series of incrementing 32 bit
45 // integers.
46
47 // If the buffer is entirely past the end of our "file", return 0.
48 if (offset >= test_read_file_size)
49 return 0;
50
51 // If the buffer extends beyond our "file" truncate it.
52 if (offset + len > test_read_file_size)
53 len = test_read_file_size - offset;
54
55 // If more data was requested than we want to send at once, truncate len.
56 if (test_max_buf_size && len > test_max_buf_size)
57 len = test_max_buf_size;
58
59 int chunk_size = sizeof(uint32_t);
60
61 // How much of len is still unaccounted for.
62 uint64_t remaining = len;
63
64 // How much overlaps with the preceeding chunk?
65 int at_start = chunk_size - (offset % chunk_size);
66 // If we don't even cover the entire previous chunk...
67 if (at_start > len)
68 at_start = len;
69 remaining -= at_start;
70
71 // How much overlaps with the following chunk?
72 int at_end = remaining % chunk_size;
73 remaining -= at_end;
74
75 // The number of chunks are the number we cover fully, plus one for each
76 // end were we partially overlap.
77 uint64_t num_chunks = remaining / chunk_size +
78 (at_start ? 1 : 0) + (at_end ? 1 : 0);
79
80 // Build this part of the file.
81 uint32_t *chunks = new uint32_t [num_chunks];
82
83 uint32_t chunk_idx = offset / chunk_size;
84 for (uint64_t i = 0; i < num_chunks; i++)
85 chunks[i] = chunk_idx++;
86
87 // Copy out to the requested buffer.
88 memcpy(buffer, ((uint8_t *)chunks) + (chunk_size - at_start), len);
89
90 // Clean up.
91 delete [] chunks;
92
93 test_total_read += len;
94 return len;
95 }
96
97 DispatchTable dt = { .m5_read_file = &test_m5_read_file };
98
99 std::string cout_output;
100
101 bool
102 run(std::initializer_list<std::string> arg_args, bool bad_file=false)
103 {
104 test_total_read = 0;
105
106 Args args(arg_args);
107
108 // Redirect cout into a stringstream.
109 std::stringstream buffer;
110 std::streambuf *orig = std::cout.rdbuf(buffer.rdbuf());
111
112 // Simulate a problem writing to cout.
113 if (bad_file)
114 std::cout.setstate(std::cout.badbit);
115
116 bool res = Command::run(dt, args);
117
118 if (bad_file)
119 std::cout.clear();
120
121 // Capture the contents of the stringstream and restore cout.
122 cout_output = buffer.str();
123 std::cout.rdbuf(orig);
124
125 return res;
126 }
127
128 void
129 test_verify_data()
130 {
131 EXPECT_EQ(test_total_read, test_read_file_size);
132 EXPECT_EQ(cout_output.size(), test_read_file_size);
133
134 auto *data32 = (const uint32_t *)cout_output.data();
135 uint64_t len = cout_output.size();
136
137 int chunk_size = sizeof(uint32_t);
138
139 uint64_t num_chunks = len / chunk_size;
140 int leftovers = len % chunk_size;
141
142 uint32_t chunk_idx;
143 for (chunk_idx = 0; chunk_idx < num_chunks; chunk_idx++)
144 EXPECT_EQ(*data32++, chunk_idx);
145
146 if (leftovers)
147 EXPECT_EQ(memcmp(&chunk_idx, data32, leftovers), 0);
148 }
149
150 TEST(Readfile, OneArgument)
151 {
152 // Call with an argument.
153 EXPECT_FALSE(run({"readfile", "foo"}));
154 EXPECT_EQ(test_total_read, 0);
155 }
156
157 TEST(Readfile, SmallFile)
158 {
159 // Read a small "file".
160 test_read_file_size = 16;
161 test_max_buf_size = 0;
162 EXPECT_TRUE(run({"readfile"}));
163 test_verify_data();
164 }
165
166 TEST(Readfile, MultipleChunks)
167 {
168 // Read a "file" which will need to be split into multiple whole chunks.
169 test_read_file_size = 256 * 1024 * 4;
170 test_max_buf_size = 0;
171 EXPECT_TRUE(run({"readfile"}));
172 test_verify_data();
173 }
174
175 TEST(Readfile, MultipleAndPartialChunks)
176 {
177 // Read a "file" which will be split into some whole and one partial chunk.
178 test_read_file_size = 256 * 1024 * 2 + 256;
179 test_max_buf_size = 0;
180 EXPECT_TRUE(run({"readfile"}));
181 test_verify_data();
182 }
183
184 TEST(Readfile, OddSizedChunks)
185 {
186 // Read a "file" in chunks that aren't nicely aligned.
187 test_read_file_size = 256 * 1024;
188 test_max_buf_size = 13;
189 EXPECT_TRUE(run({"readfile"}));
190 test_verify_data();
191 }
192
193 TEST(Readfile, CappedReadSize)
194 {
195 // Read a "file", returning less than the requested amount of data.
196 test_read_file_size = 256 * 1024 * 2 + 256;
197 test_max_buf_size = 256;
198 EXPECT_TRUE(run({"readfile"}));
199 test_verify_data();
200 }
201
202 TEST(ReadfileDeathTest, BadFile)
203 {
204 test_read_file_size = 16;
205 test_max_buf_size = 0;
206 EXPECT_EXIT(run({"readfile"}, true), ::testing::ExitedWithCode(2),
207 "Failed to write file");
208 }