1 // Copyright 2018 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file
4 
5 #include <fcntl.h>
6 #include <stdio.h>
7 #include <string.h>
8 #include <sys/stat.h>
9 
10 #include <fbl/function.h>
11 #include <fbl/string.h>
12 #include <fbl/unique_fd.h>
13 #include <fbl/vector.h>
14 #include <fs-management/mount.h>
15 #include <fs-test-utils/fixture.h>
16 #include <fs-test-utils/perftest.h>
17 #include <unittest/unittest.h>
18 
19 namespace fs_test_utils {
20 namespace {
21 
22 // File used to dump libs stdout. Allows verifying certain options.
23 constexpr char kFakeStdout[] = "/data/fake_stdout.txt";
24 
ResultSetIsValid()25 bool ResultSetIsValid() {
26     BEGIN_TEST;
27     fbl::String err;
28     PerformanceTestOptions p_options = PerformanceTestOptions::PerformanceTest();
29     p_options.result_path = "some/path";
30     ASSERT_TRUE(p_options.IsValid(&err), err.c_str());
31     END_TEST;
32 }
33 
SummaryPathSetIsValid()34 bool SummaryPathSetIsValid() {
35     BEGIN_TEST;
36     fbl::String err;
37     PerformanceTestOptions p_options = PerformanceTestOptions::PerformanceTest();
38     p_options.summary_path = "some/path";
39     ASSERT_TRUE(p_options.IsValid(&err), err.c_str());
40     END_TEST;
41 }
42 
PrintStatisticsSetIsValid()43 bool PrintStatisticsSetIsValid() {
44     BEGIN_TEST;
45     fbl::String err;
46     PerformanceTestOptions p_options = PerformanceTestOptions::PerformanceTest();
47     p_options.print_statistics = true;
48     ASSERT_TRUE(p_options.IsValid(&err), err.c_str());
49     END_TEST;
50 }
51 
NoOutputIsInvalid()52 bool NoOutputIsInvalid() {
53     BEGIN_TEST;
54     fbl::String err;
55     PerformanceTestOptions p_options = PerformanceTestOptions::PerformanceTest();
56     p_options.print_statistics = false;
57     p_options.result_path.clear();
58     p_options.summary_path.clear();
59 
60     ASSERT_FALSE(p_options.IsValid(&err), err.c_str());
61     END_TEST;
62 }
63 
InvalidOptionsReturnFalseAndPrintsUsage()64 bool InvalidOptionsReturnFalseAndPrintsUsage() {
65     BEGIN_TEST;
66     fbl::String err;
67     char arg0[] = "/some/path/binary";
68     char* argv[] = {arg0};
69     PerformanceTestOptions p_options = PerformanceTestOptions::PerformanceTest();
70     p_options.result_path = "some/path";
71     FixtureOptions f_options = FixtureOptions::Default(DISK_FORMAT_MINFS);
72     f_options.block_device_path = "some_path";
73     f_options.use_ramdisk = true;
74 
75     ASSERT_FALSE(f_options.IsValid(&err));
76 
77     FILE* fp = fopen(kFakeStdout, "w");
78     ASSERT_TRUE(fp);
79     ASSERT_FALSE(ParseCommandLineArgs(1, argv, &f_options, &p_options, fp));
80     fclose(fp);
81 
82     // Usage is printed on error.
83     struct stat st;
84     stat(kFakeStdout, &st);
85     remove(kFakeStdout);
86     ASSERT_GT(st.st_size, 0);
87     END_TEST;
88 }
89 
90 // Sanity check that we print into the stream when help option is provided.
HelpPrintsUsageMessage()91 bool HelpPrintsUsageMessage() {
92     BEGIN_TEST;
93     char arg0[] = "/some/path/binary";
94     char arg1[] = "--help";
95     char* argv[] = {arg0, arg1};
96     fbl::String err;
97     PerformanceTestOptions p_options = PerformanceTestOptions::PerformanceTest();
98     FixtureOptions f_options = FixtureOptions::Default(DISK_FORMAT_MINFS);
99 
100     FILE* fp = fopen(kFakeStdout, "w");
101     ASSERT_TRUE(fp);
102     ASSERT_FALSE(ParseCommandLineArgs(2, argv, &f_options, &p_options, fp));
103     fclose(fp);
104 
105     struct stat st;
106     stat(kFakeStdout, &st);
107     remove(kFakeStdout);
108     ASSERT_GT(st.st_size, 0);
109     END_TEST;
110 }
111 
112 // Verifies that ParseCommandLineArgs actually sets the respective fields in the
113 // option structs.
OptionsAreOverwritten()114 bool OptionsAreOverwritten() {
115     BEGIN_TEST;
116     fbl::Vector<fbl::String> argvs = {
117         "/some/binary",
118         "-p",
119         "--use_fvm",
120         "--fvm_slice_size",
121         "8192",
122         "--use_ramdisk",
123         "--ramdisk_block_size",
124         "1024",
125         "--ramdisk_block_count",
126         "500",
127         "--runs",
128         "4",
129         "--out",
130         "some_path",
131         "--summary_path",
132         "other_path",
133         "--print_statistics",
134         "--fs",
135         "blobfs",
136     };
137     const char* argv[argvs.size() + 1];
138     for (size_t i = 0; i < argvs.size(); ++i) {
139         argv[i] = argvs[i].data();
140     }
141     argv[argvs.size()] = nullptr;
142 
143     fbl::String err;
144     PerformanceTestOptions p_options = PerformanceTestOptions::PerformanceTest();
145     FixtureOptions f_options = FixtureOptions::Default(DISK_FORMAT_MINFS);
146 
147     FILE* fp = fopen(kFakeStdout, "w");
148     ASSERT_TRUE(fp);
149     ASSERT_TRUE(
150         ParseCommandLineArgs(static_cast<int>(argvs.size()), argv, &f_options, &p_options, fp));
151     fclose(fp);
152 
153     // Usage is not logged.
154     struct stat st;
155     stat(kFakeStdout, &st);
156     remove(kFakeStdout);
157     ASSERT_EQ(st.st_size, 0);
158 
159     // Parameters parsed.
160     ASSERT_TRUE(f_options.block_device_path == "");
161     ASSERT_TRUE(f_options.use_ramdisk);
162     ASSERT_EQ(f_options.ramdisk_block_size, 1024);
163     ASSERT_EQ(f_options.ramdisk_block_count, 500);
164     ASSERT_TRUE(f_options.use_fvm);
165     ASSERT_EQ(f_options.fvm_slice_size, 8192);
166     ASSERT_EQ(f_options.fs_type, DISK_FORMAT_BLOBFS);
167 
168     ASSERT_FALSE(p_options.is_unittest);
169     ASSERT_TRUE(p_options.result_path == "some_path");
170     ASSERT_TRUE(p_options.summary_path == "other_path");
171     ASSERT_TRUE(p_options.print_statistics);
172     ASSERT_EQ(p_options.sample_count, 4);
173 
174     END_TEST;
175 }
176 
RunTestCasesPreservesOrder()177 bool RunTestCasesPreservesOrder() {
178     BEGIN_TEST;
179     PerformanceTestOptions p_options = PerformanceTestOptions::UnitTest();
180     FixtureOptions f_options = FixtureOptions::Default(DISK_FORMAT_MINFS);
181     p_options.sample_count = 1;
182     fbl::Vector<int> calls;
183 
184     auto test_1 = [&calls](perftest::RepeatState* state, Fixture* fixture) {
185         state->DeclareStep("test_1");
186         while (state->KeepRunning()) {
187             calls.push_back(1);
188         }
189         return true;
190     };
191     auto test_2 = [&calls](perftest::RepeatState* state, Fixture* fixture) {
192         state->DeclareStep("test_2");
193         while (state->KeepRunning()) {
194             calls.push_back(2);
195         }
196         return true;
197     };
198     auto test_3 = [&calls](perftest::RepeatState* state, Fixture* fixture) {
199         state->DeclareStep("test_3");
200         while (state->KeepRunning()) {
201             calls.push_back(3);
202         }
203         return true;
204     };
205 
206     TestCaseInfo info;
207     info.name = "MyTestCase";
208     info.tests.push_back({std::move(test_1), "test_1", /*required_disk_space=*/0});
209     info.tests.push_back({std::move(test_2), "test_2", 0});
210     info.tests.push_back({std::move(test_3), "test_3", 0});
211     info.teardown = false;
212 
213     fbl::Vector<TestCaseInfo> test_cases;
214     test_cases.push_back(std::move(info));
215     ASSERT_TRUE(RunTestCases(f_options, p_options, test_cases, /*out=*/nullptr));
216 
217     // Verify order is preserved.
218     ASSERT_EQ(calls.size(), 3);
219     ASSERT_EQ(calls[0], 1);
220     ASSERT_EQ(calls[1], 2);
221     ASSERT_EQ(calls[2], 3);
222 
223     END_TEST;
224 }
225 
RunTestCasesPreservesOrderWithMultipleSamples()226 bool RunTestCasesPreservesOrderWithMultipleSamples() {
227     BEGIN_TEST;
228     PerformanceTestOptions p_options = PerformanceTestOptions::UnitTest();
229     FixtureOptions f_options = FixtureOptions::Default(DISK_FORMAT_MINFS);
230     p_options.is_unittest = false;
231     p_options.sample_count = 10;
232     fbl::Vector<int> calls;
233 
234     auto test_1 = [&calls](perftest::RepeatState* state, Fixture* fixture) {
235         state->DeclareStep("test_1");
236         while (state->KeepRunning()) {
237             calls.push_back(1);
238         }
239         return true;
240     };
241     auto test_2 = [&calls](perftest::RepeatState* state, Fixture* fixture) {
242         state->DeclareStep("test_2");
243         while (state->KeepRunning()) {
244             calls.push_back(2);
245         }
246         return true;
247     };
248     auto test_3 = [&calls](perftest::RepeatState* state, Fixture* fixture) {
249         state->DeclareStep("test_3");
250         while (state->KeepRunning()) {
251             calls.push_back(3);
252         }
253         return true;
254     };
255 
256     TestCaseInfo info;
257     info.sample_count = 20;
258     info.name = "MyTestCase";
259     info.tests.push_back({std::move(test_1), "test_1", /*required_disk_space=*/0});
260     info.tests.push_back({std::move(test_2), "test_2", 0});
261     info.tests.push_back({std::move(test_3), "test_3", 0});
262     info.teardown = false;
263 
264     fbl::Vector<TestCaseInfo> test_cases;
265     test_cases.push_back(std::move(info));
266     ASSERT_TRUE(RunTestCases(f_options, p_options, test_cases, /*out=*/nullptr));
267 
268     // Verify order is preserved.
269     ASSERT_EQ(calls.size(), 60);
270     for (int i = 0; i < 20; ++i) {
271         ASSERT_EQ(calls[i], 1);
272         ASSERT_EQ(calls[(20 + i)], 2);
273         ASSERT_EQ(calls[(40 + i)], 3);
274     }
275 
276     END_TEST;
277 }
278 
RunTestCasesWritesResultsAndStatistics()279 bool RunTestCasesWritesResultsAndStatistics() {
280     BEGIN_TEST;
281     PerformanceTestOptions p_options = PerformanceTestOptions::PerformanceTest();
282     p_options.result_path = "/data/results.json";
283     p_options.summary_path = "/data/summary.txt";
284     p_options.print_statistics = true;
285 
286     FixtureOptions f_options = FixtureOptions::Default(DISK_FORMAT_MINFS);
287     p_options.sample_count = 1;
288 
289     auto test_1 = [](perftest::RepeatState* state, Fixture* fixture) {
290         state->DeclareStep("test_1");
291         state->DeclareStep("test_2");
292         while (state->KeepRunning()) {
293             state->NextStep();
294         }
295         return true;
296     };
297 
298     TestCaseInfo info;
299     info.name = "MyTestCase";
300     info.tests.push_back({std::move(test_1), "test_1", /*required_disk_space=*/0});
301     info.teardown = false;
302 
303     fbl::Vector<TestCaseInfo> test_cases;
304     test_cases.push_back(std::move(info));
305 
306     FILE* fp = fopen(kFakeStdout, "w+");
307     ASSERT_TRUE(fp);
308     ASSERT_TRUE(RunTestCases(f_options, p_options, test_cases, fp));
309     fseek(fp, 0, SEEK_SET);
310     // Look for test_1.test_1 in fake_std.txt (test_name.step_name).
311     char* buffer = nullptr;
312     size_t length = 0;
313     ssize_t read;
314     bool found_1 = false;
315     bool found_2 = false;
316     while ((read = getline(&buffer, &length, fp)) != -1 && (!found_1 || !found_2)) {
317         if (strstr(buffer, "test_1.test_1")) {
318             found_1 = true;
319         } else if (strstr(buffer, "test_1.test_2")) {
320             found_2 = true;
321         }
322         free(buffer);
323         buffer = nullptr;
324         length = 0;
325     }
326     free(buffer);
327     buffer = nullptr;
328     remove(kFakeStdout);
329     fclose(fp);
330     EXPECT_TRUE(found_1);
331     EXPECT_TRUE(found_2);
332 
333     struct stat st;
334     stat(p_options.result_path.c_str(), &st);
335     remove(p_options.result_path.c_str());
336     EXPECT_GT(st.st_size, 0);
337 
338     stat(p_options.summary_path.c_str(), &st);
339     remove(p_options.summary_path.c_str());
340     EXPECT_GT(st.st_size, 0);
341 
342     END_TEST;
343 }
344 
345 BEGIN_TEST_CASE(FsPerformanceTestOptions)
346 RUN_TEST(ResultSetIsValid)
347 RUN_TEST(SummaryPathSetIsValid)
348 RUN_TEST(PrintStatisticsSetIsValid)
349 RUN_TEST(NoOutputIsInvalid)
350 END_TEST_CASE(FsPerformanceTestOptions)
351 
352 BEGIN_TEST_CASE(FsPerformanceTestLib)
353 RUN_TEST(InvalidOptionsReturnFalseAndPrintsUsage)
354 RUN_TEST(OptionsAreOverwritten)
355 RUN_TEST(HelpPrintsUsageMessage)
356 RUN_TEST(RunTestCasesPreservesOrder)
357 RUN_TEST(RunTestCasesPreservesOrderWithMultipleSamples)
358 RUN_TEST(RunTestCasesWritesResultsAndStatistics)
359 END_TEST_CASE(FsPerformanceTestLib)
360 
361 } // namespace
362 } // namespace fs_test_utils
363