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/dbtypes.hpp"
30
31#include <atf-c++.hpp>
32
33#include "model/test_program.hpp"
34#include "model/test_result.hpp"
35#include "store/exceptions.hpp"
36#include "utils/datetime.hpp"
37#include "utils/optional.ipp"
38#include "utils/sqlite/database.hpp"
39#include "utils/sqlite/statement.ipp"
40
41namespace datetime = utils::datetime;
42namespace fs = utils::fs;
43namespace sqlite = utils::sqlite;
44
45using utils::none;
46
47
48namespace {
49
50
51/// Validates that a particular bind_x/column_x sequence works.
52///
53/// \param bind The store::bind_* function to put the value.
54/// \param value The value to store and validate.
55/// \param column The store::column_* function to get the value.
56template< typename Type1, typename Type2, typename Type3 >
57static void
58do_ok_test(void (*bind)(sqlite::statement&, const char*, Type1),
59           Type2 value,
60           Type3 (*column)(sqlite::statement&, const char*))
61{
62    sqlite::database db = sqlite::database::in_memory();
63    db.exec("CREATE TABLE test (column DONTCARE)");
64
65    sqlite::statement insert = db.create_statement("INSERT INTO test "
66                                                   "VALUES (:v)");
67    bind(insert, ":v", value);
68    insert.step_without_results();
69
70    sqlite::statement query = db.create_statement("SELECT * FROM test");
71    ATF_REQUIRE(query.step());
72    ATF_REQUIRE(column(query, "column") == value);
73    ATF_REQUIRE(!query.step());
74}
75
76
77/// Validates an error condition of column_*.
78///
79/// \param value The invalid value to insert into the database.
80/// \param column The store::column_* function to get the value.
81/// \param error_regexp The expected message in the raised integrity_error.
82template< typename Type1, typename Type2 >
83static void
84do_invalid_test(Type1 value,
85                Type2 (*column)(sqlite::statement&, const char*),
86                const std::string& error_regexp)
87{
88    sqlite::database db = sqlite::database::in_memory();
89    db.exec("CREATE TABLE test (column DONTCARE)");
90
91    sqlite::statement insert = db.create_statement("INSERT INTO test "
92                                                   "VALUES (:v)");
93    insert.bind(":v", value);
94    insert.step_without_results();
95
96    sqlite::statement query = db.create_statement("SELECT * FROM test");
97    ATF_REQUIRE(query.step());
98    ATF_REQUIRE_THROW_RE(store::integrity_error, error_regexp,
99                         column(query, "column"));
100    ATF_REQUIRE(!query.step());
101}
102
103
104}  // anonymous namespace
105
106
107ATF_TEST_CASE_WITHOUT_HEAD(bool__ok);
108ATF_TEST_CASE_BODY(bool__ok)
109{
110    do_ok_test(store::bind_bool, true, store::column_bool);
111    do_ok_test(store::bind_bool, false, store::column_bool);
112}
113
114
115ATF_TEST_CASE_WITHOUT_HEAD(bool__get_invalid_type);
116ATF_TEST_CASE_BODY(bool__get_invalid_type)
117{
118    do_invalid_test(123, store::column_bool, "not a string");
119}
120
121
122ATF_TEST_CASE_WITHOUT_HEAD(bool__get_invalid_value);
123ATF_TEST_CASE_BODY(bool__get_invalid_value)
124{
125    do_invalid_test("foo", store::column_bool, "Unknown boolean.*foo");
126}
127
128
129ATF_TEST_CASE_WITHOUT_HEAD(delta__ok);
130ATF_TEST_CASE_BODY(delta__ok)
131{
132    do_ok_test(store::bind_delta, datetime::delta(15, 34), store::column_delta);
133}
134
135
136ATF_TEST_CASE_WITHOUT_HEAD(delta__get_invalid_type);
137ATF_TEST_CASE_BODY(delta__get_invalid_type)
138{
139    do_invalid_test(15.6, store::column_delta, "not an integer");
140}
141
142
143ATF_TEST_CASE_WITHOUT_HEAD(optional_string__ok);
144ATF_TEST_CASE_BODY(optional_string__ok)
145{
146    do_ok_test(store::bind_optional_string, "", store::column_optional_string);
147    do_ok_test(store::bind_optional_string, "a", store::column_optional_string);
148}
149
150
151ATF_TEST_CASE_WITHOUT_HEAD(optional_string__get_invalid_type);
152ATF_TEST_CASE_BODY(optional_string__get_invalid_type)
153{
154    do_invalid_test(35, store::column_optional_string, "Invalid string");
155}
156
157
158ATF_TEST_CASE_WITHOUT_HEAD(test_result_type__ok);
159ATF_TEST_CASE_BODY(test_result_type__ok)
160{
161    do_ok_test(store::bind_test_result_type,
162               model::test_result_passed,
163               store::column_test_result_type);
164}
165
166
167ATF_TEST_CASE_WITHOUT_HEAD(test_result_type__get_invalid_type);
168ATF_TEST_CASE_BODY(test_result_type__get_invalid_type)
169{
170    do_invalid_test(12, store::column_test_result_type, "not a string");
171}
172
173
174ATF_TEST_CASE_WITHOUT_HEAD(test_result_type__get_invalid_value);
175ATF_TEST_CASE_BODY(test_result_type__get_invalid_value)
176{
177    do_invalid_test("foo", store::column_test_result_type,
178                    "Unknown test result type foo");
179}
180
181
182ATF_TEST_CASE_WITHOUT_HEAD(timestamp__ok);
183ATF_TEST_CASE_BODY(timestamp__ok)
184{
185    do_ok_test(store::bind_timestamp,
186               datetime::timestamp::from_microseconds(0),
187               store::column_timestamp);
188    do_ok_test(store::bind_timestamp,
189               datetime::timestamp::from_microseconds(123),
190               store::column_timestamp);
191
192    do_ok_test(store::bind_timestamp,
193               datetime::timestamp::from_values(2012, 2, 9, 23, 15, 51, 987654),
194               store::column_timestamp);
195    do_ok_test(store::bind_timestamp,
196               datetime::timestamp::from_values(1980, 1, 2, 3, 4, 5, 0),
197               store::column_timestamp);
198}
199
200
201ATF_TEST_CASE_WITHOUT_HEAD(timestamp__get_invalid_type);
202ATF_TEST_CASE_BODY(timestamp__get_invalid_type)
203{
204    do_invalid_test(35.6, store::column_timestamp, "not an integer");
205}
206
207
208ATF_TEST_CASE_WITHOUT_HEAD(timestamp__get_invalid_value);
209ATF_TEST_CASE_BODY(timestamp__get_invalid_value)
210{
211    do_invalid_test(-1234, store::column_timestamp, "must be positive");
212}
213
214
215ATF_INIT_TEST_CASES(tcs)
216{
217    ATF_ADD_TEST_CASE(tcs, bool__ok);
218    ATF_ADD_TEST_CASE(tcs, bool__get_invalid_type);
219    ATF_ADD_TEST_CASE(tcs, bool__get_invalid_value);
220
221    ATF_ADD_TEST_CASE(tcs, delta__ok);
222    ATF_ADD_TEST_CASE(tcs, delta__get_invalid_type);
223
224    ATF_ADD_TEST_CASE(tcs, optional_string__ok);
225    ATF_ADD_TEST_CASE(tcs, optional_string__get_invalid_type);
226
227    ATF_ADD_TEST_CASE(tcs, test_result_type__ok);
228    ATF_ADD_TEST_CASE(tcs, test_result_type__get_invalid_type);
229    ATF_ADD_TEST_CASE(tcs, test_result_type__get_invalid_value);
230
231    ATF_ADD_TEST_CASE(tcs, timestamp__ok);
232    ATF_ADD_TEST_CASE(tcs, timestamp__get_invalid_type);
233    ATF_ADD_TEST_CASE(tcs, timestamp__get_invalid_value);
234}
235