Path: blob/main/contrib/kyua/drivers/report_junit_test.cpp
39478 views
// Copyright 2014 The Kyua Authors.1// All rights reserved.2//3// Redistribution and use in source and binary forms, with or without4// modification, are permitted provided that the following conditions are5// met:6//7// * Redistributions of source code must retain the above copyright8// notice, this list of conditions and the following disclaimer.9// * Redistributions in binary form must reproduce the above copyright10// notice, this list of conditions and the following disclaimer in the11// documentation and/or other materials provided with the distribution.12// * Neither the name of Google Inc. nor the names of its contributors13// may be used to endorse or promote products derived from this software14// without specific prior written permission.15//16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.2728#include "drivers/report_junit.hpp"2930#include <sstream>31#include <vector>3233#include <atf-c++.hpp>3435#include "drivers/scan_results.hpp"36#include "engine/filters.hpp"37#include "model/context.hpp"38#include "model/metadata.hpp"39#include "model/test_case.hpp"40#include "model/test_program.hpp"41#include "model/test_result.hpp"42#include "store/write_backend.hpp"43#include "store/write_transaction.hpp"44#include "utils/datetime.hpp"45#include "utils/format/macros.hpp"46#include "utils/fs/path.hpp"47#include "utils/optional.ipp"48#include "utils/units.hpp"4950namespace datetime = utils::datetime;51namespace fs = utils::fs;52namespace units = utils::units;5354using utils::none;555657namespace {585960/// Formatted metadata for a test case with defaults.61static const char* const default_metadata =62"allowed_architectures is empty\n"63"allowed_platforms is empty\n"64"description is empty\n"65"execenv is empty\n"66"execenv_jail_params is empty\n"67"has_cleanup = false\n"68"is_exclusive = false\n"69"required_configs is empty\n"70"required_disk_space = 0\n"71"required_files is empty\n"72"required_kmods is empty\n"73"required_memory = 0\n"74"required_programs is empty\n"75"required_user is empty\n"76"timeout = 300\n";777879/// Formatted metadata for a test case constructed with the "with_metadata" flag80/// set to true in add_tests.81static const char* const overriden_metadata =82"allowed_architectures is empty\n"83"allowed_platforms is empty\n"84"description = Textual description\n"85"execenv is empty\n"86"execenv_jail_params is empty\n"87"has_cleanup = false\n"88"is_exclusive = false\n"89"required_configs is empty\n"90"required_disk_space = 0\n"91"required_files is empty\n"92"required_kmods is empty\n"93"required_memory = 0\n"94"required_programs is empty\n"95"required_user is empty\n"96"timeout = 5678\n";979899/// Populates the context of the given database.100///101/// \param tx Transaction to use for the writes to the database.102/// \param env_vars Number of environment variables to add to the context.103static void104add_context(store::write_transaction& tx, const std::size_t env_vars)105{106std::map< std::string, std::string > env;107for (std::size_t i = 0; i < env_vars; i++)108env[F("VAR%s") % i] = F("Value %s") % i;109const model::context context(fs::path("/root"), env);110(void)tx.put_context(context);111}112113114/// Adds a new test program with various test cases to the given database.115///116/// \param tx Transaction to use for the writes to the database.117/// \param prog Test program name.118/// \param results Collection of results for the added test cases. The size of119/// this vector indicates the number of tests in the test program.120/// \param with_metadata Whether to add metadata overrides to the test cases.121/// \param with_output Whether to add stdout/stderr messages to the test cases.122static void123add_tests(store::write_transaction& tx,124const char* prog,125const std::vector< model::test_result >& results,126const bool with_metadata, const bool with_output)127{128model::test_program_builder test_program_builder(129"plain", fs::path(prog), fs::path("/root"), "suite");130131for (std::size_t j = 0; j < results.size(); j++) {132model::metadata_builder builder;133if (with_metadata) {134builder.set_description("Textual description");135builder.set_timeout(datetime::delta(5678, 0));136}137test_program_builder.add_test_case(F("t%s") % j, builder.build());138}139140const model::test_program test_program = test_program_builder.build();141const int64_t tp_id = tx.put_test_program(test_program);142143for (std::size_t j = 0; j < results.size(); j++) {144const int64_t tc_id = tx.put_test_case(test_program, F("t%s") % j,145tp_id);146const datetime::timestamp start =147datetime::timestamp::from_microseconds(0);148const datetime::timestamp end =149datetime::timestamp::from_microseconds(j * 1000000 + 500000);150tx.put_result(results[j], tc_id, start, end);151152if (with_output) {153atf::utils::create_file("fake-out", F("stdout file %s") % j);154tx.put_test_case_file("__STDOUT__", fs::path("fake-out"), tc_id);155atf::utils::create_file("fake-err", F("stderr file %s") % j);156tx.put_test_case_file("__STDERR__", fs::path("fake-err"), tc_id);157}158}159}160161162} // anonymous namespace163164165ATF_TEST_CASE_WITHOUT_HEAD(junit_classname);166ATF_TEST_CASE_BODY(junit_classname)167{168const model::test_program test_program = model::test_program_builder(169"plain", fs::path("dir1/dir2/program"), fs::path("/root"), "suite")170.build();171172ATF_REQUIRE_EQ("dir1.dir2.program", drivers::junit_classname(test_program));173}174175176ATF_TEST_CASE_WITHOUT_HEAD(junit_duration);177ATF_TEST_CASE_BODY(junit_duration)178{179ATF_REQUIRE_EQ("0.457",180drivers::junit_duration(datetime::delta(0, 456700)));181ATF_REQUIRE_EQ("3.120",182drivers::junit_duration(datetime::delta(3, 120000)));183ATF_REQUIRE_EQ("5.000", drivers::junit_duration(datetime::delta(5, 0)));184}185186187ATF_TEST_CASE_WITHOUT_HEAD(junit_metadata__defaults);188ATF_TEST_CASE_BODY(junit_metadata__defaults)189{190const model::metadata metadata = model::metadata_builder().build();191192const std::string expected = std::string()193+ drivers::junit_metadata_header194+ default_metadata;195196ATF_REQUIRE_EQ(expected, drivers::junit_metadata(metadata));197}198199200ATF_TEST_CASE_WITHOUT_HEAD(junit_metadata__overrides);201ATF_TEST_CASE_BODY(junit_metadata__overrides)202{203const model::metadata metadata = model::metadata_builder()204.add_allowed_architecture("arch1")205.add_allowed_platform("platform1")206.set_description("This is a test")207.set_execenv("jail")208.set_execenv_jail_params("vnet")209.set_has_cleanup(true)210.set_is_exclusive(true)211.add_required_config("config1")212.set_required_disk_space(units::bytes(456))213.add_required_file(fs::path("file1"))214.set_required_memory(units::bytes(123))215.add_required_program(fs::path("prog1"))216.set_required_user("root")217.set_timeout(datetime::delta(10, 0))218.build();219220const std::string expected = std::string()221+ drivers::junit_metadata_header222+ "allowed_architectures = arch1\n"223+ "allowed_platforms = platform1\n"224+ "description = This is a test\n"225+ "execenv = jail\n"226+ "execenv_jail_params = vnet\n"227+ "has_cleanup = true\n"228+ "is_exclusive = true\n"229+ "required_configs = config1\n"230+ "required_disk_space = 456\n"231+ "required_files = file1\n"232+ "required_kmods is empty\n"233+ "required_memory = 123\n"234+ "required_programs = prog1\n"235+ "required_user = root\n"236+ "timeout = 10\n";237238ATF_REQUIRE_EQ(expected, drivers::junit_metadata(metadata));239}240241242ATF_TEST_CASE_WITHOUT_HEAD(junit_timing);243ATF_TEST_CASE_BODY(junit_timing)244{245const std::string expected = std::string()246+ drivers::junit_timing_header +247"Start time: 2015-06-12T01:02:35.123456Z\n"248"End time: 2016-07-13T18:47:10.000001Z\n"249"Duration: 34364674.877s\n";250251const datetime::timestamp start_time =252datetime::timestamp::from_values(2015, 6, 12, 1, 2, 35, 123456);253const datetime::timestamp end_time =254datetime::timestamp::from_values(2016, 7, 13, 18, 47, 10, 1);255256ATF_REQUIRE_EQ(expected, drivers::junit_timing(start_time, end_time));257}258259260ATF_TEST_CASE_WITHOUT_HEAD(report_junit_hooks__minimal);261ATF_TEST_CASE_BODY(report_junit_hooks__minimal)262{263store::write_backend backend = store::write_backend::open_rw(264fs::path("test.db"));265store::write_transaction tx = backend.start_write();266add_context(tx, 0);267tx.commit();268backend.close();269270std::ostringstream output;271272drivers::report_junit_hooks hooks(output);273drivers::scan_results::drive(fs::path("test.db"),274std::set< engine::test_filter >(),275hooks);276277const char* expected =278"<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n"279"<testsuite>\n"280"<properties>\n"281"<property name=\"cwd\" value=\"/root\"/>\n"282"</properties>\n"283"</testsuite>\n";284ATF_REQUIRE_EQ(expected, output.str());285}286287288ATF_TEST_CASE_WITHOUT_HEAD(report_junit_hooks__some_tests);289ATF_TEST_CASE_BODY(report_junit_hooks__some_tests)290{291std::vector< model::test_result > results1;292results1.push_back(model::test_result(293model::test_result_broken, "Broken"));294results1.push_back(model::test_result(295model::test_result_expected_failure, "XFail"));296results1.push_back(model::test_result(297model::test_result_failed, "Failed"));298std::vector< model::test_result > results2;299results2.push_back(model::test_result(300model::test_result_passed));301results2.push_back(model::test_result(302model::test_result_skipped, "Skipped"));303304store::write_backend backend = store::write_backend::open_rw(305fs::path("test.db"));306store::write_transaction tx = backend.start_write();307add_context(tx, 2);308add_tests(tx, "dir/prog-1", results1, false, false);309add_tests(tx, "dir/sub/prog-2", results2, true, true);310tx.commit();311backend.close();312313std::ostringstream output;314315drivers::report_junit_hooks hooks(output);316drivers::scan_results::drive(fs::path("test.db"),317std::set< engine::test_filter >(),318hooks);319320const std::string expected = std::string() +321"<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n"322"<testsuite>\n"323"<properties>\n"324"<property name=\"cwd\" value=\"/root\"/>\n"325"<property name=\"env.VAR0\" value=\"Value 0\"/>\n"326"<property name=\"env.VAR1\" value=\"Value 1\"/>\n"327"</properties>\n"328329"<testcase classname=\"dir.prog-1\" name=\"t0\" time=\"0.500\">\n"330"<error message=\"Broken\"/>\n"331"<system-err>"332+ drivers::junit_metadata_header +333default_metadata334+ drivers::junit_timing_header +335"Start time: 1970-01-01T00:00:00.000000Z\n"336"End time: 1970-01-01T00:00:00.500000Z\n"337"Duration: 0.500s\n"338+ drivers::junit_stderr_header +339"<EMPTY>\n"340"</system-err>\n"341"</testcase>\n"342343"<testcase classname=\"dir.prog-1\" name=\"t1\" time=\"1.500\">\n"344"<system-err>"345"Expected failure result details\n"346"-------------------------------\n"347"\n"348"XFail\n"349"\n"350+ drivers::junit_metadata_header +351default_metadata352+ drivers::junit_timing_header +353"Start time: 1970-01-01T00:00:00.000000Z\n"354"End time: 1970-01-01T00:00:01.500000Z\n"355"Duration: 1.500s\n"356+ drivers::junit_stderr_header +357"<EMPTY>\n"358"</system-err>\n"359"</testcase>\n"360361"<testcase classname=\"dir.prog-1\" name=\"t2\" time=\"2.500\">\n"362"<failure message=\"Failed\"/>\n"363"<system-err>"364+ drivers::junit_metadata_header +365default_metadata366+ drivers::junit_timing_header +367"Start time: 1970-01-01T00:00:00.000000Z\n"368"End time: 1970-01-01T00:00:02.500000Z\n"369"Duration: 2.500s\n"370+ drivers::junit_stderr_header +371"<EMPTY>\n"372"</system-err>\n"373"</testcase>\n"374375"<testcase classname=\"dir.sub.prog-2\" name=\"t0\" time=\"0.500\">\n"376"<system-out>stdout file 0</system-out>\n"377"<system-err>"378+ drivers::junit_metadata_header +379overriden_metadata380+ drivers::junit_timing_header +381"Start time: 1970-01-01T00:00:00.000000Z\n"382"End time: 1970-01-01T00:00:00.500000Z\n"383"Duration: 0.500s\n"384+ drivers::junit_stderr_header +385"stderr file 0</system-err>\n"386"</testcase>\n"387388"<testcase classname=\"dir.sub.prog-2\" name=\"t1\" time=\"1.500\">\n"389"<skipped/>\n"390"<system-out>stdout file 1</system-out>\n"391"<system-err>"392"Skipped result details\n"393"----------------------\n"394"\n"395"Skipped\n"396"\n"397+ drivers::junit_metadata_header +398overriden_metadata399+ drivers::junit_timing_header +400"Start time: 1970-01-01T00:00:00.000000Z\n"401"End time: 1970-01-01T00:00:01.500000Z\n"402"Duration: 1.500s\n"403+ drivers::junit_stderr_header +404"stderr file 1</system-err>\n"405"</testcase>\n"406407"</testsuite>\n";408ATF_REQUIRE_EQ(expected, output.str());409}410411412ATF_INIT_TEST_CASES(tcs)413{414ATF_ADD_TEST_CASE(tcs, junit_classname);415416ATF_ADD_TEST_CASE(tcs, junit_duration);417418ATF_ADD_TEST_CASE(tcs, junit_metadata__defaults);419ATF_ADD_TEST_CASE(tcs, junit_metadata__overrides);420421ATF_ADD_TEST_CASE(tcs, junit_timing);422423ATF_ADD_TEST_CASE(tcs, report_junit_hooks__minimal);424ATF_ADD_TEST_CASE(tcs, report_junit_hooks__some_tests);425}426427428