使用C++11动态解析json并使用DataFrame进行结果对

2021-10-26  本文已影响0人  FredricZhu

本例特点,

  1. 使用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
上一篇下一篇

猜你喜欢

热点阅读