blob: 51746ee496db53e971d02acef66c7621443d86d6 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
#include "kudu/util/test_util.h"
#include <gflags/gflags.h>
#include <glog/logging.h>
#include "kudu/gutil/strings/strcat.h"
#include "kudu/gutil/strings/substitute.h"
#include "kudu/gutil/strings/util.h"
#include "kudu/gutil/walltime.h"
#include "kudu/util/env.h"
#include "kudu/util/path_util.h"
#include "kudu/util/random.h"
#include "kudu/util/spinlock_profiling.h"
DEFINE_string(test_leave_files, "on_failure",
"Whether to leave test files around after the test run. "
" Valid values are 'always', 'on_failure', or 'never'");
DEFINE_int32(test_random_seed, 0, "Random seed to use for randomized tests");
using std::string;
using strings::Substitute;
namespace kudu {
static const char* const kSlowTestsEnvVariable = "KUDU_ALLOW_SLOW_TESTS";
static const uint64 kTestBeganAtMicros = Env::Default()->NowMicros();
///////////////////////////////////////////////////
// KuduTest
///////////////////////////////////////////////////
KuduTest::KuduTest()
: env_(new EnvWrapper(Env::Default())),
test_dir_(GetTestDataDirectory()) {
}
// env passed in from subclass, for tests that run in-memory
KuduTest::KuduTest(Env *env)
: env_(env),
test_dir_(GetTestDataDirectory()) {
}
KuduTest::~KuduTest() {
// Clean up the test directory in the destructor instead of a TearDown
// method. This is better because it ensures that the child-class
// dtor runs first -- so, if the child class is using a minicluster, etc,
// we will shut that down before we remove files underneath.
if (FLAGS_test_leave_files == "always") {
LOG(INFO) << "-----------------------------------------------";
LOG(INFO) << "--test_leave_files specified, leaving files in " << test_dir_;
} else if (FLAGS_test_leave_files == "on_failure" && HasFatalFailure()) {
LOG(INFO) << "-----------------------------------------------";
LOG(INFO) << "Had fatal failures, leaving test files at " << test_dir_;
} else {
VLOG(1) << "Cleaning up temporary test files...";
WARN_NOT_OK(env_->DeleteRecursively(test_dir_),
"Couldn't remove test files");
}
}
void KuduTest::SetUp() {
InitSpinLockContentionProfiling();
}
string KuduTest::GetTestPath(const string& relative_path) {
CHECK(!test_dir_.empty()) << "Call SetUp() first";
return JoinPathSegments(test_dir_, relative_path);
}
///////////////////////////////////////////////////
// Test utility functions
///////////////////////////////////////////////////
bool AllowSlowTests() {
char *e = getenv(kSlowTestsEnvVariable);
if ((e == nullptr) ||
(strlen(e) == 0) ||
(strcasecmp(e, "false") == 0) ||
(strcasecmp(e, "0") == 0) ||
(strcasecmp(e, "no") == 0)) {
return false;
}
if ((strcasecmp(e, "true") == 0) ||
(strcasecmp(e, "1") == 0) ||
(strcasecmp(e, "yes") == 0)) {
return true;
}
LOG(FATAL) << "Unrecognized value for " << kSlowTestsEnvVariable << ": " << e;
return false;
}
void OverrideFlagForSlowTests(const std::string& flag_name,
const std::string& new_value) {
// Ensure that the flag is valid.
google::GetCommandLineFlagInfoOrDie(flag_name.c_str());
// If we're not running slow tests, don't override it.
if (!AllowSlowTests()) {
return;
}
google::SetCommandLineOptionWithMode(flag_name.c_str(), new_value.c_str(),
google::SET_FLAG_IF_DEFAULT);
}
int SeedRandom() {
int seed;
// Initialize random seed
if (FLAGS_test_random_seed == 0) {
// Not specified by user
seed = static_cast<int>(GetCurrentTimeMicros());
} else {
seed = FLAGS_test_random_seed;
}
LOG(INFO) << "Using random seed: " << seed;
srand(seed);
return seed;
}
string GetTestDataDirectory() {
const ::testing::TestInfo* const test_info =
::testing::UnitTest::GetInstance()->current_test_info();
CHECK(test_info) << "Must be running in a gtest unit test to call this function";
string dir;
CHECK_OK(Env::Default()->GetTestDirectory(&dir));
// The directory name includes some strings for specific reasons:
// - program name: identifies the directory to the test invoker
// - timestamp and pid: disambiguates with prior runs of the same test
//
// e.g. "env-test.TestEnv.TestReadFully.1409169025392361-23600"
dir += Substitute("/$0.$1.$2.$3-$4",
StringReplace(google::ProgramInvocationShortName(), "/", "_", true),
StringReplace(test_info->test_case_name(), "/", "_", true),
StringReplace(test_info->name(), "/", "_", true),
kTestBeganAtMicros,
getpid());
Status s = Env::Default()->CreateDir(dir);
CHECK(s.IsAlreadyPresent() || s.ok())
<< "Could not create directory " << dir << ": " << s.ToString();
if (s.ok()) {
string metadata;
StrAppend(&metadata, Substitute("PID=$0\n", getpid()));
StrAppend(&metadata, Substitute("PPID=$0\n", getppid()));
char* jenkins_build_id = getenv("BUILD_ID");
if (jenkins_build_id) {
StrAppend(&metadata, Substitute("BUILD_ID=$0\n", jenkins_build_id));
}
CHECK_OK(WriteStringToFile(Env::Default(), metadata,
Substitute("$0/test_metadata", dir)));
}
return dir;
}
} // namespace kudu