1// Copyright 2011 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/read_transaction.hpp"
30
31#include <map>
32#include <string>
33
34#include <atf-c++.hpp>
35
36#include "model/context.hpp"
37#include "model/metadata.hpp"
38#include "model/test_program.hpp"
39#include "model/test_result.hpp"
40#include "store/exceptions.hpp"
41#include "store/read_backend.hpp"
42#include "store/write_backend.hpp"
43#include "store/write_transaction.hpp"
44#include "utils/datetime.hpp"
45#include "utils/fs/path.hpp"
46#include "utils/logging/operations.hpp"
47#include "utils/optional.ipp"
48#include "utils/sqlite/database.hpp"
49#include "utils/sqlite/statement.ipp"
50
51namespace datetime = utils::datetime;
52namespace fs = utils::fs;
53namespace logging = utils::logging;
54namespace sqlite = utils::sqlite;
55
56
57ATF_TEST_CASE(get_context__missing);
58ATF_TEST_CASE_HEAD(get_context__missing)
59{
60    logging::set_inmemory();
61    set_md_var("require.files", store::detail::schema_file().c_str());
62}
63ATF_TEST_CASE_BODY(get_context__missing)
64{
65    store::write_backend::open_rw(fs::path("test.db"));  // Create database.
66    store::read_backend backend = store::read_backend::open_ro(
67        fs::path("test.db"));
68
69    store::read_transaction tx = backend.start_read();
70    ATF_REQUIRE_THROW_RE(store::error, "context: no data", tx.get_context());
71}
72
73
74ATF_TEST_CASE(get_context__invalid_cwd);
75ATF_TEST_CASE_HEAD(get_context__invalid_cwd)
76{
77    logging::set_inmemory();
78    set_md_var("require.files", store::detail::schema_file().c_str());
79}
80ATF_TEST_CASE_BODY(get_context__invalid_cwd)
81{
82    {
83        store::write_backend backend = store::write_backend::open_rw(
84            fs::path("test.db"));
85
86        sqlite::statement stmt = backend.database().create_statement(
87            "INSERT INTO contexts (cwd) VALUES (:cwd)");
88        const char buffer[10] = "foo bar";
89        stmt.bind(":cwd", sqlite::blob(buffer, sizeof(buffer)));
90        stmt.step_without_results();
91    }
92
93    store::read_backend backend = store::read_backend::open_ro(
94        fs::path("test.db"));
95    store::read_transaction tx = backend.start_read();
96    ATF_REQUIRE_THROW_RE(store::error, "context: .*cwd.*not a string",
97                         tx.get_context());
98}
99
100
101ATF_TEST_CASE(get_context__invalid_env_vars);
102ATF_TEST_CASE_HEAD(get_context__invalid_env_vars)
103{
104    logging::set_inmemory();
105    set_md_var("require.files", store::detail::schema_file().c_str());
106}
107ATF_TEST_CASE_BODY(get_context__invalid_env_vars)
108{
109    {
110        store::write_backend backend = store::write_backend::open_rw(
111            fs::path("test-bad-name.db"));
112        backend.database().exec("INSERT INTO contexts (cwd) "
113                                "VALUES ('/foo/bar')");
114        const char buffer[10] = "foo bar";
115
116        sqlite::statement stmt = backend.database().create_statement(
117            "INSERT INTO env_vars (var_name, var_value) "
118            "VALUES (:var_name, 'abc')");
119        stmt.bind(":var_name", sqlite::blob(buffer, sizeof(buffer)));
120        stmt.step_without_results();
121    }
122    {
123        store::read_backend backend = store::read_backend::open_ro(
124            fs::path("test-bad-name.db"));
125        store::read_transaction tx = backend.start_read();
126        ATF_REQUIRE_THROW_RE(store::error, "context: .*var_name.*not a string",
127                             tx.get_context());
128    }
129
130    {
131        store::write_backend backend = store::write_backend::open_rw(
132            fs::path("test-bad-value.db"));
133        backend.database().exec("INSERT INTO contexts (cwd) "
134                                "VALUES ('/foo/bar')");
135        const char buffer[10] = "foo bar";
136
137        sqlite::statement stmt = backend.database().create_statement(
138            "INSERT INTO env_vars (var_name, var_value) "
139            "VALUES ('abc', :var_value)");
140        stmt.bind(":var_value", sqlite::blob(buffer, sizeof(buffer)));
141        stmt.step_without_results();
142    }
143    {
144        store::read_backend backend = store::read_backend::open_ro(
145            fs::path("test-bad-value.db"));
146        store::read_transaction tx = backend.start_read();
147        ATF_REQUIRE_THROW_RE(store::error, "context: .*var_value.*not a string",
148                             tx.get_context());
149    }
150}
151
152
153ATF_TEST_CASE(get_results__none);
154ATF_TEST_CASE_HEAD(get_results__none)
155{
156    logging::set_inmemory();
157    set_md_var("require.files", store::detail::schema_file().c_str());
158}
159ATF_TEST_CASE_BODY(get_results__none)
160{
161    store::write_backend::open_rw(fs::path("test.db"));  // Create database.
162    store::read_backend backend = store::read_backend::open_ro(
163        fs::path("test.db"));
164    store::read_transaction tx = backend.start_read();
165    store::results_iterator iter = tx.get_results();
166    ATF_REQUIRE(!iter);
167}
168
169
170ATF_TEST_CASE(get_results__many);
171ATF_TEST_CASE_HEAD(get_results__many)
172{
173    logging::set_inmemory();
174    set_md_var("require.files", store::detail::schema_file().c_str());
175}
176ATF_TEST_CASE_BODY(get_results__many)
177{
178    store::write_backend backend = store::write_backend::open_rw(
179        fs::path("test.db"));
180
181    store::write_transaction tx = backend.start_write();
182
183    const model::context context(fs::path("/foo/bar"),
184                                 std::map< std::string, std::string >());
185    tx.put_context(context);
186
187    const datetime::timestamp start_time1 = datetime::timestamp::from_values(
188        2012, 01, 30, 22, 10, 00, 0);
189    const datetime::timestamp end_time1 = datetime::timestamp::from_values(
190        2012, 01, 30, 22, 15, 30, 1234);
191    const datetime::timestamp start_time2 = datetime::timestamp::from_values(
192        2012, 01, 30, 22, 15, 40, 987);
193    const datetime::timestamp end_time2 = datetime::timestamp::from_values(
194        2012, 01, 30, 22, 16, 0, 0);
195
196    atf::utils::create_file("unused.txt", "unused file\n");
197
198    const model::test_program test_program_1 = model::test_program_builder(
199        "plain", fs::path("a/prog1"), fs::path("/the/root"), "suite1")
200        .add_test_case("main")
201        .build();
202    const model::test_result result_1(model::test_result_passed);
203    {
204        const int64_t tp_id = tx.put_test_program(test_program_1);
205        const int64_t tc_id = tx.put_test_case(test_program_1, "main", tp_id);
206        atf::utils::create_file("prog1.out", "stdout of prog1\n");
207        tx.put_test_case_file("__STDOUT__", fs::path("prog1.out"), tc_id);
208        tx.put_test_case_file("unused.txt", fs::path("unused.txt"), tc_id);
209        tx.put_result(result_1, tc_id, start_time1, end_time1);
210    }
211
212    const model::test_program test_program_2 = model::test_program_builder(
213        "plain", fs::path("b/prog2"), fs::path("/the/root"), "suite2")
214        .add_test_case("main")
215        .build();
216    const model::test_result result_2(model::test_result_failed,
217                                      "Some text");
218    {
219        const int64_t tp_id = tx.put_test_program(test_program_2);
220        const int64_t tc_id = tx.put_test_case(test_program_2, "main", tp_id);
221        atf::utils::create_file("prog2.err", "stderr of prog2\n");
222        tx.put_test_case_file("__STDERR__", fs::path("prog2.err"), tc_id);
223        tx.put_test_case_file("unused.txt", fs::path("unused.txt"), tc_id);
224        tx.put_result(result_2, tc_id, start_time2, end_time2);
225    }
226
227    tx.commit();
228    backend.close();
229
230    store::read_backend backend2 = store::read_backend::open_ro(
231        fs::path("test.db"));
232    store::read_transaction tx2 = backend2.start_read();
233    store::results_iterator iter = tx2.get_results();
234    ATF_REQUIRE(iter);
235    ATF_REQUIRE_EQ(test_program_1, *iter.test_program());
236    ATF_REQUIRE_EQ("main", iter.test_case_name());
237    ATF_REQUIRE_EQ("stdout of prog1\n", iter.stdout_contents());
238    ATF_REQUIRE(iter.stderr_contents().empty());
239    ATF_REQUIRE_EQ(result_1, iter.result());
240    ATF_REQUIRE_EQ(start_time1, iter.start_time());
241    ATF_REQUIRE_EQ(end_time1, iter.end_time());
242    ATF_REQUIRE(++iter);
243    ATF_REQUIRE_EQ(test_program_2, *iter.test_program());
244    ATF_REQUIRE_EQ("main", iter.test_case_name());
245    ATF_REQUIRE(iter.stdout_contents().empty());
246    ATF_REQUIRE_EQ("stderr of prog2\n", iter.stderr_contents());
247    ATF_REQUIRE_EQ(result_2, iter.result());
248    ATF_REQUIRE_EQ(start_time2, iter.start_time());
249    ATF_REQUIRE_EQ(end_time2, iter.end_time());
250    ATF_REQUIRE(!++iter);
251}
252
253
254ATF_INIT_TEST_CASES(tcs)
255{
256    ATF_ADD_TEST_CASE(tcs, get_context__missing);
257    ATF_ADD_TEST_CASE(tcs, get_context__invalid_cwd);
258    ATF_ADD_TEST_CASE(tcs, get_context__invalid_env_vars);
259
260    ATF_ADD_TEST_CASE(tcs, get_results__none);
261    ATF_ADD_TEST_CASE(tcs, get_results__many);
262}
263