1*6a6c8299Sjmmv // Copyright 2011 Google Inc.
2*6a6c8299Sjmmv // All rights reserved.
3*6a6c8299Sjmmv //
4*6a6c8299Sjmmv // Redistribution and use in source and binary forms, with or without
5*6a6c8299Sjmmv // modification, are permitted provided that the following conditions are
6*6a6c8299Sjmmv // met:
7*6a6c8299Sjmmv //
8*6a6c8299Sjmmv // * Redistributions of source code must retain the above copyright
9*6a6c8299Sjmmv // notice, this list of conditions and the following disclaimer.
10*6a6c8299Sjmmv // * Redistributions in binary form must reproduce the above copyright
11*6a6c8299Sjmmv // notice, this list of conditions and the following disclaimer in the
12*6a6c8299Sjmmv // documentation and/or other materials provided with the distribution.
13*6a6c8299Sjmmv // * Neither the name of Google Inc. nor the names of its contributors
14*6a6c8299Sjmmv // may be used to endorse or promote products derived from this software
15*6a6c8299Sjmmv // without specific prior written permission.
16*6a6c8299Sjmmv //
17*6a6c8299Sjmmv // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18*6a6c8299Sjmmv // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19*6a6c8299Sjmmv // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20*6a6c8299Sjmmv // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21*6a6c8299Sjmmv // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22*6a6c8299Sjmmv // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23*6a6c8299Sjmmv // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24*6a6c8299Sjmmv // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25*6a6c8299Sjmmv // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26*6a6c8299Sjmmv // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27*6a6c8299Sjmmv // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28*6a6c8299Sjmmv
29*6a6c8299Sjmmv #include "engine/drivers/run_tests.hpp"
30*6a6c8299Sjmmv
31*6a6c8299Sjmmv #include "engine/action.hpp"
32*6a6c8299Sjmmv #include "engine/context.hpp"
33*6a6c8299Sjmmv #include "engine/filters.hpp"
34*6a6c8299Sjmmv #include "engine/kyuafile.hpp"
35*6a6c8299Sjmmv #include "engine/test_program.hpp"
36*6a6c8299Sjmmv #include "engine/test_result.hpp"
37*6a6c8299Sjmmv #include "store/backend.hpp"
38*6a6c8299Sjmmv #include "store/transaction.hpp"
39*6a6c8299Sjmmv #include "utils/datetime.hpp"
40*6a6c8299Sjmmv #include "utils/defs.hpp"
41*6a6c8299Sjmmv #include "utils/format/macros.hpp"
42*6a6c8299Sjmmv #include "utils/fs/auto_cleaners.hpp"
43*6a6c8299Sjmmv #include "utils/logging/macros.hpp"
44*6a6c8299Sjmmv #include "utils/optional.ipp"
45*6a6c8299Sjmmv #include "utils/signals/interrupts.hpp"
46*6a6c8299Sjmmv
47*6a6c8299Sjmmv namespace config = utils::config;
48*6a6c8299Sjmmv namespace datetime = utils::datetime;
49*6a6c8299Sjmmv namespace fs = utils::fs;
50*6a6c8299Sjmmv namespace run_tests = engine::drivers::run_tests;
51*6a6c8299Sjmmv namespace signals = utils::signals;
52*6a6c8299Sjmmv
53*6a6c8299Sjmmv using utils::optional;
54*6a6c8299Sjmmv
55*6a6c8299Sjmmv
56*6a6c8299Sjmmv namespace {
57*6a6c8299Sjmmv
58*6a6c8299Sjmmv
59*6a6c8299Sjmmv /// Test case hooks to save the output into the database.
60*6a6c8299Sjmmv class file_saver_hooks : public engine::test_case_hooks {
61*6a6c8299Sjmmv /// Open write transaction for the test case's data.
62*6a6c8299Sjmmv store::transaction& _tx;
63*6a6c8299Sjmmv
64*6a6c8299Sjmmv /// Identifier of the test case being stored.
65*6a6c8299Sjmmv const int64_t _test_case_id;
66*6a6c8299Sjmmv
67*6a6c8299Sjmmv public:
68*6a6c8299Sjmmv /// Constructs a new set of hooks.
69*6a6c8299Sjmmv ///
70*6a6c8299Sjmmv /// \param tx_ Open write transaction for the test case's data.
71*6a6c8299Sjmmv /// \param test_case_id_ Identifier of the test case being stored.
file_saver_hooks(store::transaction & tx_,const int64_t test_case_id_)72*6a6c8299Sjmmv file_saver_hooks(store::transaction& tx_,
73*6a6c8299Sjmmv const int64_t test_case_id_) :
74*6a6c8299Sjmmv _tx(tx_), _test_case_id(test_case_id_)
75*6a6c8299Sjmmv {
76*6a6c8299Sjmmv }
77*6a6c8299Sjmmv
78*6a6c8299Sjmmv /// Stores the stdout of the test case into the database.
79*6a6c8299Sjmmv ///
80*6a6c8299Sjmmv /// \param file Path to the stdout of the test case.
81*6a6c8299Sjmmv void
got_stdout(const fs::path & file)82*6a6c8299Sjmmv got_stdout(const fs::path& file)
83*6a6c8299Sjmmv {
84*6a6c8299Sjmmv _tx.put_test_case_file("__STDOUT__", file, _test_case_id);
85*6a6c8299Sjmmv }
86*6a6c8299Sjmmv
87*6a6c8299Sjmmv /// Stores the stderr of the test case into the database.
88*6a6c8299Sjmmv ///
89*6a6c8299Sjmmv /// \param file Path to the stderr of the test case.
90*6a6c8299Sjmmv void
got_stderr(const fs::path & file)91*6a6c8299Sjmmv got_stderr(const fs::path& file)
92*6a6c8299Sjmmv {
93*6a6c8299Sjmmv _tx.put_test_case_file("__STDERR__", file, _test_case_id);
94*6a6c8299Sjmmv }
95*6a6c8299Sjmmv };
96*6a6c8299Sjmmv
97*6a6c8299Sjmmv
98*6a6c8299Sjmmv /// Runs a test program in a controlled manner.
99*6a6c8299Sjmmv ///
100*6a6c8299Sjmmv /// If the test program fails to provide a list of test cases, a fake test case
101*6a6c8299Sjmmv /// named '__test_program__' is created and it is reported as broken.
102*6a6c8299Sjmmv ///
103*6a6c8299Sjmmv /// \param program The test program to execute.
104*6a6c8299Sjmmv /// \param user_config The configuration variables provided by the user.
105*6a6c8299Sjmmv /// \param filters The matching state of the filters.
106*6a6c8299Sjmmv /// \param hooks The user hooks to receive asynchronous notifications.
107*6a6c8299Sjmmv /// \param work_directory Temporary directory to use.
108*6a6c8299Sjmmv /// \param tx The store transaction into which to put the results.
109*6a6c8299Sjmmv /// \param action_id The action this program belongs to.
110*6a6c8299Sjmmv void
run_test_program(const engine::test_program & program,const config::tree & user_config,engine::filters_state & filters,run_tests::base_hooks & hooks,const fs::path & work_directory,store::transaction & tx,const int64_t action_id)111*6a6c8299Sjmmv run_test_program(const engine::test_program& program,
112*6a6c8299Sjmmv const config::tree& user_config,
113*6a6c8299Sjmmv engine::filters_state& filters,
114*6a6c8299Sjmmv run_tests::base_hooks& hooks,
115*6a6c8299Sjmmv const fs::path& work_directory,
116*6a6c8299Sjmmv store::transaction& tx,
117*6a6c8299Sjmmv const int64_t action_id)
118*6a6c8299Sjmmv {
119*6a6c8299Sjmmv LI(F("Processing test program '%s'") % program.relative_path());
120*6a6c8299Sjmmv const int64_t test_program_id = tx.put_test_program(program, action_id);
121*6a6c8299Sjmmv
122*6a6c8299Sjmmv const engine::test_cases_vector& test_cases = program.test_cases();
123*6a6c8299Sjmmv for (engine::test_cases_vector::const_iterator iter = test_cases.begin();
124*6a6c8299Sjmmv iter != test_cases.end(); iter++) {
125*6a6c8299Sjmmv const engine::test_case_ptr test_case = *iter;
126*6a6c8299Sjmmv
127*6a6c8299Sjmmv if (!filters.match_test_case(program.relative_path(),
128*6a6c8299Sjmmv test_case->name()))
129*6a6c8299Sjmmv continue;
130*6a6c8299Sjmmv
131*6a6c8299Sjmmv const int64_t test_case_id = tx.put_test_case(*test_case,
132*6a6c8299Sjmmv test_program_id);
133*6a6c8299Sjmmv file_saver_hooks test_hooks(tx, test_case_id);
134*6a6c8299Sjmmv hooks.got_test_case(test_case);
135*6a6c8299Sjmmv const datetime::timestamp start_time = datetime::timestamp::now();
136*6a6c8299Sjmmv const engine::test_result result = run_test_case(
137*6a6c8299Sjmmv test_case.get(), user_config, test_hooks, work_directory);
138*6a6c8299Sjmmv const datetime::timestamp end_time = datetime::timestamp::now();
139*6a6c8299Sjmmv tx.put_result(result, test_case_id, start_time, end_time);
140*6a6c8299Sjmmv hooks.got_result(test_case, result, end_time - start_time);
141*6a6c8299Sjmmv
142*6a6c8299Sjmmv signals::check_interrupt();
143*6a6c8299Sjmmv }
144*6a6c8299Sjmmv }
145*6a6c8299Sjmmv
146*6a6c8299Sjmmv
147*6a6c8299Sjmmv } // anonymous namespace
148*6a6c8299Sjmmv
149*6a6c8299Sjmmv
150*6a6c8299Sjmmv /// Pure abstract destructor.
~base_hooks(void)151*6a6c8299Sjmmv run_tests::base_hooks::~base_hooks(void)
152*6a6c8299Sjmmv {
153*6a6c8299Sjmmv }
154*6a6c8299Sjmmv
155*6a6c8299Sjmmv
156*6a6c8299Sjmmv /// Executes the operation.
157*6a6c8299Sjmmv ///
158*6a6c8299Sjmmv /// \param kyuafile_path The path to the Kyuafile to be loaded.
159*6a6c8299Sjmmv /// \param build_root If not none, path to the built test programs.
160*6a6c8299Sjmmv /// \param store_path The path to the store to be used.
161*6a6c8299Sjmmv /// \param raw_filters The test case filters as provided by the user.
162*6a6c8299Sjmmv /// \param user_config The end-user configuration properties.
163*6a6c8299Sjmmv /// \param hooks The hooks for this execution.
164*6a6c8299Sjmmv ///
165*6a6c8299Sjmmv /// \returns A structure with all results computed by this driver.
166*6a6c8299Sjmmv run_tests::result
drive(const fs::path & kyuafile_path,const optional<fs::path> build_root,const fs::path & store_path,const std::set<engine::test_filter> & raw_filters,const config::tree & user_config,base_hooks & hooks)167*6a6c8299Sjmmv run_tests::drive(const fs::path& kyuafile_path,
168*6a6c8299Sjmmv const optional< fs::path > build_root,
169*6a6c8299Sjmmv const fs::path& store_path,
170*6a6c8299Sjmmv const std::set< engine::test_filter >& raw_filters,
171*6a6c8299Sjmmv const config::tree& user_config,
172*6a6c8299Sjmmv base_hooks& hooks)
173*6a6c8299Sjmmv {
174*6a6c8299Sjmmv const engine::kyuafile kyuafile = engine::kyuafile::load(
175*6a6c8299Sjmmv kyuafile_path, build_root);
176*6a6c8299Sjmmv filters_state filters(raw_filters);
177*6a6c8299Sjmmv store::backend db = store::backend::open_rw(store_path);
178*6a6c8299Sjmmv store::transaction tx = db.start();
179*6a6c8299Sjmmv
180*6a6c8299Sjmmv engine::context context = engine::context::current();
181*6a6c8299Sjmmv const int64_t context_id = tx.put_context(context);
182*6a6c8299Sjmmv
183*6a6c8299Sjmmv engine::action action(context);
184*6a6c8299Sjmmv const int64_t action_id = tx.put_action(action, context_id);
185*6a6c8299Sjmmv
186*6a6c8299Sjmmv signals::interrupts_handler interrupts;
187*6a6c8299Sjmmv
188*6a6c8299Sjmmv const fs::auto_directory work_directory = fs::auto_directory::mkdtemp(
189*6a6c8299Sjmmv "kyua.XXXXXX");
190*6a6c8299Sjmmv
191*6a6c8299Sjmmv for (test_programs_vector::const_iterator iter =
192*6a6c8299Sjmmv kyuafile.test_programs().begin();
193*6a6c8299Sjmmv iter != kyuafile.test_programs().end(); iter++) {
194*6a6c8299Sjmmv const test_program_ptr& test_program = *iter;
195*6a6c8299Sjmmv
196*6a6c8299Sjmmv if (!filters.match_test_program(test_program->relative_path()))
197*6a6c8299Sjmmv continue;
198*6a6c8299Sjmmv
199*6a6c8299Sjmmv run_test_program(*test_program, user_config, filters, hooks,
200*6a6c8299Sjmmv work_directory.directory(), tx, action_id);
201*6a6c8299Sjmmv
202*6a6c8299Sjmmv signals::check_interrupt();
203*6a6c8299Sjmmv }
204*6a6c8299Sjmmv
205*6a6c8299Sjmmv tx.commit();
206*6a6c8299Sjmmv
207*6a6c8299Sjmmv return result(action_id, filters.unused());
208*6a6c8299Sjmmv }
209