本例特点,
-
使用Debug::DeathHandler类来进行异常trace打印。
注意Google GTest框架默认会捕获所有异常,导致C++的SIG_TERM信号无法被捕捉。
所以需要设置环境变量 ENV GTEST_CATCH_EXCEPTIONS 0
2.使用nlohmann::json做中间字典转换。类似一个python的万能dict类型。 nlohmann::json本身做了类型擦除,可以存储任意类型的变量。
3.使用hosseinmoein DataFrame进行数据比对,得出结果。
整体思路和Python类似。
有了Debug::DeathHandler以后,效率大幅提升,类似不需要打断点了,几乎一看就知道什么异常。
程序目录如下,
image.png
代码如下,
CMakeLists.txt
cmake_minimum_required(VERSION 2.6)
if(APPLE)
message(STATUS "This is Apple, do nothing.")
elseif(UNIX)
message(STATUS "This is linux, set CMAKE_PREFIX_PATH.")
set(CMAKE_PREFIX_PATH /vcpkg/ports/cppwork/vcpkg_installed/x64-linux/share)
endif(APPLE)
project(app_perf_test)
add_definitions(-std=c++17)
add_definitions(-g)
find_package(ZLIB)
find_package(glog REQUIRED)
find_package(OpenCV REQUIRED )
find_package(Boost REQUIRED COMPONENTS
system
filesystem
serialization
program_options
thread
)
find_package(DataFrame REQUIRED)
if(APPLE)
MESSAGE(STATUS "This is APPLE, set INCLUDE_DIRS")
set(INCLUDE_DIRS ${Boost_INCLUDE_DIRS} /usr/local/include /usr/local/iODBC/include /opt/snowflake/snowflakeodbc/include/ ${CMAKE_CURRENT_SOURCE_DIR}/../../)
elseif(UNIX)
MESSAGE(STATUS "This is linux, set INCLUDE_DIRS")
set(INCLUDE_DIRS ${Boost_INCLUDE_DIRS} /usr/local/include ${CMAKE_CURRENT_SOURCE_DIR}/../../)
endif(APPLE)
if(APPLE)
MESSAGE(STATUS "This is APPLE, set LINK_DIRS")
set(LINK_DIRS /usr/local/lib /usr/local/iODBC/lib /opt/snowflake/snowflakeodbc/lib/universal)
elseif(UNIX)
MESSAGE(STATUS "This is linux, set LINK_DIRS")
set(LINK_DIRS ${Boost_INCLUDE_DIRS} /usr/local/lib /vcpkg/ports/cppwork/vcpkg_installed/x64-linux/lib)
endif(APPLE)
if(APPLE)
MESSAGE(STATUS "This is APPLE, set ODBC_LIBS")
set(ODBC_LIBS iodbc iodbcinst)
elseif(UNIX)
MESSAGE(STATUS "This is linux, set LINK_DIRS")
set(ODBC_LIBS odbc odbcinst ltdl)
endif(APPLE)
include_directories(${INCLUDE_DIRS})
LINK_DIRECTORIES(${LINK_DIRS})
file( GLOB APP_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../impl/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../utils/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../cases/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/*.h ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../http/impl/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../yaml/impl/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../df/impl/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../death_handler/impl/*.cpp)
foreach( sourcefile ${APP_SOURCES} )
file(RELATIVE_PATH filename ${CMAKE_CURRENT_SOURCE_DIR} ${sourcefile})
string(FIND "${filename}" "test.cpp" "TEMP")
if( NOT "${TEMP}" STREQUAL "-1" )
string(REPLACE ".cpp" "" file ${filename})
add_executable(${file} ${APP_SOURCES})
target_link_libraries(${file} ${Boost_LIBRARIES} ZLIB::ZLIB glog::glog DataFrame::DataFrame ${OpenCV_LIBS})
target_link_libraries(${file} ssl crypto libgtest.a pystring libyaml-cpp.a libgmock.a ${ODBC_LIBS} libnanodbc.a pthread dl backtrace)
endif()
endforeach( sourcefile ${APP_SOURCES})
test_cfg.h
#ifndef _FREDRIC_TEST_CFG_H_
#define _FREDRIC_TEST_CFG_H_
#include <string>
#include <map>
extern std::string v2_query_path;
extern int max_compare_count;
extern std::string www_host;
extern std::string api_host;
extern std::string www_company_path;
extern std::map<std::string, std::string> www_headers;
extern std::map<std::string, std::string> api_headers;
extern bool use_file;
// App Performance related json files
extern std::string apps_meta_file;
extern std::string test_dim_parse_data_file;
#endif
utils/req.h
#ifndef _FREDRIC_REQ_H_
#define _FREDRIC_REQ_H_
#include <string>
struct Req {
static std::string make_a_www_query(const std::string& path, const std::string& body);
static std::string make_a_api_query(const std::string& path);
};
#endif
utils/req.cpp
#include "api_accuracy/utils/req.h"
#include "http/http_util.h"
#include "api_accuracy/test_cfg.h"
#include <glog/logging.h>
std::string Req::make_a_www_query(const std::string& path, const std::string& body) {
std::string www_res{};
bool get_www_res = HttpUtil::post_and_get_str(www_host, path, www_headers, body, www_res);
if(!get_www_res) {
LOG(ERROR) << "Get WWW result failure" << "\n";
return "";
}
return std::move(www_res);
}
std::string Req::make_a_api_query(const std::string& path) {
std::string api_res{};
bool get_api_res = HttpUtil::get_str(api_host, path, api_headers, api_res);
if(!get_api_res) {
LOG(ERROR) << "Get api result failure" << "\n";
return "";
}
return std::move(api_res);
}
utils/io_util.h
#ifndef _FREDRIC_IO_UTIL_H_
#define _FREDRIC_IO_UTIL_H_
#include <string>
struct IOUtil {
static std::string read_file(const std::string& file_name);
static bool write_file(const std::string& file_name, const std::string& content);
};
#endif
utils/io_util.cpp
#include "api_accuracy/utils/io_util.h"
#include <glog/logging.h>
#include <fstream>
std::string IOUtil::read_file(const std::string& file_name) {
std::fstream fs{file_name};
if(!fs.is_open()) {
LOG(ERROR) << "Open file [" << file_name << "] failed" << "\n";
return "";
}
std::stringstream ss {};
ss << fs.rdbuf();
return std::move(ss.str());
}
bool IOUtil::write_file(const std::string& file_name, const std::string& content) {
std::fstream fs{file_name, std::ios::out | std::ios::trunc};
if(!fs.is_open()) {
LOG(ERROR) << "Open file [" << file_name << "] failed" << "\n";
return false;
}
fs << content;
fs.close();
return true;
}
utils/funcs.h
#ifndef _FREDRIC_FUNCS_H_
#define _FREDRIC_FUNCS_H_
#include "json/json.hpp"
#include <vector>
#include <string>
#include <map>
using json = nlohmann::json;
std::vector<std::string> get_map_keys(const std::map<std::string, std::string>& field_mapping);
std::vector<std::string> get_map_values(const std::map<std::string, std::string>& field_mapping);
std::string read_query_file_and_replace_consts(const json& prod, const std::string& www_query_file_name);
json parse_dim_product_fields(const json& result_json, const std::vector<std::string>& dim_keys_);
json convert_dim_values_to_api_values(const json& values, const std::map<std::string, std::string>& api_dim_mapping);
bool find_diff_and_save_f(const json& values_, const json& act_www_values_, const std::string& api_data_field_name);
#endif
utils/funcs.cpp
#include "api_accuracy/utils/funcs.h"
#include "api_accuracy/utils/io_util.h"
#include "df/df.h"
#include <pystring/pystring.h>
#include <cstddef>
#include <algorithm>
std::vector<std::string> get_map_keys(const std::map<std::string, std::string>& field_mapping) {
std::vector<std::string> keys{};
std::for_each(field_mapping.begin(), field_mapping.end(), [&keys](const auto& item){
keys.push_back(item.first);
});
return keys;
}
std::vector<std::string> get_map_values(const std::map<std::string, std::string>& field_mapping) {
std::vector<std::string> values{};
std::for_each(field_mapping.begin(), field_mapping.end(), [&values](const auto& item){
values.push_back(item.second);
});
return values;
}
std::string read_query_file_and_replace_consts(const json& prod, const std::string& www_query_file_name) {
auto product_id = prod["product_id"].get<int64_t>();
auto market_code = prod["market_code"].get<std::string>();
auto start_date = prod["start_date"].get<std::string>();
auto end_date = prod["end_date"].get<std::string>();
auto country_code = prod["countries"].get<std::string>();
auto device_code = prod["devices"].get<std::string>();
auto granularity = prod["granularity"].get<std::string>();
auto file_content = IOUtil::read_file(www_query_file_name);
file_content = pystring::replace(file_content, "${product_id}", std::to_string(product_id));
file_content = pystring::replace(file_content, "${market_code}", market_code);
file_content = pystring::replace(file_content, "${start_date}", start_date);
file_content = pystring::replace(file_content, "${end_date}", end_date);
std::vector<std::string> country_list{};
pystring::split(country_code, country_list, ",");
json country_js = country_list;
file_content = pystring::replace(file_content, "${country_code}", country_js.dump());
file_content = pystring::replace(file_content, "${granularity}", granularity);
if(country_list[0] == "all_supported") {
auto content_js = json::parse(file_content);
content_js["filters"].erase("country_code");
file_content = content_js.dump();
}
std::vector<std::string> device_list{};
pystring::split(device_code, device_list, ",");
if(device_list[0] != "all_supported") {
auto content_js = json::parse(file_content);
content_js["filters"]["device_code"] = {
{"in", device_list}
};
file_content = content_js.dump();
}
return file_content;
}
json parse_dim_product_fields(const json& result_json, const std::vector<std::string>& dim_keys_) {
json values;
auto facets = result_json["data"]["facets"];
auto dims = result_json["data"]["dimensions"];
for(auto&& facet_: facets) {
json value;
for(auto&& dim_key_: dim_keys_) {
if(dim_key_.find("/") == std::string::npos) {
value[dim_key_] = facet_[dim_key_];
} else {
json tmp_val;
std::string tmp_key = "";
std::vector<std::string> one_dim_keys {};
pystring::split(dim_key_, one_dim_keys, "/");
for(int i=0; i<one_dim_keys.size(); ++i) {
if(i==0) {
tmp_key = one_dim_keys[0];
if(!facet_[tmp_key].is_null()) {
tmp_val = std::to_string(facet_[tmp_key].get<int64_t>());
}else {
break;
}
}else {
for(auto&& begin=dims.begin(), end = dims.end(); begin!=end; ++begin) {
auto in_dim_key = begin.key();
auto in_dim_val = begin.value();
if(one_dim_keys[i-1] == in_dim_key) {
for(auto&& ii_begin=in_dim_val.begin(), ii_end=in_dim_val.end(); ii_begin!=ii_end; ++ii_begin) {
auto ii_dim_key = ii_begin.key();
auto ii_dim_val = ii_begin.value();
if(ii_dim_key == tmp_val.get<std::string>()) {
if(i == (one_dim_keys.size() - 1)){
tmp_val = ii_dim_val[one_dim_keys[i]];
}else {
if(!ii_dim_val[one_dim_keys[i]].is_null()) {
tmp_val = std::to_string(ii_dim_val[one_dim_keys[i]].get<int64_t>());
} else {
break;
}
}
}
}
}
}
}
}
value[dim_key_] = tmp_val;
}
}
values.emplace_back(std::move(value));
}
return std::move(values);
}
json convert_dim_values_to_api_values(const json& values, const std::map<std::string, std::string>& api_dim_mapping) {
json new_values;
for(auto&& value: values) {
json new_value;
for(auto&& api_dim_item: api_dim_mapping) {
auto api_key = api_dim_item.first;
auto dim_key = api_dim_item.second;
new_value[api_key] = value[dim_key];
}
new_values.emplace_back(std::move(new_value));
}
return std::move(new_values);
}
bool find_diff_and_save_f(const json& values_, const json& act_www_values_, const std::string& api_data_field_name) {
std::vector<std::string> pri_keys_{};
auto df1 = df_op::convert_json_to_df(values_, pri_keys_);
auto df2 = df_op::convert_json_to_df(act_www_values_, pri_keys_);
std::string res_csv_path = "../result/" + api_data_field_name + "_res.csv";
if(values_.size() == 0 && act_www_values_.size() == 0) {
return false;
}else if(values_.size() == 0 && act_www_values_.size()!=0) {
df_op::write_to_csv(df2, res_csv_path);
return true;
}else if(values_.size() != 0 && act_www_values_.size() == 0) {
df_op::write_to_csv(df1, res_csv_path);
return true;
}else {
auto df3 = df1.concat<decltype(df2), json>(df2, concat_policy::all_columns);
std::vector<std::string> keys_{Keys};
auto df4 = df_op::remove_duplicate(df3, keys_);
auto df_rows = df4.shape().first;
if(df_rows == 0) {
return false;
}
df_op::write_to_csv(df3, res_csv_path);
return true;
}
}
utils/compare.h
#ifndef _FREDRIC_COMPARE_H_
#define _FREDRIC_COMPARE_H_
#include <json/json.hpp>
#include <map>
#include <string>
using json = nlohmann::json;
struct Compare {
Compare(const json& app_perf_result);
json get_api_products_fields(const std::map<std::string, std::string>& field_mapping);
json get_www_product_fields(const json& prod, const std::string& www_request_file_name, const std::map<std::string, std::string>& field_mapping);
bool compare_api_and_www_meta_equal(const std::map<std::string, std::string>& field_mappings, const std::string& www_query_file);
json app_perf_result_;
};
#endif
utils/compare.cpp
#include "api_accuracy/utils/compare.h"
#include "api_accuracy/utils/funcs.h"
#include "api_accuracy/utils/req.h"
#include "api_accuracy/test_cfg.h"
#include "glog/logging.h"
Compare::Compare(const json& app_perf_result) {
app_perf_result_ = app_perf_result;
}
json Compare::get_api_products_fields(const std::map<std::string, std::string>& field_mapping) {
auto start_date = app_perf_result_["start_date"];
auto end_date = app_perf_result_["end_date"];
auto countries = app_perf_result_["countries"];
auto granularity = app_perf_result_["granularity"];
auto devices = app_perf_result_["devices"];
auto products = app_perf_result_["products"];
auto api_fields = get_map_keys(field_mapping);
json api_values {};
for(auto&& product_: products) {
json api_value {};
auto product_id = product_["product_id"];
auto market_code = product_["market_code"];
json prod_{
{"product_id", product_id},
{"market_code", market_code},
{"granularity", granularity},
{"start_date", start_date},
{"end_date", end_date},
{"countries", countries},
{"devices", devices}
};
api_value["product"] = prod_;
json values {};
json value {};
for(auto api_field: api_fields) {
value[api_field] = product_[api_field];
}
values.emplace_back(std::move(value));
api_value["values"] = values;
api_values.push_back(std::move(api_value));
}
return api_values;
}
json Compare::get_www_product_fields(const json& prod, const std::string& www_request_file_name, const std::map<std::string, std::string>& field_mapping) {
auto dar_query_str = read_query_file_and_replace_consts(prod, www_request_file_name);
LOG(ERROR) << dar_query_str << "\n";
auto dar_query_res_str = Req::make_a_www_query(v2_query_path, dar_query_str);
auto dar_query_res = json::parse(dar_query_res_str);
auto field_values = get_map_values(field_mapping);
auto www_query_tmp_res = parse_dim_product_fields(dar_query_res, field_values);
auto www_query_res = convert_dim_values_to_api_values(www_query_tmp_res, field_mapping);
return www_query_res;
}
bool Compare::compare_api_and_www_meta_equal(const std::map<std::string, std::string>& field_mappings, const std::string& www_query_file) {
auto api_products_ = get_api_products_fields(field_mappings);
bool is_value_equal = true;
int i = 0;
for(auto&& api_prod_: api_products_) {
auto prod_ = api_prod_["product"];
auto values_ = api_prod_["values"];
++i;
// 对比前max_compare_count条,退出
if(i > max_compare_count) {
LOG(INFO) << "Hit " << max_compare_count << " records, return." << "\n";
break;
}
auto www_values_ = get_www_product_fields(prod_, www_query_file, field_mappings);
LOG(INFO) << values_.dump() << "\n";
LOG(INFO) << www_values_.dump() << "\n";
std::sort(values_.begin(), values_.end());
std::sort(www_values_.begin(), www_values_.end());
bool has_diff = find_diff_and_save_f(values_, www_values_, "www_meta_info");
is_value_equal = !has_diff;
if(!is_value_equal) {
LOG(ERROR) << "API and www values are not equals\n";
LOG(ERROR) << "API : www_meta_info\n";
LOG(ERROR) << values_ << "\n";
LOG(ERROR) << "WWW : www_meta_info\n";
LOG(ERROR) << www_values_ << "\n";
break;
}
}
return is_value_equal;
}
utils/case.h
#ifndef _FREDRIC_CASE_H_
#define _FREDRIC_CASE_H_
#include <string>
struct Case {
std::string name;
std::string url;
int expect_code;
};
#endif
utils/beans.h
#ifndef _FREDRIC_BEANS_H_
#define _FREDRIC_BEANS_H_
#include <map>
#include <string>
extern std::map<std::string, std::string> DimProductMapping;
#endif
utils/beans.cpp
#include "api_accuracy/utils/beans.h"
std::map<std::string, std::string> DimProductMapping {
{"product_id", "product_id"},
{"product_name", "name"},
{"product_description", "description"},
{"category_id", "category_id"},
{"unified_product_id", "unified_product_id"},
{"category_name", "category_id/name"},
{"category_slug", "category_id/slug"},
{"market_code", "market_code"},
{"publisher_name", "publisher_id/name"},
{"publisher_market", "publisher_id/market_code"},
{"company_name", "publisher_id/company_id/name"},
{"company_exchange", "publisher_id/company_id/exchange"},
{"company_stock_symbol", "publisher_id/company_id/ticker_symbol"},
{"company_website", "publisher_id/company_id/website"},
{"company_is_public", "publisher_id/company_id/is_public_company"}
};
test/app_perf_test.cpp
#include "api_accuracy/cases/app_perf_cases.h"
#include "api_accuracy/test_cfg.h"
#include "api_accuracy/utils/req.h"
#include "api_accuracy/utils/io_util.h"
#include "api_accuracy/utils/funcs.h"
#include "api_accuracy/utils/beans.h"
#include "api_accuracy/utils/compare.h"
#include "pystring/pystring.h"
#include "json/json.hpp"
#include "death_handler/death_handler.h"
#include <glog/logging.h>
#include <boost/filesystem.hpp>
#include <gtest/gtest.h>
using json = nlohmann::json;
int main(int argc, char** argv) {
FLAGS_log_dir = "./";
FLAGS_alsologtostderr = true;
Debug::DeathHandler dh;
google::InitGoogleLogging("./logs.log");
testing::InitGoogleTest(&argc, argv);
int ret = RUN_ALL_TESTS();
return ret;
}
class AppPerfTests: public testing::Test {
protected:
virtual void SetUp(){
for(auto&& case_: app_perf_pos_cases) {
auto case_name = case_.name;
auto case_url = case_.url;
if(!use_file) {
auto case_result_ = Req::make_a_api_query(case_url);
bool write_res = IOUtil::write_file(case_name, case_result_);
if(!write_res) {
// Write case result failed
ASSERT_TRUE(false);
}
}
auto content_ = IOUtil::read_file(case_name);
app_perf_results[case_name] = json::parse(content_);
}
}
virtual void TearDown(){
}
std::map<std::string, json> app_perf_results;
};
TEST_F(AppPerfTests, ReadFile) {
auto query_str = IOUtil::read_file(app_perf_pos_cases[0].name);
LOG(INFO) << query_str << "\n";
ASSERT_TRUE(query_str!="");
}
TEST_F(AppPerfTests, GetMapKeys) {
auto keys = get_map_keys(DimProductMapping);
LOG(INFO) << keys.size() << "\n";
ASSERT_EQ(15, keys.size());
}
TEST_F(AppPerfTests, GetMapValues) {
auto values = get_map_values(DimProductMapping);
LOG(INFO) << values.size() << "\n";
ASSERT_EQ(15, values.size());
}
TEST_F(AppPerfTests, GetApiProductFields) {
auto app_perf_result = app_perf_results.begin()->second;
std::cerr << app_perf_result["start_date"] << std::endl;
Compare c {app_perf_result};
auto values = c.get_api_products_fields(DimProductMapping);
ASSERT_TRUE(values.size() > 0);
}
TEST_F(AppPerfTests, JsonDumpStdVector) {
std::string country_code = "US,CN,JP,CA";
std::vector<std::string> results_{};
pystring::split(country_code, results_, ",");
json js = results_;
ASSERT_EQ(4, js.size());
}
TEST_F(AppPerfTests, JsonDelInnerKey) {
auto js_str = R"(
{
"person": {
"name": "Zhangsan",
"age": 34
},
"grade": 3
}
)";
json js = json::parse(js_str);
js["person"].erase("age");
LOG(INFO) << js.dump() << "\n";
}
TEST_F(AppPerfTests, ReadQueryFileAndReplaceConsts) {
json prod = json::parse(R"({
"product_id": 1111,
"market_code": "google-play",
"start_date": "2021-01-01",
"end_date": "2021-01-31",
"countries": "CN,US,JP",
"devices": "android-all,android-tv",
"granularity": "monthly"
})");
auto apps_meta_query_str = read_query_file_and_replace_consts(prod, apps_meta_file);
LOG(INFO) << apps_meta_query_str << "\n";
}
TEST_F(AppPerfTests, ReadQueryFileAndReplaceConstsAllCountries) {
json prod = json::parse(R"({
"product_id": 1111,
"market_code": "google-play",
"start_date": "2021-01-01",
"end_date": "2021-01-31",
"countries": "all_supported",
"devices": "android-all,android-tv",
"granularity": "monthly"
})");
auto apps_meta_query_str = read_query_file_and_replace_consts(prod, apps_meta_file);
LOG(INFO) << apps_meta_query_str << "\n";
}
TEST_F(AppPerfTests, ParseDimProductFields) {
auto dim_file_str = IOUtil::read_file(test_dim_parse_data_file);
auto dim_file = json::parse(dim_file_str);
auto dim_keys_ = get_map_values(DimProductMapping);
auto dim_values = parse_dim_product_fields(dim_file, dim_keys_);
auto api_values = convert_dim_values_to_api_values(dim_values, DimProductMapping);
LOG(INFO) << api_values << "\n";
}
TEST_F(AppPerfTests, TestProductMetadata) {
bool is_case_pass = true;
for(auto&& case_item: app_perf_results) {
auto app_perf_file = case_item.first;
auto app_perf_result = case_item.second;
auto comp_ = Compare(app_perf_result);
auto is_value_equal = comp_.compare_api_and_www_meta_equal(DimProductMapping, apps_meta_file);
if(!is_value_equal) {
LOG(ERROR) << "[" << app_perf_file << "] test [test_product_metadata] failed!\n";
is_case_pass = false;
}
}
ASSERT_TRUE(is_case_pass);
}
test/datas/app_perf/apps_meta_data.json
{
"facets": ["product_id"],
"filters": {
"product_id": {
"equal": ${product_id}
},
"granularity": {
"equal": "${granularity}"
},
"date": {
"between": [
"${start_date}",
"${end_date}"
]
},
"country_code": {
"in": ${country_code}
}
},
"fields": {
"product_id": {
"fields": [
"name",
"market_code",
"device_code",
"description",
"price",
"has_iap",
"unified_product_id",
"category_id",
"publisher_id"
],
"publisher_id": {
"fields": [
"name",
"market_code",
"website",
"company_id"
],
"company_id": {
"fields": [
"name",
"exchange",
"ticker_symbol",
"website",
"is_public_company",
"country_code"
]
}
},
"category_id": {"fields": ["name", "slug"]}
}
}
}
impl/test_cfg.cpp
#include "api_accuracy/test_cfg.h"
std::string api_host = "tai4-api.appannie.com";
std::string www_host = "api2.appannie.com";
std::string v2_query_path = "/v2/query";
int max_compare_count = 10;
std::map<std::string, std::string> www_headers{
{"Authorization", "Bearer 501b0ae76f7732aa7335564e7795ad9bb8ffe493"},
{"Content-Type", "application/json"}
};
std::map<std::string, std::string> api_headers{
{"Authorization", "Bearer cb5685cc45423934256c893cff23408b1a50208c"},
{"Content-Type", "application/json"}
};
bool use_file = true;
std::string apps_meta_file = "../datas/app_perf/apps_meta_data.json";
std::string test_dim_parse_data_file = "../datas/app_perf/test_dim_parse_data.json";
cases/app_perf_cases.h
#ifndef _FREDRIC_APP_PERF_CASES_H_
#define _FREDRIC_APP_PERF_CASES_H_
#include "api_accuracy/utils/case.h"
#include <vector>
extern std::vector<Case> app_perf_pos_cases;
#endif
cases/app_perf_cases.cpp
#include "api_accuracy/cases/app_perf_cases.h"
std::vector<Case> app_perf_pos_cases {
{"../datas/app_perf/test545__200_ok_monthly_api_response", "/v1.3/portfolio/app-performance/less?company_id=1000200000000034&granularity=monthly&start_date=2020-01-01&end_date=2020-03-31&countries=US,CN,JP,GB", 0}
};
程序输出如下,
image.png
网友评论