1*b0d29bc4SBrooks Davis // Copyright 2011 The Kyua Authors.
2*b0d29bc4SBrooks Davis // All rights reserved.
3*b0d29bc4SBrooks Davis //
4*b0d29bc4SBrooks Davis // Redistribution and use in source and binary forms, with or without
5*b0d29bc4SBrooks Davis // modification, are permitted provided that the following conditions are
6*b0d29bc4SBrooks Davis // met:
7*b0d29bc4SBrooks Davis //
8*b0d29bc4SBrooks Davis // * Redistributions of source code must retain the above copyright
9*b0d29bc4SBrooks Davis // notice, this list of conditions and the following disclaimer.
10*b0d29bc4SBrooks Davis // * Redistributions in binary form must reproduce the above copyright
11*b0d29bc4SBrooks Davis // notice, this list of conditions and the following disclaimer in the
12*b0d29bc4SBrooks Davis // documentation and/or other materials provided with the distribution.
13*b0d29bc4SBrooks Davis // * Neither the name of Google Inc. nor the names of its contributors
14*b0d29bc4SBrooks Davis // may be used to endorse or promote products derived from this software
15*b0d29bc4SBrooks Davis // without specific prior written permission.
16*b0d29bc4SBrooks Davis //
17*b0d29bc4SBrooks Davis // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18*b0d29bc4SBrooks Davis // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19*b0d29bc4SBrooks Davis // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20*b0d29bc4SBrooks Davis // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21*b0d29bc4SBrooks Davis // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22*b0d29bc4SBrooks Davis // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23*b0d29bc4SBrooks Davis // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24*b0d29bc4SBrooks Davis // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25*b0d29bc4SBrooks Davis // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26*b0d29bc4SBrooks Davis // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27*b0d29bc4SBrooks Davis // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28*b0d29bc4SBrooks Davis
29*b0d29bc4SBrooks Davis #include "drivers/scan_results.hpp"
30*b0d29bc4SBrooks Davis
31*b0d29bc4SBrooks Davis #include <set>
32*b0d29bc4SBrooks Davis
33*b0d29bc4SBrooks Davis #include <atf-c++.hpp>
34*b0d29bc4SBrooks Davis
35*b0d29bc4SBrooks Davis #include "engine/filters.hpp"
36*b0d29bc4SBrooks Davis #include "model/context.hpp"
37*b0d29bc4SBrooks Davis #include "model/metadata.hpp"
38*b0d29bc4SBrooks Davis #include "model/test_case.hpp"
39*b0d29bc4SBrooks Davis #include "model/test_program.hpp"
40*b0d29bc4SBrooks Davis #include "model/test_result.hpp"
41*b0d29bc4SBrooks Davis #include "store/exceptions.hpp"
42*b0d29bc4SBrooks Davis #include "store/read_transaction.hpp"
43*b0d29bc4SBrooks Davis #include "store/write_backend.hpp"
44*b0d29bc4SBrooks Davis #include "store/write_transaction.hpp"
45*b0d29bc4SBrooks Davis #include "utils/datetime.hpp"
46*b0d29bc4SBrooks Davis #include "utils/format/containers.ipp"
47*b0d29bc4SBrooks Davis #include "utils/format/macros.hpp"
48*b0d29bc4SBrooks Davis #include "utils/optional.ipp"
49*b0d29bc4SBrooks Davis #include "utils/sanity.hpp"
50*b0d29bc4SBrooks Davis
51*b0d29bc4SBrooks Davis namespace datetime = utils::datetime;
52*b0d29bc4SBrooks Davis namespace fs = utils::fs;
53*b0d29bc4SBrooks Davis
54*b0d29bc4SBrooks Davis using utils::none;
55*b0d29bc4SBrooks Davis using utils::optional;
56*b0d29bc4SBrooks Davis
57*b0d29bc4SBrooks Davis
58*b0d29bc4SBrooks Davis namespace {
59*b0d29bc4SBrooks Davis
60*b0d29bc4SBrooks Davis
61*b0d29bc4SBrooks Davis /// Records the callback values for futher investigation.
62*b0d29bc4SBrooks Davis class capture_hooks : public drivers::scan_results::base_hooks {
63*b0d29bc4SBrooks Davis public:
64*b0d29bc4SBrooks Davis /// Whether begin() was called or not.
65*b0d29bc4SBrooks Davis bool _begin_called;
66*b0d29bc4SBrooks Davis
67*b0d29bc4SBrooks Davis /// The captured driver result, if any.
68*b0d29bc4SBrooks Davis optional< drivers::scan_results::result > _end_result;
69*b0d29bc4SBrooks Davis
70*b0d29bc4SBrooks Davis /// The captured context, if any.
71*b0d29bc4SBrooks Davis optional< model::context > _context;
72*b0d29bc4SBrooks Davis
73*b0d29bc4SBrooks Davis /// The captured results, flattened as "program:test_case:result".
74*b0d29bc4SBrooks Davis std::set< std::string > _results;
75*b0d29bc4SBrooks Davis
76*b0d29bc4SBrooks Davis /// Constructor.
capture_hooks(void)77*b0d29bc4SBrooks Davis capture_hooks(void) :
78*b0d29bc4SBrooks Davis _begin_called(false)
79*b0d29bc4SBrooks Davis {
80*b0d29bc4SBrooks Davis }
81*b0d29bc4SBrooks Davis
82*b0d29bc4SBrooks Davis /// Callback executed before any operation is performed.
83*b0d29bc4SBrooks Davis void
begin(void)84*b0d29bc4SBrooks Davis begin(void)
85*b0d29bc4SBrooks Davis {
86*b0d29bc4SBrooks Davis _begin_called = true;
87*b0d29bc4SBrooks Davis }
88*b0d29bc4SBrooks Davis
89*b0d29bc4SBrooks Davis /// Callback executed after all operations are performed.
90*b0d29bc4SBrooks Davis ///
91*b0d29bc4SBrooks Davis /// \param r A structure with all results computed by this driver. Note
92*b0d29bc4SBrooks Davis /// that this is also returned by the drive operation.
93*b0d29bc4SBrooks Davis void
end(const drivers::scan_results::result & r)94*b0d29bc4SBrooks Davis end(const drivers::scan_results::result& r)
95*b0d29bc4SBrooks Davis {
96*b0d29bc4SBrooks Davis PRE(!_end_result);
97*b0d29bc4SBrooks Davis _end_result = r;
98*b0d29bc4SBrooks Davis }
99*b0d29bc4SBrooks Davis
100*b0d29bc4SBrooks Davis /// Callback executed when the context is loaded.
101*b0d29bc4SBrooks Davis ///
102*b0d29bc4SBrooks Davis /// \param context The context loaded from the database.
got_context(const model::context & context)103*b0d29bc4SBrooks Davis void got_context(const model::context& context)
104*b0d29bc4SBrooks Davis {
105*b0d29bc4SBrooks Davis PRE(!_context);
106*b0d29bc4SBrooks Davis _context = context;
107*b0d29bc4SBrooks Davis }
108*b0d29bc4SBrooks Davis
109*b0d29bc4SBrooks Davis /// Callback executed when a test results is found.
110*b0d29bc4SBrooks Davis ///
111*b0d29bc4SBrooks Davis /// \param iter Container for the test result's data.
got_result(store::results_iterator & iter)112*b0d29bc4SBrooks Davis void got_result(store::results_iterator& iter)
113*b0d29bc4SBrooks Davis {
114*b0d29bc4SBrooks Davis const char* type;
115*b0d29bc4SBrooks Davis switch (iter.result().type()) {
116*b0d29bc4SBrooks Davis case model::test_result_passed: type = "passed"; break;
117*b0d29bc4SBrooks Davis case model::test_result_skipped: type = "skipped"; break;
118*b0d29bc4SBrooks Davis default:
119*b0d29bc4SBrooks Davis UNREACHABLE_MSG("Formatting unimplemented");
120*b0d29bc4SBrooks Davis }
121*b0d29bc4SBrooks Davis const datetime::delta duration = iter.end_time() - iter.start_time();
122*b0d29bc4SBrooks Davis _results.insert(F("%s:%s:%s:%s:%s:%s") %
123*b0d29bc4SBrooks Davis iter.test_program()->absolute_path() %
124*b0d29bc4SBrooks Davis iter.test_case_name() % type % iter.result().reason() %
125*b0d29bc4SBrooks Davis duration.seconds % duration.useconds);
126*b0d29bc4SBrooks Davis }
127*b0d29bc4SBrooks Davis };
128*b0d29bc4SBrooks Davis
129*b0d29bc4SBrooks Davis
130*b0d29bc4SBrooks Davis /// Populates a results file.
131*b0d29bc4SBrooks Davis ///
132*b0d29bc4SBrooks Davis /// It is not OK to call this function multiple times on the same file.
133*b0d29bc4SBrooks Davis ///
134*b0d29bc4SBrooks Davis /// \param db_name The database to update.
135*b0d29bc4SBrooks Davis /// \param count The number of "elements" to insert into the results file.
136*b0d29bc4SBrooks Davis /// Determines the number of test programs and the number of test cases
137*b0d29bc4SBrooks Davis /// each has. Can be used to determine from the caller which particular
138*b0d29bc4SBrooks Davis /// results file has been loaded.
139*b0d29bc4SBrooks Davis static void
populate_results_file(const char * db_name,const int count)140*b0d29bc4SBrooks Davis populate_results_file(const char* db_name, const int count)
141*b0d29bc4SBrooks Davis {
142*b0d29bc4SBrooks Davis store::write_backend backend = store::write_backend::open_rw(
143*b0d29bc4SBrooks Davis fs::path(db_name));
144*b0d29bc4SBrooks Davis
145*b0d29bc4SBrooks Davis store::write_transaction tx = backend.start_write();
146*b0d29bc4SBrooks Davis
147*b0d29bc4SBrooks Davis std::map< std::string, std::string > env;
148*b0d29bc4SBrooks Davis for (int i = 0; i < count; i++)
149*b0d29bc4SBrooks Davis env[F("VAR%s") % i] = F("Value %s") % i;
150*b0d29bc4SBrooks Davis const model::context context(fs::path("/root"), env);
151*b0d29bc4SBrooks Davis tx.put_context(context);
152*b0d29bc4SBrooks Davis
153*b0d29bc4SBrooks Davis for (int i = 0; i < count; i++) {
154*b0d29bc4SBrooks Davis model::test_program_builder test_program_builder(
155*b0d29bc4SBrooks Davis "fake", fs::path(F("dir/prog_%s") % i), fs::path("/root"),
156*b0d29bc4SBrooks Davis F("suite_%s") % i);
157*b0d29bc4SBrooks Davis for (int j = 0; j < count; j++) {
158*b0d29bc4SBrooks Davis test_program_builder.add_test_case(F("case_%s") % j);
159*b0d29bc4SBrooks Davis }
160*b0d29bc4SBrooks Davis const model::test_program test_program = test_program_builder.build();
161*b0d29bc4SBrooks Davis const int64_t tp_id = tx.put_test_program(test_program);
162*b0d29bc4SBrooks Davis
163*b0d29bc4SBrooks Davis for (int j = 0; j < count; j++) {
164*b0d29bc4SBrooks Davis const model::test_result result(model::test_result_skipped,
165*b0d29bc4SBrooks Davis F("Count %s") % j);
166*b0d29bc4SBrooks Davis const int64_t tc_id = tx.put_test_case(test_program,
167*b0d29bc4SBrooks Davis F("case_%s") % j, tp_id);
168*b0d29bc4SBrooks Davis const datetime::timestamp start =
169*b0d29bc4SBrooks Davis datetime::timestamp::from_microseconds(1000010);
170*b0d29bc4SBrooks Davis const datetime::timestamp end =
171*b0d29bc4SBrooks Davis datetime::timestamp::from_microseconds(5000020 + i + j);
172*b0d29bc4SBrooks Davis tx.put_result(result, tc_id, start, end);
173*b0d29bc4SBrooks Davis }
174*b0d29bc4SBrooks Davis }
175*b0d29bc4SBrooks Davis
176*b0d29bc4SBrooks Davis tx.commit();
177*b0d29bc4SBrooks Davis }
178*b0d29bc4SBrooks Davis
179*b0d29bc4SBrooks Davis
180*b0d29bc4SBrooks Davis } // anonymous namespace
181*b0d29bc4SBrooks Davis
182*b0d29bc4SBrooks Davis
183*b0d29bc4SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(ok__all);
ATF_TEST_CASE_BODY(ok__all)184*b0d29bc4SBrooks Davis ATF_TEST_CASE_BODY(ok__all)
185*b0d29bc4SBrooks Davis {
186*b0d29bc4SBrooks Davis populate_results_file("test.db", 2);
187*b0d29bc4SBrooks Davis
188*b0d29bc4SBrooks Davis capture_hooks hooks;
189*b0d29bc4SBrooks Davis const drivers::scan_results::result result = drivers::scan_results::drive(
190*b0d29bc4SBrooks Davis fs::path("test.db"), std::set< engine::test_filter >(), hooks);
191*b0d29bc4SBrooks Davis ATF_REQUIRE(result.unused_filters.empty());
192*b0d29bc4SBrooks Davis ATF_REQUIRE(hooks._begin_called);
193*b0d29bc4SBrooks Davis ATF_REQUIRE(hooks._end_result);
194*b0d29bc4SBrooks Davis
195*b0d29bc4SBrooks Davis std::map< std::string, std::string > env;
196*b0d29bc4SBrooks Davis env["VAR0"] = "Value 0";
197*b0d29bc4SBrooks Davis env["VAR1"] = "Value 1";
198*b0d29bc4SBrooks Davis const model::context context(fs::path("/root"), env);
199*b0d29bc4SBrooks Davis ATF_REQUIRE(context == hooks._context.get());
200*b0d29bc4SBrooks Davis
201*b0d29bc4SBrooks Davis std::set< std::string > results;
202*b0d29bc4SBrooks Davis results.insert("/root/dir/prog_0:case_0:skipped:Count 0:4:10");
203*b0d29bc4SBrooks Davis results.insert("/root/dir/prog_0:case_1:skipped:Count 1:4:11");
204*b0d29bc4SBrooks Davis results.insert("/root/dir/prog_1:case_0:skipped:Count 0:4:11");
205*b0d29bc4SBrooks Davis results.insert("/root/dir/prog_1:case_1:skipped:Count 1:4:12");
206*b0d29bc4SBrooks Davis ATF_REQUIRE_EQ(results, hooks._results);
207*b0d29bc4SBrooks Davis }
208*b0d29bc4SBrooks Davis
209*b0d29bc4SBrooks Davis
210*b0d29bc4SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(ok__filters);
ATF_TEST_CASE_BODY(ok__filters)211*b0d29bc4SBrooks Davis ATF_TEST_CASE_BODY(ok__filters)
212*b0d29bc4SBrooks Davis {
213*b0d29bc4SBrooks Davis populate_results_file("test.db", 3);
214*b0d29bc4SBrooks Davis
215*b0d29bc4SBrooks Davis std::set< engine::test_filter > filters;
216*b0d29bc4SBrooks Davis filters.insert(engine::test_filter(fs::path("dir/prog_1"), ""));
217*b0d29bc4SBrooks Davis filters.insert(engine::test_filter(fs::path("dir/prog_1"), "no"));
218*b0d29bc4SBrooks Davis filters.insert(engine::test_filter(fs::path("dir/prog_2"), "case_1"));
219*b0d29bc4SBrooks Davis filters.insert(engine::test_filter(fs::path("dir/prog_3"), ""));
220*b0d29bc4SBrooks Davis
221*b0d29bc4SBrooks Davis capture_hooks hooks;
222*b0d29bc4SBrooks Davis const drivers::scan_results::result result = drivers::scan_results::drive(
223*b0d29bc4SBrooks Davis fs::path("test.db"), filters, hooks);
224*b0d29bc4SBrooks Davis ATF_REQUIRE(hooks._begin_called);
225*b0d29bc4SBrooks Davis ATF_REQUIRE(hooks._end_result);
226*b0d29bc4SBrooks Davis
227*b0d29bc4SBrooks Davis std::set< engine::test_filter > unused_filters;
228*b0d29bc4SBrooks Davis unused_filters.insert(engine::test_filter(fs::path("dir/prog_1"), "no"));
229*b0d29bc4SBrooks Davis unused_filters.insert(engine::test_filter(fs::path("dir/prog_3"), ""));
230*b0d29bc4SBrooks Davis ATF_REQUIRE_EQ(unused_filters, result.unused_filters);
231*b0d29bc4SBrooks Davis
232*b0d29bc4SBrooks Davis std::set< std::string > results;
233*b0d29bc4SBrooks Davis results.insert("/root/dir/prog_1:case_0:skipped:Count 0:4:11");
234*b0d29bc4SBrooks Davis results.insert("/root/dir/prog_1:case_1:skipped:Count 1:4:12");
235*b0d29bc4SBrooks Davis results.insert("/root/dir/prog_1:case_2:skipped:Count 2:4:13");
236*b0d29bc4SBrooks Davis results.insert("/root/dir/prog_2:case_1:skipped:Count 1:4:13");
237*b0d29bc4SBrooks Davis ATF_REQUIRE_EQ(results, hooks._results);
238*b0d29bc4SBrooks Davis }
239*b0d29bc4SBrooks Davis
240*b0d29bc4SBrooks Davis
241*b0d29bc4SBrooks Davis ATF_TEST_CASE_WITHOUT_HEAD(missing_db);
ATF_TEST_CASE_BODY(missing_db)242*b0d29bc4SBrooks Davis ATF_TEST_CASE_BODY(missing_db)
243*b0d29bc4SBrooks Davis {
244*b0d29bc4SBrooks Davis capture_hooks hooks;
245*b0d29bc4SBrooks Davis ATF_REQUIRE_THROW(
246*b0d29bc4SBrooks Davis store::error,
247*b0d29bc4SBrooks Davis drivers::scan_results::drive(fs::path("test.db"),
248*b0d29bc4SBrooks Davis std::set< engine::test_filter >(),
249*b0d29bc4SBrooks Davis hooks));
250*b0d29bc4SBrooks Davis }
251*b0d29bc4SBrooks Davis
252*b0d29bc4SBrooks Davis
ATF_INIT_TEST_CASES(tcs)253*b0d29bc4SBrooks Davis ATF_INIT_TEST_CASES(tcs)
254*b0d29bc4SBrooks Davis {
255*b0d29bc4SBrooks Davis ATF_ADD_TEST_CASE(tcs, ok__all);
256*b0d29bc4SBrooks Davis ATF_ADD_TEST_CASE(tcs, ok__filters);
257*b0d29bc4SBrooks Davis ATF_ADD_TEST_CASE(tcs, missing_db);
258*b0d29bc4SBrooks Davis }
259