xref: /freebsd/contrib/kyua/drivers/scan_results_test.cpp (revision b0d29bc47dba79f6f38e67eabadfb4b32ffd9390)
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