1// Copyright 2014 The Kyua Authors.
2// All rights reserved.
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:
7//
8// * Redistributions of source code must retain the above copyright
9//   notice, this list of conditions and the following disclaimer.
10// * Redistributions in binary form must reproduce the above copyright
11//   notice, this list of conditions and the following disclaimer in the
12//   documentation and/or other materials provided with the distribution.
13// * Neither the name of Google Inc. nor the names of its contributors
14//   may be used to endorse or promote products derived from this software
15//   without specific prior written permission.
16//
17// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29#include "store/layout.hpp"
30
31extern "C" {
32#include <unistd.h>
33}
34
35#include <iostream>
36
37#include <atf-c++.hpp>
38
39#include "store/exceptions.hpp"
40#include "store/layout.hpp"
41#include "utils/datetime.hpp"
42#include "utils/env.hpp"
43#include "utils/fs/operations.hpp"
44#include "utils/fs/path.hpp"
45
46namespace datetime = utils::datetime;
47namespace fs = utils::fs;
48namespace layout = store::layout;
49
50
51ATF_TEST_CASE_WITHOUT_HEAD(find_results__latest);
52ATF_TEST_CASE_BODY(find_results__latest)
53{
54    const fs::path store_dir = layout::query_store_dir();
55    fs::mkdir_p(store_dir, 0755);
56
57    const std::string test_suite = layout::test_suite_for_path(
58        fs::current_path());
59    const std::string base = (store_dir / (
60        "results." + test_suite + ".")).str();
61
62    atf::utils::create_file(base + "20140613-194515-000000.db", "");
63    ATF_REQUIRE_EQ(base + "20140613-194515-000000.db",
64                   layout::find_results("LATEST").str());
65
66    atf::utils::create_file(base + "20140614-194515-123456.db", "");
67    ATF_REQUIRE_EQ(base + "20140614-194515-123456.db",
68                   layout::find_results("LATEST").str());
69
70    atf::utils::create_file(base + "20130614-194515-999999.db", "");
71    ATF_REQUIRE_EQ(base + "20140614-194515-123456.db",
72                   layout::find_results("LATEST").str());
73}
74
75
76ATF_TEST_CASE_WITHOUT_HEAD(find_results__directory);
77ATF_TEST_CASE_BODY(find_results__directory)
78{
79    const fs::path store_dir = layout::query_store_dir();
80    fs::mkdir_p(store_dir, 0755);
81
82    const fs::path dir1("dir1/foo");
83    fs::mkdir_p(dir1, 0755);
84    const fs::path dir2("dir1/bar");
85    fs::mkdir_p(dir2, 0755);
86
87    const std::string base1 = (store_dir / (
88        "results." + layout::test_suite_for_path(dir1) + ".")).str();
89    const std::string base2 = (store_dir / (
90        "results." + layout::test_suite_for_path(dir2) + ".")).str();
91
92    atf::utils::create_file(base1 + "20140613-194515-000000.db", "");
93    ATF_REQUIRE_EQ(base1 + "20140613-194515-000000.db",
94                   layout::find_results(dir1.str()).str());
95
96    atf::utils::create_file(base2 + "20140615-111111-000000.db", "");
97    ATF_REQUIRE_EQ(base2 + "20140615-111111-000000.db",
98                   layout::find_results(dir2.str()).str());
99
100    atf::utils::create_file(base1 + "20140614-194515-123456.db", "");
101    ATF_REQUIRE_EQ(base1 + "20140614-194515-123456.db",
102                   layout::find_results(dir1.str()).str());
103
104    atf::utils::create_file(base1 + "20130614-194515-999999.db", "");
105    ATF_REQUIRE_EQ(base1 + "20140614-194515-123456.db",
106                   layout::find_results(dir1.str()).str());
107}
108
109
110ATF_TEST_CASE_WITHOUT_HEAD(find_results__file);
111ATF_TEST_CASE_BODY(find_results__file)
112{
113    const fs::path store_dir = layout::query_store_dir();
114    fs::mkdir_p(store_dir, 0755);
115
116    atf::utils::create_file("a-file.db", "");
117    ATF_REQUIRE_EQ(fs::path("a-file.db").to_absolute(),
118                   layout::find_results("a-file.db"));
119}
120
121
122ATF_TEST_CASE_WITHOUT_HEAD(find_results__id);
123ATF_TEST_CASE_BODY(find_results__id)
124{
125    const fs::path store_dir = layout::query_store_dir();
126    fs::mkdir_p(store_dir, 0755);
127
128    const fs::path dir1("dir1/foo");
129    fs::mkdir_p(dir1, 0755);
130    const fs::path dir2("dir1/bar");
131    fs::mkdir_p(dir2, 0755);
132
133    const std::string id1 = layout::test_suite_for_path(dir1);
134    const std::string base1 = (store_dir / ("results." + id1 + ".")).str();
135    const std::string id2 = layout::test_suite_for_path(dir2);
136    const std::string base2 = (store_dir / ("results." + id2 + ".")).str();
137
138    atf::utils::create_file(base1 + "20140613-194515-000000.db", "");
139    ATF_REQUIRE_EQ(base1 + "20140613-194515-000000.db",
140                   layout::find_results(id1).str());
141
142    atf::utils::create_file(base2 + "20140615-111111-000000.db", "");
143    ATF_REQUIRE_EQ(base2 + "20140615-111111-000000.db",
144                   layout::find_results(id2).str());
145
146    atf::utils::create_file(base1 + "20140614-194515-123456.db", "");
147    ATF_REQUIRE_EQ(base1 + "20140614-194515-123456.db",
148                   layout::find_results(id1).str());
149
150    atf::utils::create_file(base1 + "20130614-194515-999999.db", "");
151    ATF_REQUIRE_EQ(base1 + "20140614-194515-123456.db",
152                   layout::find_results(id1).str());
153}
154
155
156ATF_TEST_CASE_WITHOUT_HEAD(find_results__id_with_timestamp);
157ATF_TEST_CASE_BODY(find_results__id_with_timestamp)
158{
159    const fs::path store_dir = layout::query_store_dir();
160    fs::mkdir_p(store_dir, 0755);
161
162    const fs::path dir1("dir1/foo");
163    fs::mkdir_p(dir1, 0755);
164    const fs::path dir2("dir1/bar");
165    fs::mkdir_p(dir2, 0755);
166
167    const std::string id1 = layout::test_suite_for_path(dir1);
168    const std::string base1 = (store_dir / ("results." + id1 + ".")).str();
169    const std::string id2 = layout::test_suite_for_path(dir2);
170    const std::string base2 = (store_dir / ("results." + id2 + ".")).str();
171
172    atf::utils::create_file(base1 + "20140613-194515-000000.db", "");
173    atf::utils::create_file(base2 + "20140615-111111-000000.db", "");
174    atf::utils::create_file(base1 + "20140614-194515-123456.db", "");
175    atf::utils::create_file(base1 + "20130614-194515-999999.db", "");
176
177    ATF_REQUIRE_MATCH(
178        "_dir1_foo.20140613-194515-000000.db$",
179        layout::find_results(id1 + ".20140613-194515-000000").str());
180
181    ATF_REQUIRE_MATCH(
182        "_dir1_foo.20140614-194515-123456.db$",
183        layout::find_results(id1 + ".20140614-194515-123456").str());
184
185    ATF_REQUIRE_MATCH(
186        "_dir1_bar.20140615-111111-000000.db$",
187        layout::find_results(id2 + ".20140615-111111-000000").str());
188}
189
190
191ATF_TEST_CASE_WITHOUT_HEAD(find_results__not_found);
192ATF_TEST_CASE_BODY(find_results__not_found)
193{
194    ATF_REQUIRE_THROW_RE(
195        store::error,
196        "No previous results file found for test suite foo_bar",
197        layout::find_results("foo_bar"));
198
199    const fs::path store_dir = layout::query_store_dir();
200    fs::mkdir_p(store_dir, 0755);
201    ATF_REQUIRE_THROW_RE(
202        store::error,
203        "No previous results file found for test suite foo_bar",
204        layout::find_results("foo_bar"));
205
206    const char* candidates[] = {
207        "results.foo.20140613-194515-012345.db",  // Bad test suite.
208        "results.foo_bar.20140613-194515-012345",  // Missing extension.
209        "foo_bar.20140613-194515-012345.db",  // Missing prefix.
210        "results.foo_bar.2010613-194515-012345.db",  // Bad date.
211        "results.foo_bar.20140613-19515-012345.db",  // Bad time.
212        "results.foo_bar.20140613-194515-01245.db",  // Bad microseconds.
213        NULL,
214    };
215    for (const char** candidate = candidates; *candidate != NULL; ++candidate) {
216        std::cout << "Current candidate: " << *candidate << '\n';
217        atf::utils::create_file((store_dir / *candidate).str(), "");
218        ATF_REQUIRE_THROW_RE(
219            store::error,
220            "No previous results file found for test suite foo_bar",
221            layout::find_results("foo_bar"));
222    }
223
224    atf::utils::create_file(
225        (store_dir / "results.foo_bar.20140613-194515-012345.db").str(), "");
226    layout::find_results("foo_bar");  // Expected not to throw.
227}
228
229
230ATF_TEST_CASE_WITHOUT_HEAD(new_db__new);
231ATF_TEST_CASE_BODY(new_db__new)
232{
233    datetime::set_mock_now(2014, 6, 13, 19, 45, 15, 5000);
234    ATF_REQUIRE(!fs::exists(fs::path(".kyua/store")));
235    const layout::results_id_file_pair results = layout::new_db(
236        "NEW", fs::path("/some/path/to/the/suite"));
237    ATF_REQUIRE( fs::exists(fs::path(".kyua/store")));
238    ATF_REQUIRE( fs::is_directory(fs::path(".kyua/store")));
239
240    const std::string id = "some_path_to_the_suite.20140613-194515-005000";
241    ATF_REQUIRE_EQ(id, results.first);
242    ATF_REQUIRE_EQ(layout::query_store_dir() / ("results." + id + ".db"),
243                   results.second);
244}
245
246
247ATF_TEST_CASE_WITHOUT_HEAD(new_db__explicit);
248ATF_TEST_CASE_BODY(new_db__explicit)
249{
250    ATF_REQUIRE(!fs::exists(fs::path(".kyua/store")));
251    const layout::results_id_file_pair results = layout::new_db(
252        "foo/results-file.db", fs::path("unused"));
253    ATF_REQUIRE(!fs::exists(fs::path(".kyua/store")));
254    ATF_REQUIRE(!fs::exists(fs::path("foo")));
255
256    ATF_REQUIRE(results.first.empty());
257    ATF_REQUIRE_EQ(fs::path("foo/results-file.db"), results.second);
258}
259
260
261ATF_TEST_CASE_WITHOUT_HEAD(new_db_for_migration);
262ATF_TEST_CASE_BODY(new_db_for_migration)
263{
264    ATF_REQUIRE(!fs::exists(fs::path(".kyua/store")));
265    const fs::path results_file = layout::new_db_for_migration(
266        fs::path("/some/root"),
267        datetime::timestamp::from_values(2014, 7, 30, 10, 5, 20, 76500));
268    ATF_REQUIRE( fs::exists(fs::path(".kyua/store")));
269    ATF_REQUIRE( fs::is_directory(fs::path(".kyua/store")));
270
271    ATF_REQUIRE_EQ(
272        layout::query_store_dir() /
273        "results.some_root.20140730-100520-076500.db",
274        results_file);
275}
276
277
278ATF_TEST_CASE_WITHOUT_HEAD(query_store_dir__home_absolute);
279ATF_TEST_CASE_BODY(query_store_dir__home_absolute)
280{
281    const fs::path home = fs::current_path() / "homedir";
282    utils::setenv("HOME", home.str());
283    const fs::path store_dir = layout::query_store_dir();
284    ATF_REQUIRE(store_dir.is_absolute());
285    ATF_REQUIRE_EQ(home / ".kyua/store", store_dir);
286}
287
288
289ATF_TEST_CASE_WITHOUT_HEAD(query_store_dir__home_relative);
290ATF_TEST_CASE_BODY(query_store_dir__home_relative)
291{
292    const fs::path home("homedir");
293    utils::setenv("HOME", home.str());
294    const fs::path store_dir = layout::query_store_dir();
295    ATF_REQUIRE(store_dir.is_absolute());
296    ATF_REQUIRE_MATCH((home / ".kyua/store").str(), store_dir.str());
297}
298
299
300ATF_TEST_CASE_WITHOUT_HEAD(query_store_dir__no_home);
301ATF_TEST_CASE_BODY(query_store_dir__no_home)
302{
303    utils::unsetenv("HOME");
304    ATF_REQUIRE_EQ(fs::current_path(), layout::query_store_dir());
305}
306
307
308ATF_TEST_CASE_WITHOUT_HEAD(test_suite_for_path__absolute);
309ATF_TEST_CASE_BODY(test_suite_for_path__absolute)
310{
311    ATF_REQUIRE_EQ("dir1_dir2_dir3",
312                   layout::test_suite_for_path(fs::path("/dir1/dir2/dir3")));
313    ATF_REQUIRE_EQ("dir1",
314                   layout::test_suite_for_path(fs::path("/dir1")));
315    ATF_REQUIRE_EQ("dir1_dir2",
316                   layout::test_suite_for_path(fs::path("/dir1///dir2")));
317}
318
319
320ATF_TEST_CASE_WITHOUT_HEAD(test_suite_for_path__relative);
321ATF_TEST_CASE_BODY(test_suite_for_path__relative)
322{
323    const std::string test_suite = layout::test_suite_for_path(
324        fs::path("foo/bar"));
325    ATF_REQUIRE_MATCH("_foo_bar$", test_suite);
326    ATF_REQUIRE_MATCH("^[^_]", test_suite);
327}
328
329
330ATF_INIT_TEST_CASES(tcs)
331{
332    ATF_ADD_TEST_CASE(tcs, find_results__latest);
333    ATF_ADD_TEST_CASE(tcs, find_results__directory);
334    ATF_ADD_TEST_CASE(tcs, find_results__file);
335    ATF_ADD_TEST_CASE(tcs, find_results__id);
336    ATF_ADD_TEST_CASE(tcs, find_results__id_with_timestamp);
337    ATF_ADD_TEST_CASE(tcs, find_results__not_found);
338
339    ATF_ADD_TEST_CASE(tcs, new_db__new);
340    ATF_ADD_TEST_CASE(tcs, new_db__explicit);
341
342    ATF_ADD_TEST_CASE(tcs, new_db_for_migration);
343
344    ATF_ADD_TEST_CASE(tcs, query_store_dir__home_absolute);
345    ATF_ADD_TEST_CASE(tcs, query_store_dir__home_relative);
346    ATF_ADD_TEST_CASE(tcs, query_store_dir__no_home);
347
348    ATF_ADD_TEST_CASE(tcs, test_suite_for_path__absolute);
349    ATF_ADD_TEST_CASE(tcs, test_suite_for_path__relative);
350}
351