blob: 6e1e5537641f4329c1232dfc3a1bede641220ad5 [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 "testutil/test_util.h"
#ifndef __APPLE__
#else
#include <mach-o/dyld.h>
#endif
#include <common/config.h>
#include <ctype.h>
#include <glog/logging.h>
#include <libgen.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <algorithm>
#include <string>
// IWYU pragma: no_include <bits/chrono.h>
#include <chrono> // IWYU pragma: keep
#include <filesystem>
#include <fstream>
#include <iostream>
#include <random>
#include "absl/strings/substitute.h"
#include "gflags/gflags.h"
#include "olap/olap_common.h"
DEFINE_bool(gen_out, false, "generate expected check data for test");
DEFINE_bool(
gen_regression_case, false,
"generate regression test cases corrresponding to ut cases for ut cases that support it");
const std::string kApacheLicenseHeader =
R"(// 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.
)";
namespace doris {
static const char* const kSlowTestsEnvVar = "DORIS_ALLOW_SLOW_TESTS";
bool AllowSlowTests() {
return GetBooleanEnvironmentVariable(kSlowTestsEnvVar);
}
bool GetBooleanEnvironmentVariable(const char* env_var_name) {
const char* const e = getenv(env_var_name);
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) << absl::Substitute("$0: invalid value for environment variable $1", e,
env_var_name);
return false; // unreachable
}
std::string GetCurrentRunningDir() {
char exe[PATH_MAX];
ssize_t r;
#ifdef __APPLE__
uint32_t size = PATH_MAX;
if (_NSGetExecutablePath(exe, &size) < 0) {
return std::string();
}
r = strlen(exe) + 1;
#else
if ((r = readlink("/proc/self/exe", exe, PATH_MAX)) < 0) {
return std::string();
}
#endif
if (r == PATH_MAX) {
r -= 1;
}
exe[r] = 0;
char* dir = dirname(exe);
return std::string(dir);
}
void InitConfig() {
std::string conf_file = std::string(getenv("DORIS_HOME")) + "/conf/be.conf";
if (!config::init(conf_file.c_str(), false)) {
fprintf(stderr, "Init config file failed, path %s\n", conf_file.c_str());
exit(-1);
}
}
bool equal_ignore_case(std::string lhs, std::string rhs) {
std::transform(lhs.begin(), lhs.end(), lhs.begin(), ::tolower);
std::transform(rhs.begin(), rhs.end(), rhs.begin(), ::tolower);
return lhs == rhs;
}
std::mt19937_64 rng_64(std::chrono::steady_clock::now().time_since_epoch().count());
int rand_rng_int(int l, int r) {
std::uniform_int_distribution<int> u(l, r);
return u(rng_64);
}
char rand_rng_char() {
return (rand_rng_int(0, 1) ? 'a' : 'A') + rand_rng_int(0, 25);
}
std::string rand_rng_string(size_t length) {
std::string s;
while (length--) {
s += rand_rng_char();
}
return s;
}
std::string rand_rng_by_type(FieldType fieldType) {
if (fieldType == FieldType::OLAP_FIELD_TYPE_CHAR) {
return rand_rng_string(rand_rng_int(1, 8));
} else if (fieldType == FieldType::OLAP_FIELD_TYPE_VARCHAR) {
return rand_rng_string(rand_rng_int(1, 128));
} else if (fieldType == FieldType::OLAP_FIELD_TYPE_STRING) {
return rand_rng_string(rand_rng_int(1, 100000));
} else {
return std::to_string(rand_rng_int(1, 1000000));
}
}
void load_columns_data_from_file(vectorized::MutableColumns& columns,
vectorized::DataTypeSerDeSPtrs serders, char col_spliter,
std::set<int> idxes, const std::string& column_data_file,
const cctz::time_zone* tz) {
ASSERT_EQ(serders.size(), columns.size());
// Load column data and expected data from CSV files
std::vector<std::vector<std::string>> res;
struct stat buff;
if (stat(column_data_file.c_str(), &buff) == 0) {
if (S_ISREG(buff.st_mode)) {
// file
load_data_from_csv(serders, columns, column_data_file, col_spliter, idxes, tz);
} else if (S_ISDIR(buff.st_mode)) {
// dir
std::filesystem::path fs_path(column_data_file);
for (const auto& entry : std::filesystem::directory_iterator(fs_path)) {
std::string file_path = entry.path().string();
std::cout << "load data from file: " << file_path << std::endl;
load_data_from_csv(serders, columns, file_path, col_spliter, idxes, tz);
}
}
}
}
// Helper function to load data from CSV, with index which splited by spliter and load to columns
void load_data_from_csv(const vectorized::DataTypeSerDeSPtrs serders,
vectorized::MutableColumns& columns, const std::string& file_path,
const char spliter, const std::set<int> idxes, const cctz::time_zone* tz) {
ASSERT_EQ(serders.size(), columns.size())
<< "serder size: " << serders.size() << " column size: " << columns.size();
ASSERT_EQ(serders.size(), idxes.size())
<< "serder size: " << serders.size() << " idxes size: " << idxes.size();
ASSERT_EQ(serders.size(), *idxes.end())
<< "serder size: " << serders.size() << " idxes size: " << *idxes.end();
std::ifstream file(file_path);
if (!file) {
throw doris::Exception(ErrorCode::INVALID_ARGUMENT, "can not open the file: {} ",
file_path);
}
std::string line;
vectorized::DataTypeSerDe::FormatOptions options;
options.timezone = tz;
while (std::getline(file, line)) {
std::stringstream lineStream(line);
std::string value;
int l_idx = 0;
int c_idx = 0;
while (std::getline(lineStream, value, spliter)) {
if (!value.starts_with("//") && idxes.contains(l_idx)) {
Slice string_slice(value.data(), value.size());
if (auto st = serders[c_idx]->deserialize_one_cell_from_json(*columns[c_idx],
string_slice, options);
!st.ok()) {
LOG(INFO) << "error in deserialize but continue: " << st.to_string();
}
++c_idx;
}
++l_idx;
}
}
std::cout << "loading data done, file: " << file_path << ", row count: " << columns[0]->size()
<< std::endl;
}
// res_columns[i] is the i-th column of the data
void check_or_generate_res_file(const std::string& res_file_path,
const std::vector<std::vector<std::string>>& res_columns) {
if (res_columns.empty()) {
return;
}
auto row_count = res_columns[0].size();
if (FLAGS_gen_out) {
std::ofstream res_file(res_file_path);
LOG(INFO) << "gen check data with file: " << res_file_path;
if (!res_file.is_open()) {
throw std::ios_base::failure("Failed to open file: " + res_file_path);
}
for (size_t i = 1; i < res_columns.size(); ++i) {
EXPECT_EQ(res_columns[i].size(), row_count);
}
auto column_count = res_columns.size();
for (size_t i = 0; i < row_count; ++i) {
// output one row
for (size_t j = 0; j < column_count; ++j) {
const auto& column = res_columns[j];
// output one column
res_file << column[i];
if (j < column_count - 1) {
res_file << ";"; // Add semicolon between columns
}
}
if (i < row_count - 1) {
res_file << "\n";
}
}
res_file.close();
} else {
// we read generate file to check result
LOG(INFO) << "check data with file: " << res_file_path;
std::ifstream file(res_file_path);
if (!file) {
throw doris::Exception(ErrorCode::INVALID_ARGUMENT, "can not open the file: {} ",
res_file_path);
}
size_t file_line_count = 0;
std::string line;
while (getline(file, line)) {
file_line_count++;
}
EXPECT_EQ(file_line_count, row_count);
file.clear();
file.seekg(0, std::ios::beg);
size_t line_idx = 0;
while (std::getline(file, line)) {
std::stringstream line_stream(line);
std::string value;
size_t col_idx = 0;
while (std::getline(line_stream, value, ';')) {
EXPECT_EQ(value, res_columns[col_idx][line_idx]);
col_idx++;
}
line_idx++;
}
}
}
} // namespace doris