From ffa0c293ef0e22d3b19ab48fc497223f8fed8e97 Mon Sep 17 00:00:00 2001 From: tony <292224750@qq.com> Date: Mon, 19 Jun 2023 11:20:20 +0800 Subject: [PATCH 1/4] 1. add the c++ function of loading prototype from python, see test/systemTest/PQTest.cpp --- CMakeLists.txt | 2 +- benchmark/torchscripts/PQ/PQ.cpp | 72 ++++++++ benchmark/torchscripts/PQ/PQ.py | 232 ++++++++++++++++++++++++ benchmark/torchscripts/PQ/prototypes.pt | Bin 0 -> 8919 bytes commit.sh | 2 +- commit_info | 2 +- test/CMakeLists.txt | 1 + test/SystemTest/PQTest.cpp | 22 +++ test/torchscripts/PQ/PQ.cpp | 72 ++++++++ test/torchscripts/PQ/PQ.py | 232 ++++++++++++++++++++++++ test/torchscripts/PQ/prototypes.pt | Bin 0 -> 8919 bytes 11 files changed, 634 insertions(+), 3 deletions(-) create mode 100644 benchmark/torchscripts/PQ/PQ.cpp create mode 100644 benchmark/torchscripts/PQ/PQ.py create mode 100644 benchmark/torchscripts/PQ/prototypes.pt create mode 100644 test/SystemTest/PQTest.cpp create mode 100644 test/torchscripts/PQ/PQ.cpp create mode 100644 test/torchscripts/PQ/PQ.py create mode 100644 test/torchscripts/PQ/prototypes.pt diff --git a/CMakeLists.txt b/CMakeLists.txt index 92c8f911..002458d8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -67,7 +67,7 @@ add_subdirectory(src) # Add Library get_sources(IntelliStream_SOURCE_FILES) get_headers(IntelliStream_HEADER_FILES) -add_library(IntelliStream SHARED ${IntelliStream_SOURCE_FILES} ${IntelliStream_HEADER_FILES} ${CMAKE_CURRENT_BINARY_DIR}) +add_library(IntelliStream SHARED ${IntelliStream_SOURCE_FILES} ${IntelliStream_HEADER_FILES} ${CMAKE_CURRENT_BINARY_DIR} ) set_property(TARGET IntelliStream PROPERTY CXX_STANDARD 20) target_include_directories(IntelliStream PUBLIC "include") diff --git a/benchmark/torchscripts/PQ/PQ.cpp b/benchmark/torchscripts/PQ/PQ.cpp new file mode 100644 index 00000000..68026f33 --- /dev/null +++ b/benchmark/torchscripts/PQ/PQ.cpp @@ -0,0 +1,72 @@ +#include +#include + +int main() { + const int N = 3000; + const int D = 1000; + const int M = 2000; + const int C = 10; + const int K = 16; + const int D_c = D / C; + + torch::Tensor A = torch::rand({N, D}); + torch::Tensor B = torch::rand({D, M}); + + std::vector prototypes; + std::ifstream in_file("/home/haolan/PQ/prototypes.pt", std::ios::binary); + if (!in_file.is_open()) { + std::cerr << "Error opening file prototypes.pt\n"; + return 1; + } + torch::load(prototypes, in_file); + + std::vector A_encoded; + + for (int i = 0; i < A.size(0); ++i) { + auto a = A[i]; + std::vector a_encoded; + + for (int c = 0; c < C; ++c) { + torch::Tensor prototypes_c = prototypes[c]; + torch::Tensor a_subvector = a.slice(0, c * D_c, (c + 1) * D_c); + + torch::Tensor distances = torch::norm(prototypes_c - a_subvector.expand_as(prototypes_c), 1); + torch::Tensor closest_prototype_index = torch::argmin(distances); + a_encoded.push_back(closest_prototype_index); + } + A_encoded.push_back(torch::stack(a_encoded)); + } + torch::Tensor A_encoded_tensor = torch::stack(A_encoded); + + std::vector tables; + + for (int c = 0; c < C; ++c) { + torch::Tensor prototypes_c = prototypes[c]; + torch::Tensor B_subspace = B.slice(0, c * D_c, (c + 1) * D_c); + + std::vector table_c; + for (int i = 0; i < prototypes_c.size(0); ++i) { + auto prototype = prototypes_c[i]; + torch::Tensor dot_products = prototype.matmul(B_subspace); + table_c.push_back(dot_products); + } + tables.push_back(torch::stack(table_c)); + } + + std::vector result; + + for (int i = 0; i < A_encoded_tensor.size(0); ++i) { + auto a_encoded = A_encoded_tensor[i]; + torch::Tensor row_sum = torch::zeros({B.size(1)}); + for (int c = 0; c < C; ++c) { + int prototype_index = a_encoded[c].item(); + torch::Tensor table_c = tables[c]; + torch::Tensor dot_products = table_c[prototype_index]; + row_sum += dot_products; + } + result.push_back(row_sum); + } + torch::Tensor result_tensor = torch::stack(result); + + return 0; +} diff --git a/benchmark/torchscripts/PQ/PQ.py b/benchmark/torchscripts/PQ/PQ.py new file mode 100644 index 00000000..1d88d548 --- /dev/null +++ b/benchmark/torchscripts/PQ/PQ.py @@ -0,0 +1,232 @@ +import torch + +import time + +def kmeans(X, K, max_iters=100): + N, D = X.shape + + # Randomly initialize centroids + centroids = X[torch.randperm(N)[:K]] + + for _ in range(max_iters): + # Calculate distances between data points and centroids + distances = torch.cdist(X, centroids) + + # Assign data points to the nearest centroid + labels = torch.argmin(distances, dim=1) + + # Update centroids + for k in range(K): + if torch.sum(labels == k) > 0: + centroids[k] = torch.mean(X[labels == k], dim=0) + + return centroids, labels +class MyModule(torch.nn.Module): + def __init__(self,prototypes): + super(MyModule, self).__init__() + #self.fc = torch.nn.Linear(10, 5) + self.prototypes=torch.nn.Parameter(prototypes) + self.QA=torch.nn.Parameter(torch.rand(5,5)) + #self.register_parameter("prototypes", self.prototypes) + def forward(self,A: torch.Tensor): + return torch.matmul(A,A.T)+torch.sum(self.prototypes[0])+torch.sum(self.QA) + +def save_model(model, path, X): + tx = X.to('cpu') + # tx=X + model2 = model.to('cpu') + # model2=model + #model2.eval() + X = torch.tensor([1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0]) + + traced_model = torch.jit.script(model2) + ru = traced_model(tx) + + traced_model.save(path) +def main(): + learning = True + + N = 200 + D = 100 + M = 200 + + A = torch.rand(N, D) + B = torch.rand(D, M) + + trainingSet = None + if learning: + num_matrices = 10 + matrices = [torch.rand(N, D) for _ in range(num_matrices)] + trainingSet = torch.cat(matrices, dim=1) + + # Number of subspaces + C = 10 + + # Number of prototypes per subspace + K = 16 + + # Calculate dimension of each subspace + D_c = D // C + + # Initialize a list to hold prototypes for each subspace + prototypes = [] + + if trainingSet != None: + print("Starting prototypes learning on training set size of " + + str(trainingSet.shape[0]) + ", " + str(trainingSet.shape[1])) + t = time.time() + + for c in range(C): + # Get the indices for this subspace + subspace_indices = range(c * D_c, (c + 1) * D_c) + + # Slice the matrix A to get the subspace + A_subspace = trainingSet[:, subspace_indices] + + # Convert to numpy for KMeans + A_subspace_np = A_subspace.detach().numpy() + + # Run KMeans on the subspace + centroids_torch,LB = kmeans(A_subspace,K, 10) + + # Get the centroids (prototypes) + #centroids = kmeans.cluster_centers_ + + # Convert back to PyTorch tensor + #centroids_torch = torch.from_numpy(centroids) + + # Append to the list of prototypes + prototypes.append(centroids_torch) + + # Now prototypes[c] gives the K prototypes for subspace c + + print("\nPrototype Learning: " + str(time.time() - t) + "s") + model = MyModule(torch.stack(prototypes)) + save_model(model.to('cpu'), "prototypes.pt",torch.zeros(5,5)) + #model.save_model("prototypes.pt") + #torch.save(prototypes, 'prototypes.pt') + + else: + print("Loading prototypes from serialized pickle file\n") + prototypes = torch.load('prototypes.pt') + + # Initialize an empty list to store the encoded rows of A + A_encoded = [] + + total = time.time() + + for a in A: + # Initialize an empty list to store the encodings for this row + a_encoded = [] + + for c in range(C): + # Get the prototypes for this subspace + prototypes_c = prototypes[c] + + # Get the subvector for this subspace + a_subvector = a[c * D_c : (c + 1) * D_c] + + # Calculate the distances from the subvector to each prototype + distances = torch.norm(prototypes_c - a_subvector, dim=1) + + # Find the index of the closest prototype + closest_prototype_index = torch.argmin(distances) + + # Append this index to the encoded row + a_encoded.append(closest_prototype_index) + + # Convert the list of encoded subvectors to a PyTorch tensor + a_encoded_tensor = torch.tensor(a_encoded) + + # Append this encoded row to the list of all encoded rows + A_encoded.append(a_encoded_tensor) + + # Convert the list of all encoded rows to a PyTorch tensor + A_encoded_tensor = torch.stack(A_encoded) + + # Now A_encoded_tensor is the encoded version of A + + print("Encoding Function: " + str(time.time() - total) + "s") + + # Assume that B is a PyTorch tensor of size D x M + + + # Initialize a list to store the tables for each subspace + tables = [] + + t = time.time() + + for c in range(C): + # Get the prototypes for this subspace + prototypes_c = prototypes[c] + + # Slice B to get the corresponding subspace + B_subspace = B[c * D_c : (c + 1) * D_c, :] + + # Initialize an empty list to store the table for this subspace + table_c = [] + + for prototype in prototypes_c: + # Calculate the dot product of the prototype with each column in B_subspace + dot_products = torch.matmul(prototype, B_subspace) + + # Append the dot products to the table for this subspace + table_c.append(dot_products) + + # Convert the table for this subspace to a PyTorch tensor + table_c_tensor = torch.stack(table_c) + + # Append the table for this subspace to the list of all tables + tables.append(table_c_tensor) + + # Now tables[c] gives the lookup table for subspace c + + print("Table Construction: " + str(time.time() - t) + "s") + + # Initialize a list to store the result of the approximated matrix product + result = [] + + t = time.time() + + # Iterate over the encoded rows of A + for a_encoded in A_encoded_tensor: + # Initialize a tensor to store the sum of the dot products for this row + row_sum = torch.zeros(B.size(1)) + + # Iterate over each subspace + for c in range(C): + # Get the index of the closest prototype for this subspace + prototype_index = a_encoded[c].item() + + # Get the lookup table for this subspace + table_c = tables[c] + + # Get the dot products for the closest prototype + dot_products = table_c[prototype_index] + + # Add these dot products to the sum for this row + row_sum += dot_products + + # Append the sum for this row to the list of all rows + result.append(row_sum) + + # Convert the list of all rows to a PyTorch tensor + result_tensor = torch.stack(result) + + + print("Aggregation: " + str(time.time() - t) + "s") + print("\nTotal: " + str(time.time() - total) + "s") + + # Now result_tensor is the approximated matrix product + print("\nApproximate Result") + print(result_tensor) + + print("\nExact Result") + t = time.time() + eResult = torch.matmul(A, B) + print("Exact time: " + str(time.time() - t) + "s") + print(eResult) + print("\nerror: " + str(torch.norm(result_tensor - eResult, p='fro').item())) + print(len(prototypes),torch.stack(prototypes).size()) +if __name__ == '__main__': + main() diff --git a/benchmark/torchscripts/PQ/prototypes.pt b/benchmark/torchscripts/PQ/prototypes.pt new file mode 100644 index 0000000000000000000000000000000000000000..9661f33f73ff97e41137751aaa9f55ca3806a069 GIT binary patch literal 8919 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 + +#define CATCH_CONFIG_MAIN +#include "catch.hpp" +#include +using namespace std; +using namespace INTELLI; +using namespace torch; + +TEST_CASE("Test Load PQ", "[short]") +{ + torch::serialize::InputArchive archive; + archive.load_from("torchscripts/PQ/prototypes.pt"); + torch::Tensor prototypes; + archive.read("prototypes", prototypes); + auto pt_size = prototypes.sizes(); + std::cout<<"prototype size:"+ to_string(pt_size[0])+"x"+to_string(pt_size[1])+"x"+to_string(pt_size[2])< +#include + +int main() { + const int N = 3000; + const int D = 1000; + const int M = 2000; + const int C = 10; + const int K = 16; + const int D_c = D / C; + + torch::Tensor A = torch::rand({N, D}); + torch::Tensor B = torch::rand({D, M}); + + std::vector prototypes; + std::ifstream in_file("/home/haolan/PQ/prototypes.pt", std::ios::binary); + if (!in_file.is_open()) { + std::cerr << "Error opening file prototypes.pt\n"; + return 1; + } + torch::load(prototypes, in_file); + + std::vector A_encoded; + + for (int i = 0; i < A.size(0); ++i) { + auto a = A[i]; + std::vector a_encoded; + + for (int c = 0; c < C; ++c) { + torch::Tensor prototypes_c = prototypes[c]; + torch::Tensor a_subvector = a.slice(0, c * D_c, (c + 1) * D_c); + + torch::Tensor distances = torch::norm(prototypes_c - a_subvector.expand_as(prototypes_c), 1); + torch::Tensor closest_prototype_index = torch::argmin(distances); + a_encoded.push_back(closest_prototype_index); + } + A_encoded.push_back(torch::stack(a_encoded)); + } + torch::Tensor A_encoded_tensor = torch::stack(A_encoded); + + std::vector tables; + + for (int c = 0; c < C; ++c) { + torch::Tensor prototypes_c = prototypes[c]; + torch::Tensor B_subspace = B.slice(0, c * D_c, (c + 1) * D_c); + + std::vector table_c; + for (int i = 0; i < prototypes_c.size(0); ++i) { + auto prototype = prototypes_c[i]; + torch::Tensor dot_products = prototype.matmul(B_subspace); + table_c.push_back(dot_products); + } + tables.push_back(torch::stack(table_c)); + } + + std::vector result; + + for (int i = 0; i < A_encoded_tensor.size(0); ++i) { + auto a_encoded = A_encoded_tensor[i]; + torch::Tensor row_sum = torch::zeros({B.size(1)}); + for (int c = 0; c < C; ++c) { + int prototype_index = a_encoded[c].item(); + torch::Tensor table_c = tables[c]; + torch::Tensor dot_products = table_c[prototype_index]; + row_sum += dot_products; + } + result.push_back(row_sum); + } + torch::Tensor result_tensor = torch::stack(result); + + return 0; +} diff --git a/test/torchscripts/PQ/PQ.py b/test/torchscripts/PQ/PQ.py new file mode 100644 index 00000000..1d88d548 --- /dev/null +++ b/test/torchscripts/PQ/PQ.py @@ -0,0 +1,232 @@ +import torch + +import time + +def kmeans(X, K, max_iters=100): + N, D = X.shape + + # Randomly initialize centroids + centroids = X[torch.randperm(N)[:K]] + + for _ in range(max_iters): + # Calculate distances between data points and centroids + distances = torch.cdist(X, centroids) + + # Assign data points to the nearest centroid + labels = torch.argmin(distances, dim=1) + + # Update centroids + for k in range(K): + if torch.sum(labels == k) > 0: + centroids[k] = torch.mean(X[labels == k], dim=0) + + return centroids, labels +class MyModule(torch.nn.Module): + def __init__(self,prototypes): + super(MyModule, self).__init__() + #self.fc = torch.nn.Linear(10, 5) + self.prototypes=torch.nn.Parameter(prototypes) + self.QA=torch.nn.Parameter(torch.rand(5,5)) + #self.register_parameter("prototypes", self.prototypes) + def forward(self,A: torch.Tensor): + return torch.matmul(A,A.T)+torch.sum(self.prototypes[0])+torch.sum(self.QA) + +def save_model(model, path, X): + tx = X.to('cpu') + # tx=X + model2 = model.to('cpu') + # model2=model + #model2.eval() + X = torch.tensor([1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0]) + + traced_model = torch.jit.script(model2) + ru = traced_model(tx) + + traced_model.save(path) +def main(): + learning = True + + N = 200 + D = 100 + M = 200 + + A = torch.rand(N, D) + B = torch.rand(D, M) + + trainingSet = None + if learning: + num_matrices = 10 + matrices = [torch.rand(N, D) for _ in range(num_matrices)] + trainingSet = torch.cat(matrices, dim=1) + + # Number of subspaces + C = 10 + + # Number of prototypes per subspace + K = 16 + + # Calculate dimension of each subspace + D_c = D // C + + # Initialize a list to hold prototypes for each subspace + prototypes = [] + + if trainingSet != None: + print("Starting prototypes learning on training set size of " + + str(trainingSet.shape[0]) + ", " + str(trainingSet.shape[1])) + t = time.time() + + for c in range(C): + # Get the indices for this subspace + subspace_indices = range(c * D_c, (c + 1) * D_c) + + # Slice the matrix A to get the subspace + A_subspace = trainingSet[:, subspace_indices] + + # Convert to numpy for KMeans + A_subspace_np = A_subspace.detach().numpy() + + # Run KMeans on the subspace + centroids_torch,LB = kmeans(A_subspace,K, 10) + + # Get the centroids (prototypes) + #centroids = kmeans.cluster_centers_ + + # Convert back to PyTorch tensor + #centroids_torch = torch.from_numpy(centroids) + + # Append to the list of prototypes + prototypes.append(centroids_torch) + + # Now prototypes[c] gives the K prototypes for subspace c + + print("\nPrototype Learning: " + str(time.time() - t) + "s") + model = MyModule(torch.stack(prototypes)) + save_model(model.to('cpu'), "prototypes.pt",torch.zeros(5,5)) + #model.save_model("prototypes.pt") + #torch.save(prototypes, 'prototypes.pt') + + else: + print("Loading prototypes from serialized pickle file\n") + prototypes = torch.load('prototypes.pt') + + # Initialize an empty list to store the encoded rows of A + A_encoded = [] + + total = time.time() + + for a in A: + # Initialize an empty list to store the encodings for this row + a_encoded = [] + + for c in range(C): + # Get the prototypes for this subspace + prototypes_c = prototypes[c] + + # Get the subvector for this subspace + a_subvector = a[c * D_c : (c + 1) * D_c] + + # Calculate the distances from the subvector to each prototype + distances = torch.norm(prototypes_c - a_subvector, dim=1) + + # Find the index of the closest prototype + closest_prototype_index = torch.argmin(distances) + + # Append this index to the encoded row + a_encoded.append(closest_prototype_index) + + # Convert the list of encoded subvectors to a PyTorch tensor + a_encoded_tensor = torch.tensor(a_encoded) + + # Append this encoded row to the list of all encoded rows + A_encoded.append(a_encoded_tensor) + + # Convert the list of all encoded rows to a PyTorch tensor + A_encoded_tensor = torch.stack(A_encoded) + + # Now A_encoded_tensor is the encoded version of A + + print("Encoding Function: " + str(time.time() - total) + "s") + + # Assume that B is a PyTorch tensor of size D x M + + + # Initialize a list to store the tables for each subspace + tables = [] + + t = time.time() + + for c in range(C): + # Get the prototypes for this subspace + prototypes_c = prototypes[c] + + # Slice B to get the corresponding subspace + B_subspace = B[c * D_c : (c + 1) * D_c, :] + + # Initialize an empty list to store the table for this subspace + table_c = [] + + for prototype in prototypes_c: + # Calculate the dot product of the prototype with each column in B_subspace + dot_products = torch.matmul(prototype, B_subspace) + + # Append the dot products to the table for this subspace + table_c.append(dot_products) + + # Convert the table for this subspace to a PyTorch tensor + table_c_tensor = torch.stack(table_c) + + # Append the table for this subspace to the list of all tables + tables.append(table_c_tensor) + + # Now tables[c] gives the lookup table for subspace c + + print("Table Construction: " + str(time.time() - t) + "s") + + # Initialize a list to store the result of the approximated matrix product + result = [] + + t = time.time() + + # Iterate over the encoded rows of A + for a_encoded in A_encoded_tensor: + # Initialize a tensor to store the sum of the dot products for this row + row_sum = torch.zeros(B.size(1)) + + # Iterate over each subspace + for c in range(C): + # Get the index of the closest prototype for this subspace + prototype_index = a_encoded[c].item() + + # Get the lookup table for this subspace + table_c = tables[c] + + # Get the dot products for the closest prototype + dot_products = table_c[prototype_index] + + # Add these dot products to the sum for this row + row_sum += dot_products + + # Append the sum for this row to the list of all rows + result.append(row_sum) + + # Convert the list of all rows to a PyTorch tensor + result_tensor = torch.stack(result) + + + print("Aggregation: " + str(time.time() - t) + "s") + print("\nTotal: " + str(time.time() - total) + "s") + + # Now result_tensor is the approximated matrix product + print("\nApproximate Result") + print(result_tensor) + + print("\nExact Result") + t = time.time() + eResult = torch.matmul(A, B) + print("Exact time: " + str(time.time() - t) + "s") + print(eResult) + print("\nerror: " + str(torch.norm(result_tensor - eResult, p='fro').item())) + print(len(prototypes),torch.stack(prototypes).size()) +if __name__ == '__main__': + main() diff --git a/test/torchscripts/PQ/prototypes.pt b/test/torchscripts/PQ/prototypes.pt new file mode 100644 index 0000000000000000000000000000000000000000..9661f33f73ff97e41137751aaa9f55ca3806a069 GIT binary patch literal 8919 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 Date: Fri, 23 Jun 2023 21:50:55 +0800 Subject: [PATCH 2/4] 1. add the experimental feature of single thread streaming --- CMakeLists.txt | 2 +- benchmark/src/Benchmark.cpp | 29 ++ commit.sh | 2 +- commit_info | 2 +- include/AMMBench.h | 15 +- include/Streaming/SingleThreadStreamer.h | 73 ++++ include/Streaming/TimeStamper.h | 128 +++++++ include/Utils/MicroDataSet.hpp | 410 ++++++++++++----------- src/CMakeLists.txt | 1 + src/Streaming/CMakeLists.txt | 4 + src/Streaming/SingleThreadStreamer.cpp | 101 ++++++ src/Streaming/TimeStamper.cpp | 54 +++ test/CMakeLists.txt | 1 + test/SystemTest/StreamingTest.cpp | 49 +++ 14 files changed, 672 insertions(+), 199 deletions(-) create mode 100644 include/Streaming/SingleThreadStreamer.h create mode 100644 include/Streaming/TimeStamper.h create mode 100644 src/Streaming/CMakeLists.txt create mode 100644 src/Streaming/SingleThreadStreamer.cpp create mode 100644 src/Streaming/TimeStamper.cpp create mode 100644 test/SystemTest/StreamingTest.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 002458d8..92c8f911 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -67,7 +67,7 @@ add_subdirectory(src) # Add Library get_sources(IntelliStream_SOURCE_FILES) get_headers(IntelliStream_HEADER_FILES) -add_library(IntelliStream SHARED ${IntelliStream_SOURCE_FILES} ${IntelliStream_HEADER_FILES} ${CMAKE_CURRENT_BINARY_DIR} ) +add_library(IntelliStream SHARED ${IntelliStream_SOURCE_FILES} ${IntelliStream_HEADER_FILES} ${CMAKE_CURRENT_BINARY_DIR}) set_property(TARGET IntelliStream PROPERTY CXX_STANDARD 20) target_include_directories(IntelliStream PUBLIC "include") diff --git a/benchmark/src/Benchmark.cpp b/benchmark/src/Benchmark.cpp index 0a1d9b0f..d7b6c840 100644 --- a/benchmark/src/Benchmark.cpp +++ b/benchmark/src/Benchmark.cpp @@ -10,6 +10,29 @@ using namespace std; using namespace INTELLI; using namespace torch; using namespace DIVERSE_METER; +void streamingTest(ConfigMapPtr cfg,torch::Tensor A, torch::Tensor B, uint64_t sketchSize=1) +{ + AMMBench::SingleThreadStreamer ss; + ss.setConfig(cfg); + auto ssC=ss.streamingAmm(A,B,sketchSize); + + auto resultCsv=newConfigMap();; + resultCsv->edit("throughput",(double )ss.getThroughput()); + resultCsv->edit("throughputByElements",(double )(ss.getThroughput()*A.size(1))); + resultCsv->edit("95%latency",(double )ss.getLatencyPercentage(0.95)); + INTELLI_WARNING("evaluating the error, may takes some time"); + torch::Tensor rawC = torch::matmul(A, B); + double froError = INTELLI::UtilityFunctions::relativeFrobeniusNorm(rawC, ssC); + double froBNormal = B.norm().item(); + double errorBoundRatio = froError / froBNormal; + INTELLI_INFO("B normal is " + to_string(froBNormal)); + resultCsv->edit("froError", (double) froError); + resultCsv->edit("errorBoundRatio", (double) errorBoundRatio); + resultCsv->toFile( "result_streaming.csv"); + INTELLI_INFO("Done. here is overall result"); + std::cout << resultCsv->toString() << endl; + return; +} void runSingleThreadTest(std::string configName) { MeterTable meterTable; AbstractMeterPtr eMeter = nullptr; @@ -59,6 +82,12 @@ void runSingleThreadTest(std::string configName) { auto A = matLoaderPtr->getA(); auto B = matLoaderPtr->getB(); torch::Tensor C; + uint64_t isStreaming = cfg->tryU64("isStreaming", 0, true); + if(isStreaming) + { + streamingTest(cfg,A,B,sketchDimension); + return; + } //555 /*torch::manual_seed(114514); //555 diff --git a/commit.sh b/commit.sh index 1560d894..b1544b90 100755 --- a/commit.sh +++ b/commit.sh @@ -1,4 +1,4 @@ -BRANCH=PQTest +BRANCH=Streaming git init git checkout -b $BRANCH git add . diff --git a/commit_info b/commit_info index 284b9309..dbf6d99e 100644 --- a/commit_info +++ b/commit_info @@ -1 +1 @@ -1. add the c++ function of loading prototype from python, see test/systemTest/PQTest.cpp \ No newline at end of file +1. add the experimental feature of single thread streaming \ No newline at end of file diff --git a/include/AMMBench.h b/include/AMMBench.h index 427653aa..b649c04d 100755 --- a/include/AMMBench.h +++ b/include/AMMBench.h @@ -98,7 +98,7 @@ */ /** * @subsection code_stru_parallelization Parallelization -* This folder contains the parallelizationapproaches +* This folder contains the parallelization approaches * @defgroup AMMBENCH_PARALLELIZATION The parallelization classes * @{ * We define the parallelization classes of AMM. here @@ -109,6 +109,19 @@ * */ /** +* @subsection code_stru_STREAMING STREAMING +* This folder contains the STREAMING approaches +* @defgroup AMMBENCH_STREAMING The streaming classes +* @{ +* We define the streaming classes of AMM. here +**/ +#include +#include +/** + * @} + * + */ +/** * @subsection code_stru_cppalgo c++ algorithms * This folder contains the agorithms implemented under pure c++ * @defgroup AMMBENCH_CppAlgos The c++ amm algorithms diff --git a/include/Streaming/SingleThreadStreamer.h b/include/Streaming/SingleThreadStreamer.h new file mode 100644 index 00000000..2f353111 --- /dev/null +++ b/include/Streaming/SingleThreadStreamer.h @@ -0,0 +1,73 @@ +/*! \file SingleThreadStamper.h*/ +// +// Created by tony on 23/06/23. +// + +#ifndef INTELLISTREAM_SINGLETHREADSTREAMER_H +#define INTELLISTREAM_SINGLETHREADSTREAMER_H +#include +#include +#include +namespace AMMBench { + +/** + * @ingroup AMMBENCH_STREAMING + * @{ + * + */ + /** + * @class SingleThreadStreamer Streaming/SingleThreadStreamer.h + * @brief The class to run streaming amm under single thread, let each row of A coming in a streaming manner + * @ingroup AMMBENCH_STREAMING + * @note Default behavior + */ + class SingleThreadStreamer { + protected: + INTELLI::ConfigMapPtr cfgGlobal; + AMMBench::CPPAlgoTable cppAlgoTable; + uint64_t batchSize=1; + AMMBench::AbstractCPPAlgoPtr cppAlgoPtr = nullptr; + AMMBench::TensorPtr matC= nullptr; + double throughput=0.0; + public: + SingleThreadStreamer(){} + ~SingleThreadStreamer(){} + /** + * @brief the timestamps to trace the streaming process + */ + std::vector myTs; + /** + * @brief Set the GLOBAL config map related to this TimerStamper + * @param cfg The config map + * @return bool whether the config is successfully set + */ + virtual bool setConfig(INTELLI::ConfigMapPtr cfg); + /** + * @brief To run a streaming Amm, assuming the rows of A coming in a streaming manner and B is fixed + * @param A The A matrix + * @param B The B matrix + * @return bool whether the config is successfully set + */ + virtual torch::Tensor streamingAmm(torch::Tensor A, torch::Tensor B, uint64_t sketchSize=1); + /** + * @brief to get the throughput of last streaming process, the unit is rows/second + * @return the throughput + */ + double getThroughput() + { + return throughput; + } + /** + * @brief to get the latency within some fraction, such as 0.95 + * @param fraction the 0~1 fraction + * @return the latency in us + */ + double getLatencyPercentage(double fraction); + + }; +/** + * @} + */ +} // AMMBench + +#endif //INTELLISTREAM_SINGLETHREADSTREAMER_H diff --git a/include/Streaming/TimeStamper.h b/include/Streaming/TimeStamper.h new file mode 100644 index 00000000..d8376043 --- /dev/null +++ b/include/Streaming/TimeStamper.h @@ -0,0 +1,128 @@ +/*! \file TimerStamper.h*/ +// +// Created by tony on 23/06/23. +// + +#ifndef INTELLISTREAM_TIMESTAMPER_H +#define INTELLISTREAM_TIMESTAMPER_H +#include +#include +#include +#include +#include +namespace AMMBench { +/** + * @ingroup AMMBENCH_STREAMING + * @{ + * + */ + /** +* @class AMMTimeStamp Streaming/TimeStamper.h +* @brief The class to define timestamp in streaming +* @ingroup AMMBENCH_STREAMING +*/ + class AMMTimeStamp{ + public: + /** + * @brief The time when the related event (to a row or a column) happen + */ + uint64_t eventTime=0; + /** + * @brief The time when the related event (to a row or a column) arrive to the system + */ + uint64_t arrivalTime=0; + /** + * @brief the time when the related event is fully processed + */ + uint64_t processedTime=0; + AMMTimeStamp(){} + AMMTimeStamp(uint64_t te,uint64_t ta,uint64_t tp) + { + eventTime=te; + arrivalTime=ta; + processedTime=tp; + } + ~AMMTimeStamp(){} + }; + +/** + * @cite AMMTimeStampPtr + * @brief The class to describe a shared pointer to @ref AMMTimeStamp + */ + typedef std::shared_ptr AMMTimeStampPtr; +/** + * @cite newAMMTimeStampPtr + * @brief (Macro) To creat a new @ref AMMTimeStamp under shared pointer. + */ +#define newAMMTimeStamp std::make_shared + /** + * @class TimeStamper Streaming/TimeStamper.h + * @brief The basic class to generate time stamps + * @ingroup AMMBENCH_STREAMING + * @note require configs: + * - eventRateTps U64 The real-world rate of spawn event, in Tuples/s + * - streamingTupleCnt U64 The number of "streaming tuples", can be set to the #rows or #cols of a matrix + * - timeStamper_zipfEvent, U64, whether or not using the zipf for event rate, default 0 + * - timeStamper_zipfEventFactor, Double, the zpf factor for event rate, default 0.1, should be 0~1 + * @note Default behavior + * - create + * - call @ref setConfig to generate the timestamp under instructions + * - call @ref getTimeStamps to get the timestamp + */ + class TimeStamper { + protected: + INTELLI::ConfigMapPtr cfgGlobal; + INTELLI::MicroDataSet md; + uint64_t timeStamper_zipfEvent=0; + double timeStamper_zipfEventFactor=0; + uint64_t testSize; + std::vector eventS; + std::vector arrivalS; + uint64_t eventRateTps=0; + uint64_t timeStepUs=40; + uint64_t seed=114514; + /** + * + * @brief generate the vector of event + */ + void generateEvent(); + + /** + * @brief generate the vector of arrival + * @note As we do not consider OoO now, this is a dummy function + */ + void generateArrival(); + + /** + * @brief generate the final result of s and r + */ + void generateFinal(); + std::vector constructTimeStamps( + std::vector eventS, + std::vector arrivalS); + + public: + TimeStamper(){} + ~TimeStamper(){} + std::vector myTs; + /** + * @brief Set the GLOBAL config map related to this TimerStamper + * @param cfg The config map + * @return bool whether the config is successfully set + */ + virtual bool setConfig(INTELLI::ConfigMapPtr cfg); + + /** + * @brief get the vector of R tuple + * @return the vector + */ + virtual std::vector getTimeStamps(){ + return myTs; + } + }; +/** + * @} + */ +} // AMMBench + +#endif //INTELLISTREAM_TIMESTAMPER_H diff --git a/include/Utils/MicroDataSet.hpp b/include/Utils/MicroDataSet.hpp index 89fad9a7..e3bbc8ca 100644 --- a/include/Utils/MicroDataSet.hpp +++ b/include/Utils/MicroDataSet.hpp @@ -6,6 +6,7 @@ #ifndef _UTILS_MICRODATASET_H_ #define _UTILS_MICRODATASET_H_ #pragma once + #include #include #include @@ -14,6 +15,7 @@ #include #include #include + using namespace std; namespace INTELLI { /** @@ -42,206 +44,224 @@ namespace INTELLI { * @class MicroDataSet Utils/MicroDataSet.hpp * @brief The all-in-one class for the Micro dataset */ -class MicroDataSet { - private: - std::random_device rd; - std::default_random_engine e1; - bool hasSeed = false; - uint64_t seed; - //uint64_t runTime=0; - public: - /** - * @brief default construction, with auto random generator - */ - MicroDataSet() { + class MicroDataSet { + private: + std::random_device rd; + std::default_random_engine e1; + bool hasSeed = false; + uint64_t seed; + //uint64_t runTime=0; + public: + /** + * @brief default construction, with auto random generator + */ + MicroDataSet() = default; - } - /** - * @brief construction with seed - * @param seed The seed for random generator - */ - MicroDataSet(uint64_t _seed) { - seed = _seed; - hasSeed = true; - } - ~MicroDataSet() {} - /** @defgroup MICRO_GENERIC generic - * @{ - * The functions for general generation of Micro - */ - /** - * @brief To generate incremental alphabet, starting from 0 and end at len - * @tparam dType The data type in the alphabet, default uint32_t - * @param len The length of alphabet - * @return The output vector alphabet - */ - template - vector genIncrementalAlphabet(size_t len) { - vector ru(len); - /* populate */ - for (size_t i = 0; i < len; i++) { - ru[i] = i + 1; /* don't let 0 be in the alphabet */ - } - return ru; - } - /** - * @brief The function to generate a vector of integers which has zipf distribution - * @param tsType The data type of int, default is size_t - * @param len The length of output vector - * @param maxV The maximum value of integer - * @param fac The zipf factor, in [0,1] - * @return the output vector - */ - template - vector genZipfInt(size_t len, tsType maxV, double fac) { - vector ret(len); - vector alphabet = genIncrementalAlphabet(maxV); - std::mt19937_64 gen; - if (!hasSeed) { - gen = std::mt19937_64(rd()); // 以 rd() 播种的标准 mersenne_twister_engine - } else { - gen = std::mt19937_64(seed); - } - - std::uniform_real_distribution<> dis(0, 1); - vector lut = genZipfLut(maxV, fac); - for (size_t i = 0; i < len; i++) { - /* take random number */ - double r = dis(gen); - /* binary search in lookup table to determine item */ - size_t left = 0; - size_t right = maxV - 1; - size_t m; /* middle between left and right */ - size_t pos; /* position to take */ - - if (lut[0] >= r) - pos = 0; - else { - while (right - left > 1) { - m = (left + right) / 2; - - if (lut[m] < r) - left = m; - else - right = m; + /** + * @brief construction with seed + * @param seed The seed for random generator + */ + explicit MicroDataSet(uint64_t _seed) { + seed = _seed; + hasSeed = true; } - pos = right; - } - ret[i] = alphabet[pos]; - } - return ret; - } - /** - * @brief generate the vector of random integer - * @tparam tsType The data type, default uint32_t - * @tparam genType The generator type, default mt19937 (32 bit rand) - * @param len The length of output vector - * @param maxV The maximum value of output - * @param minV The minimum value of output - * @return The output vector - * @note Both signed and unsigned int are support, just make sure you have right tsType - * @note Other options for genType: - * \li mt19937_64: 64 bit rand - * \li ranlux24: 24 bit - * \li ranlux48: 48 bit - */ - template - vector genRandInt(size_t len, tsType maxV, tsType minV = 0) { - genType gen; - if (!hasSeed) { - gen = genType(rd()); - } else { - seed++; - gen = genType(seed); - } - std::uniform_int_distribution<> dis(minV, maxV); - vector ret(len); - for (size_t i = 0; i < len; i++) { - ret[i] = (tsType) dis(gen); - } - return ret; - } - /** - * @brief To generate the zipf Lut - * @tparam dType The data type in the alphabet, default double - * @param len The length of alphabet - * @param fac The zipf factor, in [0,1] - * @return The output vector lut - */ - template - vector genZipfLut(size_t len, dType fac) { - dType scaling_factor; - dType sum; - vector lut(len); - /* - * Compute scaling factor such that - * - * sum (lut[i], i=1..alphabet_size) = 1.0 - * - */ - scaling_factor = 0.0; - for (size_t i = 1; i <= len; i++) { scaling_factor += 1.0 / pow(i, fac); } - /* - * Generate the lookup table - */ - sum = 0.0; - for (size_t i = 1; i <= len; i++) { - sum += 1.0 / std::pow(i, fac); - lut[i - 1] = sum / scaling_factor; - } - return lut; - } + /** + * @brief construction with seed + * @param seed The seed for random generator + */ + void setSeed(uint64_t _seed) { + seed = _seed; + hasSeed = true; + } - /** - * @} - */ - /** - * @defgroup MICRO_TS time stamp - * @{ - * This group is specialized for time stamps, as they should follow an incremental order - */ - /** - * @brief The function to generate a vector of timestamp which grows smoothly - * @tparam tsType The data type of time stamp, default is size_t - * @param len The length of output vector - * @param step Within the step, timestamp will remain the same - * @param interval The incremental value between two steps - * @return The vector of time stamp - */ - template - vector genSmoothTimeStamp(size_t len, size_t step, size_t interval) { - vector ret(len); - tsType ts = 0; - for (size_t i = 0; i < len; i++) { - ret[i] = ts; - if (i % (step) == 0) { - ts += interval; - } - - } - return ret; - } + ~MicroDataSet() = default; + /** @defgroup MICRO_GENERIC generic + * @{ + * The functions for general generation of Micro + */ + /** + * @brief To generate incremental alphabet, starting from 0 and end at len + * @tparam dType The data type in the alphabet, default uint32_t + * @param len The length of alphabet + * @return The output vector alphabet + */ + template + vector genIncrementalAlphabet(size_t len) { + vector ru(len); + /* populate */ + for (size_t i = 0; i < len; i++) { + ru[i] = i + 1; /* don't let 0 be in the alphabet */ + } + return ru; + } - /** - * @brief The function to generate a vector of timestamp which has zipf distribution - * @param tsType The data type of time stamp, default is size_t - * @param len The length of output vector - * @param maxTime The maximum value of time stamp - * @param fac The zipf factor, in [0,1] - * @return the output vector - * @see genZipfInt - */ - template - vector genZipfTimeStamp(size_t len, tsType maxTime, double fac) { - vector ret = genZipfInt(len, maxTime, fac); - std::sort(ret.begin(), ret.end()); //just incremental re-arrange - return ret; - } - /** - * @} - */ -}; + /** + * @brief The function to generate a vector of integers which has zipf distribution + * @param tsType The data type of int, default is size_t + * @param len The length of output vector + * @param maxV The maximum value of integer + * @param fac The zipf factor, in [0,1] + * @return the output vector + */ + template + vector genZipfInt(size_t len, tsType maxV, double fac) { + vector ret(len); + vector alphabet = genIncrementalAlphabet(maxV); + std::mt19937_64 gen; + if (!hasSeed) { + gen = std::mt19937_64(rd()); // 以 rd() 播种的标准 mersenne_twister_engine + } else { + gen = std::mt19937_64(seed); + seed++; + } + + std::uniform_real_distribution<> dis(0, 1); + vector lut = genZipfLut(maxV, fac); + for (size_t i = 0; i < len; i++) { + /* take random number */ + double r = dis(gen); + /* binary search in lookup table to determine item */ + size_t left = 0; + size_t right = maxV - 1; + size_t m; /* middle between left and right */ + size_t pos; /* position to take */ + + if (lut[0] >= r) + pos = 0; + else { + while (right - left > 1) { + m = (left + right) / 2; + + if (lut[m] < r) + left = m; + else + right = m; + } + + pos = right; + } + ret[i] = alphabet[pos]; + } + return ret; + } + + /** + * @brief generate the vector of random integer + * @tparam tsType The data type, default uint32_t + * @tparam genType The generator type, default mt19937 (32 bit rand) + * @param len The length of output vector + * @param maxV The maximum value of output + * @param minV The minimum value of output + * @return The output vector + * @note Both signed and unsigned int are support, just make sure you have right tsType + * @note Other options for genType: + * \li mt19937_64: 64 bit rand + * \li ranlux24: 24 bit + * \li ranlux48: 48 bit + */ + template + vector genRandInt(size_t len, tsType maxV, tsType minV = 0) { + genType gen; + if (!hasSeed) { + gen = genType(rd()); + } else { + seed++; + gen = genType(seed); + } + std::uniform_int_distribution<> dis(minV, maxV); + vector ret(len); + for (size_t i = 0; i < len; i++) { + ret[i] = (tsType) dis(gen); + } + return ret; + } + + /** + * @brief To generate the zipf Lut + * @tparam dType The data type in the alphabet, default double + * @param len The length of alphabet + * @param fac The zipf factor, in [0,1] + * @return The output vector lut + */ + template + vector genZipfLut(size_t len, dType fac) { + dType scaling_factor; + dType sum; + vector lut(len); + /** + * Compute scaling factor such that + * + * sum (lut[i], i=1..alphabet_size) = 1.0 + * + */ + scaling_factor = 0.0; + for (size_t i = 1; i <= len; i++) { scaling_factor += 1.0 / pow(i, fac); } + /** + * Generate the lookup table + */ + sum = 0.0; + for (size_t i = 1; i <= len; i++) { + sum += 1.0 / std::pow(i, fac); + lut[i - 1] = sum / scaling_factor; + } + return lut; + } + + /** + * @} + */ + /** + * @defgroup MICRO_TS time stamp + * @{ + * This group is specialized for time stamps, as they should follow an incremental order + */ + /** + * @brief The function to generate a vector of timestamp which grows smoothly + * @tparam tsType The data type of time stamp, default is size_t + * @param len The length of output vector + * @param step Within the step, timestamp will remain the same + * @param interval The incremental value between two steps + * @return The vector of time stamp + */ + template + vector genSmoothTimeStamp(size_t len, size_t step, size_t interval) { + vector ret(len); + tsType ts = 0; + for (size_t i = 0; i < len; i++) { + ret[i] = ts; + if (i % (step) == 0) { + ts += interval; + } + + } + return ret; + } + template + vector genSmoothTimeStamp(size_t len, size_t maxTime) { + vector ret=genRandInt(len,maxTime); + std::sort(ret.begin(), ret.end()); //just incremental re-arrange + return ret; + } + /** + * @brief The function to generate a vector of timestamp which has zipf distribution + * @param tsType The data type of time stamp, default is size_t + * @param len The length of output vector + * @param maxTime The maximum value of time stamp + * @param fac The zipf factor, in [0,1] + * @return the output vector + * @see genZipfInt + */ + template + vector genZipfTimeStamp(size_t len, tsType maxTime, double fac) { + vector ret = genZipfInt(len, maxTime, fac); + std::sort(ret.begin(), ret.end()); //just incremental re-arrange + return ret; + } + /** + * @} + */ + }; } /** * @} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 618b6fa2..942ca87d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -2,6 +2,7 @@ add_subdirectory(Utils) add_subdirectory(MatrixLoader) add_subdirectory(Parallelization) add_subdirectory(CPPAlgos) +add_subdirectory(Streaming) add_sources( myVecAdd.cpp ) \ No newline at end of file diff --git a/src/Streaming/CMakeLists.txt b/src/Streaming/CMakeLists.txt new file mode 100644 index 00000000..1e2aa136 --- /dev/null +++ b/src/Streaming/CMakeLists.txt @@ -0,0 +1,4 @@ +add_sources( + TimeStamper.cpp + SingleThreadStreamer.cpp +) \ No newline at end of file diff --git a/src/Streaming/SingleThreadStreamer.cpp b/src/Streaming/SingleThreadStreamer.cpp new file mode 100644 index 00000000..acb72129 --- /dev/null +++ b/src/Streaming/SingleThreadStreamer.cpp @@ -0,0 +1,101 @@ +// +// Created by tony on 23/06/23. +// + +#include +#include +bool AMMBench::SingleThreadStreamer::setConfig(INTELLI::ConfigMapPtr cfg) { + cfgGlobal = cfg; + /** + * @brief 1.set the algo + */ + std::string cppAlgoTag = cfg->tryString("cppAlgoTag", "mm", true); + cppAlgoPtr = cppAlgoTable.findCppAlgo(cppAlgoTag); + cppAlgoPtr->setConfig(cfg); + /** + * @brief 2. set the batch size + */ + batchSize=cfg->tryU64("batchSize",1, true); + return true; +} +torch::Tensor AMMBench::SingleThreadStreamer::streamingAmm(torch::Tensor A, torch::Tensor B, uint64_t sketchSize){ + assert(sketchSize); + uint64_t aRows=A.size(0); + cfgGlobal->edit("streamingTupleCnt",(uint64_t)aRows); + if(batchSize>aRows) + { + batchSize=aRows; + } + AMMBench::TimeStamper tsGen; + tsGen.setConfig(cfgGlobal); + myTs=tsGen.getTimeStamps(); + INTELLI_INFO("Generate time stamp done"); + matC = newTensor(torch::zeros({A.size(0), B.size(1)})); + struct timeval tstart; + //INTELLI_INFO("I am mm"); + INTELLI_INFO("Start Streaming"); + uint64_t startRow=0; + uint64_t endRow=startRow+batchSize; + uint64_t tNow=0; + uint64_t tEXpectedArrival=myTs[endRow-1]->arrivalTime; + uint64_t tp=0; + uint64_t tDone=0; + gettimeofday(&tstart,NULL); + while(startRowslice(0, startRow, endRow) = cppAlgoPtr->amm(subA, B, sketchSize); + tp=INTELLI::UtilityFunctions::timeLastUs(tstart); + for(size_t i=startRow;iprocessedTime=tp; + } + /** + * @brief update the indexes + */ + startRow+=batchSize; + endRow+=batchSize; + if(endRow>=aRows) + { + endRow=aRows; + } + tEXpectedArrival=myTs[endRow-1]->arrivalTime; + } + tDone=INTELLI::UtilityFunctions::timeLastUs(tstart); + INTELLI_INFO("Done in "+ to_string(tDone)+"us"); + throughput=aRows; + throughput=throughput*1e6/tDone; + return *matC; +} +double AMMBench::SingleThreadStreamer::getLatencyPercentage(double fraction) { + size_t rLen = myTs.size(); + size_t nonZeroCnt = 0; + std::vector validLatency; + for (size_t i = 0; i < rLen; i++) { + if (myTs[i]->processedTime >= myTs[i]->arrivalTime && myTs[i]->processedTime != 0) { + validLatency.push_back(myTs[i]->processedTime - myTs[i]->arrivalTime); + nonZeroCnt++; + } + } + if (nonZeroCnt == 0) { + INTELLI_ERROR("No valid latency, maybe there is no AMM result?"); + return 0; + } + std::sort(validLatency.begin(), validLatency.end()); + double t = nonZeroCnt; + t = t * fraction; + size_t idx = (size_t) t + 1; + if (idx >= validLatency.size()) { + idx = validLatency.size() - 1; + } + return validLatency[idx]; +} \ No newline at end of file diff --git a/src/Streaming/TimeStamper.cpp b/src/Streaming/TimeStamper.cpp new file mode 100644 index 00000000..f2cecbdd --- /dev/null +++ b/src/Streaming/TimeStamper.cpp @@ -0,0 +1,54 @@ +// +// Created by tony on 23/06/23. +// + +#include +#include + +bool AMMBench::TimeStamper::setConfig(INTELLI::ConfigMapPtr cfg) { + cfgGlobal = cfg; + eventRateTps = cfg->tryU64("eventRateTps", 100, true); + timeStepUs = cfg->tryU64("timeStepUs", 100,true); + timeStamper_zipfEvent=cfg->tryU64("timeStamper_zipfEvent",0, true); + timeStamper_zipfEventFactor=cfg->tryDouble("timeStamper_zipfEventFactor",0.1, true); + testSize=cfg->tryU64("streamingTupleCnt",0, true); + md.setSeed(seed); + generateEvent(); + generateArrival(); + generateFinal(); + return true; +} +void AMMBench::TimeStamper::generateEvent() { + uint64_t maxTime=testSize*1000*1000/eventRateTps; + if (timeStamper_zipfEvent) { + INTELLI_INFO("Use zipf for event time, factor=" + to_string(timeStamper_zipfEventFactor)); + INTELLI_INFO("maxTime="+ to_string(maxTime)+"us"+"rate="+ to_string(eventRateTps)+"K, cnt="+ to_string(testSize) ); + eventS = + md.genZipfTimeStamp(testSize, maxTime, + timeStamper_zipfEventFactor); + } else { + // uint64_t tsGrow = 1000 * timeStepUs / eventRateKTps; + eventS = md.genSmoothTimeStamp(testSize,maxTime); + } + INTELLI_INFO("Finish the generation of event time"); +} + +void AMMBench::TimeStamper::generateArrival() { + + INTELLI_INFO("Finish the generation of arrival time"); +} +std::vector AMMBench::TimeStamper::constructTimeStamps( + std::vector _eventS, + std::vector _arrivalS) +{ + size_t len = _eventS.size(); + std::vector ru = std::vector(len); + for (size_t i = 0; i < len; i++) { + ru[i] = newAMMTimeStamp(_eventS[i], _arrivalS[i], 0); + } + return ru; +} + +void AMMBench::TimeStamper::generateFinal() { + myTs=constructTimeStamps(eventS,eventS); +} \ No newline at end of file diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index ebfcafbe..db9e353f 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -28,4 +28,5 @@ add_catch_test(block_partition_test SystemTest/BlockPartitionTest.cpp IntelliStr add_catch_test(tug_of_war_test SystemTest/TugOfWarTest.cpp IntelliStream) add_catch_test(int8_test SystemTest/INT8Test.cpp IntelliStream) add_catch_test(pq_test SystemTest/PQTest.cpp IntelliStream) +add_catch_test(streaming_test SystemTest/StreamingTest.cpp IntelliStream) diff --git a/test/SystemTest/StreamingTest.cpp b/test/SystemTest/StreamingTest.cpp new file mode 100644 index 00000000..44b4202c --- /dev/null +++ b/test/SystemTest/StreamingTest.cpp @@ -0,0 +1,49 @@ +#define CATCH_CONFIG_MAIN +#include "catch.hpp" +#include +#include +using namespace std; +using namespace INTELLI; +using namespace torch; + + +TEST_CASE("Test the basic streaming batch 1", "[short]") +{ + ConfigMapPtr cfg = newConfigMap(); + torch::manual_seed(114514); + auto A = torch::rand({(long) 4, (long) 4}); + auto B = torch::rand({(long) 4, (long) 4}); + auto rawC=torch::matmul(A, B); + AMMBench::SingleThreadStreamer ss; + //cfg->edit("",(uint64_t)100); + ss.setConfig(cfg); + auto ssC=ss.streamingAmm(A,B); + std::cout<<"raw C:"<edit("batchSize",(uint64_t)2); + ss.setConfig(cfg); + auto ssC=ss.streamingAmm(A,B); + std::cout<<"raw C:"< #include + using namespace std; using namespace INTELLI; using namespace torch; using namespace DIVERSE_METER; -void streamingTest(ConfigMapPtr cfg,torch::Tensor A, torch::Tensor B, uint64_t sketchSize=1) -{ + +void streamingTest(ConfigMapPtr cfg, torch::Tensor A, torch::Tensor B, uint64_t sketchSize = 1) { AMMBench::SingleThreadStreamer ss; ss.setConfig(cfg); - auto ssC=ss.streamingAmm(A,B,sketchSize); + auto ssC = ss.streamingAmm(A, B, sketchSize); - auto resultCsv=newConfigMap();; - resultCsv->edit("throughput",(double )ss.getThroughput()); - resultCsv->edit("throughputByElements",(double )(ss.getThroughput()*A.size(1))); - resultCsv->edit("95%latency",(double )ss.getLatencyPercentage(0.95)); + auto resultCsv = newConfigMap();; + resultCsv->edit("throughput", (double) ss.getThroughput()); + resultCsv->edit("throughputByElements", (double) (ss.getThroughput() * A.size(1))); + resultCsv->edit("95%latency", (double) ss.getLatencyPercentage(0.95)); INTELLI_WARNING("evaluating the error, may takes some time"); torch::Tensor rawC = torch::matmul(A, B); double froError = INTELLI::UtilityFunctions::relativeFrobeniusNorm(rawC, ssC); @@ -28,161 +29,160 @@ void streamingTest(ConfigMapPtr cfg,torch::Tensor A, torch::Tensor B, uint64_t s INTELLI_INFO("B normal is " + to_string(froBNormal)); resultCsv->edit("froError", (double) froError); resultCsv->edit("errorBoundRatio", (double) errorBoundRatio); - resultCsv->toFile( "result_streaming.csv"); + resultCsv->toFile("result_streaming.csv"); INTELLI_INFO("Done. here is overall result"); std::cout << resultCsv->toString() << endl; return; } -void runSingleThreadTest(std::string configName) { - MeterTable meterTable; - AbstractMeterPtr eMeter = nullptr; - ConfigMapPtr cfg = newConfigMap(); - cfg->fromFile(configName); - AMMBench::MatrixLoaderTable mLoaderTable; - uint64_t sketchDimension; - ConfigMapPtr breakDownResult= nullptr; - sketchDimension = cfg->tryU64("sketchDimension", 50, true); - uint64_t coreBind = cfg->tryU64("coreBind", 0, true); - uint64_t usingMeter = cfg->tryU64("usingMeter", 0, true); - std::string meterTag = cfg->tryString("meterTag", "intelMsr", true); - uint64_t useCPP = cfg->tryU64("useCPP", 0, true); - uint64_t forceMP = cfg->tryU64("forceMP", 0, true); - if (usingMeter) { - eMeter = meterTable.findMeter(meterTag); - if (eMeter != nullptr) { - eMeter->setConfig(cfg); - double staticPower = cfg->tryDouble("staticPower", 0.0, false); - if (staticPower == 0.0) { - eMeter->testStaticPower(2); - } else { - INTELLI_INFO("use pre-defined static power"); - eMeter->setStaticPower(staticPower); - } - INTELLI_INFO("static power is " + to_string(eMeter->getStaticPower()) + " W"); - } else { - INTELLI_ERROR("No meter found: " + meterTag); - } - } - UtilityFunctions::bind2Core((int) coreBind); - //torch::set_num_threads(1); - std::string ptFile = cfg->tryString("ptFile", "torchscripts/FDAMM.pt", true); +void runSingleThreadTest(std::string configName) { + MeterTable meterTable; + AbstractMeterPtr eMeter = nullptr; + ConfigMapPtr cfg = newConfigMap(); + cfg->fromFile(configName); + AMMBench::MatrixLoaderTable mLoaderTable; + uint64_t sketchDimension; + ConfigMapPtr breakDownResult = nullptr; + sketchDimension = cfg->tryU64("sketchDimension", 50, true); + uint64_t coreBind = cfg->tryU64("coreBind", 0, true); + uint64_t usingMeter = cfg->tryU64("usingMeter", 0, true); + std::string meterTag = cfg->tryString("meterTag", "intelMsr", true); + uint64_t useCPP = cfg->tryU64("useCPP", 0, true); + uint64_t forceMP = cfg->tryU64("forceMP", 0, true); + if (usingMeter) { + eMeter = meterTable.findMeter(meterTag); + if (eMeter != nullptr) { + eMeter->setConfig(cfg); + double staticPower = cfg->tryDouble("staticPower", 0.0, false); + if (staticPower == 0.0) { + eMeter->testStaticPower(2); + } else { + INTELLI_INFO("use pre-defined static power"); + eMeter->setStaticPower(staticPower); + } + INTELLI_INFO("static power is " + to_string(eMeter->getStaticPower()) + " W"); + } else { + INTELLI_ERROR("No meter found: " + meterTag); + } - //uint64_t customResultName = cfg->tryU64("customResultName", 0, true); - INTELLI_INFO("Place me at core" + to_string(coreBind)); - INTELLI_INFO( - "with sketch" + to_string(sketchDimension)); - torch::jit::script::Module module; - INTELLI_INFO("Try pt file " + ptFile); - module = torch::jit::load(ptFile); - std::string matrixLoaderTag = cfg->tryString("matrixLoaderTag", "random", true); - auto matLoaderPtr = mLoaderTable.findMatrixLoader(matrixLoaderTag); - assert(matLoaderPtr); - matLoaderPtr->setConfig(cfg); - auto A = matLoaderPtr->getA(); - auto B = matLoaderPtr->getB(); - torch::Tensor C; - uint64_t isStreaming = cfg->tryU64("isStreaming", 0, true); - if(isStreaming) - { - streamingTest(cfg,A,B,sketchDimension); - return; - } - //555 - /*torch::manual_seed(114514); -//555 -auto A = torch::rand({(long) aRow, (long) aCol}); -auto B = torch::rand({(long) aCol, (long) bCol});*/ - INTELLI_INFO("Generation done, conducting..."); - uint64_t threads = cfg->tryU64("threads", 0, true); - ThreadPerf pef(-1); - pef.setPerfList(); - AMMBench::BlockPartitionRunner br; - if (threads > 1 || forceMP) { - INTELLI_WARNING("use multithread"); - br.setConfig(cfg); - br.createABC(A, B); - if (eMeter != nullptr) { - eMeter->startMeter(); - } - pef.start(); - C = br.parallelForward(); - pef.end(); - if (eMeter != nullptr) { - eMeter->stopMeter(); } - breakDownResult=br.getBreakDown(); - } else { - AMMBench::CPPAlgoTable cppAlgoTable; - std::string cppAlgoTag = cfg->tryString("cppAlgoTag", "mm", true); - AMMBench::AbstractCPPAlgoPtr cppAlgoPtr = cppAlgoTable.findCppAlgo(cppAlgoTag); - cppAlgoPtr->setConfig(cfg); - INTELLI_WARNING("single thread, algo " + cppAlgoTag); - if (eMeter != nullptr) { - eMeter->startMeter(); + UtilityFunctions::bind2Core((int) coreBind); + //torch::set_num_threads(1); + std::string ptFile = cfg->tryString("ptFile", "torchscripts/FDAMM.pt", true); + + //uint64_t customResultName = cfg->tryU64("customResultName", 0, true); + INTELLI_INFO("Place me at core" + to_string(coreBind)); + INTELLI_INFO( + "with sketch" + to_string(sketchDimension)); + torch::jit::script::Module module; + INTELLI_INFO("Try pt file " + ptFile); + module = torch::jit::load(ptFile); + std::string matrixLoaderTag = cfg->tryString("matrixLoaderTag", "random", true); + auto matLoaderPtr = mLoaderTable.findMatrixLoader(matrixLoaderTag); + assert(matLoaderPtr); + matLoaderPtr->setConfig(cfg); + auto A = matLoaderPtr->getA(); + auto B = matLoaderPtr->getB(); + torch::Tensor C; + uint64_t isStreaming = cfg->tryU64("isStreaming", 0, true); + if (isStreaming) { + streamingTest(cfg, A, B, sketchDimension); + return; } - pef.start(); - if (useCPP && cppAlgoPtr) { - INTELLI_WARNING("this is pure c++"); - C = cppAlgoPtr->amm(A, B, sketchDimension); + //555 + /*torch::manual_seed(114514); + //555 + auto A = torch::rand({(long) aRow, (long) aCol}); + auto B = torch::rand({(long) aCol, (long) bCol});*/ + INTELLI_INFO("Generation done, conducting..."); + uint64_t threads = cfg->tryU64("threads", 0, true); + ThreadPerf pef(-1); + pef.setPerfList(); + AMMBench::BlockPartitionRunner br; + if (threads > 1 || forceMP) { + INTELLI_WARNING("use multithread"); + br.setConfig(cfg); + br.createABC(A, B); + if (eMeter != nullptr) { + eMeter->startMeter(); + } + pef.start(); + C = br.parallelForward(); + pef.end(); + if (eMeter != nullptr) { + eMeter->stopMeter(); + } + breakDownResult = br.getBreakDown(); } else { - C =module.forward({A, B, (long) sketchDimension}).toTensor(); + AMMBench::CPPAlgoTable cppAlgoTable; + std::string cppAlgoTag = cfg->tryString("cppAlgoTag", "mm", true); + AMMBench::AbstractCPPAlgoPtr cppAlgoPtr = cppAlgoTable.findCppAlgo(cppAlgoTag); + cppAlgoPtr->setConfig(cfg); + INTELLI_WARNING("single thread, algo " + cppAlgoTag); + if (eMeter != nullptr) { + eMeter->startMeter(); + } + pef.start(); + if (useCPP && cppAlgoPtr) { + INTELLI_WARNING("this is pure c++"); + C = cppAlgoPtr->amm(A, B, sketchDimension); + } else { + C =module.forward({A, B, (long) sketchDimension}).toTensor(); + } + pef.end(); + if (eMeter != nullptr) { + eMeter->stopMeter(); + } + if (useCPP && cppAlgoPtr) { + breakDownResult = cppAlgoPtr->getBreakDown(); + } } - pef.end(); + + std::string ruName = "default"; + + auto resultCsv = pef.resultToConfigMap(); if (eMeter != nullptr) { - eMeter->stopMeter(); + eMeter->stopMeter(); + double energyConsumption = eMeter->getE(); + double staticEnergyConsumption = eMeter->getStaicEnergyConsumption( + resultCsv->tryU64("perfElapsedTime", 0, false)); + double pureEnergy = energyConsumption - staticEnergyConsumption; + resultCsv->edit("energyAll", (double) energyConsumption); + resultCsv->edit("energyOnlyMe", (double) pureEnergy); } - if(useCPP && cppAlgoPtr) - { - breakDownResult=cppAlgoPtr->getBreakDown(); + if (threads > 1 || forceMP) { + INTELLI_WARNING("consider multithread elapsed time"); + resultCsv->edit("perfElapsedTime", (uint64_t) br.getElapsedTime()); + br.appendThreadInfo(resultCsv); + } + // error + INTELLI_WARNING("evaluating the error, may takes some time"); + torch::Tensor realC = torch::matmul(A, B); + double froError = INTELLI::UtilityFunctions::relativeFrobeniusNorm(realC, C); + double froBNormal = B.norm().item(); + double errorBoundRatio = froError / froBNormal; + INTELLI_INFO("B normal is " + to_string(froBNormal)); + resultCsv->edit("froError", (double) froError); + resultCsv->edit("errorBoundRatio", (double) errorBoundRatio); + resultCsv->toFile(ruName + ".csv"); + INTELLI_INFO("Done. here is overall result"); + std::cout << resultCsv->toString() << endl; + if (breakDownResult) { + INTELLI_INFO("I also have some break down result"); + std::cout << breakDownResult->toString() << endl; + breakDownResult->toFile(ruName + "_breakdown.csv"); } - } - - std::string ruName = "default"; - - auto resultCsv = pef.resultToConfigMap(); - if (eMeter != nullptr) { - eMeter->stopMeter(); - double energyConsumption = eMeter->getE(); - double staticEnergyConsumption = eMeter->getStaicEnergyConsumption(resultCsv->tryU64("perfElapsedTime", 0, false)); - double pureEnergy = energyConsumption - staticEnergyConsumption; - resultCsv->edit("energyAll", (double) energyConsumption); - resultCsv->edit("energyOnlyMe", (double) pureEnergy); - } - if (threads > 1 || forceMP) { - INTELLI_WARNING("consider multithread elapsed time"); - resultCsv->edit("perfElapsedTime", (uint64_t) br.getElapsedTime()); - br.appendThreadInfo(resultCsv); - } - // error - INTELLI_WARNING("evaluating the error, may takes some time"); - torch::Tensor realC = torch::matmul(A, B); - double froError = INTELLI::UtilityFunctions::relativeFrobeniusNorm(realC, C); - double froBNormal = B.norm().item(); - double errorBoundRatio = froError / froBNormal; - INTELLI_INFO("B normal is " + to_string(froBNormal)); - resultCsv->edit("froError", (double) froError); - resultCsv->edit("errorBoundRatio", (double) errorBoundRatio); - resultCsv->toFile(ruName + ".csv"); - INTELLI_INFO("Done. here is overall result"); - std::cout << resultCsv->toString() << endl; - if(breakDownResult) - { - INTELLI_INFO("I also have some break down result"); - std::cout << breakDownResult->toString() << endl; - breakDownResult->toFile(ruName+"_breakdown.csv"); - } } int main(int argc, char **argv) { - string configName, outPrefix = ""; - if (argc >= 2) { - configName += argv[1]; - } else { - configName = "config.csv"; - } - runSingleThreadTest(configName); - return 0; + string configName, outPrefix = ""; + if (argc >= 2) { + configName += argv[1]; + } else { + configName = "config.csv"; + } + runSingleThreadTest(configName); + return 0; } diff --git a/benchmark/torchscripts/PQ/PQ.cpp b/benchmark/torchscripts/PQ/PQ.cpp index 68026f33..857fd598 100644 --- a/benchmark/torchscripts/PQ/PQ.cpp +++ b/benchmark/torchscripts/PQ/PQ.cpp @@ -15,8 +15,8 @@ int main() { std::vector prototypes; std::ifstream in_file("/home/haolan/PQ/prototypes.pt", std::ios::binary); if (!in_file.is_open()) { - std::cerr << "Error opening file prototypes.pt\n"; - return 1; + std::cerr << "Error opening file prototypes.pt\n"; + return 1; } torch::load(prototypes, in_file); diff --git a/benchmark/torchscripts/PQ/PQ.py b/benchmark/torchscripts/PQ/PQ.py index 1d88d548..3747667f 100644 --- a/benchmark/torchscripts/PQ/PQ.py +++ b/benchmark/torchscripts/PQ/PQ.py @@ -2,6 +2,7 @@ import time + def kmeans(X, K, max_iters=100): N, D = X.shape @@ -21,28 +22,34 @@ def kmeans(X, K, max_iters=100): centroids[k] = torch.mean(X[labels == k], dim=0) return centroids, labels + + class MyModule(torch.nn.Module): - def __init__(self,prototypes): + def __init__(self, prototypes): super(MyModule, self).__init__() - #self.fc = torch.nn.Linear(10, 5) - self.prototypes=torch.nn.Parameter(prototypes) - self.QA=torch.nn.Parameter(torch.rand(5,5)) - #self.register_parameter("prototypes", self.prototypes) - def forward(self,A: torch.Tensor): - return torch.matmul(A,A.T)+torch.sum(self.prototypes[0])+torch.sum(self.QA) - + # self.fc = torch.nn.Linear(10, 5) + self.prototypes = torch.nn.Parameter(prototypes) + self.QA = torch.nn.Parameter(torch.rand(5, 5)) + # self.register_parameter("prototypes", self.prototypes) + + def forward(self, A: torch.Tensor): + return torch.matmul(A, A.T) + torch.sum(self.prototypes[0]) + torch.sum(self.QA) + + def save_model(model, path, X): tx = X.to('cpu') # tx=X model2 = model.to('cpu') # model2=model - #model2.eval() + # model2.eval() X = torch.tensor([1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0]) traced_model = torch.jit.script(model2) ru = traced_model(tx) - + traced_model.save(path) + + def main(): learning = True @@ -73,7 +80,7 @@ def main(): if trainingSet != None: print("Starting prototypes learning on training set size of " - + str(trainingSet.shape[0]) + ", " + str(trainingSet.shape[1])) + + str(trainingSet.shape[0]) + ", " + str(trainingSet.shape[1])) t = time.time() for c in range(C): @@ -87,13 +94,13 @@ def main(): A_subspace_np = A_subspace.detach().numpy() # Run KMeans on the subspace - centroids_torch,LB = kmeans(A_subspace,K, 10) + centroids_torch, LB = kmeans(A_subspace, K, 10) # Get the centroids (prototypes) - #centroids = kmeans.cluster_centers_ + # centroids = kmeans.cluster_centers_ # Convert back to PyTorch tensor - #centroids_torch = torch.from_numpy(centroids) + # centroids_torch = torch.from_numpy(centroids) # Append to the list of prototypes prototypes.append(centroids_torch) @@ -102,9 +109,9 @@ def main(): print("\nPrototype Learning: " + str(time.time() - t) + "s") model = MyModule(torch.stack(prototypes)) - save_model(model.to('cpu'), "prototypes.pt",torch.zeros(5,5)) - #model.save_model("prototypes.pt") - #torch.save(prototypes, 'prototypes.pt') + save_model(model.to('cpu'), "prototypes.pt", torch.zeros(5, 5)) + # model.save_model("prototypes.pt") + # torch.save(prototypes, 'prototypes.pt') else: print("Loading prototypes from serialized pickle file\n") @@ -122,9 +129,9 @@ def main(): for c in range(C): # Get the prototypes for this subspace prototypes_c = prototypes[c] - + # Get the subvector for this subspace - a_subvector = a[c * D_c : (c + 1) * D_c] + a_subvector = a[c * D_c: (c + 1) * D_c] # Calculate the distances from the subvector to each prototype distances = torch.norm(prototypes_c - a_subvector, dim=1) @@ -150,7 +157,6 @@ def main(): # Assume that B is a PyTorch tensor of size D x M - # Initialize a list to store the tables for each subspace tables = [] @@ -161,7 +167,7 @@ def main(): prototypes_c = prototypes[c] # Slice B to get the corresponding subspace - B_subspace = B[c * D_c : (c + 1) * D_c, :] + B_subspace = B[c * D_c: (c + 1) * D_c, :] # Initialize an empty list to store the table for this subspace table_c = [] @@ -213,7 +219,6 @@ def main(): # Convert the list of all rows to a PyTorch tensor result_tensor = torch.stack(result) - print("Aggregation: " + str(time.time() - t) + "s") print("\nTotal: " + str(time.time() - total) + "s") @@ -227,6 +232,8 @@ def main(): print("Exact time: " + str(time.time() - t) + "s") print(eResult) print("\nerror: " + str(torch.norm(result_tensor - eResult, p='fro').item())) - print(len(prototypes),torch.stack(prototypes).size()) + print(len(prototypes), torch.stack(prototypes).size()) + + if __name__ == '__main__': main() diff --git a/include/AMMBench.h b/include/AMMBench.h index b649c04d..8a3ee6b2 100755 --- a/include/AMMBench.h +++ b/include/AMMBench.h @@ -5,6 +5,7 @@ #ifndef INTELLISTREAM_AMMBENCH_H #define INTELLISTREAM_AMMBENCH_H + #include #include #include diff --git a/include/CPPAlgos/AbstractCPPAlgo.h b/include/CPPAlgos/AbstractCPPAlgo.h index d0504289..c8c47dae 100644 --- a/include/CPPAlgos/AbstractCPPAlgo.h +++ b/include/CPPAlgos/AbstractCPPAlgo.h @@ -5,12 +5,14 @@ #ifndef INTELLISTREAM_INCLUDE_CPPALGOS_ABSTRACTCPPALGO_H_ #define INTELLISTREAM_INCLUDE_CPPALGOS_ABSTRACTCPPALGO_H_ + #include #include #include #include #include #include + namespace AMMBench { /** * @ingroup AMMBENCH_CppAlgos The algorithms written in c++ @@ -20,50 +22,55 @@ namespace AMMBench { * @class AbstractCPPAlgo CPPAlgos/AbstractCPPAlgo.h * @brief The abstract class of c++ algos */ -class AbstractCPPAlgo { - protected: - /** - * @brief the default time break dowm variables - * @note By default, we decompose each AMM as - * - buildA, to translate A matrix - * - buildB, to translate B matrix - * - fABTime, to conduct mm or table look-up over the reduced A,B - * - postProcessTime, if f(A,B) is not the finall result, measure the time spend for post process - */ - uint64_t buildATime=0,buildBTime=0,fABTime=0,postProcessTime=0; + class AbstractCPPAlgo { + protected: + /** + * @brief the default time break dowm variables + * @note By default, we decompose each AMM as + * - buildA, to translate A matrix + * - buildB, to translate B matrix + * - fABTime, to conduct mm or table look-up over the reduced A,B + * - postProcessTime, if f(A,B) is not the finall result, measure the time spend for post process + */ + uint64_t buildATime = 0, buildBTime = 0, fABTime = 0, postProcessTime = 0; + + public: + AbstractCPPAlgo() { + + } + + ~AbstractCPPAlgo() { + + } + + /** + * @brief set the alo-specfic config related to one algorithm + */ + virtual void setConfig(INTELLI::ConfigMapPtr cfg); - public: - AbstractCPPAlgo() { + /** + * @brief the virtual function provided for outside callers, rewrite in children classes + * @param A the A matrix + * @param B the B matrix + * @param sketchSize the size of sketc or sampling + * @return the output c matrix + */ + virtual torch::Tensor amm(torch::Tensor A, torch::Tensor B, uint64_t sketchSize); - } - ~AbstractCPPAlgo() { + /** + * @brief to get the breakdown of this algorithm, returned as a config map + * @return the key-value table breakdown in ConfigMapPtr; + */ + virtual INTELLI::ConfigMapPtr getBreakDown(); + }; - } - /** - * @brief set the alo-specfic config related to one algorithm - */ - virtual void setConfig(INTELLI::ConfigMapPtr cfg); - /** - * @brief the virtual function provided for outside callers, rewrite in children classes - * @param A the A matrix - * @param B the B matrix - * @param sketchSize the size of sketc or sampling - * @return the output c matrix - */ - virtual torch::Tensor amm(torch::Tensor A, torch::Tensor B, uint64_t sketchSize); - /** - * @brief to get the breakdown of this algorithm, returned as a config map - * @return the key-value table breakdown in ConfigMapPtr; - */ - virtual INTELLI::ConfigMapPtr getBreakDown(); -}; /** * @ingroup AMMBENCH_CppAlgos * @typedef AbstractMatrixCppAlgoPtr * @brief The class to describe a shared pointer to @ref AbstractCPPAlgo */ -typedef std::shared_ptr AbstractCPPAlgoPtr; + typedef std::shared_ptr AbstractCPPAlgoPtr; /** * @ingroup AMMBENCH_CppAlgos * @def newAbstractCppAlgo diff --git a/include/CPPAlgos/BCRSCPPAlgo.h b/include/CPPAlgos/BCRSCPPAlgo.h index 7dfd8cff..ac859798 100644 --- a/include/CPPAlgos/BCRSCPPAlgo.h +++ b/include/CPPAlgos/BCRSCPPAlgo.h @@ -5,6 +5,7 @@ #ifndef INTELLISTREAM_BCRSCPPALGO_H #define INTELLISTREAM_BCRSCPPALGO_H + #include namespace AMMBench { @@ -16,26 +17,26 @@ namespace AMMBench { * @class BCRSCPPAlgo CPPAlgos/BCRSCPPAlgo.h * @brief The Bernoulli column row sampling (BCRS) class of c++ algos */ -class BCRSCPPAlgo : public AMMBench::AbstractCPPAlgo { - public: - BCRSCPPAlgo() { + class BCRSCPPAlgo : public AMMBench::AbstractCPPAlgo { + public: + BCRSCPPAlgo() { - } + } - ~BCRSCPPAlgo() { + ~BCRSCPPAlgo() { - } + } - /** - * @brief the virtual function provided for outside callers, rewrite in children classes - * @param A the A matrix - * @param B the B matrix - * @param sketchSize the size of sketc or sampling - * @return the output c matrix - */ - virtual torch::Tensor amm(torch::Tensor A, torch::Tensor B, uint64_t sketchSize); + /** + * @brief the virtual function provided for outside callers, rewrite in children classes + * @param A the A matrix + * @param B the B matrix + * @param sketchSize the size of sketc or sampling + * @return the output c matrix + */ + virtual torch::Tensor amm(torch::Tensor A, torch::Tensor B, uint64_t sketchSize); -}; + }; /** * @ingroup AMMBENCH_CppAlgos @@ -43,7 +44,7 @@ class BCRSCPPAlgo : public AMMBench::AbstractCPPAlgo { * @brief The class to describe a shared pointer to @ref BCRSCppAlgo */ -typedef std::shared_ptr BCRSCPPAlgoPtr; + typedef std::shared_ptr BCRSCPPAlgoPtr; /** * @ingroup AMMBENCH_CppAlgos * @def newBCRSCppAlgo diff --git a/include/CPPAlgos/BetaCoOFDCPPAlgo.h b/include/CPPAlgos/BetaCoOFDCPPAlgo.h index ae58648c..08342a3c 100644 --- a/include/CPPAlgos/BetaCoOFDCPPAlgo.h +++ b/include/CPPAlgos/BetaCoOFDCPPAlgo.h @@ -4,6 +4,7 @@ #ifndef INTELLISTREAM_BETACOOFDCPPALGO_H #define INTELLISTREAM_BETACOOFDCPPALGO_H + #include namespace AMMBench { @@ -17,31 +18,33 @@ namespace AMMBench { * @note parameters * - algoBeta Double, the beta parameters in this algo, default 1.0 */ -class BetaCoOFDCPPAlgo : public AMMBench::AbstractCPPAlgo { - protected: - float algoBeta=1.0; - public: - BetaCoOFDCPPAlgo() { - - } - - ~BetaCoOFDCPPAlgo() { - - } - /** - * @brief set the alo-specfic config related to one algorithm - */ - virtual void setConfig(INTELLI::ConfigMapPtr cfg); - /** - * @brief the virtual function provided for outside callers, rewrite in children classes - * @param A the A matrix - * @param B the B matrix - * @param sketchSize the size of sketc or sampling - * @return the output c matrix - */ - virtual torch::Tensor amm(torch::Tensor A, torch::Tensor B, uint64_t sketchSize); - -}; + class BetaCoOFDCPPAlgo : public AMMBench::AbstractCPPAlgo { + protected: + float algoBeta = 1.0; + public: + BetaCoOFDCPPAlgo() { + + } + + ~BetaCoOFDCPPAlgo() { + + } + + /** + * @brief set the alo-specfic config related to one algorithm + */ + virtual void setConfig(INTELLI::ConfigMapPtr cfg); + + /** + * @brief the virtual function provided for outside callers, rewrite in children classes + * @param A the A matrix + * @param B the B matrix + * @param sketchSize the size of sketc or sampling + * @return the output c matrix + */ + virtual torch::Tensor amm(torch::Tensor A, torch::Tensor B, uint64_t sketchSize); + + }; /** * @ingroup AMMBENCH_CppAlgos @@ -49,7 +52,7 @@ class BetaCoOFDCPPAlgo : public AMMBench::AbstractCPPAlgo { * @brief The class to describe a shared pointer to @ref BetaCoOFDCppAlgo */ -typedef std::shared_ptr BetaCoOFDCPPAlgoPtr; + typedef std::shared_ptr BetaCoOFDCPPAlgoPtr; /** * @ingroup AMMBENCH_CppAlgos * @def newBetaCoOFDCppAlgo diff --git a/include/CPPAlgos/CPPAlgoTable.h b/include/CPPAlgos/CPPAlgoTable.h index 2773557b..e0e21a3f 100644 --- a/include/CPPAlgos/CPPAlgoTable.h +++ b/include/CPPAlgos/CPPAlgoTable.h @@ -8,6 +8,7 @@ #include #include + namespace AMMBench { /** * @ingroup AMMBENCH_CppAlgos The algorithms writtrn in c++ @@ -24,33 +25,35 @@ namespace AMMBench { * - mm @ref AbstractCPPAlgo (default matmul) * - crs @ref CRSCPPAlgo (the column-row-sampling, crs) */ -class CPPAlgoTable { - protected: - std::map algoMap; - public: - CPPAlgoTable(); - ~CPPAlgoTable() {} - /** - * @brief To register a new ALGO - * @param anew The new algo - * @param tag THe name tag - */ - void registerNewCppAlgo(AMMBench::AbstractCPPAlgoPtr anew, std::string tag) { - algoMap[tag] = anew; - } + class CPPAlgoTable { + protected: + std::map algoMap; + public: + CPPAlgoTable(); + + ~CPPAlgoTable() {} + + /** + * @brief To register a new ALGO + * @param anew The new algo + * @param tag THe name tag + */ + void registerNewCppAlgo(AMMBench::AbstractCPPAlgoPtr anew, std::string tag) { + algoMap[tag] = anew; + } - /** - * @brief find a dataloader in the table according to its name - * @param name The nameTag of loader - * @return The AbstractCppAlgoPtr, nullptr if not found - */ - AMMBench::AbstractCPPAlgoPtr findCppAlgo(std::string name) { - if (algoMap.count(name)) { - return algoMap[name]; - } - return nullptr; - } -}; + /** + * @brief find a dataloader in the table according to its name + * @param name The nameTag of loader + * @return The AbstractCppAlgoPtr, nullptr if not found + */ + AMMBench::AbstractCPPAlgoPtr findCppAlgo(std::string name) { + if (algoMap.count(name)) { + return algoMap[name]; + } + return nullptr; + } + }; /** * @} */ diff --git a/include/CPPAlgos/CRSCPPAlgo.h b/include/CPPAlgos/CRSCPPAlgo.h index 015a3560..703c5818 100644 --- a/include/CPPAlgos/CRSCPPAlgo.h +++ b/include/CPPAlgos/CRSCPPAlgo.h @@ -5,7 +5,9 @@ #ifndef INTELLISTREAM_INCLUDE_CPPALGOS_CRSCppAlgo_H_ #define INTELLISTREAM_INCLUDE_CPPALGOS_CRSCppAlgo_H_ + #include + namespace AMMBench { /** * @ingroup AMMBENCH_CppAlgos The algorithms written in c++ @@ -16,31 +18,34 @@ namespace AMMBench { * @brief The column row sampling (CRS) class of c++ algos * */ -class CRSCPPAlgo : public AMMBench::AbstractCPPAlgo { - public: - CRSCPPAlgo() { - - } - ~CRSCPPAlgo() { - - } - /** - * @brief the virtual function provided for outside callers, rewrite in children classes - * @param A the A matrix - * @param B the B matrix - * @param sketchSize the size of sketc or sampling - * @return the output c matrix - */ - virtual torch::Tensor amm(torch::Tensor A, torch::Tensor B, uint64_t sketchSize); - -}; + class CRSCPPAlgo : public AMMBench::AbstractCPPAlgo { + public: + CRSCPPAlgo() { + + } + + ~CRSCPPAlgo() { + + } + + /** + * @brief the virtual function provided for outside callers, rewrite in children classes + * @param A the A matrix + * @param B the B matrix + * @param sketchSize the size of sketc or sampling + * @return the output c matrix + */ + virtual torch::Tensor amm(torch::Tensor A, torch::Tensor B, uint64_t sketchSize); + + }; + /** * @ingroup AMMBENCH_CppAlgos * @typedef AbstractMatrixCppAlgoPtr * @brief The class to describe a shared pointer to @ref CRSCppAlgo */ -typedef std::shared_ptr CRSCPPAlgoPtr; + typedef std::shared_ptr CRSCPPAlgoPtr; /** * @ingroup AMMBENCH_CppAlgos * @def newCRSCppAlgo diff --git a/include/CPPAlgos/CRSV2CPPAlgo.h b/include/CPPAlgos/CRSV2CPPAlgo.h index 8a1663f1..12a9cc4b 100644 --- a/include/CPPAlgos/CRSV2CPPAlgo.h +++ b/include/CPPAlgos/CRSV2CPPAlgo.h @@ -18,26 +18,26 @@ namespace AMMBench { * @brief The column row sampling (CRS) class of c++ algos, a second implementation * */ -class CRSV2CPPAlgo : public AMMBench::AbstractCPPAlgo { - public: - CRSV2CPPAlgo() { + class CRSV2CPPAlgo : public AMMBench::AbstractCPPAlgo { + public: + CRSV2CPPAlgo() { - } + } - ~CRSV2CPPAlgo() { + ~CRSV2CPPAlgo() { - } + } - /** - * @brief the virtual function provided for outside callers, rewrite in children classes - * @param A the A matrix - * @param B the B matrix - * @param sketchSize the size of sketc or sampling - * @return the output c matrix - */ - virtual torch::Tensor amm(torch::Tensor A, torch::Tensor B, uint64_t sketchSize); + /** + * @brief the virtual function provided for outside callers, rewrite in children classes + * @param A the A matrix + * @param B the B matrix + * @param sketchSize the size of sketc or sampling + * @return the output c matrix + */ + virtual torch::Tensor amm(torch::Tensor A, torch::Tensor B, uint64_t sketchSize); -}; + }; /** * @ingroup AMMBENCH_CppAlgos @@ -45,7 +45,7 @@ class CRSV2CPPAlgo : public AMMBench::AbstractCPPAlgo { * @brief The class to describe a shared pointer to @ref CRSV2CppAlgo */ -typedef std::shared_ptr CRSV2CPPAlgoPtr; + typedef std::shared_ptr CRSV2CPPAlgoPtr; /** * @ingroup AMMBENCH_CppAlgos * @def newCRSV2CppAlgo diff --git a/include/CPPAlgos/CoOccurringFDCPPAlgo.h b/include/CPPAlgos/CoOccurringFDCPPAlgo.h index f951cf40..7436e1dd 100644 --- a/include/CPPAlgos/CoOccurringFDCPPAlgo.h +++ b/include/CPPAlgos/CoOccurringFDCPPAlgo.h @@ -4,6 +4,7 @@ #ifndef INTELLISTREAM_COOCCURRINGFDCPPALGO_H #define INTELLISTREAM_COOCCURRINGFDCPPALGO_H + #include namespace AMMBench { @@ -16,26 +17,26 @@ namespace AMMBench { * @brief The Co-Occurring FD AMM class of c++ algos * */ -class CoOccurringFDCPPAlgo : public AMMBench::AbstractCPPAlgo { - public: - CoOccurringFDCPPAlgo() { + class CoOccurringFDCPPAlgo : public AMMBench::AbstractCPPAlgo { + public: + CoOccurringFDCPPAlgo() { - } + } - ~CoOccurringFDCPPAlgo() { + ~CoOccurringFDCPPAlgo() { - } + } - /** - * @brief the virtual function provided for outside callers, rewrite in children classes - * @param A the A matrix - * @param B the B matrix - * @param sketchSize the size of sketc or sampling - * @return the output c matrix - */ - virtual torch::Tensor amm(torch::Tensor A, torch::Tensor B, uint64_t sketchSize); + /** + * @brief the virtual function provided for outside callers, rewrite in children classes + * @param A the A matrix + * @param B the B matrix + * @param sketchSize the size of sketc or sampling + * @return the output c matrix + */ + virtual torch::Tensor amm(torch::Tensor A, torch::Tensor B, uint64_t sketchSize); -}; + }; /** * @ingroup AMMBENCH_CppAlgos @@ -43,7 +44,7 @@ class CoOccurringFDCPPAlgo : public AMMBench::AbstractCPPAlgo { * @brief The class to describe a shared pointer to @ref CoOccurringFDCppAlgo */ -typedef std::shared_ptr CoOccurringFDCPPAlgoPtr; + typedef std::shared_ptr CoOccurringFDCPPAlgoPtr; /** * @ingroup AMMBENCH_CppAlgos * @def newCoOccurringFDCppAlgo diff --git a/include/CPPAlgos/CountSketchCPPAlgo.h b/include/CPPAlgos/CountSketchCPPAlgo.h index 097584ac..85ea47ea 100644 --- a/include/CPPAlgos/CountSketchCPPAlgo.h +++ b/include/CPPAlgos/CountSketchCPPAlgo.h @@ -18,26 +18,26 @@ namespace AMMBench { * @brief The counter sketch class of c++ algos * */ -class CountSketchCPPAlgo : public AMMBench::AbstractCPPAlgo { - public: - CountSketchCPPAlgo() { + class CountSketchCPPAlgo : public AMMBench::AbstractCPPAlgo { + public: + CountSketchCPPAlgo() { - } + } - ~CountSketchCPPAlgo() { + ~CountSketchCPPAlgo() { - } + } - /** - * @brief the virtual function provided for outside callers, rewrite in children classes - * @param A the A matrix - * @param B the B matrix - * @param sketchSize the size of sketc or sampling - * @return the output c matrix - */ - torch::Tensor amm(torch::Tensor A, torch::Tensor B, uint64_t sketchSize); + /** + * @brief the virtual function provided for outside callers, rewrite in children classes + * @param A the A matrix + * @param B the B matrix + * @param sketchSize the size of sketc or sampling + * @return the output c matrix + */ + torch::Tensor amm(torch::Tensor A, torch::Tensor B, uint64_t sketchSize); -}; + }; /** * @ingroup AMMBENCH_CppAlgos @@ -45,7 +45,7 @@ class CountSketchCPPAlgo : public AMMBench::AbstractCPPAlgo { * @brief The class to describe a shared pointer to @ref CountSketchCPPAlgo */ -typedef std::shared_ptr CountSketchCPPAlgoPtr; + typedef std::shared_ptr CountSketchCPPAlgoPtr; /** * @ingroup AMMBENCH_CppAlgos * @def newCRSV2CppAlgo diff --git a/include/CPPAlgos/EWSCPPAlgo.h b/include/CPPAlgos/EWSCPPAlgo.h index fda3d4d7..08110d5f 100644 --- a/include/CPPAlgos/EWSCPPAlgo.h +++ b/include/CPPAlgos/EWSCPPAlgo.h @@ -5,6 +5,7 @@ #ifndef INTELLISTREAM_EWSCPPALGO_H #define INTELLISTREAM_EWSCPPALGO_H + #include namespace AMMBench { @@ -17,26 +18,26 @@ namespace AMMBench { * @brief The Element Wise Sampling (EWS) class of c++ algos * */ -class EWSCPPAlgo : public AMMBench::AbstractCPPAlgo { - public: - EWSCPPAlgo() { + class EWSCPPAlgo : public AMMBench::AbstractCPPAlgo { + public: + EWSCPPAlgo() { - } + } - ~EWSCPPAlgo() { + ~EWSCPPAlgo() { - } + } - /** - * @brief the virtual function provided for outside callers, rewrite in children classes - * @param A the A matrix - * @param B the B matrix - * @param sketchSize the size of sketc or sampling - * @return the output c matrix - */ - virtual torch::Tensor amm(torch::Tensor A, torch::Tensor B, uint64_t sketchSize); + /** + * @brief the virtual function provided for outside callers, rewrite in children classes + * @param A the A matrix + * @param B the B matrix + * @param sketchSize the size of sketc or sampling + * @return the output c matrix + */ + virtual torch::Tensor amm(torch::Tensor A, torch::Tensor B, uint64_t sketchSize); -}; + }; /** * @ingroup AMMBENCH_CppAlgos @@ -44,7 +45,7 @@ class EWSCPPAlgo : public AMMBench::AbstractCPPAlgo { * @brief The class to describe a shared pointer to @ref EWSCppAlgo */ -typedef std::shared_ptr EWSCPPAlgoPtr; + typedef std::shared_ptr EWSCPPAlgoPtr; /** * @ingroup AMMBENCH_CppAlgos * @def newEWSCppAlgo diff --git a/include/CPPAlgos/INT8CPPAlgo.h b/include/CPPAlgos/INT8CPPAlgo.h index 31f08320..1117efa6 100644 --- a/include/CPPAlgos/INT8CPPAlgo.h +++ b/include/CPPAlgos/INT8CPPAlgo.h @@ -5,6 +5,7 @@ #ifndef INTELLISTREAM_INCLUDE_CPPALGOS_INT8CPPALGO_H_ #define INTELLISTREAM_INCLUDE_CPPALGOS_INT8CPPALGO_H_ + #include #include #include @@ -12,6 +13,7 @@ #include #include #include + namespace AMMBench { /** * @ingroup AMMBENCH_CppAlgos The algorithms written in c++ @@ -25,64 +27,72 @@ namespace AMMBench { * @note additionally parameters * - fpMode, String, default FP32, can also use INT8 or INT16 */ -class INT8CPPAlgo : public AMMBench::AbstractCPPAlgo { - protected: - /** - * @brief the inline amm under nested loop fp32 - * @param A the A matrix - * @param B the B matrix - * @return the output c matrix - */ - torch::Tensor fp32amm(torch::Tensor A, torch::Tensor B); - /** - * @brief the inline amm under nested loop int8 - * @param A the A matrix - * @param B the B matrix - * @return the output c matrix - */ - torch::Tensor int8amm(torch::Tensor A, torch::Tensor B); - /** - * @brief the inline amm under nested loop int4 - * @param A the A matrix - * @param B the B matrix - * @return the output c matrix - */ - torch::Tensor int4amm(torch::Tensor A, torch::Tensor B); - /** - * @brief the inline amm under nested loop int16 - * @param A the A matrix - * @param B the B matrix - * @return the output c matrix - */ - torch::Tensor int16amm(torch::Tensor A, torch::Tensor B); - std::string fpMode="FP32"; - public: - INT8CPPAlgo() { + class INT8CPPAlgo : public AMMBench::AbstractCPPAlgo { + protected: + /** + * @brief the inline amm under nested loop fp32 + * @param A the A matrix + * @param B the B matrix + * @return the output c matrix + */ + torch::Tensor fp32amm(torch::Tensor A, torch::Tensor B); - } - ~INT8CPPAlgo() { + /** + * @brief the inline amm under nested loop int8 + * @param A the A matrix + * @param B the B matrix + * @return the output c matrix + */ + torch::Tensor int8amm(torch::Tensor A, torch::Tensor B); + + /** + * @brief the inline amm under nested loop int4 + * @param A the A matrix + * @param B the B matrix + * @return the output c matrix + */ + torch::Tensor int4amm(torch::Tensor A, torch::Tensor B); + + /** + * @brief the inline amm under nested loop int16 + * @param A the A matrix + * @param B the B matrix + * @return the output c matrix + */ + torch::Tensor int16amm(torch::Tensor A, torch::Tensor B); + + std::string fpMode = "FP32"; + public: + INT8CPPAlgo() { + + } + + ~INT8CPPAlgo() { + + } + + /** + * @brief the virtual function provided for outside callers, rewrite in children classes + * @param A the A matrix + * @param B the B matrix + * @param sketchSize the size of sketc or sampling + * @return the output c matrix + */ + virtual torch::Tensor amm(torch::Tensor A, torch::Tensor B, uint64_t sketchSize); + + /** + * @brief set the alo-specfic config related to one algorithm + */ + virtual void setConfig(INTELLI::ConfigMapPtr cfg); + }; - } - /** - * @brief the virtual function provided for outside callers, rewrite in children classes - * @param A the A matrix - * @param B the B matrix - * @param sketchSize the size of sketc or sampling - * @return the output c matrix - */ - virtual torch::Tensor amm(torch::Tensor A, torch::Tensor B, uint64_t sketchSize); - /** - * @brief set the alo-specfic config related to one algorithm - */ - virtual void setConfig(INTELLI::ConfigMapPtr cfg); -}; /** * @ingroup AMMBENCH_CppAlgos * @typedef INT8MatrixCppAlgoPtr * @brief The class to describe a shared pointer to @ref INT8CPPAlgo */ -typedef std::shared_ptr INT8CPPAlgoPtr; + typedef std::shared_ptr INT8CPPAlgoPtr; /** * @ingroup AMMBENCH_CppAlgos * @def newINT8CppAlgo diff --git a/include/CPPAlgos/SMPPCACPPAlgo.h b/include/CPPAlgos/SMPPCACPPAlgo.h index d5dbfc99..18ff5be1 100644 --- a/include/CPPAlgos/SMPPCACPPAlgo.h +++ b/include/CPPAlgos/SMPPCACPPAlgo.h @@ -5,7 +5,9 @@ #ifndef INTELLISTREAM_INCLUDE_CPPALGOS_SMPPCACppAlgo_H_ #define INTELLISTREAM_INCLUDE_CPPALGOS_SMPPCACppAlgo_H_ + #include + namespace AMMBench { /** * @ingroup AMMBENCH_CppAlgos The algorithms written in c++ @@ -16,31 +18,34 @@ namespace AMMBench { * @brief sketch scaled JL class of c++ algos * */ -class SMPPCACPPAlgo : public AMMBench::AbstractCPPAlgo { - public: - SMPPCACPPAlgo() { - - } - ~SMPPCACPPAlgo() { - - } - /** - * @brief the virtual function provided for outside callers, rewrite in children classes - * @param A the A matrix - * @param B the B matrix - * @param sketchSize the size of sketch - * @return the output c matrix - */ - virtual torch::Tensor amm(torch::Tensor A, torch::Tensor B, uint64_t sketchSize); - -}; + class SMPPCACPPAlgo : public AMMBench::AbstractCPPAlgo { + public: + SMPPCACPPAlgo() { + + } + + ~SMPPCACPPAlgo() { + + } + + /** + * @brief the virtual function provided for outside callers, rewrite in children classes + * @param A the A matrix + * @param B the B matrix + * @param sketchSize the size of sketch + * @return the output c matrix + */ + virtual torch::Tensor amm(torch::Tensor A, torch::Tensor B, uint64_t sketchSize); + + }; + /** * @ingroup AMMBENCH_CppAlgos * @typedef AbstractMatrixCppAlgoPtr * @brief The class to describe a shared pointer to @ref SMPPCACppAlgo */ -typedef std::shared_ptr SMPPCACPPAlgoPtr; + typedef std::shared_ptr SMPPCACPPAlgoPtr; /** * @ingroup AMMBENCH_CppAlgos * @def newSMPPCACppAlgo diff --git a/include/CPPAlgos/TugOfWarCPPAlgo.h b/include/CPPAlgos/TugOfWarCPPAlgo.h index 25c155d1..5304214d 100644 --- a/include/CPPAlgos/TugOfWarCPPAlgo.h +++ b/include/CPPAlgos/TugOfWarCPPAlgo.h @@ -19,35 +19,36 @@ namespace AMMBench { * @note parameters * - algoDelta Double, the delta parameter in this algo, default 0.02 */ -class TugOfWarCPPAlgo : public AMMBench::AbstractCPPAlgo { - double algoDelta = 0.02; + class TugOfWarCPPAlgo : public AMMBench::AbstractCPPAlgo { + double algoDelta = 0.02; - public: - TugOfWarCPPAlgo() { + public: + TugOfWarCPPAlgo() { - } + } - ~TugOfWarCPPAlgo() { + ~TugOfWarCPPAlgo() { - } - /** - * @brief set the algo-specfic config related to one algorithm - */ - virtual void setConfig(INTELLI::ConfigMapPtr cfg); + } - /** - * @brief the virtual function provided for outside callers, rewrite in children classes - * @param A the A matrix - * @param B the B matrix - * @param sketchSize the size of sketc or sampling - * @return the output c matrix - */ - virtual torch::Tensor amm(torch::Tensor A, torch::Tensor B, uint64_t sketchSize); + /** + * @brief set the algo-specfic config related to one algorithm + */ + virtual void setConfig(INTELLI::ConfigMapPtr cfg); - private: - torch::Tensor generateTugOfWarMatrix(int64_t m, int64_t n); + /** + * @brief the virtual function provided for outside callers, rewrite in children classes + * @param A the A matrix + * @param B the B matrix + * @param sketchSize the size of sketc or sampling + * @return the output c matrix + */ + virtual torch::Tensor amm(torch::Tensor A, torch::Tensor B, uint64_t sketchSize); -}; + private: + torch::Tensor generateTugOfWarMatrix(int64_t m, int64_t n); + + }; /** * @ingroup AMMBENCH_CppAlgos @@ -55,7 +56,7 @@ class TugOfWarCPPAlgo : public AMMBench::AbstractCPPAlgo { * @brief The class to describe a shared pointer to @ref TugOfWarCppAlgo */ -typedef std::shared_ptr TugOfWarCPPAlgoPtr; + typedef std::shared_ptr TugOfWarCPPAlgoPtr; /** * @ingroup AMMBENCH_CppAlgos * @def newTugOfWarCppAlgo diff --git a/include/CPPAlgos/WeightedCRCPPAlgo.h b/include/CPPAlgos/WeightedCRCPPAlgo.h index 20ca1d5b..58f5f630 100644 --- a/include/CPPAlgos/WeightedCRCPPAlgo.h +++ b/include/CPPAlgos/WeightedCRCPPAlgo.h @@ -5,7 +5,9 @@ #ifndef INTELLISTREAM_INCLUDE_CPPALGOS_WeightedCRCPPAlgo_H_ #define INTELLISTREAM_INCLUDE_CPPALGOS_WeightedCRCPPAlgo_H_ + #include + namespace AMMBench { /** * @ingroup AMMBENCH_CPPAlgos The algorithms writtrn in c++ @@ -16,31 +18,34 @@ namespace AMMBench { * @brief The weighted cloumn row sampling class of c++ algos * */ -class WeightedCRCPPAlgo : public AMMBench::AbstractCPPAlgo { - public: - WeightedCRCPPAlgo() { - - } - ~WeightedCRCPPAlgo() { - - } - /** - * @brief the virtual function provided for outside callers, rewrite in children classes - * @param A the A matrix - * @param B the B matrix - * @param sketchSize the size of sketc or sampling - * @return the output c matrix - */ - virtual torch::Tensor amm(torch::Tensor A, torch::Tensor B, uint64_t sketchSize); - -}; + class WeightedCRCPPAlgo : public AMMBench::AbstractCPPAlgo { + public: + WeightedCRCPPAlgo() { + + } + + ~WeightedCRCPPAlgo() { + + } + + /** + * @brief the virtual function provided for outside callers, rewrite in children classes + * @param A the A matrix + * @param B the B matrix + * @param sketchSize the size of sketc or sampling + * @return the output c matrix + */ + virtual torch::Tensor amm(torch::Tensor A, torch::Tensor B, uint64_t sketchSize); + + }; + /** * @ingroup AMMBENCH_CPPAlgos * @typedef AbstractMatrixCPPAlgoPtr * @brief The class to describe a shared pointer to @ref WeightedCRCPPAlgo */ -typedef std::shared_ptr WeightedCRCPPAlgoPtr; + typedef std::shared_ptr WeightedCRCPPAlgoPtr; /** * @ingroup AMMBENCH_CPPAlgos * @def newWeightedCRCPPAlgo diff --git a/include/MatrixLoader/AbstractMatrixLoader.h b/include/MatrixLoader/AbstractMatrixLoader.h index 57886227..f358958e 100644 --- a/include/MatrixLoader/AbstractMatrixLoader.h +++ b/include/MatrixLoader/AbstractMatrixLoader.h @@ -10,6 +10,7 @@ #include #include #include + namespace AMMBench { /** * @ingroup AMMBENCH_MatrixLOADER @@ -30,36 +31,40 @@ namespace AMMBench { * - call @ref setConfig, this function will also generate the tensor A and B correspondingly * - call @ref getA and @ref getB (assuming we are benchmarking torch.mm(A,B)) */ -class AbstractMatrixLoader { - public: - AbstractMatrixLoader() = default; + class AbstractMatrixLoader { + public: + AbstractMatrixLoader() = default; + + ~AbstractMatrixLoader() = default; + + /** + * @brief Set the GLOBAL config map related to this loader + * @param cfg The config map + * @return bool whether the config is successfully set + * @note + */ + virtual bool setConfig(INTELLI::ConfigMapPtr cfg); + + /** + * @brief get the A matrix + * @return the generated A matrix + */ + virtual torch::Tensor getA(); + + /** + * @brief get the B matrix + * @return the generated B matrix + */ + virtual torch::Tensor getB(); + }; - ~AbstractMatrixLoader() = default; - /** - * @brief Set the GLOBAL config map related to this loader - * @param cfg The config map - * @return bool whether the config is successfully set - * @note - */ - virtual bool setConfig(INTELLI::ConfigMapPtr cfg); - /** - * @brief get the A matrix - * @return the generated A matrix - */ - virtual torch::Tensor getA(); - /** - * @brief get the B matrix - * @return the generated B matrix - */ - virtual torch::Tensor getB(); -}; /** * @ingroup AMMBENCH_MatrixLOADER_abstract * @typedef AbstractMatrixLoaderPtr * @brief The class to describe a shared pointer to @ref AbstractMatrixLoader */ -typedef std::shared_ptr AbstractMatrixLoaderPtr; + typedef std::shared_ptr AbstractMatrixLoaderPtr; /** * @ingroup AMMBENCH_MatrixLOADER_abstract * @def newAbstractMatrixLoader diff --git a/include/MatrixLoader/BetaMatrixLoader.h b/include/MatrixLoader/BetaMatrixLoader.h index e0fc7210..cce5ceb0 100644 --- a/include/MatrixLoader/BetaMatrixLoader.h +++ b/include/MatrixLoader/BetaMatrixLoader.h @@ -4,7 +4,9 @@ #ifndef INTELLISTREAM_BETAMATRIXLOADER_H #define INTELLISTREAM_BETAMATRIXLOADER_H + #include + namespace AMMBench { /** * @ingroup AMMBENCH_MatrixLOADER @@ -39,19 +41,23 @@ namespace AMMBench { torch::Tensor A, B; uint64_t aRow, aCol, bCol, seed; double a, b; + /** * @brief Inline logic of reading a config file * @param cfg the config */ void paraseConfig(INTELLI::ConfigMapPtr cfg); + /** * @brief inline logic of generating A and B */ void generateAB(); + public: BetaMatrixLoader() = default; ~BetaMatrixLoader() = default; + /** * @brief Set the GLOBAL config map related to this loader * @param cfg The config map @@ -59,17 +65,20 @@ namespace AMMBench { * @note */ virtual bool setConfig(INTELLI::ConfigMapPtr cfg); + /** * @brief get the A matrix * @return the generated A matrix */ virtual torch::Tensor getA(); + /** * @brief get the B matrix * @return the generated B matrix */ virtual torch::Tensor getB(); }; + /** * @ingroup AMMBENCH_MatrixLOADER_Beta * @typedef BetaMatrixLoaderPtr diff --git a/include/MatrixLoader/BinomialMatrixLoader.h b/include/MatrixLoader/BinomialMatrixLoader.h index a7096bff..51fc4df3 100644 --- a/include/MatrixLoader/BinomialMatrixLoader.h +++ b/include/MatrixLoader/BinomialMatrixLoader.h @@ -4,7 +4,9 @@ #ifndef INTELLISTREAM_BINOMIALMATRIXLOADER_H #define INTELLISTREAM_BINOMIALMATRIXLOADER_H + #include + namespace AMMBench { /** * @ingroup AMMBENCH_MatrixLOADER @@ -39,19 +41,23 @@ namespace AMMBench { torch::Tensor A, B; uint64_t aRow, aCol, bCol, seed, trials; double probability; + /** * @brief Inline logic of reading a config file * @param cfg the config */ void paraseConfig(INTELLI::ConfigMapPtr cfg); + /** * @brief inline logic of generating A and B */ void generateAB(); + public: BinomialMatrixLoader() = default; ~BinomialMatrixLoader() = default; + /** * @brief Set the GLOBAL config map related to this loader * @param cfg The config map @@ -59,17 +65,20 @@ namespace AMMBench { * @note */ virtual bool setConfig(INTELLI::ConfigMapPtr cfg); + /** * @brief get the A matrix * @return the generated A matrix */ virtual torch::Tensor getA(); + /** * @brief get the B matrix * @return the generated B matrix */ virtual torch::Tensor getB(); }; + /** * @ingroup AMMBENCH_MatrixLOADER_Binomial * @typedef BinomialMatrixLoaderPtr diff --git a/include/MatrixLoader/ExponentialMatrixLoader.h b/include/MatrixLoader/ExponentialMatrixLoader.h index c7032acb..1fc03629 100644 --- a/include/MatrixLoader/ExponentialMatrixLoader.h +++ b/include/MatrixLoader/ExponentialMatrixLoader.h @@ -4,7 +4,9 @@ #ifndef INTELLISTREAM_EXPONENTIALMATRIXLOADER_H #define INTELLISTREAM_EXPONENTIALMATRIXLOADER_H + #include + namespace AMMBench { /** * @ingroup AMMBENCH_MatrixLOADER @@ -36,19 +38,23 @@ namespace AMMBench { protected: torch::Tensor A, B; uint64_t aRow, aCol, bCol, seed; + /** * @brief Inline logic of reading a config file * @param cfg the config */ void paraseConfig(INTELLI::ConfigMapPtr cfg); + /** * @brief inline logic of generating A and B */ void generateAB(); + public: ExponentialMatrixLoader() = default; ~ExponentialMatrixLoader() = default; + /** * @brief Set the GLOBAL config map related to this loader * @param cfg The config map @@ -56,17 +62,20 @@ namespace AMMBench { * @note */ virtual bool setConfig(INTELLI::ConfigMapPtr cfg); + /** * @brief get the A matrix * @return the generated A matrix */ virtual torch::Tensor getA(); + /** * @brief get the B matrix * @return the generated B matrix */ virtual torch::Tensor getB(); }; + /** * @ingroup AMMBENCH_MatrixLOADER_Exponential * @typedef ExponentialMatrixLoaderPtr diff --git a/include/MatrixLoader/GaussianMatrixLoader.h b/include/MatrixLoader/GaussianMatrixLoader.h index 5220b837..d88cced1 100644 --- a/include/MatrixLoader/GaussianMatrixLoader.h +++ b/include/MatrixLoader/GaussianMatrixLoader.h @@ -4,7 +4,9 @@ #ifndef INTELLISTREAM_GAUSSIANMATRIXLOADER_H #define INTELLISTREAM_GAUSSIANMATRIXLOADER_H + #include + namespace AMMBench { /** * @ingroup AMMBENCH_MatrixLOADER @@ -36,19 +38,23 @@ namespace AMMBench { protected: torch::Tensor A, B; uint64_t aRow, aCol, bCol, seed; + /** * @brief Inline logic of reading a config file * @param cfg the config */ void paraseConfig(INTELLI::ConfigMapPtr cfg); + /** * @brief inline logic of generating A and B */ void generateAB(); + public: GaussianMatrixLoader() = default; ~GaussianMatrixLoader() = default; + /** * @brief Set the GLOBAL config map related to this loader * @param cfg The config map @@ -56,17 +62,20 @@ namespace AMMBench { * @note */ virtual bool setConfig(INTELLI::ConfigMapPtr cfg); + /** * @brief get the A matrix * @return the generated A matrix */ virtual torch::Tensor getA(); + /** * @brief get the B matrix * @return the generated B matrix */ virtual torch::Tensor getB(); }; + /** * @ingroup AMMBENCH_MatrixLOADER_Gaussian * @typedef GaussianMatrixLoaderPtr diff --git a/include/MatrixLoader/MatrixLoaderTable.h b/include/MatrixLoader/MatrixLoaderTable.h index 7fbdbc58..19b687dd 100644 --- a/include/MatrixLoader/MatrixLoaderTable.h +++ b/include/MatrixLoader/MatrixLoaderTable.h @@ -4,8 +4,10 @@ #ifndef INTELLISTREAM_INCLUDE_MATRIXLOADER_MATRIXLOADERTABLE_H_ #define INTELLISTREAM_INCLUDE_MATRIXLOADER_MATRIXLOADERTABLE_H_ + #include #include + namespace AMMBench { /** * @ingroup AMMBENCH_MatrixLOADER @@ -27,53 +29,54 @@ namespace AMMBench { * - random @ref RandomMatrixLoader * - sparse @ref SparseMatrixLoader */ -class MatrixLoaderTable { - protected: - std::map loaderMap; - public: - /** - * @brief The constructing function - * @note If new MatrixLoader wants to be included by default, please revise the following in *.cpp - */ - MatrixLoaderTable(); + class MatrixLoaderTable { + protected: + std::map loaderMap; + public: + /** + * @brief The constructing function + * @note If new MatrixLoader wants to be included by default, please revise the following in *.cpp + */ + MatrixLoaderTable(); - ~MatrixLoaderTable() { - } + ~MatrixLoaderTable() { + } - /** - * @brief To register a new loader - * @param onew The new operator - * @param tag THe name tag - */ - void registerNewDataLoader(AMMBench::AbstractMatrixLoaderPtr dnew, std::string tag) { - loaderMap[tag] = dnew; - } + /** + * @brief To register a new loader + * @param onew The new operator + * @param tag THe name tag + */ + void registerNewDataLoader(AMMBench::AbstractMatrixLoaderPtr dnew, std::string tag) { + loaderMap[tag] = dnew; + } - /** - * @brief find a dataloader in the table according to its name - * @param name The nameTag of loader - * @return The MatrixLoader, nullptr if not found - */ - AMMBench::AbstractMatrixLoaderPtr findMatrixLoader(std::string name) { - if (loaderMap.count(name)) { - return loaderMap[name]; - } - return nullptr; - } - /** - * @ingroup AMMBENCH_MatrixLOADER_Table - * @typedef MatrixLoaderTablePtr - * @brief The class to describe a shared pointer to @ref MatrixLoaderTable + /** + * @brief find a dataloader in the table according to its name + * @param name The nameTag of loader + * @return The MatrixLoader, nullptr if not found + */ + AMMBench::AbstractMatrixLoaderPtr findMatrixLoader(std::string name) { + if (loaderMap.count(name)) { + return loaderMap[name]; + } + return nullptr; + } - */ - typedef std::shared_ptr MatrixLoaderTablePtr; + /** + * @ingroup AMMBENCH_MatrixLOADER_Table + * @typedef MatrixLoaderTablePtr + * @brief The class to describe a shared pointer to @ref MatrixLoaderTable + + */ + typedef std::shared_ptr MatrixLoaderTablePtr; /** * @ingroup AMMBENCH_MatrixLOADER_Table * @def newMatrixLoaderTable * @brief (Macro) To creat a new @ref MatrixLoaderTable under shared pointer. */ #define newMatrixLoaderTable std::make_shared -}; + }; /** * @} */ diff --git a/include/MatrixLoader/PoissonMatrixLoader.h b/include/MatrixLoader/PoissonMatrixLoader.h index 579fc7ce..4894056c 100644 --- a/include/MatrixLoader/PoissonMatrixLoader.h +++ b/include/MatrixLoader/PoissonMatrixLoader.h @@ -4,7 +4,9 @@ #ifndef INTELLISTREAM_POISSONMATRIXLOADER_H #define INTELLISTREAM_POISSONMATRIXLOADER_H + #include + namespace AMMBench { /** * @ingroup AMMBENCH_MatrixLOADER @@ -36,19 +38,23 @@ namespace AMMBench { protected: torch::Tensor A, B; uint64_t aRow, aCol, bCol, seed; + /** * @brief Inline logic of reading a config file * @param cfg the config */ void paraseConfig(INTELLI::ConfigMapPtr cfg); + /** * @brief inline logic of generating A and B */ void generateAB(); + public: PoissonMatrixLoader() = default; ~PoissonMatrixLoader() = default; + /** * @brief Set the GLOBAL config map related to this loader * @param cfg The config map @@ -56,17 +62,20 @@ namespace AMMBench { * @note */ virtual bool setConfig(INTELLI::ConfigMapPtr cfg); + /** * @brief get the A matrix * @return the generated A matrix */ virtual torch::Tensor getA(); + /** * @brief get the B matrix * @return the generated B matrix */ virtual torch::Tensor getB(); }; + /** * @ingroup AMMBENCH_MatrixLOADER_Poisson * @typedef PoissonMatrixLoaderPtr diff --git a/include/MatrixLoader/RandomMatrixLoader.h b/include/MatrixLoader/RandomMatrixLoader.h index b2466c02..8d44391e 100644 --- a/include/MatrixLoader/RandomMatrixLoader.h +++ b/include/MatrixLoader/RandomMatrixLoader.h @@ -5,7 +5,9 @@ #ifndef INTELLISTREAM_INCLUDE_MATRIXLOADER_RANDOMMATRIXLOADER_H_ #define INTELLISTREAM_INCLUDE_MATRIXLOADER_RANDOMMATRIXLOADER_H_ + #include + namespace AMMBench { /** * @ingroup AMMBENCH_MatrixLOADER @@ -33,48 +35,55 @@ namespace AMMBench { * @note: default name tags * "random": @ref RandomMatrixLoader */ -class RandomMatrixLoader : public AbstractMatrixLoader { - protected: - torch::Tensor A, B; - uint64_t aRow, aCol, bCol, seed; - /** - * @brief Inline logic of reading a config file - * @param cfg the config - */ - void paraseConfig(INTELLI::ConfigMapPtr cfg); - /** - * @brief inline logic of generating A and B - */ - void generateAB(); - public: - RandomMatrixLoader() = default; + class RandomMatrixLoader : public AbstractMatrixLoader { + protected: + torch::Tensor A, B; + uint64_t aRow, aCol, bCol, seed; + + /** + * @brief Inline logic of reading a config file + * @param cfg the config + */ + void paraseConfig(INTELLI::ConfigMapPtr cfg); + + /** + * @brief inline logic of generating A and B + */ + void generateAB(); + + public: + RandomMatrixLoader() = default; + + ~RandomMatrixLoader() = default; + + /** + * @brief Set the GLOBAL config map related to this loader + * @param cfg The config map + * @return bool whether the config is successfully set + * @note + */ + virtual bool setConfig(INTELLI::ConfigMapPtr cfg); + + /** + * @brief get the A matrix + * @return the generated A matrix + */ + virtual torch::Tensor getA(); + + /** + * @brief get the B matrix + * @return the generated B matrix + */ + virtual torch::Tensor getB(); + }; - ~RandomMatrixLoader() = default; - /** - * @brief Set the GLOBAL config map related to this loader - * @param cfg The config map - * @return bool whether the config is successfully set - * @note - */ - virtual bool setConfig(INTELLI::ConfigMapPtr cfg); - /** - * @brief get the A matrix - * @return the generated A matrix - */ - virtual torch::Tensor getA(); - /** - * @brief get the B matrix - * @return the generated B matrix - */ - virtual torch::Tensor getB(); -}; /** * @ingroup AMMBENCH_MatrixLOADER_Random * @typedef RandomMatrixLoaderPtr * @brief The class to describe a shared pointer to @ref RandomMatrixLoader */ -typedef std::shared_ptr RandomMatrixLoaderPtr; + typedef std::shared_ptr RandomMatrixLoaderPtr; /** * @ingroup AMMBENCH_MatrixLOADER_Random * @def newRandomMatrixLoader diff --git a/include/MatrixLoader/SparseMatrixLoader.h b/include/MatrixLoader/SparseMatrixLoader.h index 50e632d2..afda758b 100644 --- a/include/MatrixLoader/SparseMatrixLoader.h +++ b/include/MatrixLoader/SparseMatrixLoader.h @@ -5,7 +5,9 @@ #ifndef INTELLISTREAM_INCLUDE_MATRIXLOADER_SparseMATRIXLOADER_H_ #define INTELLISTREAM_INCLUDE_MATRIXLOADER_SparseMATRIXLOADER_H_ + #include + namespace AMMBench { /** * @ingroup AMMBENCH_MatrixLOADER @@ -37,58 +39,65 @@ namespace AMMBench { * @note: default name tags * "sparse": @ref SparseMatrixLoader */ -class SparseMatrixLoader : public AbstractMatrixLoader { - protected: - torch::Tensor A, B; - uint64_t aRow, aCol, bCol, seed, aReduce, bReduce; - double aDensity, bDensity; + class SparseMatrixLoader : public AbstractMatrixLoader { + protected: + torch::Tensor A, B; + uint64_t aRow, aCol, bCol, seed, aReduce, bReduce; + double aDensity, bDensity; + + /** + * @brief Inline logic of generate the sparse matrix + * @param m the rows + * @param n the cols + * @param density the density in 0~1 + * @param reduceRows the number of rows to be reduced + */ + torch::Tensor genSparseMatrix(uint64_t m, uint64_t n, double density, uint64_t reduceRows); + + /** + * @brief Inline logic of reading a config file + * @param cfg the config + */ + void paraseConfig(INTELLI::ConfigMapPtr cfg); + + /** + * @brief inline logic of generating A and B + */ + void generateAB(); + + public: + SparseMatrixLoader() = default; + + ~SparseMatrixLoader() = default; + + /** + * @brief Set the GLOBAL config map related to this loader + * @param cfg The config map + * @return bool whether the config is successfully set + * @note + */ + virtual bool setConfig(INTELLI::ConfigMapPtr cfg); + + /** + * @brief get the A matrix + * @return the generated A matrix + */ + virtual torch::Tensor getA(); - /** - * @brief Inline logic of generate the sparse matrix - * @param m the rows - * @param n the cols - * @param density the density in 0~1 - * @param reduceRows the number of rows to be reduced - */ - torch::Tensor genSparseMatrix(uint64_t m, uint64_t n, double density, uint64_t reduceRows); - /** - * @brief Inline logic of reading a config file - * @param cfg the config - */ - void paraseConfig(INTELLI::ConfigMapPtr cfg); - /** - * @brief inline logic of generating A and B - */ - void generateAB(); - public: - SparseMatrixLoader() = default; + /** + * @brief get the B matrix + * @return the generated B matrix + */ + virtual torch::Tensor getB(); + }; - ~SparseMatrixLoader() = default; - /** - * @brief Set the GLOBAL config map related to this loader - * @param cfg The config map - * @return bool whether the config is successfully set - * @note - */ - virtual bool setConfig(INTELLI::ConfigMapPtr cfg); - /** - * @brief get the A matrix - * @return the generated A matrix - */ - virtual torch::Tensor getA(); - /** - * @brief get the B matrix - * @return the generated B matrix - */ - virtual torch::Tensor getB(); -}; /** * @ingroup AMMBENCH_MatrixLOADER_Sparse * @typedef SparseMatrixLoaderPtr * @brief The class to describe a shared pointer to @ref SparseMatrixLoader */ -typedef std::shared_ptr SparseMatrixLoaderPtr; + typedef std::shared_ptr SparseMatrixLoaderPtr; /** * @ingroup AMMBENCH_MatrixLOADER_Sparse * @def newSparseMatrixLoader diff --git a/include/Parallelization/BlockPartitionRunner.h b/include/Parallelization/BlockPartitionRunner.h index b4d5e11d..d6a68bf8 100644 --- a/include/Parallelization/BlockPartitionRunner.h +++ b/include/Parallelization/BlockPartitionRunner.h @@ -5,6 +5,7 @@ #ifndef INTELLISTREAM_INCLUDE_PARALLELIZATION_BLOCKPARTITIONRUNNER_H_ #define INTELLISTREAM_INCLUDE_PARALLELIZATION_BLOCKPARTITIONRUNNER_H_ + #include #include #include @@ -12,10 +13,11 @@ #include #include #include + namespace AMMBench { #define newTensor make_shared -typedef std::shared_ptr TensorPtr; + typedef std::shared_ptr TensorPtr; /** * @ingroup AMMBENCH_PARALLELIZATION * @{ @@ -26,76 +28,85 @@ typedef std::shared_ptr TensorPtr; * @ingroup PARTITION_RUNNER * @brief The basic partition worker */ -class BlockPartitionWorker : public INTELLI::AbstractC20Thread { - protected: - virtual void inlineMain(); - AMMBench::CPPAlgoTable cppAlgoTable; - struct timeval tstart, tend; - uint64_t useCPP = 0; - uint64_t osScheduling = 0; - AMMBench::AbstractCPPAlgoPtr cppAlgoPtr = nullptr; - /** - * @brief Input matrix A - */ - TensorPtr matA = nullptr; // Input matrix A - /** - * @brief Input matrix B - */ - TensorPtr matB = nullptr; // Input matrix B - /** - * @brief OUTput matrix C - */ - TensorPtr matC = nullptr; // Output matrix C - - INTELLI::ConfigMapPtr cfg; - torch::jit::script::Module module; - uint64_t sketchDimension = 0; - int coreBind; - public: - torch::Tensor irC, subA; - uint64_t startRow = 0; // Start row index for the assigned range - uint64_t endRow = 0; // End row index (exclusive) for the assigned range - - BlockPartitionWorker() { - - } - /** - * @brief set the config map - * @param _cfg - */ - void setConfig(INTELLI::ConfigMapPtr _cfg); - /** - * @brief set the pointer to A,B,C matrix - */ - void setABC(TensorPtr A, TensorPtr B, TensorPtr C); - /** - * @brief set work parmeters - * @param aStart The start row in A - * @param aEnd The end row in A - * @param mycore the core to be binded - */ - void setWorkParameters(uint64_t aStart, uint64_t aEnd, int mycore); - void setCoreBInd(int cno) { - coreBind = cno; - } - ~BlockPartitionWorker() { - - } - uint64_t getElapsedTime(); - /** - * @brief to export the algorithm breakdown - * @note only valid for c++ algo - * @return the key-value table breakdown in ConfigMapPtr; - */ - virtual INTELLI::ConfigMapPtr getBreakDown(); -}; + class BlockPartitionWorker : public INTELLI::AbstractC20Thread { + protected: + virtual void inlineMain(); + + AMMBench::CPPAlgoTable cppAlgoTable; + struct timeval tstart, tend; + uint64_t useCPP = 0; + uint64_t osScheduling = 0; + AMMBench::AbstractCPPAlgoPtr cppAlgoPtr = nullptr; + /** + * @brief Input matrix A + */ + TensorPtr matA = nullptr; // Input matrix A + /** + * @brief Input matrix B + */ + TensorPtr matB = nullptr; // Input matrix B + /** + * @brief OUTput matrix C + */ + TensorPtr matC = nullptr; // Output matrix C + + INTELLI::ConfigMapPtr cfg; + torch::jit::script::Module module; + uint64_t sketchDimension = 0; + int coreBind; + public: + torch::Tensor irC, subA; + uint64_t startRow = 0; // Start row index for the assigned range + uint64_t endRow = 0; // End row index (exclusive) for the assigned range + + BlockPartitionWorker() { + + } + + /** + * @brief set the config map + * @param _cfg + */ + void setConfig(INTELLI::ConfigMapPtr _cfg); + + /** + * @brief set the pointer to A,B,C matrix + */ + void setABC(TensorPtr A, TensorPtr B, TensorPtr C); + + /** + * @brief set work parmeters + * @param aStart The start row in A + * @param aEnd The end row in A + * @param mycore the core to be binded + */ + void setWorkParameters(uint64_t aStart, uint64_t aEnd, int mycore); + + void setCoreBInd(int cno) { + coreBind = cno; + } + + ~BlockPartitionWorker() { + + } + + uint64_t getElapsedTime(); + + /** + * @brief to export the algorithm breakdown + * @note only valid for c++ algo + * @return the key-value table breakdown in ConfigMapPtr; + */ + virtual INTELLI::ConfigMapPtr getBreakDown(); + }; /** * @ingroup PARTITION_RUNNER * @def newBlockPartitionWorker * @brief (Macro) To creat a new @ref BlockPartitionWorker under shared pointer. */ #define newBlockPartitionWorker std::make_shared -typedef std::shared_ptr BlockPartitionWorkerPtr; + typedef std::shared_ptr BlockPartitionWorkerPtr; + /** * @class BlockPartitionRunner Parallelization/BlockPartitionRunner.h * @ingroup PARTITION_RUNNER @@ -111,76 +122,83 @@ typedef std::shared_ptr BlockPartitionWorkerPtr; * - call @ref runAMM and return result * - call @ref getElapsedTime */ -class BlockPartitionRunner { - - protected: - INTELLI::ConfigMapPtr cfg; - uint64_t threads = 0; - /** - * @brief Input matrix A - */ - TensorPtr matA = nullptr; // Input matrix A - /** - * @brief Input matrix B - */ - TensorPtr matB = nullptr; // Input matrix B - /** - * @brief OUTput matrix C - */ - TensorPtr matC = nullptr; // Output matrix C - std::vector workers; - /** - * @brief special bind of first core, if need - */ - uint64_t firstCoreBind = 0; - public: - BlockPartitionRunner() {} - ~BlockPartitionRunner() {} - /** - * @brief set the config map - * @param _cfg - */ - void setConfig(INTELLI::ConfigMapPtr _cfg); - /** - * @brief create the A,B,C matrix and pass it to all workers - * @param A The A matrix - * @param B The B matrix - * @warnning call after @ref setConfig - */ - void createABC(torch::Tensor A, torch::Tensor B); - - /** -* @brief run a parallel forward of A,B, and return C -* @return C=matA*matB -* @warnning call after @ref createABC -*/ - torch::Tensor parallelForward(); - /** - * @brief conducte the multithread AMM and return - * @param A The A matrix - * @param B The B matrix - * @return The AMM(A,B) - * @warnning call after @ref setConfig - */ - torch::Tensor runAMM(torch::Tensor A, torch::Tensor B); - /** - * @brief get the elapsed time of multithread running - * @return the elapsed time - * @note Exclude the overhead of cleaning thread states such as loaded module - */ - uint64_t getElapsedTime(); - /** - * @brief append the running information of each thread to the result csv - * @param ru The result csv to be appended - */ - void appendThreadInfo(INTELLI::ConfigMapPtr ru); - /** - * @brief to export the algorithm breakdown - * @note only valid for c++ algo - * @return the key-value table breakdown in ConfigMapPtr; - */ - virtual INTELLI::ConfigMapPtr getBreakDown(); -}; + class BlockPartitionRunner { + + protected: + INTELLI::ConfigMapPtr cfg; + uint64_t threads = 0; + /** + * @brief Input matrix A + */ + TensorPtr matA = nullptr; // Input matrix A + /** + * @brief Input matrix B + */ + TensorPtr matB = nullptr; // Input matrix B + /** + * @brief OUTput matrix C + */ + TensorPtr matC = nullptr; // Output matrix C + std::vector workers; + /** + * @brief special bind of first core, if need + */ + uint64_t firstCoreBind = 0; + public: + BlockPartitionRunner() {} + + ~BlockPartitionRunner() {} + + /** + * @brief set the config map + * @param _cfg + */ + void setConfig(INTELLI::ConfigMapPtr _cfg); + + /** + * @brief create the A,B,C matrix and pass it to all workers + * @param A The A matrix + * @param B The B matrix + * @warnning call after @ref setConfig + */ + void createABC(torch::Tensor A, torch::Tensor B); + + /** + * @brief run a parallel forward of A,B, and return C + * @return C=matA*matB + * @warnning call after @ref createABC + */ + torch::Tensor parallelForward(); + + /** + * @brief conducte the multithread AMM and return + * @param A The A matrix + * @param B The B matrix + * @return The AMM(A,B) + * @warnning call after @ref setConfig + */ + torch::Tensor runAMM(torch::Tensor A, torch::Tensor B); + + /** + * @brief get the elapsed time of multithread running + * @return the elapsed time + * @note Exclude the overhead of cleaning thread states such as loaded module + */ + uint64_t getElapsedTime(); + + /** + * @brief append the running information of each thread to the result csv + * @param ru The result csv to be appended + */ + void appendThreadInfo(INTELLI::ConfigMapPtr ru); + + /** + * @brief to export the algorithm breakdown + * @note only valid for c++ algo + * @return the key-value table breakdown in ConfigMapPtr; + */ + virtual INTELLI::ConfigMapPtr getBreakDown(); + }; } // AMMBench /** diff --git a/include/Streaming/SingleThreadStreamer.h b/include/Streaming/SingleThreadStreamer.h index 2f353111..2fea9e17 100644 --- a/include/Streaming/SingleThreadStreamer.h +++ b/include/Streaming/SingleThreadStreamer.h @@ -5,9 +5,11 @@ #ifndef INTELLISTREAM_SINGLETHREADSTREAMER_H #define INTELLISTREAM_SINGLETHREADSTREAMER_H + #include #include #include + namespace AMMBench { /** @@ -25,38 +27,43 @@ namespace AMMBench { protected: INTELLI::ConfigMapPtr cfgGlobal; AMMBench::CPPAlgoTable cppAlgoTable; - uint64_t batchSize=1; + uint64_t batchSize = 1; AMMBench::AbstractCPPAlgoPtr cppAlgoPtr = nullptr; - AMMBench::TensorPtr matC= nullptr; - double throughput=0.0; + AMMBench::TensorPtr matC = nullptr; + double throughput = 0.0; public: - SingleThreadStreamer(){} - ~SingleThreadStreamer(){} + SingleThreadStreamer() {} + + ~SingleThreadStreamer() {} + /** * @brief the timestamps to trace the streaming process */ std::vector myTs; + /** * @brief Set the GLOBAL config map related to this TimerStamper * @param cfg The config map * @return bool whether the config is successfully set */ virtual bool setConfig(INTELLI::ConfigMapPtr cfg); + /** * @brief To run a streaming Amm, assuming the rows of A coming in a streaming manner and B is fixed * @param A The A matrix * @param B The B matrix * @return bool whether the config is successfully set */ - virtual torch::Tensor streamingAmm(torch::Tensor A, torch::Tensor B, uint64_t sketchSize=1); + virtual torch::Tensor streamingAmm(torch::Tensor A, torch::Tensor B, uint64_t sketchSize = 1); + /** * @brief to get the throughput of last streaming process, the unit is rows/second * @return the throughput */ - double getThroughput() - { + double getThroughput() { return throughput; } + /** * @brief to get the latency within some fraction, such as 0.95 * @param fraction the 0~1 fraction diff --git a/include/Streaming/TimeStamper.h b/include/Streaming/TimeStamper.h index d8376043..2939ffbb 100644 --- a/include/Streaming/TimeStamper.h +++ b/include/Streaming/TimeStamper.h @@ -5,11 +5,13 @@ #ifndef INTELLISTREAM_TIMESTAMPER_H #define INTELLISTREAM_TIMESTAMPER_H + #include #include #include #include #include + namespace AMMBench { /** * @ingroup AMMBENCH_STREAMING @@ -21,28 +23,30 @@ namespace AMMBench { * @brief The class to define timestamp in streaming * @ingroup AMMBENCH_STREAMING */ - class AMMTimeStamp{ + class AMMTimeStamp { public: /** * @brief The time when the related event (to a row or a column) happen */ - uint64_t eventTime=0; + uint64_t eventTime = 0; /** * @brief The time when the related event (to a row or a column) arrive to the system */ - uint64_t arrivalTime=0; + uint64_t arrivalTime = 0; /** * @brief the time when the related event is fully processed */ - uint64_t processedTime=0; - AMMTimeStamp(){} - AMMTimeStamp(uint64_t te,uint64_t ta,uint64_t tp) - { - eventTime=te; - arrivalTime=ta; - processedTime=tp; + uint64_t processedTime = 0; + + AMMTimeStamp() {} + + AMMTimeStamp(uint64_t te, uint64_t ta, uint64_t tp) { + eventTime = te; + arrivalTime = ta; + processedTime = tp; } - ~AMMTimeStamp(){} + + ~AMMTimeStamp() {} }; /** @@ -55,6 +59,7 @@ namespace AMMBench { * @brief (Macro) To creat a new @ref AMMTimeStamp under shared pointer. */ #define newAMMTimeStamp std::make_shared + /** * @class TimeStamper Streaming/TimeStamper.h * @brief The basic class to generate time stamps @@ -73,14 +78,15 @@ namespace AMMBench { protected: INTELLI::ConfigMapPtr cfgGlobal; INTELLI::MicroDataSet md; - uint64_t timeStamper_zipfEvent=0; - double timeStamper_zipfEventFactor=0; + uint64_t timeStamper_zipfEvent = 0; + double timeStamper_zipfEventFactor = 0; uint64_t testSize; std::vector eventS; std::vector arrivalS; - uint64_t eventRateTps=0; - uint64_t timeStepUs=40; - uint64_t seed=114514; + uint64_t eventRateTps = 0; + uint64_t timeStepUs = 40; + uint64_t seed = 114514; + /** * * @brief generate the vector of event @@ -97,28 +103,32 @@ namespace AMMBench { * @brief generate the final result of s and r */ void generateFinal(); + std::vector constructTimeStamps( - std::vector eventS, - std::vector arrivalS); + std::vector eventS, + std::vector arrivalS); public: - TimeStamper(){} - ~TimeStamper(){} + TimeStamper() {} + + ~TimeStamper() {} + std::vector myTs; + /** * @brief Set the GLOBAL config map related to this TimerStamper * @param cfg The config map * @return bool whether the config is successfully set */ - virtual bool setConfig(INTELLI::ConfigMapPtr cfg); + virtual bool setConfig(INTELLI::ConfigMapPtr cfg); /** * @brief get the vector of R tuple * @return the vector */ - virtual std::vector getTimeStamps(){ + virtual std::vector getTimeStamps() { return myTs; - } + } }; /** * @} diff --git a/include/Utils/AbstractC20Thread.hpp b/include/Utils/AbstractC20Thread.hpp index 41c10b1a..abefdd6c 100644 --- a/include/Utils/AbstractC20Thread.hpp +++ b/include/Utils/AbstractC20Thread.hpp @@ -6,6 +6,7 @@ #ifndef _INCLUDE_UTILS_ABSTRACTC20THREAD_H_ #define _INCLUDE_UTILS_ABSTRACTC20THREAD_H_ #pragma once + #include #include #include @@ -26,51 +27,55 @@ namespace INTELLI { * @brief The base class and abstraction of C++20 thread, * and it can be derived into other threads */ -class AbstractC20Thread { - protected: - /** - * @brief The inline 'main" function of thread, as an interface - * @note Normally re-write this in derived classes - */ - virtual void inlineMain() { + class AbstractC20Thread { + protected: + /** + * @brief The inline 'main" function of thread, as an interface + * @note Normally re-write this in derived classes + */ + virtual void inlineMain() { + + } + + std::shared_ptr threadPtr; + public: + AbstractC20Thread() {} + + ~AbstractC20Thread() {} + + /** + * @brief to start this thread + */ + void startThread() { + auto fun = [this]() { + inlineMain(); + }; + threadPtr = std::make_shared(fun); + // table=make_shared(5000); + } - } + /** + * @brief the thread join function + */ + void joinThread() { + threadPtr->join(); + } - std::shared_ptr threadPtr; - public: - AbstractC20Thread() {} - ~AbstractC20Thread() {} - /** - * @brief to start this thread - */ - void startThread() { - auto fun = [this]() { - inlineMain(); }; - threadPtr = std::make_shared(fun); - // table=make_shared(5000); - } - /** - * @brief the thread join function - */ - void joinThread() { - threadPtr->join(); - } -}; /** * @ingroup INTELLI_UTIL_OTHERC20 * @typedef AbstractC20ThreadPtr * @brief The class to describe a shared pointer to @ref AbstractC20Thread */ -typedef std::shared_ptr AbstractC20ThreadPtr; + typedef std::shared_ptr AbstractC20ThreadPtr; /** * @ingroup INTELLI_UTIL_OTHERC20 * @def newAbstractC20Thread * @brief (Macro) To creat a new @ref newAbstractC20Thread under shared pointer. */ #define newAbstractC20Thread std::make_shared -typedef std::shared_ptr> BarrierPtr; + typedef std::shared_ptr> BarrierPtr; } diff --git a/include/Utils/C20Buffers.hpp b/include/Utils/C20Buffers.hpp index 625c85ca..056e8af3 100644 --- a/include/Utils/C20Buffers.hpp +++ b/include/Utils/C20Buffers.hpp @@ -8,6 +8,7 @@ #include #include + #if defined(__GNUC__) && (__GNUC__ >= 4) #define ADB_memcpy(dst, src, size) __builtin_memcpy(dst, src, size) #else @@ -27,97 +28,106 @@ namespace INTELLI { * @class C20Buffer Utils/C20Buffers.hpp * @tparam dataType The type of your buffering element */ -template -class C20Buffer { - protected: - size_t pos = 0; - public: - std::vector area; - /** - * @brief reset this buffer, set pos back to 0 - */ - void reset() { - pos = 0; - } - C20Buffer() { reset(); } - ~C20Buffer() {} + template + class C20Buffer { + protected: + size_t pos = 0; + public: + std::vector area; + + /** + * @brief reset this buffer, set pos back to 0 + */ + void reset() { + pos = 0; + } + + C20Buffer() { reset(); } + + ~C20Buffer() {} + + /** + * @brief Init with original length of buffer + * @param len THe original length of buffer + */ + C20Buffer(size_t len) { + area = std::vector(len); + reset(); + } + + /** + * @brief To get how many elements are allowed in the buffer + * @return The size of buffer area, i.e., area.size() + * @note: This is NOT the size of valid data + * @see size + */ + size_t bufferSize() { + return area.size(); + } + + /** + * @brief To get how many VALID elements are existed in the buffer + * @return The size of VALID elements + * @note: This is NOT the size of total buffer + * @see bufferSize + */ + size_t size() { + return pos; + } + + /** + * @brief To get the original memory area ponter of data + * @return The memory area address (pointer) that stores the data + */ + dataType *data() { + return &area[0]; + } + + /** + * @brief To get the original memory area ponter of data, with offset + * @param offset Offset of data + * @return The memory area address (pointer) that stores the data + * @warning Please ensure the offset is NOT larger than the area.size()-1 + */ + dataType *data(size_t offset) { + return &area[offset]; + } + + /** + * @brief Append the data to the buffer + * @param da Data to be appended + * @note Exceed length will lead to a push_back in vector + * @return The valid size after this append + */ + size_t append(dataType da) { + /*if(pos(len); - reset(); - } - /** - * @brief To get how many elements are allowed in the buffer - * @return The size of buffer area, i.e., area.size() - * @note: This is NOT the size of valid data - * @see size - */ - size_t bufferSize() { - return area.size(); - } - /** - * @brief To get how many VALID elements are existed in the buffer - * @return The size of VALID elements - * @note: This is NOT the size of total buffer - * @see bufferSize - */ - size_t size() { - return pos; - } - /** - * @brief To get the original memory area ponter of data - * @return The memory area address (pointer) that stores the data - */ - dataType *data() { - return &area[0]; - } - /** - * @brief To get the original memory area ponter of data, with offset - * @param offset Offset of data - * @return The memory area address (pointer) that stores the data - * @warning Please ensure the offset is NOT larger than the area.size()-1 - */ - dataType *data(size_t offset) { - return &area[offset]; - } - /** - * @brief Append the data to the buffer - * @param da Data to be appended - * @note Exceed length will lead to a push_back in vector - * @return The valid size after this append - */ - size_t append(dataType da) { - /*if(pos #include #include @@ -26,243 +27,262 @@ namespace INTELLI { * @class ConfigMap Utils/ConfigMap.hpp * @brief The unified map structure to store configurations in a key-value style */ -class ConfigMap { - protected: - std::map u64Map; - std::map i64Map; - std::map doubleMap; - std::map strMap; + class ConfigMap { + protected: + std::map u64Map; + std::map i64Map; + std::map doubleMap; + std::map strMap; - void spilt(const std::string s, const std::string &c, vector &v) { - std::string::size_type pos1, pos2; - pos2 = s.find(c); - pos1 = 0; - while (std::string::npos != pos2) { - v.push_back(s.substr(pos1, pos2 - pos1)); + void spilt(const std::string s, const std::string &c, vector &v) { + std::string::size_type pos1, pos2; + pos2 = s.find(c); + pos1 = 0; + while (std::string::npos != pos2) { + v.push_back(s.substr(pos1, pos2 - pos1)); - pos1 = pos2 + c.size(); - pos2 = s.find(c, pos1); - } - if (pos1 != s.length()) - v.push_back(s.substr(pos1)); - } - public: - ConfigMap() {} - ~ConfigMap() {} - /** - * @brief Edit the config map. If not exit the config, will create new, or will overwrite - * @param key The look up key in std::string - * @param value The u64 value - */ - void edit(std::string key, uint64_t value) { - u64Map[key] = value; - } - /** - * @brief Edit the config map. If not exit the config, will create new, or will overwrite - * @param key The look up key in std::string - * @param value The i64 value - */ - void edit(std::string key, int64_t value) { - i64Map[key] = value; - } - /** - * @brief Edit the config map. If not exit the config, will create new, or will overwrite - * @param key The look up key in std::string - * @param value The double value - */ - void edit(std::string key, double value) { - doubleMap[key] = value; - } - /** - * @brief Edit the config map. If not exit the config, will create new, or will overwrite - * @param key The look up key in std::string - * @param value The std::string value - */ - void edit(std::string key, std::string value) { - strMap[key] = value; - } - /** - * @brief To detect whether the key exists and related to a U64 - * @param key - * @return bool for the result - */ - bool existU64(std::string key) { - return (u64Map.count(key) == 1); - } - /** - * @brief To detect whether the key exists and related to a I64 - * @param key - * @return bool for the result - */ - bool existI64(std::string key) { - return (i64Map.count(key) == 1); - } - /** - * @brief To detect whether the key exists and related to a double - * @param key - * @return bool for the result - */ - bool existDouble(std::string key) { - return (doubleMap.count(key) == 1); - } - /** - * @brief To detect whether the key exists and related to a std::string - * @param key - * @return bool for the result - */ - bool existString(std::string key) { - return (strMap.count(key) == 1); - } - /** - * @brief To detect whether the key exists - * @param key - * @return bool for the result - */ - bool exist(std::string key) { - return existU64(key) || existI64(key) || existDouble(key) || existString(key); - } - /** - * @brief To get a U64 value by key - * @param key - * @return value - * @warning the key must exist!! - */ - uint64_t getU64(std::string key) { - return u64Map.at(key); - } - /** - * @brief To get a I64 value by key - * @param key - * @return value - * @warning the key must exist!! - */ - int64_t getI64(std::string key) { - return i64Map.at(key); - } - /** - * @brief To get a double value by key - * @param key - * @return value - * @warning the key must exist!! - */ - double getDouble(std::string key) { - return doubleMap.at(key); - } - /** - * @brief To get a std::string value by key - * @param key - * @return value - * @warning the key must exist!! - */ - std::string getString(std::string key) { - return strMap.at(key); - } - /** - * @brief convert the whole map to std::string and retuen - * @param separator The separator std::string, default "\t" - * @param newLine The newline std::string, default "\n" - * @return the result - */ - std::string toString(std::string separator = "\t", std::string newLine = "\n") { - std::string str = "key" + separator + "value" + separator + "type" + newLine; - for (auto &iter : u64Map) { - std::string col = iter.first + separator + to_string(iter.second) + separator + "U64" + newLine; - str += col; - } - for (auto &iter : i64Map) { - std::string col = iter.first + separator + to_string(iter.second) + separator + "I64" + newLine; - str += col; - } - for (auto &iter : doubleMap) { - std::string col = iter.first + separator + to_string(iter.second) + separator + "Double" + newLine; - str += col; - } - for (auto &iter : strMap) { - std::string col = iter.first + separator + (iter.second) + separator + "String" + newLine; - str += col; - } - return str; - } - /** - * @brief clone this config into destination - * @param dest The clone destination - */ - void cloneInto(ConfigMap &dest) { - for (auto &iter : u64Map) { - dest.edit(iter.first, (uint64_t) iter.second); - } - for (auto &iter : i64Map) { - dest.edit(iter.first, (int64_t) iter.second); - } - for (auto &iter : doubleMap) { - dest.edit(iter.first, (double) iter.second); - } - for (auto &iter : strMap) { - dest.edit(iter.first, (std::string) iter.second); - } - } - /** - * @brief convert the whole map to file - * @param fname The file name - * @param separator The separator std::string, default "," for csv style - * @param newLine The newline std::string, default "\n" - * @return bool, whether the file is created - */ - bool toFile(std::string fname, std::string separator = ",", std::string newLine = "\n") { - ofstream of; - of.open(fname); - if (of.fail()) { - return false; - } - of << toString(separator, newLine); - of.close(); - return true; - } - /** - * @brief update the whole map from file - * @param fname The file name - * @param separator The separator std::string, default "," for csv style - * @param newLine The newline std::string, default "\n" - * @return bool, whether the file is loaded - */ - bool fromFile(std::string fname, std::string separator = ",", std::string newLine = "\n") { - ifstream ins; - ins.open(fname); - assert(separator.data()); - assert(newLine.data()); - if (ins.fail()) { - return false; - } - std::string readStr; - // cout << "read file\r\n"; - while (std::getline(ins, readStr, newLine.data()[0])) { - vector cols; - // readStr.erase(readStr.size()-1); - spilt(readStr, separator, cols); - // cout<= 3) { - istringstream iss(cols[1]); - if (cols[2] == "U64") { - uint64_t value; - iss >> value; - edit(cols[0], (uint64_t) value); - } else if (cols[2] == "I64") { - int64_t value; - iss >> value; - edit(cols[0], (int64_t) value); - } else if (cols[2] == "Double") { - double value; - iss >> value; - edit(cols[0], (double) value); - } else if (cols[2] == "String") { - edit(cols[0], (std::string) cols[1]); + pos1 = pos2 + c.size(); + pos2 = s.find(c, pos1); + } + if (pos1 != s.length()) + v.push_back(s.substr(pos1)); + } + + public: + ConfigMap() {} + + ~ConfigMap() {} + + /** + * @brief Edit the config map. If not exit the config, will create new, or will overwrite + * @param key The look up key in std::string + * @param value The u64 value + */ + void edit(std::string key, uint64_t value) { + u64Map[key] = value; + } + + /** + * @brief Edit the config map. If not exit the config, will create new, or will overwrite + * @param key The look up key in std::string + * @param value The i64 value + */ + void edit(std::string key, int64_t value) { + i64Map[key] = value; + } + + /** + * @brief Edit the config map. If not exit the config, will create new, or will overwrite + * @param key The look up key in std::string + * @param value The double value + */ + void edit(std::string key, double value) { + doubleMap[key] = value; + } + + /** + * @brief Edit the config map. If not exit the config, will create new, or will overwrite + * @param key The look up key in std::string + * @param value The std::string value + */ + void edit(std::string key, std::string value) { + strMap[key] = value; + } + + /** + * @brief To detect whether the key exists and related to a U64 + * @param key + * @return bool for the result + */ + bool existU64(std::string key) { + return (u64Map.count(key) == 1); + } + + /** + * @brief To detect whether the key exists and related to a I64 + * @param key + * @return bool for the result + */ + bool existI64(std::string key) { + return (i64Map.count(key) == 1); + } + + /** + * @brief To detect whether the key exists and related to a double + * @param key + * @return bool for the result + */ + bool existDouble(std::string key) { + return (doubleMap.count(key) == 1); + } + + /** + * @brief To detect whether the key exists and related to a std::string + * @param key + * @return bool for the result + */ + bool existString(std::string key) { + return (strMap.count(key) == 1); + } + + /** + * @brief To detect whether the key exists + * @param key + * @return bool for the result + */ + bool exist(std::string key) { + return existU64(key) || existI64(key) || existDouble(key) || existString(key); + } + + /** + * @brief To get a U64 value by key + * @param key + * @return value + * @warning the key must exist!! + */ + uint64_t getU64(std::string key) { + return u64Map.at(key); + } + + /** + * @brief To get a I64 value by key + * @param key + * @return value + * @warning the key must exist!! + */ + int64_t getI64(std::string key) { + return i64Map.at(key); + } + + /** + * @brief To get a double value by key + * @param key + * @return value + * @warning the key must exist!! + */ + double getDouble(std::string key) { + return doubleMap.at(key); + } + + /** + * @brief To get a std::string value by key + * @param key + * @return value + * @warning the key must exist!! + */ + std::string getString(std::string key) { + return strMap.at(key); + } + + /** + * @brief convert the whole map to std::string and retuen + * @param separator The separator std::string, default "\t" + * @param newLine The newline std::string, default "\n" + * @return the result + */ + std::string toString(std::string separator = "\t", std::string newLine = "\n") { + std::string str = "key" + separator + "value" + separator + "type" + newLine; + for (auto &iter: u64Map) { + std::string col = iter.first + separator + to_string(iter.second) + separator + "U64" + newLine; + str += col; + } + for (auto &iter: i64Map) { + std::string col = iter.first + separator + to_string(iter.second) + separator + "I64" + newLine; + str += col; + } + for (auto &iter: doubleMap) { + std::string col = iter.first + separator + to_string(iter.second) + separator + "Double" + newLine; + str += col; + } + for (auto &iter: strMap) { + std::string col = iter.first + separator + (iter.second) + separator + "String" + newLine; + str += col; + } + return str; + } + + /** + * @brief clone this config into destination + * @param dest The clone destination + */ + void cloneInto(ConfigMap &dest) { + for (auto &iter: u64Map) { + dest.edit(iter.first, (uint64_t) iter.second); + } + for (auto &iter: i64Map) { + dest.edit(iter.first, (int64_t) iter.second); + } + for (auto &iter: doubleMap) { + dest.edit(iter.first, (double) iter.second); + } + for (auto &iter: strMap) { + dest.edit(iter.first, (std::string) iter.second); + } + } + + /** + * @brief convert the whole map to file + * @param fname The file name + * @param separator The separator std::string, default "," for csv style + * @param newLine The newline std::string, default "\n" + * @return bool, whether the file is created + */ + bool toFile(std::string fname, std::string separator = ",", std::string newLine = "\n") { + ofstream of; + of.open(fname); + if (of.fail()) { + return false; + } + of << toString(separator, newLine); + of.close(); + return true; + } + + /** + * @brief update the whole map from file + * @param fname The file name + * @param separator The separator std::string, default "," for csv style + * @param newLine The newline std::string, default "\n" + * @return bool, whether the file is loaded + */ + bool fromFile(std::string fname, std::string separator = ",", std::string newLine = "\n") { + ifstream ins; + ins.open(fname); + assert(separator.data()); + assert(newLine.data()); + if (ins.fail()) { + return false; + } + std::string readStr; + // cout << "read file\r\n"; + while (std::getline(ins, readStr, newLine.data()[0])) { + vector cols; + // readStr.erase(readStr.size()-1); + spilt(readStr, separator, cols); + // cout<= 3) { + istringstream iss(cols[1]); + if (cols[2] == "U64") { + uint64_t value; + iss >> value; + edit(cols[0], (uint64_t) value); + } else if (cols[2] == "I64") { + int64_t value; + iss >> value; + edit(cols[0], (int64_t) value); + } else if (cols[2] == "Double") { + double value; + iss >> value; + edit(cols[0], (double) value); + } else if (cols[2] == "String") { + edit(cols[0], (std::string) cols[1]); + } + } + } + //ins>>readStr; + ins.close(); + return true; } - } - } - //ins>>readStr; - ins.close(); - return true; - } /** * @brief Try to get an I64 from config map, if not exist, use default value instead @@ -271,19 +291,19 @@ class ConfigMap { * @param showWarning Whether show warning logs if not found * @return The returned value */ - int64_t tryI64(const string &key, int64_t defaultValue = 0, bool showWarning = false) { - int64_t ru = defaultValue; - if (this->existI64(key)) { - ru = this->getI64(key); - // INTELLI_INFO(key + " = " + to_string(ru)); - } else { - if (showWarning) { - INTELLI_WARNING("Leaving " + key + " as blank, will use " + to_string(defaultValue) + " instead"); - } - // WM_WARNNING("Leaving " + key + " as blank, will use " + to_string(defaultValue) + " instead"); - } - return ru; - } + int64_t tryI64(const string &key, int64_t defaultValue = 0, bool showWarning = false) { + int64_t ru = defaultValue; + if (this->existI64(key)) { + ru = this->getI64(key); + // INTELLI_INFO(key + " = " + to_string(ru)); + } else { + if (showWarning) { + INTELLI_WARNING("Leaving " + key + " as blank, will use " + to_string(defaultValue) + " instead"); + } + // WM_WARNNING("Leaving " + key + " as blank, will use " + to_string(defaultValue) + " instead"); + } + return ru; + } /** * @brief Try to get an U64 from config map, if not exist, use default value instead @@ -292,19 +312,19 @@ class ConfigMap { * @param showWarning Whether show warning logs if not found * @return The returned value */ - uint64_t tryU64(const string &key, uint64_t defaultValue = 0, bool showWarning = false) { - uint64_t ru = defaultValue; - if (this->existU64(key)) { - ru = this->getU64(key); - // INTELLI_INFO(key + " = " + to_string(ru)); - } else { - if (showWarning) { - INTELLI_WARNING("Leaving " + key + " as blank, will use " + to_string(defaultValue) + " instead"); - } - // WM_WARNNING("Leaving " + key + " as blank, will use " + to_string(defaultValue) + " instead"); - } - return ru; - } + uint64_t tryU64(const string &key, uint64_t defaultValue = 0, bool showWarning = false) { + uint64_t ru = defaultValue; + if (this->existU64(key)) { + ru = this->getU64(key); + // INTELLI_INFO(key + " = " + to_string(ru)); + } else { + if (showWarning) { + INTELLI_WARNING("Leaving " + key + " as blank, will use " + to_string(defaultValue) + " instead"); + } + // WM_WARNNING("Leaving " + key + " as blank, will use " + to_string(defaultValue) + " instead"); + } + return ru; + } /** * @brief Try to get a double from config map, if not exist, use default value instead @@ -313,19 +333,19 @@ class ConfigMap { * @param showWarning Whether show warning logs if not found * @return The returned value */ - double tryDouble(const string &key, double defaultValue = 0, bool showWarning = false) { - double ru = defaultValue; - if (this->existDouble(key)) { - ru = this->getDouble(key); - // INTELLI_INFO(key + " = " + to_string(ru)); - } else { - if (showWarning) { - INTELLI_WARNING("Leaving " + key + " as blank, will use " + to_string(defaultValue) + " instead"); - } - // WM_WARNNING("Leaving " + key + " as blank, will use " + to_string(defaultValue) + " instead"); - } - return ru; - } + double tryDouble(const string &key, double defaultValue = 0, bool showWarning = false) { + double ru = defaultValue; + if (this->existDouble(key)) { + ru = this->getDouble(key); + // INTELLI_INFO(key + " = " + to_string(ru)); + } else { + if (showWarning) { + INTELLI_WARNING("Leaving " + key + " as blank, will use " + to_string(defaultValue) + " instead"); + } + // WM_WARNNING("Leaving " + key + " as blank, will use " + to_string(defaultValue) + " instead"); + } + return ru; + } /** * @brief Try to get an String from config map, if not exist, use default value instead @@ -334,28 +354,28 @@ class ConfigMap { * @param showWarning Whether show warning logs if not found * @return The returned value */ - string tryString(const string &key, const string &defaultValue = "", bool showWarning = false) { - string ru = defaultValue; - if (this->existString(key)) { - ru = this->getString(key); - //INTELLI_INFO(key + " = " + (ru)); - } else { - if (showWarning) { - INTELLI_WARNING("Leaving " + key + " as blank, will use " + (defaultValue) + " instead"); - } - // WM_WARNNING("Leaving " + key + " as blank, will use " + (defaultValue) + " instead"); - } - return ru; - } + string tryString(const string &key, const string &defaultValue = "", bool showWarning = false) { + string ru = defaultValue; + if (this->existString(key)) { + ru = this->getString(key); + //INTELLI_INFO(key + " = " + (ru)); + } else { + if (showWarning) { + INTELLI_WARNING("Leaving " + key + " as blank, will use " + (defaultValue) + " instead"); + } + // WM_WARNNING("Leaving " + key + " as blank, will use " + (defaultValue) + " instead"); + } + return ru; + } -}; + }; /** * @ingroup INTELLI_UTIL_CONFIGS * @typedef ConfigMapPtr * @brief The class to describe a shared pointer to @ref ConfigMap */ -typedef std::shared_ptr ConfigMapPtr; + typedef std::shared_ptr ConfigMapPtr; /** * @ingroup INTELLI_UTIL_CONFIGS * @def newConfigMap diff --git a/include/Utils/IntelliLog.h b/include/Utils/IntelliLog.h index 1ac69e33..9b2bac3f 100755 --- a/include/Utils/IntelliLog.h +++ b/include/Utils/IntelliLog.h @@ -26,25 +26,25 @@ namespace INTELLI { * @class IntelliLog Utils/IntelliLog.hpp * @brief The log functions packed in class */ -class IntelliLog { - public: - /** - * @brief Produce a log - * @param level The log level you want to indicate - * @param message The log message you want to indicate - * @param source reserved - * @note message is automatically appended with a "\n" - */ - static void log(std::string level, - std::string_view message, - std::source_location const source = std::source_location::current()); + class IntelliLog { + public: + /** + * @brief Produce a log + * @param level The log level you want to indicate + * @param message The log message you want to indicate + * @param source reserved + * @note message is automatically appended with a "\n" + */ + static void log(std::string level, + std::string_view message, + std::source_location const source = std::source_location::current()); - /** - * @brief set up the logging file by its name - * @param fname the name of file - */ - static void setupLoggingFile(string fname); -}; + /** + * @brief set up the logging file by its name + * @param fname the name of file + */ + static void setupLoggingFile(string fname); + }; /** * @ingroup INTELLI_UTIL_INTELLILOG @@ -52,59 +52,59 @@ class IntelliLog { * @brief The protector for concurrent log on a file * @warning This class is preserved for internal use only! */ -class IntelliLog_FileProtector { - private: - std::mutex m_mut; - ofstream of; - bool isOpened = false; - public: - IntelliLog_FileProtector() = default; + class IntelliLog_FileProtector { + private: + std::mutex m_mut; + ofstream of; + bool isOpened = false; + public: + IntelliLog_FileProtector() = default; - ~IntelliLog_FileProtector() { - if (isOpened) { - of.close(); - } - } + ~IntelliLog_FileProtector() { + if (isOpened) { + of.close(); + } + } - /** - * @brief lock this protector - */ - void lock() { - while (!m_mut.try_lock()); - } + /** + * @brief lock this protector + */ + void lock() { + while (!m_mut.try_lock()); + } - /** - * @brief unlock this protector - */ - void unlock() { - m_mut.unlock(); - } + /** + * @brief unlock this protector + */ + void unlock() { + m_mut.unlock(); + } - /** - * @brief try to open a file - * @param fname The name of file - */ - void openLogFile(const string &fname) { - of.open(fname, std::ios_base::app); - if (of.fail()) { - return; - } - isOpened = true; - } + /** + * @brief try to open a file + * @param fname The name of file + */ + void openLogFile(const string &fname) { + of.open(fname, std::ios_base::app); + if (of.fail()) { + return; + } + isOpened = true; + } - /** - * @brief try to appened something to the file, if it's opened - * @param msg The message to appened - */ - void appendLogFile(const string &msg) { - if (!isOpened) { - return; - } - lock(); - of << msg; - unlock(); - } -}; + /** + * @brief try to appened something to the file, if it's opened + * @param msg The message to appened + */ + void appendLogFile(const string &msg) { + if (!isOpened) { + return; + } + lock(); + of << msg; + unlock(); + } + }; /** * @ingroup INTELLI_UTIL_INTELLILOG * @def INTELLI_INFO diff --git a/include/Utils/Meters/AbstractMeter.hpp b/include/Utils/Meters/AbstractMeter.hpp index 3035caf7..10dbece6 100644 --- a/include/Utils/Meters/AbstractMeter.hpp +++ b/include/Utils/Meters/AbstractMeter.hpp @@ -6,9 +6,11 @@ #include #include #include + #define METER_ERROR(n) INTELLI_ERROR(n) #include + using namespace std; namespace DIVERSE_METER { /** @@ -32,88 +34,96 @@ namespace DIVERSE_METER { * - call @ref getE(), @ref getPeak(), etc to get the measurement resluts * */ -class AbstractMeter { - protected: - /** - * @brief static power of a system in W - */ - double staticPower = 0; - INTELLI::ConfigMapPtr cfg = nullptr; - - private: - - public: - AbstractMeter(/* args */) { - - } - //if exist in another name - - ~AbstractMeter() { - - } - /** - * @brief to set the configmap - * @param cfg the config map - */ - virtual void setConfig(INTELLI::ConfigMapPtr _cfg) { - cfg = _cfg; - } - /** - * @brief to manually set the static power - * @param _sp - */ - void setStaticPower(double _sp) { - staticPower = _sp; - } - /** - * @brief to test the static power of a system by sleeping - * @param sleepingSecond The seconds for sleep - */ - void testStaticPower(uint64_t sleepingSecond); - /** - * @brief to start the meter into some measuring tasks - */ - virtual void startMeter() { - - } - /** - * @brief to stop the meter into some measuring tasks - */ - virtual void stopMeter() { - - } - //energy in J - /** - * @brief to get the energy in J, including static energy consumption of system - */ - virtual double getE() { - return 0.0; - } + class AbstractMeter { + protected: + /** + * @brief static power of a system in W + */ + double staticPower = 0; + INTELLI::ConfigMapPtr cfg = nullptr; - /** - * @brief to get the peak power in W, including static power of system - */ - virtual double getPeak() { - return 0.0; - } - - virtual bool isValid() { - return false; - } - /** - * @brief to return the tested static power - * return the @ref staticPower - */ - double getStaticPower(); - /** -* @brief to return the static energy consumption of a system under several us - * @param runningUs The time in us of a running - * return the @ref staticPower -*/ - double getStaicEnergyConsumption(uint64_t runningUs); + private: + + public: + AbstractMeter(/* args */) { + + } + //if exist in another name + + ~AbstractMeter() { + + } + + /** + * @brief to set the configmap + * @param cfg the config map + */ + virtual void setConfig(INTELLI::ConfigMapPtr _cfg) { + cfg = _cfg; + } + + /** + * @brief to manually set the static power + * @param _sp + */ + void setStaticPower(double _sp) { + staticPower = _sp; + } + + /** + * @brief to test the static power of a system by sleeping + * @param sleepingSecond The seconds for sleep + */ + void testStaticPower(uint64_t sleepingSecond); + + /** + * @brief to start the meter into some measuring tasks + */ + virtual void startMeter() { + + } + + /** + * @brief to stop the meter into some measuring tasks + */ + virtual void stopMeter() { + + } + //energy in J + /** + * @brief to get the energy in J, including static energy consumption of system + */ + virtual double getE() { + return 0.0; + } + + /** + * @brief to get the peak power in W, including static power of system + */ + virtual double getPeak() { + return 0.0; + } + + virtual bool isValid() { + return false; + } + + /** + * @brief to return the tested static power + * return the @ref staticPower + */ + double getStaticPower(); + + /** + * @brief to return the static energy consumption of a system under several us + * @param runningUs The time in us of a running + * return the @ref staticPower + */ + double getStaicEnergyConsumption(uint64_t runningUs); + + }; -}; -typedef std::shared_ptr AbstractMeterPtr; + typedef std::shared_ptr AbstractMeterPtr; /** * @} */ diff --git a/include/Utils/Meters/EspMeterUart/EspMeterUart.hpp b/include/Utils/Meters/EspMeterUart/EspMeterUart.hpp index 9b6d8612..9532511c 100644 --- a/include/Utils/Meters/EspMeterUart/EspMeterUart.hpp +++ b/include/Utils/Meters/EspMeterUart/EspMeterUart.hpp @@ -24,46 +24,53 @@ namespace DIVERSE_METER { * - meterAddress, String, The file system path of meter, default "/dev/ttyUSB0"; * @note tag is "espUart" */ -class EspMeterUart : public AbstractMeter { - private: - int devFd = -1; - /** - * @brief The file system path of meter - */ - std::string meterAddress = "/dev/ttyUSB0"; - void openUartDev(); - // uint64_t accessEsp32(uint64_t cmd); - public: - EspMeterUart(/* args */); - ~EspMeterUart(); - /** - * @brief to set the configmap - * @param cfg the config map - */ - virtual void setConfig(INTELLI::ConfigMapPtr _cfg); - /** - * @brief to start the meter into some measuring tasks - */ - void startMeter(); - /** - * @brief to stop the meter into some measuring tasks - */ - void stopMeter(); - /** -* @brief to get the energy in J, including static energy consumption of system -*/ - double getE(); - //peak power in mW - /** - * @brief to get the peak power in W, including static power of system - */ - double getPeak(); + class EspMeterUart : public AbstractMeter { + private: + int devFd = -1; + /** + * @brief The file system path of meter + */ + std::string meterAddress = "/dev/ttyUSB0"; + + void openUartDev(); + // uint64_t accessEsp32(uint64_t cmd); + public: + EspMeterUart(/* args */); + + ~EspMeterUart(); + + /** + * @brief to set the configmap + * @param cfg the config map + */ + virtual void setConfig(INTELLI::ConfigMapPtr _cfg); + + /** + * @brief to start the meter into some measuring tasks + */ + void startMeter(); + + /** + * @brief to stop the meter into some measuring tasks + */ + void stopMeter(); + + /** + * @brief to get the energy in J, including static energy consumption of system + */ + double getE(); + //peak power in mW + /** + * @brief to get the peak power in W, including static power of system + */ + double getPeak(); + + bool isValid() { + return (devFd != -1); + } + }; - bool isValid() { - return (devFd != -1); - } -}; -typedef std::shared_ptr EspMeterUartPtr; + typedef std::shared_ptr EspMeterUartPtr; #define newEspMeterUart() std::make_shared(); } diff --git a/include/Utils/Meters/IntelMeter/IntelMeter.hpp b/include/Utils/Meters/IntelMeter/IntelMeter.hpp index 98aab374..e3eeb5cc 100644 --- a/include/Utils/Meters/IntelMeter/IntelMeter.hpp +++ b/include/Utils/Meters/IntelMeter/IntelMeter.hpp @@ -1,5 +1,6 @@ #ifndef ADB_INCLUDE_UTILS_IntelMeter_HPP_ #define ADB_INCLUDE_UTILS_IntelMeter_HPP_ + #include #include #include @@ -17,13 +18,14 @@ #include #include #include + using namespace std; namespace DIVERSE_METER { -typedef struct rapl_power_unit { - double PU; //power units - double ESU; //energy status units - double TU; //time units -} rapl_power_unit; + typedef struct rapl_power_unit { + double PU; //power units + double ESU; //energy status units + double TU; //time units + } rapl_power_unit; /*class:IntelMeter description:the entity of intel msr-based power meter, providing all function including: E,PeakPower @@ -45,39 +47,48 @@ date:20211202 * @warning: only works for some x64 machines * @note: no peak power support, tag is "intelMsr" */ -class IntelMeter : public AbstractMeter { - private: - int devFd; - uint64_t rdmsr(int cpu, uint32_t reg); - rapl_power_unit get_rapl_power_unit(); - double eSum = 0; + class IntelMeter : public AbstractMeter { + private: + int devFd; - uint32_t maxCpu = 0; - vector cpus; - vector st; - vector en; - vector count; - rapl_power_unit power_units; - public: - /** -* @brief to set the configmap -* @param cfg the config map -*/ - virtual void setConfig(INTELLI::ConfigMapPtr _cfg); - IntelMeter(/* args */); - ~IntelMeter(); - void startMeter(); - void stopMeter(); - //energy in J - double getE(); - //peak power in mW - // double getPeak(); + uint64_t rdmsr(int cpu, uint32_t reg); + + rapl_power_unit get_rapl_power_unit(); + + double eSum = 0; + + uint32_t maxCpu = 0; + vector cpus; + vector st; + vector en; + vector count; + rapl_power_unit power_units; + public: + /** + * @brief to set the configmap + * @param cfg the config map + */ + virtual void setConfig(INTELLI::ConfigMapPtr _cfg); + + IntelMeter(/* args */); + + ~IntelMeter(); + + void startMeter(); + + void stopMeter(); + + //energy in J + double getE(); + //peak power in mW + // double getPeak(); + + bool isValid() { + return (devFd != -1); + } + }; - bool isValid() { - return (devFd != -1); - } -}; -typedef std::shared_ptr IntelMeterPtr; + typedef std::shared_ptr IntelMeterPtr; #define newIntelMeter() std::make_shared(); } diff --git a/include/Utils/Meters/MeterTable.h b/include/Utils/Meters/MeterTable.h index 6237fa07..569f7651 100644 --- a/include/Utils/Meters/MeterTable.h +++ b/include/Utils/Meters/MeterTable.h @@ -1,8 +1,10 @@ /*! \file MeterTable.hpp*/ #ifndef INTELLISTREAM_UTILS_METERTABLE_H_ #define INTELLISTREAM_UTILS_METERTABLE_H_ + #include #include + namespace DIVERSE_METER { /** @@ -17,53 +19,54 @@ namespace DIVERSE_METER { * - espUart @ref EspMeterUart * - intelMsr @ref IntelMeter */ -class MeterTable { - protected: - std::map meterMap; - public: - /** - * @brief The constructing function - * @note If new MatrixLoader wants to be included by default, please revise the following in *.cpp - */ - MeterTable(); + class MeterTable { + protected: + std::map meterMap; + public: + /** + * @brief The constructing function + * @note If new MatrixLoader wants to be included by default, please revise the following in *.cpp + */ + MeterTable(); - ~MeterTable() { - } + ~MeterTable() { + } - /** - * @brief To register a new meter - * @param onew The new operator - * @param tag THe name tag - */ - void registerNewMeter(DIVERSE_METER::AbstractMeterPtr dnew, std::string tag) { - meterMap[tag] = dnew; - } + /** + * @brief To register a new meter + * @param onew The new operator + * @param tag THe name tag + */ + void registerNewMeter(DIVERSE_METER::AbstractMeterPtr dnew, std::string tag) { + meterMap[tag] = dnew; + } - /** - * @brief find a meter in the table according to its name - * @param name The nameTag of loader - * @return The Meter, nullptr if not found - */ - DIVERSE_METER::AbstractMeterPtr findMeter(std::string name) { - if (meterMap.count(name)) { - return meterMap[name]; - } - return nullptr; - } - /** - * @ingroup INTELLI_UTIL_METER - * @typedef MeterTablePtr - * @brief The class to describe a shared pointer to @ref MeterTable + /** + * @brief find a meter in the table according to its name + * @param name The nameTag of loader + * @return The Meter, nullptr if not found + */ + DIVERSE_METER::AbstractMeterPtr findMeter(std::string name) { + if (meterMap.count(name)) { + return meterMap[name]; + } + return nullptr; + } - */ - typedef std::shared_ptr MeterTablePtr; + /** + * @ingroup INTELLI_UTIL_METER + * @typedef MeterTablePtr + * @brief The class to describe a shared pointer to @ref MeterTable + + */ + typedef std::shared_ptr MeterTablePtr; /** * @ingroup INTELLI_UTIL_METER * @def newMeterTable * @brief (Macro) To creat a new @ref MeterTable under shared pointer. */ #define newMeterTable std::make_shared -}; + }; } /** * @} diff --git a/include/Utils/MicroDataSet.hpp b/include/Utils/MicroDataSet.hpp index e3bbc8ca..23ea7c93 100644 --- a/include/Utils/MicroDataSet.hpp +++ b/include/Utils/MicroDataSet.hpp @@ -237,12 +237,14 @@ namespace INTELLI { } return ret; } + template vector genSmoothTimeStamp(size_t len, size_t maxTime) { - vector ret=genRandInt(len,maxTime); + vector ret = genRandInt(len, maxTime); std::sort(ret.begin(), ret.end()); //just incremental re-arrange return ret; } + /** * @brief The function to generate a vector of timestamp which has zipf distribution * @param tsType The data type of time stamp, default is size_t diff --git a/include/Utils/SPSCQueue.hpp b/include/Utils/SPSCQueue.hpp index 347f8de0..d6665b91 100644 --- a/include/Utils/SPSCQueue.hpp +++ b/include/Utils/SPSCQueue.hpp @@ -16,251 +16,256 @@ #include #include #include + using namespace std::literals::chrono_literals; using namespace std; namespace INTELLI { -template> -class SPSCQueue { + template> + class SPSCQueue { #if defined(__cpp_if_constexpr) && defined(__cpp_lib_void_t) - template - struct has_allocate_at_least : std::false_type { - }; - - template - struct has_allocate_at_least< - Alloc2, std::void_t().allocate_at_least( - size_t{}))>> : std::true_type { - }; + template + struct has_allocate_at_least : std::false_type { + }; + + template + struct has_allocate_at_least< + Alloc2, std::void_t().allocate_at_least( + size_t{}))>> : std::true_type { + }; #endif - public: - pthread_cond_t cond; - pthread_mutex_t mutex; - explicit SPSCQueue(const size_t capacity, - const Allocator &allocator = Allocator()) - : capacity_(capacity), allocator_(allocator) { - - // The queue needs at least one element - if (capacity_ < 1) { - capacity_ = 1; - } - capacity_++; // Needs one slack element - // Prevent overflowing size_t - if (capacity_ > SIZE_MAX - 2 * kPadding) { - capacity_ = SIZE_MAX - 2 * kPadding; - } + public: + pthread_cond_t cond; + pthread_mutex_t mutex; + + explicit SPSCQueue(const size_t capacity, + const Allocator &allocator = Allocator()) + : capacity_(capacity), allocator_(allocator) { + + // The queue needs at least one element + if (capacity_ < 1) { + capacity_ = 1; + } + capacity_++; // Needs one slack element + // Prevent overflowing size_t + if (capacity_ > SIZE_MAX - 2 * kPadding) { + capacity_ = SIZE_MAX - 2 * kPadding; + } #if defined(__cpp_if_constexpr) && defined(__cpp_lib_void_t) - if constexpr (has_allocate_at_least::value) { - auto res = allocator_.allocate_at_least(capacity_ + 2 * kPadding); - slots_ = res.ptr; - capacity_ = res.count - 2 * kPadding; - } else { - slots_ = std::allocator_traits::allocate( - allocator_, capacity_ + 2 * kPadding); - } + if constexpr (has_allocate_at_least::value) { + auto res = allocator_.allocate_at_least(capacity_ + 2 * kPadding); + slots_ = res.ptr; + capacity_ = res.count - 2 * kPadding; + } else { + slots_ = std::allocator_traits::allocate( + allocator_, capacity_ + 2 * kPadding); + } #else - slots_ = std::allocator_traits::allocate( - allocator_, capacity_ + 2 * kPadding); + slots_ = std::allocator_traits::allocate( + allocator_, capacity_ + 2 * kPadding); #endif - static_assert(alignof(SPSCQueue) == kCacheLineSize, ""); - static_assert(sizeof(SPSCQueue) >= 3 * kCacheLineSize, ""); - assert(reinterpret_cast(&readIdx_) - - reinterpret_cast(&writeIdx_) >= - static_cast(kCacheLineSize)); - } - - ~SPSCQueue() { - while (front()) { - pop(); - } - std::allocator_traits::deallocate(allocator_, slots_, - capacity_ + 2 * kPadding); - } - - // non-copyable and non-movable - SPSCQueue(const SPSCQueue &) = delete; - - SPSCQueue &operator=(const SPSCQueue &) = delete; - std::mutex g_mutex; - condition_variable g_con; - - void wakeUpSink(void) { - //std::unique_lock lock(g_mutex); - - g_con.notify_one(); - - - //lock.unlock(); - } - void waitForSource(void) { // printf("enter sleep\r\n"); - std::unique_lock lock(g_mutex); - g_con.wait(lock); - - // printf("end sleep\r\n"); - // pthread_mutex_lock(&mutex); - - // pthread_mutex_unlock(&mutex); - // - - - } - template - void emplace(Args &&...args) - noexcept( - std::is_nothrow_constructible::value) { - static_assert(std::is_constructible::value, - "T must be constructible with Args&&..."); - auto const writeIdx = writeIdx_.load(std::memory_order_relaxed); - auto nextWriteIdx = writeIdx + 1; - if (nextWriteIdx == capacity_) { - nextWriteIdx = 0; - } - while (nextWriteIdx == readIdxCache_) { - readIdxCache_ = readIdx_.load(std::memory_order_acquire); - } - new(&slots_[writeIdx + kPadding]) T(std::forward(args)...); - writeIdx_.store(nextWriteIdx, std::memory_order_release); - } - - template - bool try_emplace(Args &&...args) - noexcept( - std::is_nothrow_constructible::value) { - static_assert(std::is_constructible::value, - "T must be constructible with Args&&..."); - auto const writeIdx = writeIdx_.load(std::memory_order_relaxed); - auto nextWriteIdx = writeIdx + 1; - if (nextWriteIdx == capacity_) { - nextWriteIdx = 0; - } - if (nextWriteIdx == readIdxCache_) { - readIdxCache_ = readIdx_.load(std::memory_order_acquire); - if (nextWriteIdx == readIdxCache_) { - return false; - } - } - new(&slots_[writeIdx + kPadding]) T(std::forward(args)...); - writeIdx_.store(nextWriteIdx, std::memory_order_release); - return true; - } - - void push(const T &v) - noexcept(std::is_nothrow_copy_constructible::value) { - static_assert(std::is_copy_constructible::value, - "T must be copy constructible"); - emplace(v); - // g_con.notify_all(); - } - - template::value>::type> - void push(P &&v) - noexcept(std::is_nothrow_constructible::value) { - emplace(std::forward

(v)); - } - - bool - try_push(const T &v) - noexcept(std::is_nothrow_copy_constructible::value) { - static_assert(std::is_copy_constructible::value, - "T must be copy constructible"); - return try_emplace(v); - } - - template::value>::type> - bool try_push(P &&v) - noexcept(std::is_nothrow_constructible::value) { - return try_emplace(std::forward

(v)); - } - - T *front() - noexcept { - auto const readIdx = readIdx_.load(std::memory_order_relaxed); - if (readIdx == writeIdxCache_) { - writeIdxCache_ = writeIdx_.load(std::memory_order_acquire); - if (writeIdxCache_ == readIdx) { - return nullptr; - } - } - return &slots_[readIdx + kPadding]; - } - - void pop() - noexcept { - static_assert(std::is_nothrow_destructible::value, - "T must be nothrow destructible"); - auto const readIdx = readIdx_.load(std::memory_order_relaxed); - assert(writeIdx_.load(std::memory_order_acquire) != readIdx); - slots_[readIdx + kPadding].~T(); - auto nextReadIdx = readIdx + 1; - if (nextReadIdx == capacity_) { - nextReadIdx = 0; - } - readIdx_.store(nextReadIdx, std::memory_order_release); - } - - size_t size() const - noexcept { - std::ptrdiff_t diff = writeIdx_.load(std::memory_order_acquire) - - readIdx_.load(std::memory_order_acquire); - if (diff < 0) { - diff += capacity_; - } - return static_cast(diff); - } - - bool empty() const - noexcept { - - return size() == 0; - - } - - size_t capacity() const - noexcept { return capacity_ - 1; } - - private: + static_assert(alignof(SPSCQueue) == kCacheLineSize, ""); + static_assert(sizeof(SPSCQueue) >= 3 * kCacheLineSize, ""); + assert(reinterpret_cast(&readIdx_) - + reinterpret_cast(&writeIdx_) >= + static_cast(kCacheLineSize)); + } + + ~SPSCQueue() { + while (front()) { + pop(); + } + std::allocator_traits::deallocate(allocator_, slots_, + capacity_ + 2 * kPadding); + } + + // non-copyable and non-movable + SPSCQueue(const SPSCQueue &) = delete; + + SPSCQueue &operator=(const SPSCQueue &) = delete; + + std::mutex g_mutex; + condition_variable g_con; + + void wakeUpSink(void) { + //std::unique_lock lock(g_mutex); + + g_con.notify_one(); + + + //lock.unlock(); + } + + void waitForSource(void) { // printf("enter sleep\r\n"); + std::unique_lock lock(g_mutex); + g_con.wait(lock); + + // printf("end sleep\r\n"); + // pthread_mutex_lock(&mutex); + + // pthread_mutex_unlock(&mutex); + // + + + } + + template + void emplace(Args &&...args) + noexcept( + std::is_nothrow_constructible::value) { + static_assert(std::is_constructible::value, + "T must be constructible with Args&&..."); + auto const writeIdx = writeIdx_.load(std::memory_order_relaxed); + auto nextWriteIdx = writeIdx + 1; + if (nextWriteIdx == capacity_) { + nextWriteIdx = 0; + } + while (nextWriteIdx == readIdxCache_) { + readIdxCache_ = readIdx_.load(std::memory_order_acquire); + } + new(&slots_[writeIdx + kPadding]) T(std::forward(args)...); + writeIdx_.store(nextWriteIdx, std::memory_order_release); + } + + template + bool try_emplace(Args &&...args) + noexcept( + std::is_nothrow_constructible::value) { + static_assert(std::is_constructible::value, + "T must be constructible with Args&&..."); + auto const writeIdx = writeIdx_.load(std::memory_order_relaxed); + auto nextWriteIdx = writeIdx + 1; + if (nextWriteIdx == capacity_) { + nextWriteIdx = 0; + } + if (nextWriteIdx == readIdxCache_) { + readIdxCache_ = readIdx_.load(std::memory_order_acquire); + if (nextWriteIdx == readIdxCache_) { + return false; + } + } + new(&slots_[writeIdx + kPadding]) T(std::forward(args)...); + writeIdx_.store(nextWriteIdx, std::memory_order_release); + return true; + } + + void push(const T &v) + noexcept(std::is_nothrow_copy_constructible::value) { + static_assert(std::is_copy_constructible::value, + "T must be copy constructible"); + emplace(v); + // g_con.notify_all(); + } + + template::value>::type> + void push(P &&v) + noexcept(std::is_nothrow_constructible::value) { + emplace(std::forward

(v)); + } + + bool + try_push(const T &v) + noexcept(std::is_nothrow_copy_constructible::value) { + static_assert(std::is_copy_constructible::value, + "T must be copy constructible"); + return try_emplace(v); + } + + template::value>::type> + bool try_push(P &&v) + noexcept(std::is_nothrow_constructible::value) { + return try_emplace(std::forward

(v)); + } + + T *front() + noexcept { + auto const readIdx = readIdx_.load(std::memory_order_relaxed); + if (readIdx == writeIdxCache_) { + writeIdxCache_ = writeIdx_.load(std::memory_order_acquire); + if (writeIdxCache_ == readIdx) { + return nullptr; + } + } + return &slots_[readIdx + kPadding]; + } + + void pop() + noexcept { + static_assert(std::is_nothrow_destructible::value, + "T must be nothrow destructible"); + auto const readIdx = readIdx_.load(std::memory_order_relaxed); + assert(writeIdx_.load(std::memory_order_acquire) != readIdx); + slots_[readIdx + kPadding].~T(); + auto nextReadIdx = readIdx + 1; + if (nextReadIdx == capacity_) { + nextReadIdx = 0; + } + readIdx_.store(nextReadIdx, std::memory_order_release); + } + + size_t size() const + noexcept { + std::ptrdiff_t diff = writeIdx_.load(std::memory_order_acquire) - + readIdx_.load(std::memory_order_acquire); + if (diff < 0) { + diff += capacity_; + } + return static_cast(diff); + } + + bool empty() const + noexcept { + + return size() == 0; + + } + + size_t capacity() const + noexcept { return capacity_ - 1; } + + private: #ifdef __cpp_lib_hardware_interference_size - static constexpr size_t kCacheLineSize = - std::hardware_destructive_interference_size; + static constexpr size_t kCacheLineSize = + std::hardware_destructive_interference_size; #else - static constexpr size_t - kCacheLineSize = 64; + static constexpr size_t + kCacheLineSize = 64; #endif - // Padding to avoid false sharing between slots_ and adjacent allocations - static constexpr size_t - kPadding = (kCacheLineSize - 1) / sizeof(T) + 1; + // Padding to avoid false sharing between slots_ and adjacent allocations + static constexpr size_t + kPadding = (kCacheLineSize - 1) / sizeof(T) + 1; - private: - size_t capacity_; - T *slots_; + private: + size_t capacity_; + T *slots_; #if defined(__has_cpp_attribute) && __has_cpp_attribute(no_unique_address) - Allocator allocator_ [[no_unique_address]]; + Allocator allocator_ [[no_unique_address]]; #else - Allocator allocator_; + Allocator allocator_; #endif - // Align to cache line size in order to avoid false sharing - // readIdxCache_ and writeIdxCache_ is used to reduce the amount of cache - // coherency traffic - alignas(kCacheLineSize) - std::atomic writeIdx_ = {0}; - alignas(kCacheLineSize) - size_t readIdxCache_ = 0; - alignas(kCacheLineSize) - std::atomic readIdx_ = {0}; - alignas(kCacheLineSize) - size_t writeIdxCache_ = 0; - - // Padding to avoid adjacent allocations to share cache line with - // writeIdxCache_ - char padding_[kCacheLineSize - sizeof(writeIdxCache_)]; -}; + // Align to cache line size in order to avoid false sharing + // readIdxCache_ and writeIdxCache_ is used to reduce the amount of cache + // coherency traffic + alignas(kCacheLineSize) + std::atomic writeIdx_ = {0}; + alignas(kCacheLineSize) + size_t readIdxCache_ = 0; + alignas(kCacheLineSize) + std::atomic readIdx_ = {0}; + alignas(kCacheLineSize) + size_t writeIdxCache_ = 0; + + // Padding to avoid adjacent allocations to share cache line with + // writeIdxCache_ + char padding_[kCacheLineSize - sizeof(writeIdxCache_)]; + }; } // namespace rigtorp \ No newline at end of file diff --git a/include/Utils/ThreadPerf.hpp b/include/Utils/ThreadPerf.hpp index dab3d800..2f907a65 100644 --- a/include/Utils/ThreadPerf.hpp +++ b/include/Utils/ThreadPerf.hpp @@ -6,6 +6,7 @@ #ifndef INTELLISTREAM_INCLUDE_UTILS_THREADPERF_H_ #define INTELLISTREAM_INCLUDE_UTILS_THREADPERF_H_ #pragma once + #include #include #include @@ -28,68 +29,69 @@ #include #include #include + #define PERF_ERROR(n) printf(n) namespace INTELLI { /** * @enum perfTrace * @brief The low level description of perf events, used inside, don't touch me UNLESS you know what you are doing */ -enum perfTrace { - /* sw tracepoints */ - COUNT_SW_CPU_CLOCK = 0, - COUNT_SW_TASK_CLOCK = 1, - COUNT_SW_CONTEXT_SWITCHES = 2, - COUNT_SW_CPU_MIGRATIONS = 3, - COUNT_SW_PAGE_FAULTS = 4, - COUNT_SW_PAGE_FAULTS_MIN = 5, - COUNT_SW_PAGE_FAULTS_MAJ = 6, - - /* hw counters */ - COUNT_HW_CPU_CYCLES = 7, - COUNT_HW_INSTRUCTIONS = 8, - COUNT_HW_CACHE_REFERENCES = 9, - COUNT_HW_CACHE_MISSES = 10, - COUNT_HW_BRANCH_INSTRUCTIONS = 11, - COUNT_HW_BRANCH_MISSES = 12, - COUNT_HW_BUS_CYCLES = 13, - - /* cache counters */ - - /* L1D - data cache */ - COUNT_HW_CACHE_L1D_LOADS = 14, - COUNT_HW_CACHE_L1D_LOADS_MISSES = 15, - COUNT_HW_CACHE_L1D_STORES = 16, - COUNT_HW_CACHE_L1D_STORES_MISSES = 17, - COUNT_HW_CACHE_L1D_PREFETCHES = 18, - - /* L1I - instruction cache */ - COUNT_HW_CACHE_L1I_LOADS = 19, - COUNT_HW_CACHE_L1I_LOADS_MISSES = 20, - - /* LL - last level cache */ - COUNT_HW_CACHE_LL_LOADS = 21, - COUNT_HW_CACHE_LL_LOADS_MISSES = 22, - COUNT_HW_CACHE_LL_STORES = 23, - COUNT_HW_CACHE_LL_STORES_MISSES = 24, - - /* DTLB - data translation lookaside buffer */ - COUNT_HW_CACHE_DTLB_LOADS = 25, - COUNT_HW_CACHE_DTLB_LOADS_MISSES = 26, - COUNT_HW_CACHE_DTLB_STORES = 27, - COUNT_HW_CACHE_DTLB_STORES_MISSES = 28, - - /* ITLB - instructiont translation lookaside buffer */ - COUNT_HW_CACHE_ITLB_LOADS = 29, - COUNT_HW_CACHE_ITLB_LOADS_MISSES = 30, - - /* BPU - branch prediction unit */ - COUNT_HW_CACHE_BPU_LOADS = 31, - COUNT_HW_CACHE_BPU_LOADS_MISSES = 32, - - /* Special internally defined "counter" */ - /* this is the _only_ floating point value */ - //LIB_SW_WALL_TIME = 33 -}; + enum perfTrace { + /* sw tracepoints */ + COUNT_SW_CPU_CLOCK = 0, + COUNT_SW_TASK_CLOCK = 1, + COUNT_SW_CONTEXT_SWITCHES = 2, + COUNT_SW_CPU_MIGRATIONS = 3, + COUNT_SW_PAGE_FAULTS = 4, + COUNT_SW_PAGE_FAULTS_MIN = 5, + COUNT_SW_PAGE_FAULTS_MAJ = 6, + + /* hw counters */ + COUNT_HW_CPU_CYCLES = 7, + COUNT_HW_INSTRUCTIONS = 8, + COUNT_HW_CACHE_REFERENCES = 9, + COUNT_HW_CACHE_MISSES = 10, + COUNT_HW_BRANCH_INSTRUCTIONS = 11, + COUNT_HW_BRANCH_MISSES = 12, + COUNT_HW_BUS_CYCLES = 13, + + /* cache counters */ + + /* L1D - data cache */ + COUNT_HW_CACHE_L1D_LOADS = 14, + COUNT_HW_CACHE_L1D_LOADS_MISSES = 15, + COUNT_HW_CACHE_L1D_STORES = 16, + COUNT_HW_CACHE_L1D_STORES_MISSES = 17, + COUNT_HW_CACHE_L1D_PREFETCHES = 18, + + /* L1I - instruction cache */ + COUNT_HW_CACHE_L1I_LOADS = 19, + COUNT_HW_CACHE_L1I_LOADS_MISSES = 20, + + /* LL - last level cache */ + COUNT_HW_CACHE_LL_LOADS = 21, + COUNT_HW_CACHE_LL_LOADS_MISSES = 22, + COUNT_HW_CACHE_LL_STORES = 23, + COUNT_HW_CACHE_LL_STORES_MISSES = 24, + + /* DTLB - data translation lookaside buffer */ + COUNT_HW_CACHE_DTLB_LOADS = 25, + COUNT_HW_CACHE_DTLB_LOADS_MISSES = 26, + COUNT_HW_CACHE_DTLB_STORES = 27, + COUNT_HW_CACHE_DTLB_STORES_MISSES = 28, + + /* ITLB - instructiont translation lookaside buffer */ + COUNT_HW_CACHE_ITLB_LOADS = 29, + COUNT_HW_CACHE_ITLB_LOADS_MISSES = 30, + + /* BPU - branch prediction unit */ + COUNT_HW_CACHE_BPU_LOADS = 31, + COUNT_HW_CACHE_BPU_LOADS_MISSES = 32, + + /* Special internally defined "counter" */ + /* this is the _only_ floating point value */ + //LIB_SW_WALL_TIME = 33 + }; /** * @ingroup INTELLI_UTIL_OTHERC20 @@ -106,307 +108,340 @@ enum perfTrace { * - call @ref end * - get the results, by @ref getResultById, @ref getResultByName, or @ref resultToConfigMap */ -class ThreadPerf { - protected: - - /** - * @class PerfPair Utils/ThreadPerf.hpp - * @brief a record pair of perf events - */ - class PerfPair { - public: - int ref; - std::string name; - uint64_t record; - PerfPair(int _ref, std::string _name) { - ref = _ref; - name = _name; - record = 0; - } - ~PerfPair() {} - }; - class PerfTool { - private: - /** - * @class PerfEntry Utils/ThreadPerf.hpp -* @brief The low-level entry record of perf, don't touch me -*/ - class PerfEntry { - public: - //struct perf_event_attr attr; - int fds; - bool addressable; - uint64_t prevVale; - PerfEntry() { addressable = false; } - ~PerfEntry() {} - }; - /* data */ - std::vector entries; - pid_t myPid; - int myCpu; - uint64_t prevValue; + class ThreadPerf { + protected: + + /** + * @class PerfPair Utils/ThreadPerf.hpp + * @brief a record pair of perf events + */ + class PerfPair { + public: + int ref; + std::string name; + uint64_t record; + + PerfPair(int _ref, std::string _name) { + ref = _ref; + name = _name; + record = 0; + } + + ~PerfPair() {} + }; + + class PerfTool { + private: + /** + * @class PerfEntry Utils/ThreadPerf.hpp + * @brief The low-level entry record of perf, don't touch me + */ + class PerfEntry { + public: + //struct perf_event_attr attr; + int fds; + bool addressable; + uint64_t prevVale; + + PerfEntry() { addressable = false; } + + ~PerfEntry() {} + }; + + /* data */ + std::vector entries; + pid_t myPid; + int myCpu; + uint64_t prevValue; #define LIBPERF_ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) - /** - * @struct default_attrs - * @brief The low-level perf descriptions passed to OS - */ - struct perf_event_attr default_attrs[32] = { - {.type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_CPU_CLOCK}, //1 - {.type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_TASK_CLOCK}, //2 - {.type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_CONTEXT_SWITCHES},//3 - {.type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_CPU_MIGRATIONS},//4 - {.type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_PAGE_FAULTS},//5 - {.type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_PAGE_FAULTS_MIN},//6 - {.type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_PAGE_FAULTS_MAJ},//7 - {.type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_CPU_CYCLES},//8 - {.type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_INSTRUCTIONS},//9 - {.type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_CACHE_REFERENCES},//10 - {.type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_CACHE_MISSES},//11 - {.type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_BRANCH_INSTRUCTIONS},//12 - {.type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_BRANCH_MISSES},//13 - {.type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_BUS_CYCLES},//14 - //15 - {.type = PERF_TYPE_HW_CACHE, .config = (PERF_COUNT_HW_CACHE_L1D | (PERF_COUNT_HW_CACHE_OP_READ << 8) - | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))}, - //16 - {.type = PERF_TYPE_HW_CACHE, .config = (PERF_COUNT_HW_CACHE_L1D | (PERF_COUNT_HW_CACHE_OP_READ << 8) - | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))}, - //17, no x64 - {.type = PERF_TYPE_HW_CACHE, .config = (PERF_COUNT_HW_CACHE_L1D | (PERF_COUNT_HW_CACHE_OP_WRITE << 8) - | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))}, - //18, no x64, no rk3399 - {.type = PERF_TYPE_HW_CACHE, .config = (PERF_COUNT_HW_CACHE_L1D | (PERF_COUNT_HW_CACHE_OP_WRITE << 8) - | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))}, - //19, no x64 - {.type = PERF_TYPE_HW_CACHE, .config = (PERF_COUNT_HW_CACHE_L1D | (PERF_COUNT_HW_CACHE_OP_PREFETCH << 8) - | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))}, - //20 - {.type = PERF_TYPE_HW_CACHE, .config = (PERF_COUNT_HW_CACHE_L1I | (PERF_COUNT_HW_CACHE_OP_READ << 8) - | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))}, - //21, no rk3399 - {.type = PERF_TYPE_HW_CACHE, .config = (PERF_COUNT_HW_CACHE_L1I | (PERF_COUNT_HW_CACHE_OP_READ << 8) - | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))}, - //22, no rk3399 - {.type = PERF_TYPE_HW_CACHE, .config = (PERF_COUNT_HW_CACHE_LL | (PERF_COUNT_HW_CACHE_OP_READ << 8) - | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))}, - //23, no rk3399 - {.type = PERF_TYPE_HW_CACHE, .config = (PERF_COUNT_HW_CACHE_LL | (PERF_COUNT_HW_CACHE_OP_READ << 8) - | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))}, - //24,no rk3399 - {.type = PERF_TYPE_HW_CACHE, .config = (PERF_COUNT_HW_CACHE_LL | (PERF_COUNT_HW_CACHE_OP_WRITE << 8) - | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))}, - //25 - {.type = PERF_TYPE_HW_CACHE, .config = (PERF_COUNT_HW_CACHE_LL | (PERF_COUNT_HW_CACHE_OP_WRITE << 8) - | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))}, - //26 - {.type = PERF_TYPE_HW_CACHE, .config = (PERF_COUNT_HW_CACHE_DTLB | (PERF_COUNT_HW_CACHE_OP_READ << 8) - | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))}, - //27, no rk3399 - {.type = PERF_TYPE_HW_CACHE, .config = (PERF_COUNT_HW_CACHE_DTLB | (PERF_COUNT_HW_CACHE_OP_READ << 8) - | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))}, - //28 - {.type = PERF_TYPE_HW_CACHE, .config = (PERF_COUNT_HW_CACHE_DTLB | (PERF_COUNT_HW_CACHE_OP_WRITE << 8) - | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))}, - //29,no rk3399 - {.type = PERF_TYPE_HW_CACHE, .config = (PERF_COUNT_HW_CACHE_DTLB | (PERF_COUNT_HW_CACHE_OP_WRITE << 8) - | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))}, - //30 - {.type = PERF_TYPE_HW_CACHE, .config = (PERF_COUNT_HW_CACHE_ITLB | (PERF_COUNT_HW_CACHE_OP_READ << 8) - | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))}, - //31 - {.type = PERF_TYPE_HW_CACHE, .config = (PERF_COUNT_HW_CACHE_ITLB | (PERF_COUNT_HW_CACHE_OP_READ << 8) - | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))}, - //32 - {.type = PERF_TYPE_HW_CACHE, .config = (PERF_COUNT_HW_CACHE_BPU | (PERF_COUNT_HW_CACHE_OP_READ << 8) - | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))}, - /* { .type = PERF_TYPE_HW_CACHE, .config = (PERF_COUNT_HW_CACHE_BPU | (PERF_COUNT_HW_CACHE_OP_READ << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))}, */ - }; - long - sys_perf_event_open(struct perf_event_attr *hw_event, - pid_t pid, int cpu, int group_fd, - unsigned long flags) { - return syscall(__NR_perf_event_open, hw_event, pid, cpu, - group_fd, flags); - } - public: - /** - * @struct default_attrs - * @brief The low-level perf events send to OS call, don't touch me - */ + /** + * @struct default_attrs + * @brief The low-level perf descriptions passed to OS + */ + struct perf_event_attr default_attrs[32] = { + {.type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_CPU_CLOCK}, //1 + {.type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_TASK_CLOCK}, //2 + {.type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_CONTEXT_SWITCHES},//3 + {.type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_CPU_MIGRATIONS},//4 + {.type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_PAGE_FAULTS},//5 + {.type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_PAGE_FAULTS_MIN},//6 + {.type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_PAGE_FAULTS_MAJ},//7 + {.type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_CPU_CYCLES},//8 + {.type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_INSTRUCTIONS},//9 + {.type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_CACHE_REFERENCES},//10 + {.type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_CACHE_MISSES},//11 + {.type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_BRANCH_INSTRUCTIONS},//12 + {.type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_BRANCH_MISSES},//13 + {.type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_BUS_CYCLES},//14 + //15 + {.type = PERF_TYPE_HW_CACHE, .config = (PERF_COUNT_HW_CACHE_L1D | (PERF_COUNT_HW_CACHE_OP_READ << 8) + | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))}, + //16 + {.type = PERF_TYPE_HW_CACHE, .config = (PERF_COUNT_HW_CACHE_L1D | (PERF_COUNT_HW_CACHE_OP_READ << 8) + | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))}, + //17, no x64 + {.type = PERF_TYPE_HW_CACHE, .config = (PERF_COUNT_HW_CACHE_L1D | + (PERF_COUNT_HW_CACHE_OP_WRITE << 8) + | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))}, + //18, no x64, no rk3399 + {.type = PERF_TYPE_HW_CACHE, .config = (PERF_COUNT_HW_CACHE_L1D | + (PERF_COUNT_HW_CACHE_OP_WRITE << 8) + | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))}, + //19, no x64 + {.type = PERF_TYPE_HW_CACHE, .config = (PERF_COUNT_HW_CACHE_L1D | + (PERF_COUNT_HW_CACHE_OP_PREFETCH << 8) + | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))}, + //20 + {.type = PERF_TYPE_HW_CACHE, .config = (PERF_COUNT_HW_CACHE_L1I | (PERF_COUNT_HW_CACHE_OP_READ << 8) + | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))}, + //21, no rk3399 + {.type = PERF_TYPE_HW_CACHE, .config = (PERF_COUNT_HW_CACHE_L1I | (PERF_COUNT_HW_CACHE_OP_READ << 8) + | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))}, + //22, no rk3399 + {.type = PERF_TYPE_HW_CACHE, .config = (PERF_COUNT_HW_CACHE_LL | (PERF_COUNT_HW_CACHE_OP_READ << 8) + | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))}, + //23, no rk3399 + {.type = PERF_TYPE_HW_CACHE, .config = (PERF_COUNT_HW_CACHE_LL | (PERF_COUNT_HW_CACHE_OP_READ << 8) + | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))}, + //24,no rk3399 + {.type = PERF_TYPE_HW_CACHE, .config = (PERF_COUNT_HW_CACHE_LL | (PERF_COUNT_HW_CACHE_OP_WRITE << 8) + | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))}, + //25 + {.type = PERF_TYPE_HW_CACHE, .config = (PERF_COUNT_HW_CACHE_LL | (PERF_COUNT_HW_CACHE_OP_WRITE << 8) + | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))}, + //26 + {.type = PERF_TYPE_HW_CACHE, .config = (PERF_COUNT_HW_CACHE_DTLB | + (PERF_COUNT_HW_CACHE_OP_READ << 8) + | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))}, + //27, no rk3399 + {.type = PERF_TYPE_HW_CACHE, .config = (PERF_COUNT_HW_CACHE_DTLB | + (PERF_COUNT_HW_CACHE_OP_READ << 8) + | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))}, + //28 + {.type = PERF_TYPE_HW_CACHE, .config = (PERF_COUNT_HW_CACHE_DTLB | + (PERF_COUNT_HW_CACHE_OP_WRITE << 8) + | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))}, + //29,no rk3399 + {.type = PERF_TYPE_HW_CACHE, .config = (PERF_COUNT_HW_CACHE_DTLB | + (PERF_COUNT_HW_CACHE_OP_WRITE << 8) + | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))}, + //30 + {.type = PERF_TYPE_HW_CACHE, .config = (PERF_COUNT_HW_CACHE_ITLB | + (PERF_COUNT_HW_CACHE_OP_READ << 8) + | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))}, + //31 + {.type = PERF_TYPE_HW_CACHE, .config = (PERF_COUNT_HW_CACHE_ITLB | + (PERF_COUNT_HW_CACHE_OP_READ << 8) + | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))}, + //32 + {.type = PERF_TYPE_HW_CACHE, .config = (PERF_COUNT_HW_CACHE_BPU | (PERF_COUNT_HW_CACHE_OP_READ << 8) + | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))}, + /* { .type = PERF_TYPE_HW_CACHE, .config = (PERF_COUNT_HW_CACHE_BPU | (PERF_COUNT_HW_CACHE_OP_READ << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))}, */ + }; + + long + sys_perf_event_open(struct perf_event_attr *hw_event, + pid_t pid, int cpu, int group_fd, + unsigned long flags) { + return syscall(__NR_perf_event_open, hw_event, pid, cpu, + group_fd, flags); + } + + public: + /** + * @struct default_attrs + * @brief The low-level perf events send to OS call, don't touch me + */ + + PerfTool() { - PerfTool() { - - } - PerfTool(pid_t pid, int cpu) { - if (pid == -1) { pid = gettid(); } - myPid = pid; - myCpu = cpu; - int nr_counters = 32; - for (int i = 0; i < nr_counters; i++) { - PerfEntry entry; - default_attrs[i].size = sizeof(struct perf_event_attr); - entry.fds = sys_perf_event_open(&default_attrs[i], pid, cpu, -1, 0); - if (entry.fds < 0) { - entry.addressable = false; - } else { - entry.addressable = true; - ioctl(entry.fds, PERF_EVENT_IOC_DISABLE); - ioctl(entry.fds, PERF_EVENT_IOC_RESET); + } + + PerfTool(pid_t pid, int cpu) { + if (pid == -1) { pid = gettid(); } + myPid = pid; + myCpu = cpu; + int nr_counters = 32; + for (int i = 0; i < nr_counters; i++) { + PerfEntry entry; + default_attrs[i].size = sizeof(struct perf_event_attr); + entry.fds = sys_perf_event_open(&default_attrs[i], pid, cpu, -1, 0); + if (entry.fds < 0) { + entry.addressable = false; + } else { + entry.addressable = true; + ioctl(entry.fds, PERF_EVENT_IOC_DISABLE); + ioctl(entry.fds, PERF_EVENT_IOC_RESET); + } + entries.push_back(entry); + } + } + + ~PerfTool() { + for (size_t i = 0; i < entries.size(); i++) { + if (entries[i].addressable == true) { + close(entries[i].fds); + //printf("close perf %d\r\n",i); + } + } + } + + // reading result from a perf trace on [ch], will return 0 if the channel is invaild + uint64_t readPerf(size_t ch) { + if (ch > entries.size()) { + return 0; + } + if (entries[ch].addressable == false) { + return 0; + } + uint64_t value; + int ru = read(entries[ch].fds, &value, sizeof(uint64_t)); + if (ru < 0) { + PERF_ERROR("invalid read"); + } + return value; + } + + // start the perf trace on [ch] + int startPerf(size_t ch) { + ioctl(entries[ch].fds, PERF_EVENT_IOC_ENABLE); + return 1; + } + + // st the perf trace on [ch] + int stopPerf(size_t ch) { + if (ch > entries.size()) { + return -1; + } + if (entries[ch].addressable == false) { + return -1; + } + ioctl(entries[ch].fds, PERF_EVENT_IOC_DISABLE); + ioctl(entries[ch].fds, PERF_EVENT_IOC_RESET); + return 1; + } + + //check the addressability of [ch] + bool isValidChannel(size_t ch) { + if (ch > entries.size()) { + return false; + } + return entries[ch].addressable; + } + }; + + typedef std::shared_ptr PerfToolPtr; + + std::string getChValueAsString(size_t idx); + + PerfToolPtr myTool; + /** + * @brief To contain all of your interested perf events + */ + std::vector pairs; + struct timeval tstart, tend; + uint64_t latency; + public: + ThreadPerf() {} + + /** + * @brief To setup this perf to specific cpu + * @param cpu >=0 for any specific cpu, =-1 for all cpu that may run this process + */ + ThreadPerf(int cpu) { + myTool = std::make_shared(0, cpu); + //setPerfList(); + } + + /** + * @brief To set up all your interest perf events + */ + virtual void setPerfList() { + pairs.push_back(PerfPair(COUNT_HW_CPU_CYCLES, "cpuCycle")); + pairs.push_back(PerfPair(COUNT_HW_INSTRUCTIONS, "instructions")); + pairs.push_back(PerfPair(COUNT_HW_CACHE_REFERENCES, "cacheRefs")); + pairs.push_back(PerfPair(COUNT_HW_CACHE_MISSES, "cacheMiss")); + pairs.push_back(PerfPair(COUNT_SW_CPU_CLOCK, "cpuClock")); + pairs.push_back(PerfPair(COUNT_SW_TASK_CLOCK, "taskClock")); + //pairs.push_back(PerfPair(COUNT_HW_CACHE_L1I_LOADS_MISSES, "L1ILoadMiss")); } - entries.push_back(entry); - } - } - ~PerfTool() { - for (size_t i = 0; i < entries.size(); i++) { - if (entries[i].addressable == true) { - close(entries[i].fds); - //printf("close perf %d\r\n",i); + + /** + * @brief To start perf tracing + * @note call after @ref setPerfList + */ + void start() { + for (size_t i = 0; i < pairs.size(); i++) { + myTool->startPerf(pairs[i].ref); + } + gettimeofday(&tstart, NULL); + } + + /** + * @brief To end a perf tracing + */ + void end() { + gettimeofday(&tend, NULL); + for (size_t i = 0; i < pairs.size(); i++) { + pairs[i].record = myTool->readPerf(pairs[i].ref); + myTool->stopPerf(pairs[i].ref); + } + } + + /** + * @brief Get the perf result by its index of @ref PerfPair + * @param idx The index + * @return The value + */ + uint64_t getResultById(size_t idx) { + if (idx > pairs.size()) { + return 0; + } + size_t ch = pairs[idx].ref; + if (myTool->isValidChannel(ch) == false) { + return 0; + } + return pairs[idx].record; + } + + /** + * @brief Get the perf result by its name of of @ref PerfPair + * @param idx The index + * @return The value + */ + uint64_t getResultByName(string name) { + for (size_t i = 0; i < pairs.size(); i++) { + if (pairs[i].name == name) { + return pairs[i].record; + } + } + return 0; + } + + size_t timeLastUs(struct timeval ts, struct timeval te) { + int64_t s0, e0, s1, e1; + s0 = ts.tv_sec; + s1 = ts.tv_usec; + e0 = te.tv_sec; + e1 = te.tv_usec; + return 1000000 * (e0 - s0) + (e1 - s1); } - } - } - // reading result from a perf trace on [ch], will return 0 if the channel is invaild - uint64_t readPerf(size_t ch) { - if (ch > entries.size()) { - return 0; - } - if (entries[ch].addressable == false) { - return 0; - } - uint64_t value; - int ru = read(entries[ch].fds, &value, sizeof(uint64_t)); - if (ru < 0) { - PERF_ERROR("invalid read"); - } - return value; - } - // start the perf trace on [ch] - int startPerf(size_t ch) { - ioctl(entries[ch].fds, PERF_EVENT_IOC_ENABLE); - return 1; - } - // st the perf trace on [ch] - int stopPerf(size_t ch) { - if (ch > entries.size()) { - return -1; - } - if (entries[ch].addressable == false) { - return -1; - } - ioctl(entries[ch].fds, PERF_EVENT_IOC_DISABLE); - ioctl(entries[ch].fds, PERF_EVENT_IOC_RESET); - return 1; - } - //check the addressability of [ch] - bool isValidChannel(size_t ch) { - if (ch > entries.size()) { - return false; - } - return entries[ch].addressable; - } - }; - typedef std::shared_ptr PerfToolPtr; - std::string getChValueAsString(size_t idx); - - PerfToolPtr myTool; - /** - * @brief To contain all of your interested perf events - */ - std::vector pairs; - struct timeval tstart, tend; - uint64_t latency; - public: - ThreadPerf() {} - /** - * @brief To setup this perf to specific cpu - * @param cpu >=0 for any specific cpu, =-1 for all cpu that may run this process - */ - ThreadPerf(int cpu) { - myTool = std::make_shared(0, cpu); - //setPerfList(); - } - /** - * @brief To set up all your interest perf events - */ - virtual void setPerfList() { - pairs.push_back(PerfPair(COUNT_HW_CPU_CYCLES, "cpuCycle")); - pairs.push_back(PerfPair(COUNT_HW_INSTRUCTIONS, "instructions")); - pairs.push_back(PerfPair(COUNT_HW_CACHE_REFERENCES, "cacheRefs")); - pairs.push_back(PerfPair(COUNT_HW_CACHE_MISSES, "cacheMiss")); - pairs.push_back(PerfPair(COUNT_SW_CPU_CLOCK, "cpuClock")); - pairs.push_back(PerfPair(COUNT_SW_TASK_CLOCK, "taskClock")); - //pairs.push_back(PerfPair(COUNT_HW_CACHE_L1I_LOADS_MISSES, "L1ILoadMiss")); - } - /** - * @brief To start perf tracing - * @note call after @ref setPerfList - */ - void start() { - for (size_t i = 0; i < pairs.size(); i++) { - myTool->startPerf(pairs[i].ref); - } - gettimeofday(&tstart, NULL); - } - /** - * @brief To end a perf tracing - */ - void end() { - gettimeofday(&tend, NULL); - for (size_t i = 0; i < pairs.size(); i++) { - pairs[i].record = myTool->readPerf(pairs[i].ref); - myTool->stopPerf(pairs[i].ref); - } - } - /** - * @brief Get the perf result by its index of @ref PerfPair - * @param idx The index - * @return The value - */ - uint64_t getResultById(size_t idx) { - if (idx > pairs.size()) { - return 0; - } - size_t ch = pairs[idx].ref; - if (myTool->isValidChannel(ch) == false) { - return 0; - } - return pairs[idx].record; - } - /** - * @brief Get the perf result by its name of of @ref PerfPair - * @param idx The index - * @return The value - */ - uint64_t getResultByName(string name) { - for (size_t i = 0; i < pairs.size(); i++) { - if (pairs[i].name == name) { - return pairs[i].record; - } - } - return 0; - } - size_t timeLastUs(struct timeval ts, struct timeval te) { - int64_t s0, e0, s1, e1; - s0 = ts.tv_sec; - s1 = ts.tv_usec; - e0 = te.tv_sec; - e1 = te.tv_usec; - return 1000000 * (e0 - s0) + (e1 - s1); - } - /** - * @brief convert the perf result into a @ref ConfigMap - * @return The key-value store of configMap, in shared pointer - * @note must stop after calling stop - */ - ConfigMapPtr resultToConfigMap() { - ConfigMapPtr ru = newConfigMap(); - for (size_t i = 0; i < pairs.size(); i++) { - ru->edit(pairs[i].name, (uint64_t) pairs[i].record); - } - //additional test the elapsed time - ru->edit("perfElapsedTime", (uint64_t) timeLastUs(tstart, tend)); - return ru; - } -}; + + /** + * @brief convert the perf result into a @ref ConfigMap + * @return The key-value store of configMap, in shared pointer + * @note must stop after calling stop + */ + ConfigMapPtr resultToConfigMap() { + ConfigMapPtr ru = newConfigMap(); + for (size_t i = 0; i < pairs.size(); i++) { + ru->edit(pairs[i].name, (uint64_t) pairs[i].record); + } + //additional test the elapsed time + ru->edit("perfElapsedTime", (uint64_t) timeLastUs(tstart, tend)); + return ru; + } + }; } #endif //INTELLISTREAM_INCLUDE_UTILS_THREADPERF_H_ diff --git a/include/Utils/UtilityFunctions.h b/include/Utils/UtilityFunctions.h index a57d467c..2f5f1337 100755 --- a/include/Utils/UtilityFunctions.h +++ b/include/Utils/UtilityFunctions.h @@ -20,69 +20,71 @@ #include namespace INTELLI { -typedef std::shared_ptr> BarrierPtr; + typedef std::shared_ptr> BarrierPtr; #define TIME_LAST_UNIT_MS 1000 #define TIME_LAST_UNIT_US 1000000 /** * @defgroup */ -class UtilityFunctions { - - public: - UtilityFunctions(); - - //static std::shared_ptr> createBarrier(int count); - - // static void timerStart(Result &result); - - //static void timerEnd(Result &result); - - static size_t timeLast(struct timeval past, struct timeval now); - - static size_t timeLastUs(struct timeval past); - - //bind to CPU - /*! - bind to CPU - \li bind the thread to core according to id - \param id the core you plan to bind, -1 means let os decide - \return cpuId, the real core that bind to - \todo unsure about hyper-thread - */ - static int bind2Core(int id); - //partition - - static std::vector avgPartitionSizeFinal(size_t inS, std::vector partitionWeight); - - static std::vector weightedPartitionSizeFinal(size_t inS, std::vector partitionWeight); - - static size_t to_periodical(size_t val, size_t period) { - if (val < period) { - return val; - } - size_t ru = val % period; - /* if(ru==0) - { - return period; - }*/ - return ru; - } - static double relativeFrobeniusNorm(torch::Tensor A, torch::Tensor B) { - torch::Tensor error = A - B; - double frobeniusNormA = A.norm().item(); - double frobeniusNormError = error.norm().item(); - - return frobeniusNormError / frobeniusNormA; - } - static double errorBoundRatio(torch::Tensor A, torch::Tensor B) { - torch::Tensor error = A - B; - double frobeniusNormA = A.norm().item(); - double frobeniusNormB = B.norm().item(); - double frobeniusNormError = error.norm().item(); - - return frobeniusNormError / frobeniusNormA / frobeniusNormB; - } -}; + class UtilityFunctions { + + public: + UtilityFunctions(); + + //static std::shared_ptr> createBarrier(int count); + + // static void timerStart(Result &result); + + //static void timerEnd(Result &result); + + static size_t timeLast(struct timeval past, struct timeval now); + + static size_t timeLastUs(struct timeval past); + + //bind to CPU + /*! + bind to CPU + \li bind the thread to core according to id + \param id the core you plan to bind, -1 means let os decide + \return cpuId, the real core that bind to + \todo unsure about hyper-thread + */ + static int bind2Core(int id); + //partition + + static std::vector avgPartitionSizeFinal(size_t inS, std::vector partitionWeight); + + static std::vector weightedPartitionSizeFinal(size_t inS, std::vector partitionWeight); + + static size_t to_periodical(size_t val, size_t period) { + if (val < period) { + return val; + } + size_t ru = val % period; + /* if(ru==0) + { + return period; + }*/ + return ru; + } + + static double relativeFrobeniusNorm(torch::Tensor A, torch::Tensor B) { + torch::Tensor error = A - B; + double frobeniusNormA = A.norm().item(); + double frobeniusNormError = error.norm().item(); + + return frobeniusNormError / frobeniusNormA; + } + + static double errorBoundRatio(torch::Tensor A, torch::Tensor B) { + torch::Tensor error = A - B; + double frobeniusNormA = A.norm().item(); + double frobeniusNormB = B.norm().item(); + double frobeniusNormError = error.norm().item(); + + return frobeniusNormError / frobeniusNormA / frobeniusNormB; + } + }; } #endif //IntelliStream_SRC_UTILS_UTILITYFUNCTIONS_HPP_ diff --git a/src/CPPAlgos/AbstractCPPAlgo.cpp b/src/CPPAlgos/AbstractCPPAlgo.cpp index c78b6dea..f58efc17 100644 --- a/src/CPPAlgos/AbstractCPPAlgo.cpp +++ b/src/CPPAlgos/AbstractCPPAlgo.cpp @@ -6,23 +6,26 @@ #include #include #include + void AMMBench::AbstractCPPAlgo::setConfig(INTELLI::ConfigMapPtr cfg) { - assert(cfg); + assert(cfg); } + torch::Tensor AMMBench::AbstractCPPAlgo::amm(torch::Tensor A, torch::Tensor B, uint64_t sketchSize) { - assert(sketchSize); - struct timeval tstart; - //INTELLI_INFO("I am mm"); - gettimeofday(&tstart,NULL); - auto C= torch::matmul(A, B); - fABTime=INTELLI::UtilityFunctions::timeLastUs(tstart); - return C; + assert(sketchSize); + struct timeval tstart; + //INTELLI_INFO("I am mm"); + gettimeofday(&tstart, NULL); + auto C = torch::matmul(A, B); + fABTime = INTELLI::UtilityFunctions::timeLastUs(tstart); + return C; } + INTELLI::ConfigMapPtr AMMBench::AbstractCPPAlgo::getBreakDown() { - INTELLI::ConfigMapPtr cfg = newConfigMap(); - cfg->edit("buildATime", (uint64_t)buildATime); - cfg->edit("buildBTime", (uint64_t)buildBTime); - cfg->edit("fABTime", (uint64_t)fABTime); - cfg->edit("postProcessTime",(uint64_t)postProcessTime); - return cfg; + INTELLI::ConfigMapPtr cfg = newConfigMap(); + cfg->edit("buildATime", (uint64_t) buildATime); + cfg->edit("buildBTime", (uint64_t) buildBTime); + cfg->edit("fABTime", (uint64_t) fABTime); + cfg->edit("postProcessTime", (uint64_t) postProcessTime); + return cfg; } \ No newline at end of file diff --git a/src/CPPAlgos/BCRSCPPAlgo.cpp b/src/CPPAlgos/BCRSCPPAlgo.cpp index 2bc761b2..dfa893e6 100644 --- a/src/CPPAlgos/BCRSCPPAlgo.cpp +++ b/src/CPPAlgos/BCRSCPPAlgo.cpp @@ -4,32 +4,32 @@ #include namespace AMMBench { -torch::Tensor BCRSCPPAlgo::amm(torch::Tensor A, torch::Tensor B, uint64_t k2) { - A = A.t(); - auto A_size = A.sizes(); - int64_t n = A_size[0]; - //int64_t m = A_size[1]; - int64_t k = (int64_t) k2; - assert(n == B.size(0)); - //TORCH_CHECK(n == B.size(0)); - //TORCH_CHECK(k < n); + torch::Tensor BCRSCPPAlgo::amm(torch::Tensor A, torch::Tensor B, uint64_t k2) { + A = A.t(); + auto A_size = A.sizes(); + int64_t n = A_size[0]; + //int64_t m = A_size[1]; + int64_t k = (int64_t) k2; + assert(n == B.size(0)); + //TORCH_CHECK(n == B.size(0)); + //TORCH_CHECK(k < n); - //INTELLI_INFO("Running Bernoulli CRS CPP"); + //INTELLI_INFO("Running Bernoulli CRS CPP"); - // probability distribution - torch::Tensor sample = torch::rand({n}); // default: uniform - sample = sample.div(sample.sum() / k); // sum = k as per the paper + // probability distribution + torch::Tensor sample = torch::rand({n}); // default: uniform + sample = sample.div(sample.sum() / k); // sum = k as per the paper - // diagonal scaling matrix P (nxn) - torch::Tensor P = torch::diag(1.0 / torch::sqrt(sample)); + // diagonal scaling matrix P (nxn) + torch::Tensor P = torch::diag(1.0 / torch::sqrt(sample)); - // random diagonal sampling matrix K (nxn) - sample = (torch::rand({n}) < sample).to(torch::kFloat32); - torch::Tensor K = torch::diag(sample); + // random diagonal sampling matrix K (nxn) + sample = (torch::rand({n}) < sample).to(torch::kFloat32); + torch::Tensor K = torch::diag(sample); - torch::Tensor a = torch::matmul(torch::matmul(A.t(), P), K); - torch::Tensor b = torch::matmul(torch::matmul(a, K), P); + torch::Tensor a = torch::matmul(torch::matmul(A.t(), P), K); + torch::Tensor b = torch::matmul(torch::matmul(a, K), P); - return torch::matmul(b, B); -} + return torch::matmul(b, B); + } } \ No newline at end of file diff --git a/src/CPPAlgos/BetaCoOFDCPPAlgo.cpp b/src/CPPAlgos/BetaCoOFDCPPAlgo.cpp index 0b315fc8..d5cb5c3c 100644 --- a/src/CPPAlgos/BetaCoOFDCPPAlgo.cpp +++ b/src/CPPAlgos/BetaCoOFDCPPAlgo.cpp @@ -2,115 +2,118 @@ // Created by haolan on 5/30/23. // #include + torch::Scalar get_first_element(const torch::Tensor &tensor) { - if (tensor.numel() == 1) { - return tensor.item(); - } else { - return tensor[0].item(); - } + if (tensor.numel() == 1) { + return tensor.item(); + } else { + return tensor[0].item(); + } } bool is_empty_tensor(const torch::Tensor &tensor) { - return tensor.numel() == 0; + return tensor.numel() == 0; } namespace AMMBench { -torch::Tensor attenuate(float beta, const torch::Tensor &k, int l) { - return (torch::exp(k * beta / (l - 1)) - 1) / (torch::exp(torch::tensor(beta)) - 1); -} + torch::Tensor attenuate(float beta, const torch::Tensor &k, int l) { + return (torch::exp(k * beta / (l - 1)) - 1) / (torch::exp(torch::tensor(beta)) - 1); + } -torch::Tensor paramerizedReduceRank(const torch::Tensor &SV, float delta, int l, float beta) { - torch::Tensor indices = torch::arange(l, torch::kFloat32); - torch::Tensor attenuated_values = attenuate(beta, indices, l); - torch::Tensor parameterizedReduceRank = delta * attenuated_values; - torch::Tensor SV_shrunk = torch::clamp(SV - parameterizedReduceRank, 0); - return SV_shrunk; -} -void AMMBench::BetaCoOFDCPPAlgo::setConfig(INTELLI::ConfigMapPtr cfg) { - algoBeta=cfg->tryDouble("algoBeta",1.0,true); -} -torch::Tensor BetaCoOFDCPPAlgo::amm(const torch::Tensor A, const torch::Tensor B, uint64_t l2) { - torch::Tensor B_t = B.t(); - float beta =algoBeta; - INTELLI_INFO("BETA-COOCURRING, use beta "+ to_string(beta)); - TORCH_CHECK(A.size(1) == B_t.size(1), "Shapes of A and B are incompatible"); - int64_t mx = A.size(0); - int64_t my = B_t.size(0); - int64_t n = A.size(1); - int64_t l = (int64_t) l2; - // Initialize sketch matrices - torch::Tensor BX = torch::zeros({mx, l}); - torch::Tensor BY = torch::zeros({my, l}); - - // The first l iterations - for (int64_t i = 0; i < l; ++i) { - BX.slice(1, i, i + 1) = A.slice(1, i, i + 1); - BY.slice(1, i, i + 1) = B_t.slice(1, i, i + 1); - } - - torch::Tensor zero_columns = torch::tensor({0}); - zero_columns = zero_columns.slice(0, 1); - - // Iteration l to n: insert if available, else shrink sketch matrices - for (int64_t i = l; i < n; ++i) { - // Acquire the index of a zero-valued column - if (!is_empty_tensor(zero_columns)) { - int idx = get_first_element(zero_columns).toInt(); - BX.slice(1, idx, idx + 1) = A.slice(1, i, i + 1); - BY.slice(1, idx, idx + 1) = B_t.slice(1, i, i + 1); - zero_columns = zero_columns.slice(0, 1); + torch::Tensor paramerizedReduceRank(const torch::Tensor &SV, float delta, int l, float beta) { + torch::Tensor indices = torch::arange(l, torch::kFloat32); + torch::Tensor attenuated_values = attenuate(beta, indices, l); + torch::Tensor parameterizedReduceRank = delta * attenuated_values; + torch::Tensor SV_shrunk = torch::clamp(SV - parameterizedReduceRank, 0); + return SV_shrunk; } - // If no zero-valued column, shrink accordingly - else { - torch::Tensor QX, RX; - std::tie(QX, RX) = torch::linalg_qr(BX); - torch::Tensor QY, RY; - std::tie(QY, RY) = torch::linalg_qr(BY); - torch::Tensor U, SV, V; - std::tie(U, SV, V) = torch::svd(torch::matmul(RX, RY.t())); - - // Find the median of singular values - torch::Tensor S_sorted, S_indices; - std::tie(S_sorted, S_indices) = SV.sort(); - - float delta; - if (S_sorted.size(0) % 2 == 1) { - delta = S_sorted[S_sorted.size(0) / 2].item().toFloat(); - } else { - delta = torch::median(S_sorted).item().toFloat(); - } - // delta = S_sorted[0].item().toFloat(); - - // Shrink the singular values with delta - torch::Tensor SV_shrunk = paramerizedReduceRank(SV, delta, l, beta); - - // Restore SV diagonal matrix - SV = torch::diag_embed(SV_shrunk); - torch::Tensor SV_sqrt = torch::sqrt(SV); - - // Update indices of zero-valued columns - torch::Tensor zero_indices = torch::nonzero(SV_shrunk == 0).squeeze(); - zero_indices = zero_indices.view({-1}); // Convert to a 1D tensor - zero_columns = torch::cat({zero_columns, zero_indices}); - // Convert tensor to a std::vector - std::vector - vec(zero_columns.data_ptr(), zero_columns.data_ptr() + zero_columns.numel()); - - // Sort the vector - std::sort(vec.begin(), vec.end()); - - // Remove duplicates - vec.erase(std::unique(vec.begin(), vec.end()), vec.end()); - - // Convert std::vector back to a tensor - zero_columns = torch::from_blob(vec.data(), {static_cast(vec.size())}, torch::kInt64).clone(); - - // Update sketch matrices - BX = torch::matmul(torch::matmul(QX, U), SV_sqrt); - BY = torch::matmul(torch::matmul(QY, V), SV_sqrt); + + void AMMBench::BetaCoOFDCPPAlgo::setConfig(INTELLI::ConfigMapPtr cfg) { + algoBeta = cfg->tryDouble("algoBeta", 1.0, true); } - } - return torch::matmul(BX, BY.t()); -} + torch::Tensor BetaCoOFDCPPAlgo::amm(const torch::Tensor A, const torch::Tensor B, uint64_t l2) { + torch::Tensor B_t = B.t(); + float beta = algoBeta; + INTELLI_INFO("BETA-COOCURRING, use beta " + to_string(beta)); + TORCH_CHECK(A.size(1) == B_t.size(1), "Shapes of A and B are incompatible"); + int64_t mx = A.size(0); + int64_t my = B_t.size(0); + int64_t n = A.size(1); + int64_t l = (int64_t) l2; + // Initialize sketch matrices + torch::Tensor BX = torch::zeros({mx, l}); + torch::Tensor BY = torch::zeros({my, l}); + + // The first l iterations + for (int64_t i = 0; i < l; ++i) { + BX.slice(1, i, i + 1) = A.slice(1, i, i + 1); + BY.slice(1, i, i + 1) = B_t.slice(1, i, i + 1); + } + + torch::Tensor zero_columns = torch::tensor({0}); + zero_columns = zero_columns.slice(0, 1); + + // Iteration l to n: insert if available, else shrink sketch matrices + for (int64_t i = l; i < n; ++i) { + // Acquire the index of a zero-valued column + if (!is_empty_tensor(zero_columns)) { + int idx = get_first_element(zero_columns).toInt(); + BX.slice(1, idx, idx + 1) = A.slice(1, i, i + 1); + BY.slice(1, idx, idx + 1) = B_t.slice(1, i, i + 1); + zero_columns = zero_columns.slice(0, 1); + } + // If no zero-valued column, shrink accordingly + else { + torch::Tensor QX, RX; + std::tie(QX, RX) = torch::linalg_qr(BX); + torch::Tensor QY, RY; + std::tie(QY, RY) = torch::linalg_qr(BY); + torch::Tensor U, SV, V; + std::tie(U, SV, V) = torch::svd(torch::matmul(RX, RY.t())); + + // Find the median of singular values + torch::Tensor S_sorted, S_indices; + std::tie(S_sorted, S_indices) = SV.sort(); + + float delta; + if (S_sorted.size(0) % 2 == 1) { + delta = S_sorted[S_sorted.size(0) / 2].item().toFloat(); + } else { + delta = torch::median(S_sorted).item().toFloat(); + } + // delta = S_sorted[0].item().toFloat(); + + // Shrink the singular values with delta + torch::Tensor SV_shrunk = paramerizedReduceRank(SV, delta, l, beta); + + // Restore SV diagonal matrix + SV = torch::diag_embed(SV_shrunk); + torch::Tensor SV_sqrt = torch::sqrt(SV); + + // Update indices of zero-valued columns + torch::Tensor zero_indices = torch::nonzero(SV_shrunk == 0).squeeze(); + zero_indices = zero_indices.view({-1}); // Convert to a 1D tensor + zero_columns = torch::cat({zero_columns, zero_indices}); + // Convert tensor to a std::vector + std::vector + vec(zero_columns.data_ptr(), zero_columns.data_ptr() + zero_columns.numel()); + + // Sort the vector + std::sort(vec.begin(), vec.end()); + + // Remove duplicates + vec.erase(std::unique(vec.begin(), vec.end()), vec.end()); + + // Convert std::vector back to a tensor + zero_columns = torch::from_blob(vec.data(), {static_cast(vec.size())}, torch::kInt64).clone(); + + // Update sketch matrices + BX = torch::matmul(torch::matmul(QX, U), SV_sqrt); + BY = torch::matmul(torch::matmul(QY, V), SV_sqrt); + } + } + + return torch::matmul(BX, BY.t()); + } } \ No newline at end of file diff --git a/src/CPPAlgos/CPPAlgoTable.cpp b/src/CPPAlgos/CPPAlgoTable.cpp index 757cd422..24cb46eb 100644 --- a/src/CPPAlgos/CPPAlgoTable.cpp +++ b/src/CPPAlgos/CPPAlgoTable.cpp @@ -16,20 +16,21 @@ #include #include #include + namespace AMMBench { -AMMBench::CPPAlgoTable::CPPAlgoTable() { - algoMap["mm"] = newAbstractCPPAlgo(); - algoMap["crs"] = newCRSCPPAlgo(); - algoMap["crsV2"] = newCRSV2CPPAlgo(); - algoMap["countSketch"] = newCountSketchCPPAlgo(); - algoMap["bcrs"] = newBCRSCPPAlgo(); - algoMap["ews"] = newEWSCPPAlgo(); - algoMap["cooFD"] = newCoOccurringFDCPPAlgo(); - algoMap["bcooFD"] = newBetaCoOFDCPPAlgo(); - algoMap["int8"] = newINT8CPPAlgo(); - algoMap["tugOfWar"] = newTugOfWarCPPAlgo(); - algoMap["weighted-cr"] = newWeightedCRCPPAlgo(); - algoMap["smp-pca"] = newSMPPCACPPAlgo(); -} + AMMBench::CPPAlgoTable::CPPAlgoTable() { + algoMap["mm"] = newAbstractCPPAlgo(); + algoMap["crs"] = newCRSCPPAlgo(); + algoMap["crsV2"] = newCRSV2CPPAlgo(); + algoMap["countSketch"] = newCountSketchCPPAlgo(); + algoMap["bcrs"] = newBCRSCPPAlgo(); + algoMap["ews"] = newEWSCPPAlgo(); + algoMap["cooFD"] = newCoOccurringFDCPPAlgo(); + algoMap["bcooFD"] = newBetaCoOFDCPPAlgo(); + algoMap["int8"] = newINT8CPPAlgo(); + algoMap["tugOfWar"] = newTugOfWarCPPAlgo(); + algoMap["weighted-cr"] = newWeightedCRCPPAlgo(); + algoMap["smp-pca"] = newSMPPCACPPAlgo(); + } } // AMMBench diff --git a/src/CPPAlgos/CRSCPPAlgo.cpp b/src/CPPAlgos/CRSCPPAlgo.cpp index 03a33021..e394a141 100644 --- a/src/CPPAlgos/CRSCPPAlgo.cpp +++ b/src/CPPAlgos/CRSCPPAlgo.cpp @@ -5,29 +5,29 @@ #include namespace AMMBench { -torch::Tensor AMMBench::CRSCPPAlgo::amm(torch::Tensor A, torch::Tensor B, uint64_t k) { - A = A.t(); - //INTELLI_INFO("I am CPP-CRS"); - int64_t n = A.size(0); - //int64_t m = A.size(1); + torch::Tensor AMMBench::CRSCPPAlgo::amm(torch::Tensor A, torch::Tensor B, uint64_t k) { + A = A.t(); + //INTELLI_INFO("I am CPP-CRS"); + int64_t n = A.size(0); + //int64_t m = A.size(1); - assert(n == B.size(0)); - // Probability distribution - torch::Tensor probs = torch::ones(n) / n; // default: uniform + assert(n == B.size(0)); + // Probability distribution + torch::Tensor probs = torch::ones(n) / n; // default: uniform - // Sample k indices from range 0 to n for given probability distribution - torch::Tensor indices = torch::multinomial(probs, k, true); + // Sample k indices from range 0 to n for given probability distribution + torch::Tensor indices = torch::multinomial(probs, k, true); - // Sample k columns from A - torch::Tensor A_sampled = A.index_select(0, indices); - int64_t ratio = std::ceil(static_cast(n) / k); - A_sampled = (A_sampled / (int) k).t().div(probs.index_select(0, torch::arange(0, n, ratio))); + // Sample k columns from A + torch::Tensor A_sampled = A.index_select(0, indices); + int64_t ratio = std::ceil(static_cast(n) / k); + A_sampled = (A_sampled / (int) k).t().div(probs.index_select(0, torch::arange(0, n, ratio))); - // Sample k rows from B - torch::Tensor B_sampled = B.index_select(0, indices); + // Sample k rows from B + torch::Tensor B_sampled = B.index_select(0, indices); - // Compute the matrix product - torch::Tensor result = torch::matmul(A_sampled, B_sampled); - return result; -} + // Compute the matrix product + torch::Tensor result = torch::matmul(A_sampled, B_sampled); + return result; + } } // AMMBench \ No newline at end of file diff --git a/src/CPPAlgos/CRSV2CPPAlgo.cpp b/src/CPPAlgos/CRSV2CPPAlgo.cpp index d8222695..bf1de09d 100644 --- a/src/CPPAlgos/CRSV2CPPAlgo.cpp +++ b/src/CPPAlgos/CRSV2CPPAlgo.cpp @@ -5,36 +5,36 @@ #include namespace AMMBench { -torch::Tensor AMMBench::CRSV2CPPAlgo::amm(torch::Tensor A, torch::Tensor B, uint64_t k2) { - A = A.t(); - auto A_size = A.sizes(); - int64_t n = A_size[0]; - // int64_t m = A_size[1]; - int64_t k = (int64_t) k2; - assert(n == B.size(0)); - //TORCH_CHECK(n == B.size(0)); - //TORCH_CHECK(k < n); - - //INTELLI_INFO("Running CRS V2 CPP"); - - // probability distribution - torch::Tensor sample = torch::rand({n}); // default: uniform - - // diagonal scaling matrix D (nxn) - sample = sample.div(sample.sum()); - torch::Tensor D = torch::diag(1.0 / torch::sqrt(k * sample)); - - // sampling matrix S (kxn) - torch::Tensor column_indices = torch::multinomial(sample, k, true); - torch::Tensor S = torch::zeros({k, n}); - for (int64_t row = 0; row < k; row++) { - int64_t col = column_indices[row].item(); - S[row][col] = 1; - } - - torch::Tensor a = torch::matmul(torch::matmul(A.t(), D), S.t()); - torch::Tensor b = torch::matmul(torch::matmul(a, S), D); - - return torch::matmul(b, B); -} + torch::Tensor AMMBench::CRSV2CPPAlgo::amm(torch::Tensor A, torch::Tensor B, uint64_t k2) { + A = A.t(); + auto A_size = A.sizes(); + int64_t n = A_size[0]; + // int64_t m = A_size[1]; + int64_t k = (int64_t) k2; + assert(n == B.size(0)); + //TORCH_CHECK(n == B.size(0)); + //TORCH_CHECK(k < n); + + //INTELLI_INFO("Running CRS V2 CPP"); + + // probability distribution + torch::Tensor sample = torch::rand({n}); // default: uniform + + // diagonal scaling matrix D (nxn) + sample = sample.div(sample.sum()); + torch::Tensor D = torch::diag(1.0 / torch::sqrt(k * sample)); + + // sampling matrix S (kxn) + torch::Tensor column_indices = torch::multinomial(sample, k, true); + torch::Tensor S = torch::zeros({k, n}); + for (int64_t row = 0; row < k; row++) { + int64_t col = column_indices[row].item(); + S[row][col] = 1; + } + + torch::Tensor a = torch::matmul(torch::matmul(A.t(), D), S.t()); + torch::Tensor b = torch::matmul(torch::matmul(a, S), D); + + return torch::matmul(b, B); + } } // AMMBench diff --git a/src/CPPAlgos/CoOccurringFDCPPAlgo.cpp b/src/CPPAlgos/CoOccurringFDCPPAlgo.cpp index 0b531bd1..d0874cc9 100644 --- a/src/CPPAlgos/CoOccurringFDCPPAlgo.cpp +++ b/src/CPPAlgos/CoOccurringFDCPPAlgo.cpp @@ -4,101 +4,101 @@ #include namespace AMMBench { -torch::Scalar get_first_element(const torch::Tensor &tensor) { - if (tensor.numel() == 1) { - return tensor.item(); - } else { - return tensor[0].item(); - } -} - -bool is_empty_tensor(const torch::Tensor &tensor) { - return tensor.numel() == 0; -} - -torch::Tensor medianReduceRank(const torch::Tensor &SV, float delta) { - return torch::clamp(SV - delta, 0); -} - -torch::Tensor CoOccurringFDCPPAlgo::amm(const torch::Tensor A, const torch::Tensor B, uint64_t l2) { - torch::Tensor B_t = B.t(); - - TORCH_CHECK(A.size(1) == B_t.size(1), "Shapes of A and B are incompatible"); - int64_t mx = A.size(0); - int64_t my = B_t.size(0); - int64_t n = A.size(1); - int64_t l = (int64_t) l2; - // Initialize sketch matrices - torch::Tensor BX = torch::zeros({mx, l}); - torch::Tensor BY = torch::zeros({my, l}); - - // The first l iterations - for (int i = 0; i < l; ++i) { - BX.slice(1, i, i + 1) = A.slice(1, i, i + 1); - BY.slice(1, i, i + 1) = B_t.slice(1, i, i + 1); - } - - torch::Tensor zero_columns = torch::tensor({0}); - zero_columns = zero_columns.slice(0, 1); - - // Iteration l to n: insert if available, else shrink sketch matrices - for (int i = l; i < n; ++i) { - // Acquire the index of a zero-valued column - if (!is_empty_tensor(zero_columns)) { - int idx = get_first_element(zero_columns).toInt(); - BX.slice(1, idx, idx + 1) = A.slice(1, i, i + 1); - BY.slice(1, idx, idx + 1) = B_t.slice(1, i, i + 1); - zero_columns = zero_columns.slice(0, 1); + torch::Scalar get_first_element(const torch::Tensor &tensor) { + if (tensor.numel() == 1) { + return tensor.item(); + } else { + return tensor[0].item(); + } } - // If no zero-valued column, shrink accordingly - else { - torch::Tensor QX, RX; - std::tie(QX, RX) = torch::linalg_qr(BX); - torch::Tensor QY, RY; - std::tie(QY, RY) = torch::linalg_qr(BY); - torch::Tensor U, SV, V; - std::tie(U, SV, V) = torch::svd(torch::matmul(RX, RY.t())); - - // Find the median of singular values - torch::Tensor S_sorted, S_indices; - std::tie(S_sorted, S_indices) = SV.sort(); - - float delta; - if (S_sorted.size(0) % 2 == 1) { - delta = S_sorted[S_sorted.size(0) / 2].item().toFloat(); - } else { - delta = torch::median(S_sorted).item().toFloat(); - } - // Shrink the singular values with delta - torch::Tensor SV_shrunk = medianReduceRank(SV, delta); - - // Restore SV diagonal matrix - SV = torch::diag_embed(SV_shrunk); - torch::Tensor SV_sqrt = torch::sqrt(SV); - - // Update indices of zero-valued columns - torch::Tensor zero_indices = torch::nonzero(SV_shrunk == 0).squeeze(); - zero_columns = torch::cat({zero_columns, zero_indices}); - - // Convert tensor to a std::vector - std::vector - vec(zero_columns.data_ptr(), zero_columns.data_ptr() + zero_columns.numel()); - - // Sort the vector - std::sort(vec.begin(), vec.end()); - - // Remove duplicates - vec.erase(std::unique(vec.begin(), vec.end()), vec.end()); - - // Convert std::vector back to a tensor - zero_columns = torch::from_blob(vec.data(), {static_cast(vec.size())}, torch::kInt64).clone(); - - // Update sketch matrices - BX = torch::matmul(torch::matmul(QX, U), SV_sqrt); - BY = torch::matmul(torch::matmul(QY, V), SV_sqrt); + + bool is_empty_tensor(const torch::Tensor &tensor) { + return tensor.numel() == 0; + } + + torch::Tensor medianReduceRank(const torch::Tensor &SV, float delta) { + return torch::clamp(SV - delta, 0); } - } - return torch::matmul(BX, BY.t()); -} + torch::Tensor CoOccurringFDCPPAlgo::amm(const torch::Tensor A, const torch::Tensor B, uint64_t l2) { + torch::Tensor B_t = B.t(); + + TORCH_CHECK(A.size(1) == B_t.size(1), "Shapes of A and B are incompatible"); + int64_t mx = A.size(0); + int64_t my = B_t.size(0); + int64_t n = A.size(1); + int64_t l = (int64_t) l2; + // Initialize sketch matrices + torch::Tensor BX = torch::zeros({mx, l}); + torch::Tensor BY = torch::zeros({my, l}); + + // The first l iterations + for (int i = 0; i < l; ++i) { + BX.slice(1, i, i + 1) = A.slice(1, i, i + 1); + BY.slice(1, i, i + 1) = B_t.slice(1, i, i + 1); + } + + torch::Tensor zero_columns = torch::tensor({0}); + zero_columns = zero_columns.slice(0, 1); + + // Iteration l to n: insert if available, else shrink sketch matrices + for (int i = l; i < n; ++i) { + // Acquire the index of a zero-valued column + if (!is_empty_tensor(zero_columns)) { + int idx = get_first_element(zero_columns).toInt(); + BX.slice(1, idx, idx + 1) = A.slice(1, i, i + 1); + BY.slice(1, idx, idx + 1) = B_t.slice(1, i, i + 1); + zero_columns = zero_columns.slice(0, 1); + } + // If no zero-valued column, shrink accordingly + else { + torch::Tensor QX, RX; + std::tie(QX, RX) = torch::linalg_qr(BX); + torch::Tensor QY, RY; + std::tie(QY, RY) = torch::linalg_qr(BY); + torch::Tensor U, SV, V; + std::tie(U, SV, V) = torch::svd(torch::matmul(RX, RY.t())); + + // Find the median of singular values + torch::Tensor S_sorted, S_indices; + std::tie(S_sorted, S_indices) = SV.sort(); + + float delta; + if (S_sorted.size(0) % 2 == 1) { + delta = S_sorted[S_sorted.size(0) / 2].item().toFloat(); + } else { + delta = torch::median(S_sorted).item().toFloat(); + } + // Shrink the singular values with delta + torch::Tensor SV_shrunk = medianReduceRank(SV, delta); + + // Restore SV diagonal matrix + SV = torch::diag_embed(SV_shrunk); + torch::Tensor SV_sqrt = torch::sqrt(SV); + + // Update indices of zero-valued columns + torch::Tensor zero_indices = torch::nonzero(SV_shrunk == 0).squeeze(); + zero_columns = torch::cat({zero_columns, zero_indices}); + + // Convert tensor to a std::vector + std::vector + vec(zero_columns.data_ptr(), zero_columns.data_ptr() + zero_columns.numel()); + + // Sort the vector + std::sort(vec.begin(), vec.end()); + + // Remove duplicates + vec.erase(std::unique(vec.begin(), vec.end()), vec.end()); + + // Convert std::vector back to a tensor + zero_columns = torch::from_blob(vec.data(), {static_cast(vec.size())}, torch::kInt64).clone(); + + // Update sketch matrices + BX = torch::matmul(torch::matmul(QX, U), SV_sqrt); + BY = torch::matmul(torch::matmul(QY, V), SV_sqrt); + } + } + + return torch::matmul(BX, BY.t()); + } } \ No newline at end of file diff --git a/src/CPPAlgos/CountSketchCPPAlgo.cpp b/src/CPPAlgos/CountSketchCPPAlgo.cpp index 1d8290aa..8460600a 100644 --- a/src/CPPAlgos/CountSketchCPPAlgo.cpp +++ b/src/CPPAlgos/CountSketchCPPAlgo.cpp @@ -5,30 +5,30 @@ #include namespace AMMBench { -torch::Tensor AMMBench::CountSketchCPPAlgo::amm(torch::Tensor A, torch::Tensor B, uint64_t k2) { - INTELLI_INFO("I am counter sketch"); - int64_t m1 = A.size(0); - int64_t n = A.size(1); - int64_t m2 = B.size(1); - int64_t k = (int64_t) k2; - // Initialize sketch matrices - torch::Tensor Ca = torch::zeros({m1, k}); - torch::Tensor Cb = torch::zeros({k, m2}); + torch::Tensor AMMBench::CountSketchCPPAlgo::amm(torch::Tensor A, torch::Tensor B, uint64_t k2) { + INTELLI_INFO("I am counter sketch"); + int64_t m1 = A.size(0); + int64_t n = A.size(1); + int64_t m2 = B.size(1); + int64_t k = (int64_t) k2; + // Initialize sketch matrices + torch::Tensor Ca = torch::zeros({m1, k}); + torch::Tensor Cb = torch::zeros({k, m2}); - torch::Tensor L = torch::randint(k, {n}); - torch::Tensor G = torch::randint(2, {n}); + torch::Tensor L = torch::randint(k, {n}); + torch::Tensor G = torch::randint(2, {n}); - // Modify the random column with random sign - for (int64_t i = 0; i < n; ++i) { - if (G[i].item() == 1) { - Ca.index({torch::indexing::Slice(), L[i]}).add_(A.index({torch::indexing::Slice(), i})); - Cb.index({L[i], torch::indexing::Slice()}).add_(B.index({i, torch::indexing::Slice()})); - } else { - Ca.index({torch::indexing::Slice(), L[i]}).sub_(A.index({torch::indexing::Slice(), i})); - Cb.index({L[i], torch::indexing::Slice()}).sub_(B.index({i, torch::indexing::Slice()})); - } - } + // Modify the random column with random sign + for (int64_t i = 0; i < n; ++i) { + if (G[i].item() == 1) { + Ca.index({torch::indexing::Slice(), L[i]}).add_(A.index({torch::indexing::Slice(), i})); + Cb.index({L[i], torch::indexing::Slice()}).add_(B.index({i, torch::indexing::Slice()})); + } else { + Ca.index({torch::indexing::Slice(), L[i]}).sub_(A.index({torch::indexing::Slice(), i})); + Cb.index({L[i], torch::indexing::Slice()}).sub_(B.index({i, torch::indexing::Slice()})); + } + } - return torch::matmul(Ca, Cb); -} + return torch::matmul(Ca, Cb); + } } // AMMBench diff --git a/src/CPPAlgos/EWSCPPAlgo.cpp b/src/CPPAlgos/EWSCPPAlgo.cpp index c2fbbff0..c6b587d9 100644 --- a/src/CPPAlgos/EWSCPPAlgo.cpp +++ b/src/CPPAlgos/EWSCPPAlgo.cpp @@ -5,30 +5,30 @@ #include namespace AMMBench { -torch::Tensor AMMBench::EWSCPPAlgo::amm(torch::Tensor A, torch::Tensor B, uint64_t k2) { - auto A_size = A.sizes(); - int64_t m = A_size[0]; - int64_t n = A_size[1]; - int64_t k = (int64_t) k2; - TORCH_CHECK(n == B.size(0)); - TORCH_CHECK(k < n); + torch::Tensor AMMBench::EWSCPPAlgo::amm(torch::Tensor A, torch::Tensor B, uint64_t k2) { + auto A_size = A.sizes(); + int64_t m = A_size[0]; + int64_t n = A_size[1]; + int64_t k = (int64_t) k2; + TORCH_CHECK(n == B.size(0)); + TORCH_CHECK(k < n); - int64_t p = B.size(1); + int64_t p = B.size(1); - // probability distribution - torch::Tensor probs = torch::rand({m, n}); + // probability distribution + torch::Tensor probs = torch::rand({m, n}); - // S matrix that samples A with scaling - torch::Tensor mask = torch::rand_like(probs) < probs; - torch::Tensor S = torch::zeros_like(A); - S.masked_scatter_(mask, A.masked_select(mask) / probs.masked_select(mask)); + // S matrix that samples A with scaling + torch::Tensor mask = torch::rand_like(probs) < probs; + torch::Tensor S = torch::zeros_like(A); + S.masked_scatter_(mask, A.masked_select(mask) / probs.masked_select(mask)); - // R matrix that samples B with scaling - torch::Tensor probs_r = torch::rand({n, p}); // a different probabilistic distribution - torch::Tensor mask_r = torch::rand_like(probs_r) < probs_r; - torch::Tensor R = torch::zeros_like(B); - R.masked_scatter_(mask_r, B.masked_select(mask_r) / probs_r.masked_select(mask_r)); + // R matrix that samples B with scaling + torch::Tensor probs_r = torch::rand({n, p}); // a different probabilistic distribution + torch::Tensor mask_r = torch::rand_like(probs_r) < probs_r; + torch::Tensor R = torch::zeros_like(B); + R.masked_scatter_(mask_r, B.masked_select(mask_r) / probs_r.masked_select(mask_r)); - return torch::matmul(S, R); -} + return torch::matmul(S, R); + } } \ No newline at end of file diff --git a/src/CPPAlgos/INT8CPPAlgo.cpp b/src/CPPAlgos/INT8CPPAlgo.cpp index f4a485da..4fbae5cb 100644 --- a/src/CPPAlgos/INT8CPPAlgo.cpp +++ b/src/CPPAlgos/INT8CPPAlgo.cpp @@ -4,306 +4,298 @@ #include #include -torch::Tensor AMMBench::INT8CPPAlgo::fp32amm(torch::Tensor tensor1, torch::Tensor tensor2) -{ - auto A_size = tensor1.sizes(); - auto B_size =tensor2.sizes(); - struct timeval tstart; - //INTELLI_INFO("I am mm"); - gettimeofday(&tstart,NULL); - int64_t rows1 = A_size[0]; - int64_t cols1 = A_size[1]; - int64_t cols2 = B_size[1]; - /** - * @brief build A into std vector - */ - std::vector matrix1(tensor1.data_ptr(), tensor1.data_ptr() + rows1 * cols1); - buildATime=INTELLI::UtilityFunctions::timeLastUs(tstart); - std::vector matrix2(tensor2.data_ptr(), tensor2.data_ptr() + cols1 * cols2); - buildBTime=INTELLI::UtilityFunctions::timeLastUs(tstart)-buildATime; - // Create the output matrix - std::vector result(rows1 * cols2, 0.0); - for (int64_t i = 0; i < rows1; ++i) { - for (int64_t j = 0; j < cols2; ++j) { - for (int64_t k = 0; k < cols1; ++k) { - result[i * cols2 + j] += matrix1[i * cols1 + k] * matrix2[k * cols2 + j]; - } + +torch::Tensor AMMBench::INT8CPPAlgo::fp32amm(torch::Tensor tensor1, torch::Tensor tensor2) { + auto A_size = tensor1.sizes(); + auto B_size = tensor2.sizes(); + struct timeval tstart; + //INTELLI_INFO("I am mm"); + gettimeofday(&tstart, NULL); + int64_t rows1 = A_size[0]; + int64_t cols1 = A_size[1]; + int64_t cols2 = B_size[1]; + /** + * @brief build A into std vector + */ + std::vector matrix1(tensor1.data_ptr(), tensor1.data_ptr() + rows1 * cols1); + buildATime = INTELLI::UtilityFunctions::timeLastUs(tstart); + std::vector matrix2(tensor2.data_ptr(), tensor2.data_ptr() + cols1 * cols2); + buildBTime = INTELLI::UtilityFunctions::timeLastUs(tstart) - buildATime; + // Create the output matrix + std::vector result(rows1 * cols2, 0.0); + for (int64_t i = 0; i < rows1; ++i) { + for (int64_t j = 0; j < cols2; ++j) { + for (int64_t k = 0; k < cols1; ++k) { + result[i * cols2 + j] += matrix1[i * cols1 + k] * matrix2[k * cols2 + j]; + } + } } - } - // exit(-1); - fABTime = INTELLI::UtilityFunctions::timeLastUs(tstart)-buildATime-buildBTime; - torch::Tensor resultTensor = torch::from_blob(result.data(), {rows1,cols2}); - //torch::Tensor resultTensor = torch::zeros({rows1,cols2}); - postProcessTime=INTELLI::UtilityFunctions::timeLastUs(tstart)-buildATime-buildBTime-fABTime; + // exit(-1); + fABTime = INTELLI::UtilityFunctions::timeLastUs(tstart) - buildATime - buildBTime; + torch::Tensor resultTensor = torch::from_blob(result.data(), {rows1, cols2}); + //torch::Tensor resultTensor = torch::zeros({rows1,cols2}); + postProcessTime = INTELLI::UtilityFunctions::timeLastUs(tstart) - buildATime - buildBTime - fABTime; - return resultTensor.clone(); + return resultTensor.clone(); } -static float getScaleingFactor( float scalingBase,std::vector matrix1Float) -{ - float maxa=*std::max_element(matrix1Float.begin(), matrix1Float.end()); - float minaAbs= abs(*std::min_element(matrix1Float.begin(), matrix1Float.end())); - if (minaAbs>maxa) - { - maxa=minaAbs; - } - return scalingBase/maxa; + +static float getScaleingFactor(float scalingBase, std::vector matrix1Float) { + float maxa = *std::max_element(matrix1Float.begin(), matrix1Float.end()); + float minaAbs = abs(*std::min_element(matrix1Float.begin(), matrix1Float.end())); + if (minaAbs > maxa) { + maxa = minaAbs; + } + return scalingBase / maxa; } -torch::Tensor AMMBench::INT8CPPAlgo::int4amm(torch::Tensor tensor1, torch::Tensor tensor2) -{ - auto A_size = tensor1.sizes(); - auto B_size =tensor2.sizes(); - struct timeval tstart; - //INTELLI_INFO("I am mm"); - gettimeofday(&tstart,NULL); - int64_t rows1 = A_size[0]; - int64_t cols1 = A_size[1]; - int64_t cols2 = B_size[1]; - /** - * @brief build A - */ - std::vector matrix1Float(tensor1.data_ptr(), tensor1.data_ptr() + rows1 * cols1); - // Convert the input matrices to int8 - std::vector matrix1(rows1 * cols1); - float scale1 = getScaleingFactor(7.0,matrix1Float); - for (int i = 0; i < rows1 * cols1; ++i) { - matrix1[i] = static_cast(matrix1Float[i] * scale1); - } - buildATime=INTELLI::UtilityFunctions::timeLastUs(tstart); +torch::Tensor AMMBench::INT8CPPAlgo::int4amm(torch::Tensor tensor1, torch::Tensor tensor2) { + auto A_size = tensor1.sizes(); + auto B_size = tensor2.sizes(); + struct timeval tstart; + //INTELLI_INFO("I am mm"); + gettimeofday(&tstart, NULL); + int64_t rows1 = A_size[0]; + int64_t cols1 = A_size[1]; + int64_t cols2 = B_size[1]; + /** + * @brief build A + */ + std::vector matrix1Float(tensor1.data_ptr(), tensor1.data_ptr() + rows1 * cols1); + // Convert the input matrices to int8 + std::vector matrix1(rows1 * cols1); - /** - * @brief build B - */ - std::vector matrix2Float(tensor2.data_ptr(), tensor2.data_ptr() + cols1 * cols2); - std::vector matrix2(cols1 * cols2); - float scale2 = getScaleingFactor(7.0,matrix2Float); - for (int i = 0; i < cols1 * cols2; ++i) { - matrix2[i] = static_cast(matrix2Float[i] * scale2); - } - buildBTime=INTELLI::UtilityFunctions::timeLastUs(tstart)-buildATime; - /** - * @brief run fAB - */ - std::vector result(rows1 * cols2, 0); + float scale1 = getScaleingFactor(7.0, matrix1Float); + for (int i = 0; i < rows1 * cols1; ++i) { + matrix1[i] = static_cast(matrix1Float[i] * scale1); + } + buildATime = INTELLI::UtilityFunctions::timeLastUs(tstart); - // Perform matrix multiplication using nested loops - for (int64_t i = 0; i < rows1; ++i) { - for (int64_t j = 0; j < cols2; ++j) { - int16_t sRu=0; - int64_t k=0; - /** - * @brief 32/4=8, so we simulate a 8-way SHARED-NOTHING speed up in one loop - */ - while ( k < cols1-8) { - int8_t tru1=matrix1[i * cols1 + k]*matrix2[k * cols2 + j]; - int8_t tru2=matrix1[i * cols1 + k+1]*matrix2[(k+1) * cols2 + j]; - int8_t tru3=matrix1[i * cols1 + (k+2)]*matrix2[(k+2) * cols2 + j]; - int8_t tru4=matrix1[i * cols1 + (k+3)]*matrix2[(k+3) * cols2 + j]; - // - int8_t tru5=matrix1[i * cols1 + k+4]*matrix2[(k+4) * cols2 + j]; - int8_t tru6=matrix1[i * cols1 + k+5]*matrix2[(k+5) * cols2 + j]; - int8_t tru7=matrix1[i * cols1 + (k+6)]*matrix2[(k+6) * cols2 + j]; - int8_t tru8=matrix1[i * cols1 + (k+7)]*matrix2[(k+7) * cols2 + j]; - sRu+= tru1+tru2+tru3+tru4+tru5+tru6+tru7+tru8; - k+=8; - } - for(int64_t k2=k;k2 matrix2Float(tensor2.data_ptr(), tensor2.data_ptr() + cols1 * cols2); + std::vector matrix2(cols1 * cols2); + float scale2 = getScaleingFactor(7.0, matrix2Float); + for (int i = 0; i < cols1 * cols2; ++i) { + matrix2[i] = static_cast(matrix2Float[i] * scale2); } - } - fABTime = INTELLI::UtilityFunctions::timeLastUs(tstart)-buildATime-buildBTime; + buildBTime = INTELLI::UtilityFunctions::timeLastUs(tstart) - buildATime; + /** + * @brief run fAB + */ + std::vector result(rows1 * cols2, 0); - // Scale the result back to int8 - /** - * @brief post process - */ - std::vector resultFP32(rows1 * cols2); - float scaleResult = 1.0 / (scale1 * scale2); - for (int i = 0; i < rows1 * cols2; ++i) { - resultFP32[i] = static_cast(result[i] * scaleResult); - } - torch::Tensor resultTensor = torch::from_blob(resultFP32.data(), {rows1,cols2}); - //torch::Tensor resultTensor = torch::zeros({rows1,cols2}); - postProcessTime=INTELLI::UtilityFunctions::timeLastUs(tstart)-buildATime-buildBTime-fABTime; + // Perform matrix multiplication using nested loops + for (int64_t i = 0; i < rows1; ++i) { + for (int64_t j = 0; j < cols2; ++j) { + int16_t sRu = 0; + int64_t k = 0; + /** + * @brief 32/4=8, so we simulate a 8-way SHARED-NOTHING speed up in one loop + */ + while (k < cols1 - 8) { + int8_t tru1 = matrix1[i * cols1 + k] * matrix2[k * cols2 + j]; + int8_t tru2 = matrix1[i * cols1 + k + 1] * matrix2[(k + 1) * cols2 + j]; + int8_t tru3 = matrix1[i * cols1 + (k + 2)] * matrix2[(k + 2) * cols2 + j]; + int8_t tru4 = matrix1[i * cols1 + (k + 3)] * matrix2[(k + 3) * cols2 + j]; + // + int8_t tru5 = matrix1[i * cols1 + k + 4] * matrix2[(k + 4) * cols2 + j]; + int8_t tru6 = matrix1[i * cols1 + k + 5] * matrix2[(k + 5) * cols2 + j]; + int8_t tru7 = matrix1[i * cols1 + (k + 6)] * matrix2[(k + 6) * cols2 + j]; + int8_t tru8 = matrix1[i * cols1 + (k + 7)] * matrix2[(k + 7) * cols2 + j]; + sRu += tru1 + tru2 + tru3 + tru4 + tru5 + tru6 + tru7 + tru8; + k += 8; + } + for (int64_t k2 = k; k2 < cols1; ++k2) { + int16_t tru = matrix1[i * cols1 + k2] * matrix2[k2 * cols2 + j]; + sRu += tru; + } + result[i * cols2 + j] = sRu; + } + } + fABTime = INTELLI::UtilityFunctions::timeLastUs(tstart) - buildATime - buildBTime; - return resultTensor.clone(); + // Scale the result back to int8 + /** + * @brief post process + */ + std::vector resultFP32(rows1 * cols2); + float scaleResult = 1.0 / (scale1 * scale2); + for (int i = 0; i < rows1 * cols2; ++i) { + resultFP32[i] = static_cast(result[i] * scaleResult); + } + torch::Tensor resultTensor = torch::from_blob(resultFP32.data(), {rows1, cols2}); + //torch::Tensor resultTensor = torch::zeros({rows1,cols2}); + postProcessTime = INTELLI::UtilityFunctions::timeLastUs(tstart) - buildATime - buildBTime - fABTime; + + return resultTensor.clone(); } -torch::Tensor AMMBench::INT8CPPAlgo::int8amm(torch::Tensor tensor1, torch::Tensor tensor2) -{ - auto A_size = tensor1.sizes(); - auto B_size =tensor2.sizes(); - struct timeval tstart; - //INTELLI_INFO("I am mm"); - gettimeofday(&tstart,NULL); - int64_t rows1 = A_size[0]; - int64_t cols1 = A_size[1]; - int64_t cols2 = B_size[1]; - /** - * @brief build A - */ - std::vector matrix1Float(tensor1.data_ptr(), tensor1.data_ptr() + rows1 * cols1); - // Convert the input matrices to int8 - std::vector matrix1(rows1 * cols1); - float scale1 = getScaleingFactor(127.0,matrix1Float); - for (int i = 0; i < rows1 * cols1; ++i) { - matrix1[i] = static_cast(matrix1Float[i] * scale1); - } - buildATime=INTELLI::UtilityFunctions::timeLastUs(tstart); +torch::Tensor AMMBench::INT8CPPAlgo::int8amm(torch::Tensor tensor1, torch::Tensor tensor2) { + auto A_size = tensor1.sizes(); + auto B_size = tensor2.sizes(); + struct timeval tstart; + //INTELLI_INFO("I am mm"); + gettimeofday(&tstart, NULL); + int64_t rows1 = A_size[0]; + int64_t cols1 = A_size[1]; + int64_t cols2 = B_size[1]; + /** + * @brief build A + */ + std::vector matrix1Float(tensor1.data_ptr(), tensor1.data_ptr() + rows1 * cols1); + // Convert the input matrices to int8 + std::vector matrix1(rows1 * cols1); + float scale1 = getScaleingFactor(127.0, matrix1Float); + for (int i = 0; i < rows1 * cols1; ++i) { + matrix1[i] = static_cast(matrix1Float[i] * scale1); + } + buildATime = INTELLI::UtilityFunctions::timeLastUs(tstart); - /** - * @brief build B - */ - std::vector matrix2Float(tensor2.data_ptr(), tensor2.data_ptr() + cols1 * cols2); - std::vector matrix2(cols1 * cols2); - float scale2 = getScaleingFactor(127.0,matrix2Float); - for (int i = 0; i < cols1 * cols2; ++i) { - matrix2[i] = static_cast(matrix2Float[i] * scale2); - } - buildBTime=INTELLI::UtilityFunctions::timeLastUs(tstart)-buildATime; - /** - * @brief run fAB - */ - std::vector result(rows1 * cols2, 0); + /** + * @brief build B + */ + std::vector matrix2Float(tensor2.data_ptr(), tensor2.data_ptr() + cols1 * cols2); + std::vector matrix2(cols1 * cols2); + float scale2 = getScaleingFactor(127.0, matrix2Float); + for (int i = 0; i < cols1 * cols2; ++i) { + matrix2[i] = static_cast(matrix2Float[i] * scale2); + } + buildBTime = INTELLI::UtilityFunctions::timeLastUs(tstart) - buildATime; + /** + * @brief run fAB + */ + std::vector result(rows1 * cols2, 0); - // Perform matrix multiplication using nested loops - for (int64_t i = 0; i < rows1; ++i) { - for (int64_t j = 0; j < cols2; ++j) { - int32_t sRu=0; - int64_t k=0; - /** - * @brief 32/8=4, so we simulate a 4-way SHARED-NOTHING speed up in one loop - */ - while ( k < cols1-4) { - int16_t tru1=matrix1[i * cols1 + k]*matrix2[k * cols2 + j]; - int16_t tru2=matrix1[i * cols1 + k+1]*matrix2[(k+1) * cols2 + j]; - int16_t tru3=matrix1[i * cols1 + (k+2)]*matrix2[(k+2) * cols2 + j]; - int16_t tru4=matrix1[i * cols1 + (k+3)]*matrix2[(k+3) * cols2 + j]; - sRu+= tru1+tru2+tru3+tru4; - k+=4; - } - for(int64_t k2=k;k2 resultFP32(rows1 * cols2); - float scaleResult = 1.0 / (scale1 * scale2); - for (int i = 0; i < rows1 * cols2; ++i) { - resultFP32[i] = static_cast(result[i] * scaleResult); - } - torch::Tensor resultTensor = torch::from_blob(resultFP32.data(), {rows1,cols2}); - //torch::Tensor resultTensor = torch::zeros({rows1,cols2}); - postProcessTime=INTELLI::UtilityFunctions::timeLastUs(tstart)-buildATime-buildBTime-fABTime; + // Scale the result back to int8 + /** + * @brief post process + */ + std::vector resultFP32(rows1 * cols2); + float scaleResult = 1.0 / (scale1 * scale2); + for (int i = 0; i < rows1 * cols2; ++i) { + resultFP32[i] = static_cast(result[i] * scaleResult); + } + torch::Tensor resultTensor = torch::from_blob(resultFP32.data(), {rows1, cols2}); + //torch::Tensor resultTensor = torch::zeros({rows1,cols2}); + postProcessTime = INTELLI::UtilityFunctions::timeLastUs(tstart) - buildATime - buildBTime - fABTime; - return resultTensor.clone(); + return resultTensor.clone(); } -torch::Tensor AMMBench::INT8CPPAlgo::int16amm(torch::Tensor tensor1, torch::Tensor tensor2) -{ - auto A_size = tensor1.sizes(); - auto B_size =tensor2.sizes(); - struct timeval tstart; - //INTELLI_INFO("I am mm"); - gettimeofday(&tstart,NULL); - int64_t rows1 = A_size[0]; - int64_t cols1 = A_size[1]; - int64_t cols2 = B_size[1]; - /** - * @brief build A - */ - std::vector matrix1Float(tensor1.data_ptr(), tensor1.data_ptr() + rows1 * cols1); - // Convert the input matrices to int8 - std::vector matrix1(rows1 * cols1); - float scale1 = getScaleingFactor(32767.0,matrix1Float); - for (int i = 0; i < rows1 * cols1; ++i) { - matrix1[i] = static_cast(matrix1Float[i] * scale1); - } - buildATime=INTELLI::UtilityFunctions::timeLastUs(tstart); - /** - * @brief build B - */ - std::vector matrix2Float(tensor2.data_ptr(), tensor2.data_ptr() + cols1 * cols2); - std::vector matrix2(cols1 * cols2); - float scale2 =getScaleingFactor(32767.0,matrix2Float); - for (int i = 0; i < cols1 * cols2; ++i) { - matrix2[i] = static_cast(matrix2Float[i] * scale2); - } - buildBTime=INTELLI::UtilityFunctions::timeLastUs(tstart)-buildATime; - /** - * @brief run fAB - */ - std::vector result(rows1 * cols2, 0); +torch::Tensor AMMBench::INT8CPPAlgo::int16amm(torch::Tensor tensor1, torch::Tensor tensor2) { + auto A_size = tensor1.sizes(); + auto B_size = tensor2.sizes(); + struct timeval tstart; + //INTELLI_INFO("I am mm"); + gettimeofday(&tstart, NULL); + int64_t rows1 = A_size[0]; + int64_t cols1 = A_size[1]; + int64_t cols2 = B_size[1]; + /** + * @brief build A + */ + std::vector matrix1Float(tensor1.data_ptr(), tensor1.data_ptr() + rows1 * cols1); + // Convert the input matrices to int8 + std::vector matrix1(rows1 * cols1); + float scale1 = getScaleingFactor(32767.0, matrix1Float); + for (int i = 0; i < rows1 * cols1; ++i) { + matrix1[i] = static_cast(matrix1Float[i] * scale1); + } + buildATime = INTELLI::UtilityFunctions::timeLastUs(tstart); - // Perform matrix multiplication using nested loops - for (int64_t i = 0; i < rows1; ++i) { - for (int64_t j = 0; j < cols2; ++j) { - int64_t sRu=0; - int64_t k=0; - /** - * @brief 32/16=2, so we simulate a 2-way SHARED-NOTHING speed up in one loop - */ - while ( k < cols1-2) { - int32_t tru1=matrix1[i * cols1 + k]*matrix2[k * cols2 + j]; - int32_t tru2=matrix1[i * cols1 + k+1]*matrix2[(k+1) * cols2 + j]; - sRu+= tru1+tru2; - k+=2; - } - for(int64_t k2=k;k2 matrix2Float(tensor2.data_ptr(), tensor2.data_ptr() + cols1 * cols2); + std::vector matrix2(cols1 * cols2); + float scale2 = getScaleingFactor(32767.0, matrix2Float); + for (int i = 0; i < cols1 * cols2; ++i) { + matrix2[i] = static_cast(matrix2Float[i] * scale2); } - } - fABTime = INTELLI::UtilityFunctions::timeLastUs(tstart)-buildATime-buildBTime; + buildBTime = INTELLI::UtilityFunctions::timeLastUs(tstart) - buildATime; + /** + * @brief run fAB + */ + std::vector result(rows1 * cols2, 0); - // Scale the result back to int8 - /** - * @brief post process - */ - std::vector resultFP32(rows1 * cols2); - float scaleResult = 1.0 / (scale1 * scale2); - for (int i = 0; i < rows1 * cols2; ++i) { - resultFP32[i] = static_cast(result[i] * scaleResult); - } - torch::Tensor resultTensor = torch::from_blob(resultFP32.data(), {rows1,cols2}); - //torch::Tensor resultTensor = torch::zeros({rows1,cols2}); - postProcessTime=INTELLI::UtilityFunctions::timeLastUs(tstart)-buildATime-buildBTime-fABTime; + // Perform matrix multiplication using nested loops + for (int64_t i = 0; i < rows1; ++i) { + for (int64_t j = 0; j < cols2; ++j) { + int64_t sRu = 0; + int64_t k = 0; + /** + * @brief 32/16=2, so we simulate a 2-way SHARED-NOTHING speed up in one loop + */ + while (k < cols1 - 2) { + int32_t tru1 = matrix1[i * cols1 + k] * matrix2[k * cols2 + j]; + int32_t tru2 = matrix1[i * cols1 + k + 1] * matrix2[(k + 1) * cols2 + j]; + sRu += tru1 + tru2; + k += 2; + } + for (int64_t k2 = k; k2 < cols1; ++k2) { + int32_t tru = matrix1[i * cols1 + k2] * matrix2[k2 * cols2 + j]; + sRu += tru; + } + result[i * cols2 + j] = sRu; + } + } + fABTime = INTELLI::UtilityFunctions::timeLastUs(tstart) - buildATime - buildBTime; + + // Scale the result back to int8 + /** + * @brief post process + */ + std::vector resultFP32(rows1 * cols2); + float scaleResult = 1.0 / (scale1 * scale2); + for (int i = 0; i < rows1 * cols2; ++i) { + resultFP32[i] = static_cast(result[i] * scaleResult); + } + torch::Tensor resultTensor = torch::from_blob(resultFP32.data(), {rows1, cols2}); + //torch::Tensor resultTensor = torch::zeros({rows1,cols2}); + postProcessTime = INTELLI::UtilityFunctions::timeLastUs(tstart) - buildATime - buildBTime - fABTime; - return resultTensor.clone(); + return resultTensor.clone(); } + void AMMBench::INT8CPPAlgo::setConfig(INTELLI::ConfigMapPtr cfg) { - AbstractCPPAlgo::setConfig(cfg); - fpMode=cfg->tryString("fpMode","INT8",true); + AbstractCPPAlgo::setConfig(cfg); + fpMode = cfg->tryString("fpMode", "INT8", true); } + torch::Tensor AMMBench::INT8CPPAlgo::amm(torch::Tensor A, torch::Tensor B, uint64_t sketchSize) { - assert(sketchSize); - if(fpMode=="INT4") - { - return int4amm(A,B); - } - else if(fpMode=="INT8") - { - return int8amm(A,B); - } - else if(fpMode=="INT16") - { - return int16amm(A,B); - } - return fp32amm(A,B); + assert(sketchSize); + if (fpMode == "INT4") { + return int4amm(A, B); + } else if (fpMode == "INT8") { + return int8amm(A, B); + } else if (fpMode == "INT16") { + return int16amm(A, B); + } + return fp32amm(A, B); } \ No newline at end of file diff --git a/src/CPPAlgos/SMPPCACPPAlgo.cpp b/src/CPPAlgos/SMPPCACPPAlgo.cpp index 0c35161d..a654ee1a 100644 --- a/src/CPPAlgos/SMPPCACPPAlgo.cpp +++ b/src/CPPAlgos/SMPPCACPPAlgo.cpp @@ -5,32 +5,36 @@ #include namespace AMMBench { -torch::Tensor AMMBench::SMPPCACPPAlgo::amm(torch::Tensor A, torch::Tensor B, uint64_t k2) { - // Step 1: Input A:n1*d B:d*n2 - A = A.t(); // d*n1 - int64_t d = A.size(0); - int64_t n1 = A.size(1); - int64_t n2 = B.size(1); - int64_t k=(int64_t)k2; - // Step 2: Get sketched matrix - torch::Tensor pi = 1/std::sqrt(k) * torch::randn({k, d}); // Gaussian sketching matrix - torch::Tensor A_tilde = torch::matmul(pi, A); // k*n1 - torch::Tensor B_tilde = torch::matmul(pi, B); // k*n2 + torch::Tensor AMMBench::SMPPCACPPAlgo::amm(torch::Tensor A, torch::Tensor B, uint64_t k2) { + // Step 1: Input A:n1*d B:d*n2 + A = A.t(); // d*n1 + int64_t d = A.size(0); + int64_t n1 = A.size(1); + int64_t n2 = B.size(1); + int64_t k = (int64_t) k2; + // Step 2: Get sketched matrix + torch::Tensor pi = 1 / std::sqrt(k) * torch::randn({k, d}); // Gaussian sketching matrix + torch::Tensor A_tilde = torch::matmul(pi, A); // k*n1 + torch::Tensor B_tilde = torch::matmul(pi, B); // k*n2 - torch::Tensor A_tilde_B_tilde = torch::matmul(A_tilde.t(), B_tilde); - - // Compute column norms of A and B - torch::Tensor col_norm_A = torch::linalg::vector_norm(A, 2, {0}, false, c10::nullopt); // ||Ai|| for i in [n1] - torch::Tensor col_norm_B = torch::linalg::vector_norm(B, 2, {0}, false, c10::nullopt); // ||Bj|| for j in [n2] + torch::Tensor A_tilde_B_tilde = torch::matmul(A_tilde.t(), B_tilde); - torch::Tensor col_norm_A_tilde = torch::linalg::vector_norm(A_tilde, 2, {0}, false, c10::nullopt); // ||Ai|| for i in [n1] - torch::Tensor col_norm_B_tilde = torch::linalg::vector_norm(B_tilde, 2, {0}, false, c10::nullopt); // ||Bj|| for j in [n2] - - // Compute M_tilde - torch::Tensor col_norm_A_col_norm_B = torch::matmul(col_norm_A.reshape({n1,1}), col_norm_B.reshape({1,n2})); - torch::Tensor col_norm_A_tilde_col_norm_B_tilde = torch::matmul(col_norm_A_tilde.reshape({n1,1}), col_norm_B_tilde.reshape({1,n2})); - torch::Tensor M_tilde = torch::div(torch::mul(A_tilde_B_tilde, col_norm_A_col_norm_B), col_norm_A_tilde_col_norm_B_tilde); + // Compute column norms of A and B + torch::Tensor col_norm_A = torch::linalg::vector_norm(A, 2, {0}, false, c10::nullopt); // ||Ai|| for i in [n1] + torch::Tensor col_norm_B = torch::linalg::vector_norm(B, 2, {0}, false, c10::nullopt); // ||Bj|| for j in [n2] - return M_tilde; -} + torch::Tensor col_norm_A_tilde = torch::linalg::vector_norm(A_tilde, 2, {0}, false, + c10::nullopt); // ||Ai|| for i in [n1] + torch::Tensor col_norm_B_tilde = torch::linalg::vector_norm(B_tilde, 2, {0}, false, + c10::nullopt); // ||Bj|| for j in [n2] + + // Compute M_tilde + torch::Tensor col_norm_A_col_norm_B = torch::matmul(col_norm_A.reshape({n1, 1}), col_norm_B.reshape({1, n2})); + torch::Tensor col_norm_A_tilde_col_norm_B_tilde = torch::matmul(col_norm_A_tilde.reshape({n1, 1}), + col_norm_B_tilde.reshape({1, n2})); + torch::Tensor M_tilde = torch::div(torch::mul(A_tilde_B_tilde, col_norm_A_col_norm_B), + col_norm_A_tilde_col_norm_B_tilde); + + return M_tilde; + } } // AMMBench \ No newline at end of file diff --git a/src/CPPAlgos/TugOfWarCPPAlgo.cpp b/src/CPPAlgos/TugOfWarCPPAlgo.cpp index 1306612d..53e134f4 100644 --- a/src/CPPAlgos/TugOfWarCPPAlgo.cpp +++ b/src/CPPAlgos/TugOfWarCPPAlgo.cpp @@ -5,50 +5,50 @@ #include namespace AMMBench { -void TugOfWarCPPAlgo::setConfig(INTELLI::ConfigMapPtr cfg) { - algoDelta = cfg->tryDouble("algoDelta",algoDelta,true); -} - -torch::Tensor TugOfWarCPPAlgo::generateTugOfWarMatrix(int64_t m, int64_t n) { - double e = 1.0 / std::sqrt(m); - torch::Tensor M = torch::randint(2, {m, n}); - return e * (2 * M - 1); -} - -torch::Tensor TugOfWarCPPAlgo::amm(torch::Tensor A, torch::Tensor B, uint64_t l2) { - int64_t n = A.size(1); - int64_t p = B.size(1); - int64_t l = (int64_t) l2; - - double delta = algoDelta; - - int64_t i_iters = static_cast(-std::log(delta)); - int64_t j_iters = static_cast(2 * (-std::log(delta) + std::log(-std::log(delta)))); - - torch::Tensor z = torch::empty({i_iters}); - std::vector AS; - std::vector SB; - - for (int64_t i = 0; i < i_iters; ++i) { - torch::Tensor S = generateTugOfWarMatrix(l, n); - SB.push_back(S.matmul(B)); - AS.push_back(A.matmul(S.t())); - - torch::Tensor y = torch::empty({j_iters}); - - for (int64_t j = 0; j < j_iters; ++j) { - torch::Tensor Q = generateTugOfWarMatrix(16, p); - torch::Tensor X = A.matmul(B.matmul(Q.t())); - torch::Tensor X_hat = AS[i].matmul(SB[i].matmul(Q.t())); - y[j] = torch::norm(X - X_hat).pow(2); + void TugOfWarCPPAlgo::setConfig(INTELLI::ConfigMapPtr cfg) { + algoDelta = cfg->tryDouble("algoDelta", algoDelta, true); } - z[i] = at::median(y); - } + torch::Tensor TugOfWarCPPAlgo::generateTugOfWarMatrix(int64_t m, int64_t n) { + double e = 1.0 / std::sqrt(m); + torch::Tensor M = torch::randint(2, {m, n}); + return e * (2 * M - 1); + } + + torch::Tensor TugOfWarCPPAlgo::amm(torch::Tensor A, torch::Tensor B, uint64_t l2) { + int64_t n = A.size(1); + int64_t p = B.size(1); + int64_t l = (int64_t) l2; + + double delta = algoDelta; + + int64_t i_iters = static_cast(-std::log(delta)); + int64_t j_iters = static_cast(2 * (-std::log(delta) + std::log(-std::log(delta)))); + + torch::Tensor z = torch::empty({i_iters}); + std::vector AS; + std::vector SB; - torch::Tensor z_argmin = z.argmin(); - int64_t i_star = z_argmin.item(); + for (int64_t i = 0; i < i_iters; ++i) { + torch::Tensor S = generateTugOfWarMatrix(l, n); + SB.push_back(S.matmul(B)); + AS.push_back(A.matmul(S.t())); - return AS[i_star].matmul(SB[i_star]); -} + torch::Tensor y = torch::empty({j_iters}); + + for (int64_t j = 0; j < j_iters; ++j) { + torch::Tensor Q = generateTugOfWarMatrix(16, p); + torch::Tensor X = A.matmul(B.matmul(Q.t())); + torch::Tensor X_hat = AS[i].matmul(SB[i].matmul(Q.t())); + y[j] = torch::norm(X - X_hat).pow(2); + } + + z[i] = at::median(y); + } + + torch::Tensor z_argmin = z.argmin(); + int64_t i_star = z_argmin.item(); + + return AS[i_star].matmul(SB[i_star]); + } } // AMMBench diff --git a/src/CPPAlgos/WeightedCRCPPAlgo.cpp b/src/CPPAlgos/WeightedCRCPPAlgo.cpp index f6f056a1..623b673a 100644 --- a/src/CPPAlgos/WeightedCRCPPAlgo.cpp +++ b/src/CPPAlgos/WeightedCRCPPAlgo.cpp @@ -6,37 +6,41 @@ #include namespace AMMBench { -torch::Tensor AMMBench::WeightedCRCPPAlgo::amm(torch::Tensor A, torch::Tensor B, uint64_t c) { - - int64_t n = A.size(1); // A: m*n, B: n*d - std::cout << "A shape: " << A.sizes() << std::endl; - std::cout << "B shape: " << B.sizes() << std::endl; - - // Probability distribution - torch::Tensor col_norm_A = torch::norm(A, /*p=*/2, /*dim=*/0); // norm on columns of A - torch::Tensor row_norm_B = torch::norm(B, /*p=*/2, /*dim=*/1); // norm on rows of B - torch::Tensor probability_distribution = torch::mul(col_norm_A, row_norm_B); - probability_distribution /= probability_distribution.sum(); - - // S - torch::Tensor sample_indices = torch::multinomial(probability_distribution, /*num_samples*/c, /*replacement*/true); - - torch::Tensor unique_indices, _, occurences; - std::tie(unique_indices, _, occurences) = at::_unique2(sample_indices, /*sorted*/false, /*return_inverse*/false, /*return_counts*/true); - - torch::Tensor S = torch::zeros({n, unique_indices.size(0)}); - torch::Tensor trial = torch::arange(unique_indices.size(0)); - S.index_put_({unique_indices, trial}, 1); - - // D - torch::Tensor D = torch::diag(torch::sqrt(occurences) / torch::sqrt(torch::tensor({static_cast(c)}, torch::kLong) * probability_distribution.index_select(0, unique_indices))); - - // ASD(SD)^TB - torch::Tensor SS = torch::matmul(S, D); - torch::Tensor C = torch::matmul(A, SS); - torch::Tensor R = torch::matmul(SS.t(), B); - torch::Tensor weighted_CR = torch::matmul(C, R); - - return weighted_CR; -} + torch::Tensor AMMBench::WeightedCRCPPAlgo::amm(torch::Tensor A, torch::Tensor B, uint64_t c) { + + int64_t n = A.size(1); // A: m*n, B: n*d + std::cout << "A shape: " << A.sizes() << std::endl; + std::cout << "B shape: " << B.sizes() << std::endl; + + // Probability distribution + torch::Tensor col_norm_A = torch::norm(A, /*p=*/2, /*dim=*/0); // norm on columns of A + torch::Tensor row_norm_B = torch::norm(B, /*p=*/2, /*dim=*/1); // norm on rows of B + torch::Tensor probability_distribution = torch::mul(col_norm_A, row_norm_B); + probability_distribution /= probability_distribution.sum(); + + // S + torch::Tensor sample_indices = torch::multinomial(probability_distribution, /*num_samples*/c, /*replacement*/ + true); + + torch::Tensor unique_indices, _, occurences; + std::tie(unique_indices, _, occurences) = at::_unique2(sample_indices, /*sorted*/false, /*return_inverse*/ + false, /*return_counts*/true); + + torch::Tensor S = torch::zeros({n, unique_indices.size(0)}); + torch::Tensor trial = torch::arange(unique_indices.size(0)); + S.index_put_({unique_indices, trial}, 1); + + // D + torch::Tensor D = torch::diag(torch::sqrt(occurences) / torch::sqrt( + torch::tensor({static_cast(c)}, torch::kLong) * + probability_distribution.index_select(0, unique_indices))); + + // ASD(SD)^TB + torch::Tensor SS = torch::matmul(S, D); + torch::Tensor C = torch::matmul(A, SS); + torch::Tensor R = torch::matmul(SS.t(), B); + torch::Tensor weighted_CR = torch::matmul(C, R); + + return weighted_CR; + } } // AMMBench diff --git a/src/MatrixLoader/AbstractMatrixLoader.cpp b/src/MatrixLoader/AbstractMatrixLoader.cpp index 1ea2aef0..a5a67a67 100644 --- a/src/MatrixLoader/AbstractMatrixLoader.cpp +++ b/src/MatrixLoader/AbstractMatrixLoader.cpp @@ -6,12 +6,14 @@ //do nothing in abstract class bool AMMBench::AbstractMatrixLoader::setConfig(INTELLI::ConfigMapPtr cfg) { - assert(cfg); - return true; + assert(cfg); + return true; } + torch::Tensor AMMBench::AbstractMatrixLoader::getA() { - return torch::rand({1, 1}); + return torch::rand({1, 1}); } + torch::Tensor AMMBench::AbstractMatrixLoader::getB() { - return torch::rand({1, 1}); + return torch::rand({1, 1}); } diff --git a/src/MatrixLoader/BetaMatrixLoader.cpp b/src/MatrixLoader/BetaMatrixLoader.cpp index 92aad95e..19d5e6b0 100644 --- a/src/MatrixLoader/BetaMatrixLoader.cpp +++ b/src/MatrixLoader/BetaMatrixLoader.cpp @@ -3,6 +3,7 @@ // #include #include + void AMMBench::BetaMatrixLoader::paraseConfig(INTELLI::ConfigMapPtr cfg) { aRow = cfg->tryU64("aRow", 100, true); aCol = cfg->tryU64("aCol", 1000, true); @@ -14,6 +15,7 @@ void AMMBench::BetaMatrixLoader::paraseConfig(INTELLI::ConfigMapPtr cfg) { "Generating [" + to_string(aRow) + "x" + to_string(aCol) + "]*[" + to_string(aCol) + "x" + to_string(bCol) + "]" + " Parameter: " + to_string(a) + ", " + to_string(b)); } + void AMMBench::BetaMatrixLoader::generateAB() { torch::manual_seed(seed); @@ -40,9 +42,11 @@ bool AMMBench::BetaMatrixLoader::setConfig(INTELLI::ConfigMapPtr cfg) { generateAB(); return true; } + torch::Tensor AMMBench::BetaMatrixLoader::getA() { return A; } + torch::Tensor AMMBench::BetaMatrixLoader::getB() { return B; } \ No newline at end of file diff --git a/src/MatrixLoader/BinomialMatrixLoader.cpp b/src/MatrixLoader/BinomialMatrixLoader.cpp index a29e4bbe..b6f9d744 100644 --- a/src/MatrixLoader/BinomialMatrixLoader.cpp +++ b/src/MatrixLoader/BinomialMatrixLoader.cpp @@ -3,6 +3,7 @@ // #include #include + void AMMBench::BinomialMatrixLoader::paraseConfig(INTELLI::ConfigMapPtr cfg) { aRow = cfg->tryU64("aRow", 100, true); aCol = cfg->tryU64("aCol", 1000, true); @@ -14,12 +15,13 @@ void AMMBench::BinomialMatrixLoader::paraseConfig(INTELLI::ConfigMapPtr cfg) { "Generating [" + to_string(aRow) + "x" + to_string(aCol) + "]*[" + to_string(aCol) + "x" + to_string(bCol) + "]" + " Parameter: " + to_string(trials) + ", " + to_string(probability)); } + void AMMBench::BinomialMatrixLoader::generateAB() { torch::manual_seed(seed); A = torch::zeros({(long) aRow, (long) aCol}); B = torch::zeros({(long) aCol, (long) bCol}); - for(int i = 0; i < trials; i++) { + for (int i = 0; i < trials; i++) { // Create a tensor filled with random numbers between 0 and 1 torch::Tensor rand_tensor = torch::rand({(long) aRow, (long) aCol}); @@ -27,7 +29,7 @@ void AMMBench::BinomialMatrixLoader::generateAB() { A += (rand_tensor < probability).to(torch::kInt); } - for(int i = 0; i < trials; i++) { + for (int i = 0; i < trials; i++) { // Create a tensor filled with random numbers between 0 and 1 torch::Tensor rand_tensor = torch::rand({(long) aCol, (long) bCol}); @@ -42,9 +44,11 @@ bool AMMBench::BinomialMatrixLoader::setConfig(INTELLI::ConfigMapPtr cfg) { generateAB(); return true; } + torch::Tensor AMMBench::BinomialMatrixLoader::getA() { return A; } + torch::Tensor AMMBench::BinomialMatrixLoader::getB() { return B; } \ No newline at end of file diff --git a/src/MatrixLoader/ExponentialMatrixLoader.cpp b/src/MatrixLoader/ExponentialMatrixLoader.cpp index 8697ebfe..9467be62 100644 --- a/src/MatrixLoader/ExponentialMatrixLoader.cpp +++ b/src/MatrixLoader/ExponentialMatrixLoader.cpp @@ -3,6 +3,7 @@ // #include #include + void AMMBench::ExponentialMatrixLoader::paraseConfig(INTELLI::ConfigMapPtr cfg) { aRow = cfg->tryU64("aRow", 100, true); aCol = cfg->tryU64("aCol", 1000, true); @@ -12,6 +13,7 @@ void AMMBench::ExponentialMatrixLoader::paraseConfig(INTELLI::ConfigMapPtr cfg) "Generating [" + to_string(aRow) + "x" + to_string(aCol) + "]*[" + to_string(aCol) + "x" + to_string(bCol) + "]"); } + void AMMBench::ExponentialMatrixLoader::generateAB() { torch::manual_seed(seed); A = torch::exponential(torch::empty({(long) aRow, (long) aCol})); @@ -24,9 +26,11 @@ bool AMMBench::ExponentialMatrixLoader::setConfig(INTELLI::ConfigMapPtr cfg) { generateAB(); return true; } + torch::Tensor AMMBench::ExponentialMatrixLoader::getA() { return A; } + torch::Tensor AMMBench::ExponentialMatrixLoader::getB() { return B; } \ No newline at end of file diff --git a/src/MatrixLoader/GaussianMatrixLoader.cpp b/src/MatrixLoader/GaussianMatrixLoader.cpp index 0c8e3e83..6a43c296 100644 --- a/src/MatrixLoader/GaussianMatrixLoader.cpp +++ b/src/MatrixLoader/GaussianMatrixLoader.cpp @@ -3,6 +3,7 @@ // #include #include + void AMMBench::GaussianMatrixLoader::paraseConfig(INTELLI::ConfigMapPtr cfg) { aRow = cfg->tryU64("aRow", 100, true); aCol = cfg->tryU64("aCol", 1000, true); @@ -12,20 +13,24 @@ void AMMBench::GaussianMatrixLoader::paraseConfig(INTELLI::ConfigMapPtr cfg) { "Generating [" + to_string(aRow) + "x" + to_string(aCol) + "]*[" + to_string(aCol) + "x" + to_string(bCol) + "]"); } + void AMMBench::GaussianMatrixLoader::generateAB() { torch::manual_seed(seed); A = torch::randn({(long) aRow, (long) aCol}); B = torch::randn({(long) aCol, (long) bCol}); } + //do nothing in abstract class bool AMMBench::GaussianMatrixLoader::setConfig(INTELLI::ConfigMapPtr cfg) { paraseConfig(cfg); generateAB(); return true; } + torch::Tensor AMMBench::GaussianMatrixLoader::getA() { return A; } + torch::Tensor AMMBench::GaussianMatrixLoader::getB() { return B; } \ No newline at end of file diff --git a/src/MatrixLoader/MatrixLoaderTable.cpp b/src/MatrixLoader/MatrixLoaderTable.cpp index 37979e2f..da0523a0 100644 --- a/src/MatrixLoader/MatrixLoaderTable.cpp +++ b/src/MatrixLoader/MatrixLoaderTable.cpp @@ -10,18 +10,19 @@ #include #include #include + namespace AMMBench { /** * @note revise me if you need new loader */ -AMMBench::MatrixLoaderTable::MatrixLoaderTable() { - loaderMap["random"] = newRandomMatrixLoader(); - loaderMap["sparse"] = newSparseMatrixLoader(); - loaderMap["gaussian"] = newGaussianMatrixLoader(); - loaderMap["exponential"] = newExponentialMatrixLoader(); - loaderMap["binomial"] = newBinomialMatrixLoader(); - loaderMap["poisson"] = newPoissonMatrixLoader(); - loaderMap["beta"] = newBetaMatrixLoader(); -} + AMMBench::MatrixLoaderTable::MatrixLoaderTable() { + loaderMap["random"] = newRandomMatrixLoader(); + loaderMap["sparse"] = newSparseMatrixLoader(); + loaderMap["gaussian"] = newGaussianMatrixLoader(); + loaderMap["exponential"] = newExponentialMatrixLoader(); + loaderMap["binomial"] = newBinomialMatrixLoader(); + loaderMap["poisson"] = newPoissonMatrixLoader(); + loaderMap["beta"] = newBetaMatrixLoader(); + } } // AMMBench \ No newline at end of file diff --git a/src/MatrixLoader/PoissonMatrixLoader.cpp b/src/MatrixLoader/PoissonMatrixLoader.cpp index 99f0c3be..4f1abc34 100644 --- a/src/MatrixLoader/PoissonMatrixLoader.cpp +++ b/src/MatrixLoader/PoissonMatrixLoader.cpp @@ -3,6 +3,7 @@ // #include #include + void AMMBench::PoissonMatrixLoader::paraseConfig(INTELLI::ConfigMapPtr cfg) { aRow = cfg->tryU64("aRow", 100, true); aCol = cfg->tryU64("aCol", 1000, true); @@ -12,6 +13,7 @@ void AMMBench::PoissonMatrixLoader::paraseConfig(INTELLI::ConfigMapPtr cfg) { "Generating [" + to_string(aRow) + "x" + to_string(aCol) + "]*[" + to_string(aCol) + "x" + to_string(bCol) + "]"); } + void AMMBench::PoissonMatrixLoader::generateAB() { torch::manual_seed(seed); A = torch::poisson(torch::ones({(long) aRow, (long) aCol})); @@ -24,9 +26,11 @@ bool AMMBench::PoissonMatrixLoader::setConfig(INTELLI::ConfigMapPtr cfg) { generateAB(); return true; } + torch::Tensor AMMBench::PoissonMatrixLoader::getA() { return A; } + torch::Tensor AMMBench::PoissonMatrixLoader::getB() { return B; } \ No newline at end of file diff --git a/src/MatrixLoader/RandomMatrixLoader.cpp b/src/MatrixLoader/RandomMatrixLoader.cpp index f5089b85..ffa07d7e 100644 --- a/src/MatrixLoader/RandomMatrixLoader.cpp +++ b/src/MatrixLoader/RandomMatrixLoader.cpp @@ -4,29 +4,34 @@ #include #include + void AMMBench::RandomMatrixLoader::paraseConfig(INTELLI::ConfigMapPtr cfg) { - aRow = cfg->tryU64("aRow", 100, true); - aCol = cfg->tryU64("aCol", 1000, true); - bCol = cfg->tryU64("bCol", 500, true); - seed = cfg->tryU64("seed", 114514, true); - INTELLI_INFO( - "Generating [" + to_string(aRow) + "x" + to_string(aCol) + "]*[" + to_string(aCol) + "x" + to_string(bCol) - + "]"); + aRow = cfg->tryU64("aRow", 100, true); + aCol = cfg->tryU64("aCol", 1000, true); + bCol = cfg->tryU64("bCol", 500, true); + seed = cfg->tryU64("seed", 114514, true); + INTELLI_INFO( + "Generating [" + to_string(aRow) + "x" + to_string(aCol) + "]*[" + to_string(aCol) + "x" + to_string(bCol) + + "]"); } + void AMMBench::RandomMatrixLoader::generateAB() { - torch::manual_seed(seed); - A = torch::rand({(long) aRow, (long) aCol}); - B = torch::rand({(long) aCol, (long) bCol}); + torch::manual_seed(seed); + A = torch::rand({(long) aRow, (long) aCol}); + B = torch::rand({(long) aCol, (long) bCol}); } + //do nothing in abstract class bool AMMBench::RandomMatrixLoader::setConfig(INTELLI::ConfigMapPtr cfg) { - paraseConfig(cfg); - generateAB(); - return true; + paraseConfig(cfg); + generateAB(); + return true; } + torch::Tensor AMMBench::RandomMatrixLoader::getA() { - return A; + return A; } + torch::Tensor AMMBench::RandomMatrixLoader::getB() { - return B; + return B; } diff --git a/src/MatrixLoader/SparseMatrixLoader.cpp b/src/MatrixLoader/SparseMatrixLoader.cpp index d19af797..6c6e1c44 100644 --- a/src/MatrixLoader/SparseMatrixLoader.cpp +++ b/src/MatrixLoader/SparseMatrixLoader.cpp @@ -6,91 +6,97 @@ #include #include #include + torch::Tensor AMMBench::SparseMatrixLoader::genSparseMatrix(uint64_t m, uint64_t n, double density, uint64_t reduceRows) { - /** - * @brief 1. gen random mat - */ - auto mat = torch::rand({(long) m, (long) n}); + /** + * @brief 1. gen random mat + */ + auto mat = torch::rand({(long) m, (long) n}); - // Iterate over each element of A and zero out the element with probability p - /** - * @brief 2. make it sparse according to density - */ - if (density < 1.0) { - for (uint64_t i = 0; i < m; i++) { - for (uint64_t j = 0; j < n; j++) { - if (torch::rand({1}).item() >= density) { - mat[i][j] = 0.0; + // Iterate over each element of A and zero out the element with probability p + /** + * @brief 2. make it sparse according to density + */ + if (density < 1.0) { + for (uint64_t i = 0; i < m; i++) { + for (uint64_t j = 0; j < n; j++) { + if (torch::rand({1}).item() >= density) { + mat[i][j] = 0.0; + } + } } - } } - } - /** - * @brief 3. reduce rows - */ - if (reduceRows == 0) { - return mat; - } + /** + * @brief 3. reduce rows + */ + if (reduceRows == 0) { + return mat; + } - std::vector selected_rows(reduceRows); - std::iota(selected_rows.begin(), selected_rows.end(), 0); - for (uint64_t i = reduceRows; i < m; ++i) { - uint64_t j = std::rand() % (i + 1); - if (j < reduceRows) { - selected_rows[j] = i; + std::vector selected_rows(reduceRows); + std::iota(selected_rows.begin(), selected_rows.end(), 0); + for (uint64_t i = reduceRows; i < m; ++i) { + uint64_t j = std::rand() % (i + 1); + if (j < reduceRows) { + selected_rows[j] = i; + } } - } - for (const uint64_t &row_idx : selected_rows) { - //mat[row_idx]=mat[0]*torch::rand({1}).item(); - mat[row_idx] = mat[0]; - // do something with the row, e.g. print it - //std::cout << row_idx << std::endl; + for (const uint64_t &row_idx: selected_rows) { + //mat[row_idx]=mat[0]*torch::rand({1}).item(); + mat[row_idx] = mat[0]; + // do something with the row, e.g. print it + //std::cout << row_idx << std::endl; - } - return mat; + } + return mat; } + void AMMBench::SparseMatrixLoader::paraseConfig(INTELLI::ConfigMapPtr cfg) { - aRow = cfg->tryU64("aRow", 100, true); - aCol = cfg->tryU64("aCol", 1000, true); - bCol = cfg->tryU64("bCol", 500, true); - seed = cfg->tryU64("seed", 114514, true); - aDensity = cfg->tryDouble("aDensity", 1.0, true); - bDensity = cfg->tryDouble("bDensity", 1.0, true); - aReduce = cfg->tryU64("aReduce", 0, true); - bReduce = cfg->tryU64("bReduce", 0, true); - if (aReduce >= aRow) { - aReduce = aRow - 1; - } - if (bReduce >= aCol) { - aReduce = aCol - 1; - } - INTELLI_INFO( - "Generating [" + to_string(aRow) + "x" + to_string(aCol) + "]*[" + to_string(aCol) + "x" + to_string(bCol) - + "], please wait for a while"); + aRow = cfg->tryU64("aRow", 100, true); + aCol = cfg->tryU64("aCol", 1000, true); + bCol = cfg->tryU64("bCol", 500, true); + seed = cfg->tryU64("seed", 114514, true); + aDensity = cfg->tryDouble("aDensity", 1.0, true); + bDensity = cfg->tryDouble("bDensity", 1.0, true); + aReduce = cfg->tryU64("aReduce", 0, true); + bReduce = cfg->tryU64("bReduce", 0, true); + if (aReduce >= aRow) { + aReduce = aRow - 1; + } + if (bReduce >= aCol) { + aReduce = aCol - 1; + } + INTELLI_INFO( + "Generating [" + to_string(aRow) + "x" + to_string(aCol) + "]*[" + to_string(aCol) + "x" + to_string(bCol) + + "], please wait for a while"); } + void AMMBench::SparseMatrixLoader::generateAB() { - torch::manual_seed(seed); - std::srand(seed); - A = genSparseMatrix(aRow, aCol, aDensity, aReduce); - INTELLI_INFO( - "Sparse matrix A is done"); - B = genSparseMatrix(aCol, bCol, bDensity, bReduce); - INTELLI_INFO( - "Sparse matrix B is done"); + torch::manual_seed(seed); + std::srand(seed); + A = genSparseMatrix(aRow, aCol, aDensity, aReduce); + INTELLI_INFO( + "Sparse matrix A is done"); + B = genSparseMatrix(aCol, bCol, bDensity, bReduce); + INTELLI_INFO( + "Sparse matrix B is done"); } + //do nothing in abstract class bool AMMBench::SparseMatrixLoader::setConfig(INTELLI::ConfigMapPtr cfg) { - paraseConfig(cfg); - generateAB(); - return true; + paraseConfig(cfg); + generateAB(); + return true; } + torch::Tensor AMMBench::SparseMatrixLoader::getA() { - return A; + return A; } + torch::Tensor AMMBench::SparseMatrixLoader::getB() { - return B; + return B; } diff --git a/src/Parallelization/BlockPartitionRunner.cpp b/src/Parallelization/BlockPartitionRunner.cpp index fdefe7d4..fa8e2fb0 100644 --- a/src/Parallelization/BlockPartitionRunner.cpp +++ b/src/Parallelization/BlockPartitionRunner.cpp @@ -4,144 +4,155 @@ #include #include + void AMMBench::BlockPartitionWorker::setConfig(INTELLI::ConfigMapPtr _cfg) { - cfg = _cfg; - sketchDimension = cfg->tryU64("sketchDimension", 50, true); - osScheduling = cfg->tryU64("osScheduling", 0, false); - std::string ptFile = cfg->tryString("ptFile", "torchscripts/FDAMM.pt", true); - useCPP = cfg->tryU64("useCPP", 0, true); - if (useCPP) { - std::string cppAlgoTag = cfg->tryString("cppAlgoTag", "mm", true); - cppAlgoPtr = cppAlgoTable.findCppAlgo(cppAlgoTag); - cppAlgoPtr->setConfig(_cfg); - } - if (!useCPP || (cppAlgoPtr == nullptr)) { - module = torch::jit::load(ptFile); + cfg = _cfg; + sketchDimension = cfg->tryU64("sketchDimension", 50, true); + osScheduling = cfg->tryU64("osScheduling", 0, false); + std::string ptFile = cfg->tryString("ptFile", "torchscripts/FDAMM.pt", true); + useCPP = cfg->tryU64("useCPP", 0, true); if (useCPP) { - INTELLI_ERROR("No cpp algorithm found, go back to pt module"); - useCPP = 0; + std::string cppAlgoTag = cfg->tryString("cppAlgoTag", "mm", true); + cppAlgoPtr = cppAlgoTable.findCppAlgo(cppAlgoTag); + cppAlgoPtr->setConfig(_cfg); + } + if (!useCPP || (cppAlgoPtr == nullptr)) { + module = torch::jit::load(ptFile); + if (useCPP) { + INTELLI_ERROR("No cpp algorithm found, go back to pt module"); + useCPP = 0; + } } - } } + void AMMBench::BlockPartitionWorker::setWorkParameters(uint64_t aStart, uint64_t aEnd, int mycore) { - startRow = aStart; - endRow = aEnd; - coreBind = mycore; - //matC=newTensor(torch::zeros({(long)(endRow+1-startRow),matB->size(1)})); + startRow = aStart; + endRow = aEnd; + coreBind = mycore; + //matC=newTensor(torch::zeros({(long)(endRow+1-startRow),matB->size(1)})); } + void AMMBench::BlockPartitionWorker::setABC(AMMBench::TensorPtr A, AMMBench::TensorPtr B, AMMBench::TensorPtr C) { - matA = A; - matB = B; - matC = C; - //assert(C); + matA = A; + matB = B; + matC = C; + //assert(C); } + void AMMBench::BlockPartitionWorker::inlineMain() { - //std::cout<<"thread at "+ to_string(coreBind)+" start\r\n"; - /** - * @brief 1. bind core and torch setting - */ - if (!osScheduling) { - INTELLI::UtilityFunctions::bind2Core((int) coreBind); - } - torch::set_num_threads(1); - /** - * @brief 2. multiply sub-matrix of A - */ - gettimeofday(&tstart, NULL); - //torch::Tensor - //torch::Tensor subC = module.forward({subA, *matB, (long) sketchDimension}).toTensor(); - // Copy the results back to the output matrix C - //matC->slice(0, startRow, endRow) = subC; - subA = matA->slice(0, startRow, endRow); - //irC=module.forward({subA, *matB, (long) sketchDimension}).toTensor(); - if (useCPP) { - INTELLI_WARNING("USE CPP ALGO"); - matC->slice(0, startRow, endRow) = cppAlgoPtr->amm(subA, *matB, sketchDimension); - } else { - matC->slice(0, startRow, endRow) =module.forward({subA, *matB, (long) sketchDimension}).toTensor(); - } - - gettimeofday(&tend, NULL); - //std::cout<slice(0, startRow, endRow) = subC; + subA = matA->slice(0, startRow, endRow); + //irC=module.forward({subA, *matB, (long) sketchDimension}).toTensor(); + if (useCPP) { + INTELLI_WARNING("USE CPP ALGO"); + matC->slice(0, startRow, endRow) = cppAlgoPtr->amm(subA, *matB, sketchDimension); + } else { + matC->slice(0, startRow, endRow) =module.forward({subA, *matB, (long) sketchDimension}).toTensor(); + } + + gettimeofday(&tend, NULL); + //std::cout<getBreakDown(); - } - return nullptr; + if (useCPP && cppAlgoPtr) { + return cppAlgoPtr->getBreakDown(); + } + return nullptr; } + uint64_t AMMBench::BlockPartitionWorker::getElapsedTime() { - return INTELLI::UtilityFunctions::timeLast(tstart, tend); + return INTELLI::UtilityFunctions::timeLast(tstart, tend); } + void AMMBench::BlockPartitionRunner::setConfig(INTELLI::ConfigMapPtr _cfg) { - cfg = _cfg; - threads = cfg->tryU64("threads", 2, true); - workers = std::vector(threads); - firstCoreBind = cfg->tryU64("firstCoreBind", 0, false); - for (uint64_t i = 0; i < threads; i++) { - workers[i] = newBlockPartitionWorker(); - workers[i]->setConfig(cfg); - } - - INTELLI_INFO("set up " + to_string(threads) + "workers."); + cfg = _cfg; + threads = cfg->tryU64("threads", 2, true); + workers = std::vector(threads); + firstCoreBind = cfg->tryU64("firstCoreBind", 0, false); + for (uint64_t i = 0; i < threads; i++) { + workers[i] = newBlockPartitionWorker(); + workers[i]->setConfig(cfg); + } + + INTELLI_INFO("set up " + to_string(threads) + "workers."); } + void AMMBench::BlockPartitionRunner::createABC(torch::Tensor A, torch::Tensor B) { - matA = newTensor(A); - matB = newTensor(B); - matC = newTensor(torch::zeros({A.size(0), B.size(1)})); - for (uint64_t i = 0; i < threads; i++) { - uint64_t rows_per_worker = A.size(0) / threads; - uint64_t start_row = i * rows_per_worker; - uint64_t end_row = (i == threads - 1) ? A.size(0) : start_row + rows_per_worker; - workers[i]->setABC(matA, matB, matC); - workers[i]->setWorkParameters(start_row, end_row, i); - } - if (firstCoreBind != 0) { - workers[0]->setCoreBInd((int) firstCoreBind); - INTELLI_INFO("first thread is bound to core" + to_string(firstCoreBind)); - } + matA = newTensor(A); + matB = newTensor(B); + matC = newTensor(torch::zeros({A.size(0), B.size(1)})); + for (uint64_t i = 0; i < threads; i++) { + uint64_t rows_per_worker = A.size(0) / threads; + uint64_t start_row = i * rows_per_worker; + uint64_t end_row = (i == threads - 1) ? A.size(0) : start_row + rows_per_worker; + workers[i]->setABC(matA, matB, matC); + workers[i]->setWorkParameters(start_row, end_row, i); + } + if (firstCoreBind != 0) { + workers[0]->setCoreBInd((int) firstCoreBind); + INTELLI_INFO("first thread is bound to core" + to_string(firstCoreBind)); + } } + torch::Tensor AMMBench::BlockPartitionRunner::parallelForward() { - for (uint64_t i = 0; i < threads; i++) { - workers[i]->startThread(); - } - INTELLI_INFO("start " + to_string(threads) + "workers."); - for (uint64_t i = 0; i < threads; i++) { - workers[i]->joinThread(); - } - /* for(uint64_t i=0;islice(0, workers[i]->startRow, workers[i]->endRow) = workers[i]->irC; - }*/ - return *matC; + for (uint64_t i = 0; i < threads; i++) { + workers[i]->startThread(); + } + INTELLI_INFO("start " + to_string(threads) + "workers."); + for (uint64_t i = 0; i < threads; i++) { + workers[i]->joinThread(); + } + /* for(uint64_t i=0;islice(0, workers[i]->startRow, workers[i]->endRow) = workers[i]->irC; + }*/ + return *matC; } + torch::Tensor AMMBench::BlockPartitionRunner::runAMM(torch::Tensor A, torch::Tensor B) { - createABC(A, B); - return parallelForward(); + createABC(A, B); + return parallelForward(); } + uint64_t AMMBench::BlockPartitionRunner::getElapsedTime() { - uint64_t ti = 0; - uint64_t tMax = 0; - for (uint64_t i = 0; i < threads; i++) { - ti = workers[i]->getElapsedTime(); - if (ti > tMax) { - tMax = ti; + uint64_t ti = 0; + uint64_t tMax = 0; + for (uint64_t i = 0; i < threads; i++) { + ti = workers[i]->getElapsedTime(); + if (ti > tMax) { + tMax = ti; + } } - } - return tMax; + return tMax; } + void AMMBench::BlockPartitionRunner::appendThreadInfo(INTELLI::ConfigMapPtr ru) { - for (uint64_t i = 0; i < threads; i++) { - std::string keyElapesedTime = "thread" + to_string(i) + "RunTime"; - ru->edit(keyElapesedTime, (uint64_t) workers[i]->getElapsedTime()); - } + for (uint64_t i = 0; i < threads; i++) { + std::string keyElapesedTime = "thread" + to_string(i) + "RunTime"; + ru->edit(keyElapesedTime, (uint64_t) workers[i]->getElapsedTime()); + } } INTELLI::ConfigMapPtr AMMBench::BlockPartitionRunner::getBreakDown() { - return workers[0]->getBreakDown(); + return workers[0]->getBreakDown(); } \ No newline at end of file diff --git a/src/Streaming/SingleThreadStreamer.cpp b/src/Streaming/SingleThreadStreamer.cpp index acb72129..52779e71 100644 --- a/src/Streaming/SingleThreadStreamer.cpp +++ b/src/Streaming/SingleThreadStreamer.cpp @@ -4,6 +4,7 @@ #include #include + bool AMMBench::SingleThreadStreamer::setConfig(INTELLI::ConfigMapPtr cfg) { cfgGlobal = cfg; /** @@ -15,67 +16,64 @@ bool AMMBench::SingleThreadStreamer::setConfig(INTELLI::ConfigMapPtr cfg) { /** * @brief 2. set the batch size */ - batchSize=cfg->tryU64("batchSize",1, true); + batchSize = cfg->tryU64("batchSize", 1, true); return true; } -torch::Tensor AMMBench::SingleThreadStreamer::streamingAmm(torch::Tensor A, torch::Tensor B, uint64_t sketchSize){ + +torch::Tensor AMMBench::SingleThreadStreamer::streamingAmm(torch::Tensor A, torch::Tensor B, uint64_t sketchSize) { assert(sketchSize); - uint64_t aRows=A.size(0); - cfgGlobal->edit("streamingTupleCnt",(uint64_t)aRows); - if(batchSize>aRows) - { - batchSize=aRows; + uint64_t aRows = A.size(0); + cfgGlobal->edit("streamingTupleCnt", (uint64_t) aRows); + if (batchSize > aRows) { + batchSize = aRows; } AMMBench::TimeStamper tsGen; tsGen.setConfig(cfgGlobal); - myTs=tsGen.getTimeStamps(); + myTs = tsGen.getTimeStamps(); INTELLI_INFO("Generate time stamp done"); matC = newTensor(torch::zeros({A.size(0), B.size(1)})); struct timeval tstart; //INTELLI_INFO("I am mm"); INTELLI_INFO("Start Streaming"); - uint64_t startRow=0; - uint64_t endRow=startRow+batchSize; - uint64_t tNow=0; - uint64_t tEXpectedArrival=myTs[endRow-1]->arrivalTime; - uint64_t tp=0; - uint64_t tDone=0; - gettimeofday(&tstart,NULL); - while(startRowarrivalTime; + uint64_t tp = 0; + uint64_t tDone = 0; + gettimeofday(&tstart, NULL); + while (startRow < aRows) { + tNow = INTELLI::UtilityFunctions::timeLastUs(tstart); + auto subA = A.slice(0, startRow, endRow); + while (tNow < tEXpectedArrival) { + tNow = INTELLI::UtilityFunctions::timeLastUs(tstart); usleep(1); } /** * @brief now, the whole batch has arrived, compute */ matC->slice(0, startRow, endRow) = cppAlgoPtr->amm(subA, B, sketchSize); - tp=INTELLI::UtilityFunctions::timeLastUs(tstart); - for(size_t i=startRow;iprocessedTime=tp; + tp = INTELLI::UtilityFunctions::timeLastUs(tstart); + for (size_t i = startRow; i < endRow; i++) { + myTs[i]->processedTime = tp; } /** * @brief update the indexes */ - startRow+=batchSize; - endRow+=batchSize; - if(endRow>=aRows) - { - endRow=aRows; + startRow += batchSize; + endRow += batchSize; + if (endRow >= aRows) { + endRow = aRows; } - tEXpectedArrival=myTs[endRow-1]->arrivalTime; + tEXpectedArrival = myTs[endRow - 1]->arrivalTime; } - tDone=INTELLI::UtilityFunctions::timeLastUs(tstart); - INTELLI_INFO("Done in "+ to_string(tDone)+"us"); - throughput=aRows; - throughput=throughput*1e6/tDone; + tDone = INTELLI::UtilityFunctions::timeLastUs(tstart); + INTELLI_INFO("Done in " + to_string(tDone) + "us"); + throughput = aRows; + throughput = throughput * 1e6 / tDone; return *matC; } + double AMMBench::SingleThreadStreamer::getLatencyPercentage(double fraction) { size_t rLen = myTs.size(); size_t nonZeroCnt = 0; diff --git a/src/Streaming/TimeStamper.cpp b/src/Streaming/TimeStamper.cpp index f2cecbdd..856d285a 100644 --- a/src/Streaming/TimeStamper.cpp +++ b/src/Streaming/TimeStamper.cpp @@ -8,41 +8,43 @@ bool AMMBench::TimeStamper::setConfig(INTELLI::ConfigMapPtr cfg) { cfgGlobal = cfg; eventRateTps = cfg->tryU64("eventRateTps", 100, true); - timeStepUs = cfg->tryU64("timeStepUs", 100,true); - timeStamper_zipfEvent=cfg->tryU64("timeStamper_zipfEvent",0, true); - timeStamper_zipfEventFactor=cfg->tryDouble("timeStamper_zipfEventFactor",0.1, true); - testSize=cfg->tryU64("streamingTupleCnt",0, true); + timeStepUs = cfg->tryU64("timeStepUs", 100, true); + timeStamper_zipfEvent = cfg->tryU64("timeStamper_zipfEvent", 0, true); + timeStamper_zipfEventFactor = cfg->tryDouble("timeStamper_zipfEventFactor", 0.1, true); + testSize = cfg->tryU64("streamingTupleCnt", 0, true); md.setSeed(seed); generateEvent(); generateArrival(); generateFinal(); return true; } + void AMMBench::TimeStamper::generateEvent() { - uint64_t maxTime=testSize*1000*1000/eventRateTps; + uint64_t maxTime = testSize * 1000 * 1000 / eventRateTps; if (timeStamper_zipfEvent) { INTELLI_INFO("Use zipf for event time, factor=" + to_string(timeStamper_zipfEventFactor)); - INTELLI_INFO("maxTime="+ to_string(maxTime)+"us"+"rate="+ to_string(eventRateTps)+"K, cnt="+ to_string(testSize) ); + INTELLI_INFO("maxTime=" + to_string(maxTime) + "us" + "rate=" + to_string(eventRateTps) + "K, cnt=" + + to_string(testSize)); eventS = md.genZipfTimeStamp(testSize, maxTime, timeStamper_zipfEventFactor); } else { - // uint64_t tsGrow = 1000 * timeStepUs / eventRateKTps; - eventS = md.genSmoothTimeStamp(testSize,maxTime); + // uint64_t tsGrow = 1000 * timeStepUs / eventRateKTps; + eventS = md.genSmoothTimeStamp(testSize, maxTime); } INTELLI_INFO("Finish the generation of event time"); } -void AMMBench::TimeStamper::generateArrival() { +void AMMBench::TimeStamper::generateArrival() { INTELLI_INFO("Finish the generation of arrival time"); } + std::vector AMMBench::TimeStamper::constructTimeStamps( std::vector _eventS, - std::vector _arrivalS) -{ + std::vector _arrivalS) { size_t len = _eventS.size(); - std::vector ru = std::vector(len); + std::vector ru = std::vector(len); for (size_t i = 0; i < len; i++) { ru[i] = newAMMTimeStamp(_eventS[i], _arrivalS[i], 0); } @@ -50,5 +52,5 @@ std::vector AMMBench::TimeStamper::constructTimeStamp } void AMMBench::TimeStamper::generateFinal() { - myTs=constructTimeStamps(eventS,eventS); + myTs = constructTimeStamps(eventS, eventS); } \ No newline at end of file diff --git a/src/Utils/IntelliLog.cpp b/src/Utils/IntelliLog.cpp index 54d7ce7b..a8503b8d 100755 --- a/src/Utils/IntelliLog.cpp +++ b/src/Utils/IntelliLog.cpp @@ -8,29 +8,29 @@ INTELLI::IntelliLog_FileProtector fp_doNotTouchMe; void INTELLI::IntelliLog::setupLoggingFile(string fname) { - fp_doNotTouchMe.openLogFile(fname); + fp_doNotTouchMe.openLogFile(fname); } void INTELLI::IntelliLog::log(std::string level, std::string_view message, const std::source_location source) { - time_t now = time(0); + time_t now = time(0); - // 把 now 转换为字符串形式 - char *dt = ctime(&now); - std::string str = level + ":"; - dt[strlen(dt) - 1] = 0; - str += dt; - //str+= static_cast(level); - str += ":"; - str += source.file_name(); - str += ":"; - str += to_string(source.line()); - str += +"|"; - str += +source.function_name(); - str += +"|"; + // 把 now 转换为字符串形式 + char *dt = ctime(&now); + std::string str = level + ":"; + dt[strlen(dt) - 1] = 0; + str += dt; + //str+= static_cast(level); + str += ":"; + str += source.file_name(); + str += ":"; + str += to_string(source.line()); + str += +"|"; + str += +source.function_name(); + str += +"|"; - str += message; - cout << str + "\n"; + str += message; + cout << str + "\n"; - fp_doNotTouchMe.appendLogFile(str + "\n"); - //fp_doNotTouchMe.unlock(); + fp_doNotTouchMe.appendLogFile(str + "\n"); + //fp_doNotTouchMe.unlock(); } \ No newline at end of file diff --git a/src/Utils/Meters/AbstractMeter.cpp b/src/Utils/Meters/AbstractMeter.cpp index 7ac4feab..88f9291a 100644 --- a/src/Utils/Meters/AbstractMeter.cpp +++ b/src/Utils/Meters/AbstractMeter.cpp @@ -1,16 +1,19 @@ #include + void DIVERSE_METER::AbstractMeter::testStaticPower(uint64_t sleepingSecond) { - startMeter(); - sleep(sleepingSecond); - stopMeter(); - staticPower = getE(); - staticPower = staticPower / sleepingSecond; + startMeter(); + sleep(sleepingSecond); + stopMeter(); + staticPower = getE(); + staticPower = staticPower / sleepingSecond; } + double DIVERSE_METER::AbstractMeter::getStaicEnergyConsumption(uint64_t runningUs) { - double t = runningUs; - t = t * staticPower / 1e6; - return t; + double t = runningUs; + t = t * staticPower / 1e6; + return t; } + double DIVERSE_METER::AbstractMeter::getStaticPower() { - return staticPower; + return staticPower; } \ No newline at end of file diff --git a/src/Utils/Meters/EspMeterUart/EspMeterUart.cpp b/src/Utils/Meters/EspMeterUart/EspMeterUart.cpp index ba7c2b6f..d0b7d8f2 100644 --- a/src/Utils/Meters/EspMeterUart/EspMeterUart.cpp +++ b/src/Utils/Meters/EspMeterUart/EspMeterUart.cpp @@ -26,47 +26,51 @@ using namespace DIVERSE_METER; enum { - UART_VCMD_START = 1, - UART_VCMD_STOP, - UART_VCMD_I, - UART_VCMD_V, - UART_VCMD_P, - UART_VCMD_E, - UART_VCMD_PEAK, + UART_VCMD_START = 1, + UART_VCMD_STOP, + UART_VCMD_I, + UART_VCMD_V, + UART_VCMD_P, + UART_VCMD_E, + UART_VCMD_PEAK, }; + void EspMeterUart::setConfig(INTELLI::ConfigMapPtr _cfg) { - AbstractMeter::setConfig(_cfg); - meterAddress = cfg->tryString("meterAddress", "/dev/ttyUSB0", true); - // openUartDev(); + AbstractMeter::setConfig(_cfg); + meterAddress = cfg->tryString("meterAddress", "/dev/ttyUSB0", true); + // openUartDev(); } + void EspMeterUart::openUartDev() { - devFd = open(meterAddress.data(), O_RDWR | O_NOCTTY); - if (devFd == -1) { + devFd = open(meterAddress.data(), O_RDWR | O_NOCTTY); + if (devFd == -1) { - METER_ERROR("can not open device meter"); - } - //char *welcome="hello world"; - struct termios termios_p; - tcgetattr(devFd, &termios_p); - /** - * @brief set up uart, 115200, maximum compatability - */ - termios_p.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON); - termios_p.c_oflag &= ~OPOST; - termios_p.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); - termios_p.c_cflag &= ~(CSIZE | PARENB); - termios_p.c_cflag = B115200 | CS8 | CLOCAL | CREAD; - termios_p.c_cc[VTIME] = 0; - termios_p.c_cc[VMIN] = 1; - tcsetattr(devFd, TCSANOW, &termios_p); - tcflush(devFd, TCIOFLUSH); - fcntl(devFd, F_SETFL, O_NONBLOCK); - //close(devFd); + METER_ERROR("can not open device meter"); + } + //char *welcome="hello world"; + struct termios termios_p; + tcgetattr(devFd, &termios_p); + /** + * @brief set up uart, 115200, maximum compatability + */ + termios_p.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON); + termios_p.c_oflag &= ~OPOST; + termios_p.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); + termios_p.c_cflag &= ~(CSIZE | PARENB); + termios_p.c_cflag = B115200 | CS8 | CLOCAL | CREAD; + termios_p.c_cc[VTIME] = 0; + termios_p.c_cc[VMIN] = 1; + tcsetattr(devFd, TCSANOW, &termios_p); + tcflush(devFd, TCIOFLUSH); + fcntl(devFd, F_SETFL, O_NONBLOCK); + //close(devFd); } + EspMeterUart::EspMeterUart(/* args */) { } + /* EspMeterUart::EspMeterUart(string name) { devFd = open(name.data(), O_RDWR); @@ -76,54 +80,57 @@ EspMeterUart::EspMeterUart(string name) { } }*/ EspMeterUart::~EspMeterUart() { - /*if (devFd != -1) { - close(devFd); - }*/ + /*if (devFd != -1) { + close(devFd); + }*/ } + void EspMeterUart::startMeter() { - openUartDev(); - uint8_t cmdSend = UART_VCMD_START; - //double ru=0; - write(devFd, &cmdSend, 1); - close(devFd); + openUartDev(); + uint8_t cmdSend = UART_VCMD_START; + //double ru=0; + write(devFd, &cmdSend, 1); + close(devFd); } + void EspMeterUart::stopMeter() { - openUartDev(); - uint8_t cmdSend = UART_VCMD_STOP; - //double ru=0; - write(devFd, &cmdSend, 1); - close(devFd); + openUartDev(); + uint8_t cmdSend = UART_VCMD_STOP; + //double ru=0; + write(devFd, &cmdSend, 1); + close(devFd); } + double EspMeterUart::getE() { - openUartDev(); - double ru = 0; - uint8_t cmdSend = UART_VCMD_E; - write(devFd, &cmdSend, 1); + openUartDev(); + double ru = 0; + uint8_t cmdSend = UART_VCMD_E; + write(devFd, &cmdSend, 1); //usleep(1000); //double ru; - int ret = -1; - uint64_t tryCnt = 0; - while (ret < 0 && tryCnt < 1000) { - ret = read(devFd, &ru, sizeof(double)); - tryCnt++; - usleep(1000); - } - close(devFd); - return ru; + int ret = -1; + uint64_t tryCnt = 0; + while (ret < 0 && tryCnt < 1000) { + ret = read(devFd, &ru, sizeof(double)); + tryCnt++; + usleep(1000); + } + close(devFd); + return ru; } double EspMeterUart::getPeak() { - double ru = 0; - uint8_t cmdSend = UART_VCMD_PEAK; - write(devFd, &cmdSend, 1); + double ru = 0; + uint8_t cmdSend = UART_VCMD_PEAK; + write(devFd, &cmdSend, 1); //usleep(1000); //double ru; - int ret = -1; - uint64_t tryCnt = 0; - while (ret < 0 && tryCnt < 1000) { - ret = read(devFd, &ru, sizeof(double)); - tryCnt++; - usleep(1000); - } - return ru; + int ret = -1; + uint64_t tryCnt = 0; + while (ret < 0 && tryCnt < 1000) { + ret = read(devFd, &ru, sizeof(double)); + tryCnt++; + usleep(1000); + } + return ru; } \ No newline at end of file diff --git a/src/Utils/Meters/IntelMeter/IntelMeter.cpp b/src/Utils/Meters/IntelMeter/IntelMeter.cpp index 42a5a1c4..03f27166 100644 --- a/src/Utils/Meters/IntelMeter/IntelMeter.cpp +++ b/src/Utils/Meters/IntelMeter/IntelMeter.cpp @@ -1,93 +1,101 @@ #include #include + using namespace DIVERSE_METER; IntelMeter::IntelMeter(/* args */) { - //system("modprobe cpuid\r\n"); - //system("modprobe msr\r\n"); + //system("modprobe cpuid\r\n"); + //system("modprobe msr\r\n"); - //en=vector(maxCpu); + //en=vector(maxCpu); } + void IntelMeter::setConfig(INTELLI::ConfigMapPtr _cfg) { - AbstractMeter::setConfig(_cfg); - maxCpu = std::thread::hardware_concurrency(); - power_units = get_rapl_power_unit(); - uint32_t i; - //printf("we have %d ,%dcores\r\n",maxCpu,i); - cpus = vector(maxCpu); - st = vector(maxCpu); - en = vector(maxCpu); - count = vector(maxCpu); + AbstractMeter::setConfig(_cfg); + maxCpu = std::thread::hardware_concurrency(); + power_units = get_rapl_power_unit(); + uint32_t i; + //printf("we have %d ,%dcores\r\n",maxCpu,i); + cpus = vector(maxCpu); + st = vector(maxCpu); + en = vector(maxCpu); + count = vector(maxCpu); - for (i = 0; i < maxCpu; i++) { - cpus[i] = i; - } + for (i = 0; i < maxCpu; i++) { + cpus[i] = i; + } } + IntelMeter::~IntelMeter() { } + uint64_t IntelMeter::rdmsr(int cpu, uint32_t reg) { - char buf[1024]; - sprintf(buf, "/dev/cpu/%d/msr", cpu); - int msr_file = open(buf, O_RDONLY); - if (msr_file < 0) { - perror("rdmsr: open"); - return msr_file; - } - uint64_t data; - if (pread(msr_file, &data, sizeof(data), reg) != sizeof(data)) { - fprintf(stderr, "read msr register 0x%x error.\n", reg); - perror("rdmsr: read msr"); - return -1; - } - close(msr_file); - return data; + char buf[1024]; + sprintf(buf, "/dev/cpu/%d/msr", cpu); + int msr_file = open(buf, O_RDONLY); + if (msr_file < 0) { + perror("rdmsr: open"); + return msr_file; + } + uint64_t data; + if (pread(msr_file, &data, sizeof(data), reg) != sizeof(data)) { + fprintf(stderr, "read msr register 0x%x error.\n", reg); + perror("rdmsr: read msr"); + return -1; + } + close(msr_file); + return data; } + rapl_power_unit IntelMeter::get_rapl_power_unit() { - rapl_power_unit ret; - uint64_t data = rdmsr(0, 0x606); - double t = (1 << (data & 0xf)); - t = 1.0 / t; - ret.PU = t; - t = (1 << ((data >> 8) & 0x1f)); - ret.ESU = 1.0 / t; - t = (1 << ((data >> 16) & 0xf)); - ret.TU = 1.0 / t; - return ret; + rapl_power_unit ret; + uint64_t data = rdmsr(0, 0x606); + double t = (1 << (data & 0xf)); + t = 1.0 / t; + ret.PU = t; + t = (1 << ((data >> 8) & 0x1f)); + ret.ESU = 1.0 / t; + t = (1 << ((data >> 16) & 0xf)); + ret.TU = 1.0 / t; + return ret; } + void IntelMeter::startMeter() { - double energy_units = power_units.ESU; - uint32_t cpu, i; - uint64_t data; - size_t n = st.size(); - for (i = 0; i < n; ++i) { - cpu = cpus[i]; - data = rdmsr(cpu, 0x611); - st[i] = (data & 0xffffffff) * energy_units; - } + double energy_units = power_units.ESU; + uint32_t cpu, i; + uint64_t data; + size_t n = st.size(); + for (i = 0; i < n; ++i) { + cpu = cpus[i]; + data = rdmsr(cpu, 0x611); + st[i] = (data & 0xffffffff) * energy_units; + } } + void IntelMeter::stopMeter() { - double energy_units = power_units.ESU; - uint32_t cpu, i; - uint64_t data; - size_t n = st.size(); - eSum = 0; - for (i = 0; i < n; ++i) { - cpu = cpus[i]; - data = rdmsr(cpu, 0x611); - en[i] = (data & 0xffffffff) * energy_units; - count[i] = 0; - if (en[i] < st[i]) { - count[i] = (double) (1ll << 32) + en[i] - st[i]; - } else { - count[i] = en[i] - st[i]; + double energy_units = power_units.ESU; + uint32_t cpu, i; + uint64_t data; + size_t n = st.size(); + eSum = 0; + for (i = 0; i < n; ++i) { + cpu = cpus[i]; + data = rdmsr(cpu, 0x611); + en[i] = (data & 0xffffffff) * energy_units; + count[i] = 0; + if (en[i] < st[i]) { + count[i] = (double) (1ll << 32) + en[i] - st[i]; + } else { + count[i] = en[i] - st[i]; + } + eSum += count[i]; } - eSum += count[i]; - } } + double IntelMeter::getE() { - return eSum / 1000.0; + return eSum / 1000.0; } diff --git a/src/Utils/Meters/MeterTable.cpp b/src/Utils/Meters/MeterTable.cpp index 0a51a4d9..c72e24d3 100644 --- a/src/Utils/Meters/MeterTable.cpp +++ b/src/Utils/Meters/MeterTable.cpp @@ -1,13 +1,14 @@ #include #include #include + namespace DIVERSE_METER { /** * @note revise me if you need new loader */ -DIVERSE_METER::MeterTable::MeterTable() { - meterMap["espUart"] = newEspMeterUart(); - meterMap["intelMsr"] = newIntelMeter(); -} + DIVERSE_METER::MeterTable::MeterTable() { + meterMap["espUart"] = newEspMeterUart(); + meterMap["intelMsr"] = newIntelMeter(); + } } \ No newline at end of file diff --git a/src/Utils/UtilityFunctions.cpp b/src/Utils/UtilityFunctions.cpp index f6eaacba..ef0cae03 100755 --- a/src/Utils/UtilityFunctions.cpp +++ b/src/Utils/UtilityFunctions.cpp @@ -9,6 +9,7 @@ #include #include #include + using namespace std; /* @@ -23,77 +24,77 @@ void INTELLI::UtilityFunctions::timerEnd(Result &result) { result.timeTaken /= 1000.0; }*/ int INTELLI::UtilityFunctions::bind2Core(int id) { - if (id == -1) //OS scheduling - { - return -1; - } - int maxCpu = std::thread::hardware_concurrency(); - int cpuId = id % maxCpu; - cpu_set_t mask; - CPU_ZERO(&mask); - CPU_SET(cpuId, &mask); - /** - * @brief fixed some core bind bugs - */ - if (sched_setaffinity(0, sizeof(cpu_set_t), &mask) < 0) { - AT_ERROR("Error: setaffinity()\n"); - exit(0); - } - return cpuId; + if (id == -1) //OS scheduling + { + return -1; + } + int maxCpu = std::thread::hardware_concurrency(); + int cpuId = id % maxCpu; + cpu_set_t mask; + CPU_ZERO(&mask); + CPU_SET(cpuId, &mask); + /** + * @brief fixed some core bind bugs + */ + if (sched_setaffinity(0, sizeof(cpu_set_t), &mask) < 0) { + AT_ERROR("Error: setaffinity()\n"); + exit(0); + } + return cpuId; } vector INTELLI::UtilityFunctions::avgPartitionSizeFinal(size_t inS, std::vector partitionWeight) { - size_t partitions = partitionWeight.size(); - vector partitionSizeFinals = vector(partitions); - size_t divideLen = inS / partitions; - size_t tEnd = 0; + size_t partitions = partitionWeight.size(); + vector partitionSizeFinals = vector(partitions); + size_t divideLen = inS / partitions; + size_t tEnd = 0; - for (size_t i = 0; i < partitions - 1; i++) { - tEnd += divideLen; - partitionSizeFinals[i] = divideLen; - } - partitionSizeFinals[partitions - 1] = inS - tEnd; - return partitionSizeFinals; + for (size_t i = 0; i < partitions - 1; i++) { + tEnd += divideLen; + partitionSizeFinals[i] = divideLen; + } + partitionSizeFinals[partitions - 1] = inS - tEnd; + return partitionSizeFinals; } vector INTELLI::UtilityFunctions::weightedPartitionSizeFinal(size_t inS, std::vector partitionWeight) { - vector partitionSizes; - vector partitionSizeFinals; - size_t fraction = accumulate(partitionWeight.begin(), partitionWeight.end(), 0); - size_t tsize = 0; - for (size_t i = 0; i < partitionWeight.size() - 1; i++) { - tsize = inS * partitionWeight[i] / fraction; - partitionSizes.push_back(tsize); - } + vector partitionSizes; + vector partitionSizeFinals; + size_t fraction = accumulate(partitionWeight.begin(), partitionWeight.end(), 0); + size_t tsize = 0; + for (size_t i = 0; i < partitionWeight.size() - 1; i++) { + tsize = inS * partitionWeight[i] / fraction; + partitionSizes.push_back(tsize); + } - //check if the partition is vaild - size_t tEnd = 0; - for (size_t i = 0; i < partitionSizes.size() - 1; i++) { - if (partitionSizes[i] != 0) { - tEnd += partitionSizes[i]; - partitionSizeFinals.push_back(partitionSizes[i]); + //check if the partition is vaild + size_t tEnd = 0; + for (size_t i = 0; i < partitionSizes.size() - 1; i++) { + if (partitionSizes[i] != 0) { + tEnd += partitionSizes[i]; + partitionSizeFinals.push_back(partitionSizes[i]); + } } - } - partitionSizeFinals[partitionSizes.size() - 1] = inS - tEnd; - return partitionSizeFinals; + partitionSizeFinals[partitionSizes.size() - 1] = inS - tEnd; + return partitionSizeFinals; } size_t INTELLI::UtilityFunctions::timeLast(struct timeval ts, struct timeval te) { - int64_t s0, e0, s1, e1; - s0 = ts.tv_sec; - s1 = ts.tv_usec; - e0 = te.tv_sec; - e1 = te.tv_usec; - return 1000000 * (e0 - s0) + (e1 - s1); + int64_t s0, e0, s1, e1; + s0 = ts.tv_sec; + s1 = ts.tv_usec; + e0 = te.tv_sec; + e1 = te.tv_usec; + return 1000000 * (e0 - s0) + (e1 - s1); } size_t INTELLI::UtilityFunctions::timeLastUs(struct timeval ts) { - struct timeval te; - gettimeofday(&te, NULL); - int64_t s0, e0, s1, e1; - s0 = ts.tv_sec; - s1 = ts.tv_usec; - e0 = te.tv_sec; - e1 = te.tv_usec; - return 1000000 * (e0 - s0) + (e1 - s1); + struct timeval te; + gettimeofday(&te, NULL); + int64_t s0, e0, s1, e1; + s0 = ts.tv_sec; + s1 = ts.tv_usec; + e0 = te.tv_sec; + e1 = te.tv_usec; + return 1000000 * (e0 - s0) + (e1 - s1); } diff --git a/src/myVecAdd.cpp b/src/myVecAdd.cpp index 6f7286c5..94d1faba 100644 --- a/src/myVecAdd.cpp +++ b/src/myVecAdd.cpp @@ -5,6 +5,7 @@ #include using namespace torch; + /** * * @brief The c++ extension operation to pytorch @@ -14,60 +15,60 @@ using namespace torch; * @return tensor */ torch::Tensor myVecAdd(torch::Tensor a, torch::Tensor b) { - c10::IntArrayRef tsize = a.sizes(); - int h = tsize[0]; - int w = tsize[1]; - /** - * @brief convert tensor to vector - */ - auto ta = a.reshape({1, h * w}); - auto tb = b.reshape({1, h * w}); - std::vector va(ta.data_ptr(), ta.data_ptr() + ta.numel()); - std::vector vb(tb.data_ptr(), tb.data_ptr() + tb.numel()); - std::vector vc = std::vector(va.size()); - for (size_t i = 0; i < vc.size(); i++) { - vc[i] = va[i] + vb[i]; - } - /** - * @brief convert vector back to tensor - */ - auto opts = torch::TensorOptions().dtype(torch::kFloat32); - auto tensor2 = torch::from_blob(vc.data(), {int64_t(vc.size())}, opts).clone(); - tensor2 = tensor2.reshape({h, w}); + c10::IntArrayRef tsize = a.sizes(); + int h = tsize[0]; + int w = tsize[1]; + /** + * @brief convert tensor to vector + */ + auto ta = a.reshape({1, h * w}); + auto tb = b.reshape({1, h * w}); + std::vector va(ta.data_ptr(), ta.data_ptr() + ta.numel()); + std::vector vb(tb.data_ptr(), tb.data_ptr() + tb.numel()); + std::vector vc = std::vector(va.size()); + for (size_t i = 0; i < vc.size(); i++) { + vc[i] = va[i] + vb[i]; + } + /** + * @brief convert vector back to tensor + */ + auto opts = torch::TensorOptions().dtype(torch::kFloat32); + auto tensor2 = torch::from_blob(vc.data(), {int64_t(vc.size())}, opts).clone(); + tensor2 = tensor2.reshape({h, w}); - return tensor2.clone(); - // END output_tensor + return tensor2.clone(); + // END output_tensor } torch::Tensor myVecSub(torch::Tensor a, torch::Tensor b) { - c10::IntArrayRef tsize = a.sizes(); - int h = tsize[0]; - int w = tsize[1]; - /** - * @brief convert tensor to vector - */ - auto ta = a.reshape({1, h * w}); - auto tb = b.reshape({1, h * w}); - std::vector va(ta.data_ptr(), ta.data_ptr() + ta.numel()); - std::vector vb(tb.data_ptr(), tb.data_ptr() + tb.numel()); - std::vector vc = std::vector(va.size()); - for (size_t i = 0; i < vc.size(); i++) { - vc[i] = va[i] - vb[i]; - } - /** - * @brief convert vector back to tensor - */ - auto opts = torch::TensorOptions().dtype(torch::kFloat32); - auto tensor2 = torch::from_blob(vc.data(), {int64_t(vc.size())}, opts).clone(); - tensor2 = tensor2.reshape({h, w}); - return tensor2.clone(); - // END output_tensor + c10::IntArrayRef tsize = a.sizes(); + int h = tsize[0]; + int w = tsize[1]; + /** + * @brief convert tensor to vector + */ + auto ta = a.reshape({1, h * w}); + auto tb = b.reshape({1, h * w}); + std::vector va(ta.data_ptr(), ta.data_ptr() + ta.numel()); + std::vector vb(tb.data_ptr(), tb.data_ptr() + tb.numel()); + std::vector vc = std::vector(va.size()); + for (size_t i = 0; i < vc.size(); i++) { + vc[i] = va[i] - vb[i]; + } + /** + * @brief convert vector back to tensor + */ + auto opts = torch::TensorOptions().dtype(torch::kFloat32); + auto tensor2 = torch::from_blob(vc.data(), {int64_t(vc.size())}, opts).clone(); + tensor2 = tensor2.reshape({h, w}); + return tensor2.clone(); + // END output_tensor } /** * @brief Declare the function to pytorch * @note The of lib is myLib */ TORCH_LIBRARY(myLib, m) { - m.def("myVecAdd", myVecAdd); - m.def("myVecSub", myVecSub); + m.def("myVecAdd", myVecAdd); + m.def("myVecSub", myVecSub); } \ No newline at end of file diff --git a/test/SystemTest/BlockPartitionTest.cpp b/test/SystemTest/BlockPartitionTest.cpp index 64edf7f8..56371375 100644 --- a/test/SystemTest/BlockPartitionTest.cpp +++ b/test/SystemTest/BlockPartitionTest.cpp @@ -4,70 +4,74 @@ #include #define CATCH_CONFIG_MAIN + #include "catch.hpp" #include + using namespace std; using namespace INTELLI; using namespace torch; + void runSingleThreadTest(std::string configName) { - ConfigMapPtr cfg = newConfigMap(); - cfg->fromFile(configName); - AMMBench::MatrixLoaderTable mLoaderTable; - uint64_t sketchDimension; - sketchDimension = cfg->tryU64("sketchDimension", 50, true); - uint64_t coreBind = cfg->tryU64("coreBind", 0, true); - UtilityFunctions::bind2Core((int) coreBind); - torch::set_num_threads(1); - std::string ptFile = cfg->tryString("ptFile", "torchscripts/FDAMM.pt", true); + ConfigMapPtr cfg = newConfigMap(); + cfg->fromFile(configName); + AMMBench::MatrixLoaderTable mLoaderTable; + uint64_t sketchDimension; + sketchDimension = cfg->tryU64("sketchDimension", 50, true); + uint64_t coreBind = cfg->tryU64("coreBind", 0, true); + UtilityFunctions::bind2Core((int) coreBind); + torch::set_num_threads(1); + std::string ptFile = cfg->tryString("ptFile", "torchscripts/FDAMM.pt", true); - //uint64_t customResultName = cfg->tryU64("customResultName", 0, true); - INTELLI_INFO("Place me at core" + to_string(coreBind)); - INTELLI_INFO( - "with sketch" + to_string(sketchDimension)); - torch::jit::script::Module module; - INTELLI_INFO("Try pt file " + ptFile); - module = torch::jit::load(ptFile); - std::string matrixLoaderTag = cfg->tryString("matrixLoaderTag", "random", true); - auto matLoaderPtr = mLoaderTable.findMatrixLoader(matrixLoaderTag); - assert(matLoaderPtr); - matLoaderPtr->setConfig(cfg); - auto A = matLoaderPtr->getA(); - auto B = matLoaderPtr->getB(); - /*torch::manual_seed(114514); -//555 -auto A = torch::rand({(long) aRow, (long) aCol}); -auto B = torch::rand({(long) aCol, (long) bCol});*/ - INTELLI_INFO("Generation done, conducting..."); - ThreadPerf pef((int) coreBind); - pef.setPerfList(); - pef.start(); - auto C =module.forward({A, B, (long) sketchDimension}).toTensor(); - pef.end(); - std::string ruName = "default"; + //uint64_t customResultName = cfg->tryU64("customResultName", 0, true); + INTELLI_INFO("Place me at core" + to_string(coreBind)); + INTELLI_INFO( + "with sketch" + to_string(sketchDimension)); + torch::jit::script::Module module; + INTELLI_INFO("Try pt file " + ptFile); + module = torch::jit::load(ptFile); + std::string matrixLoaderTag = cfg->tryString("matrixLoaderTag", "random", true); + auto matLoaderPtr = mLoaderTable.findMatrixLoader(matrixLoaderTag); + assert(matLoaderPtr); + matLoaderPtr->setConfig(cfg); + auto A = matLoaderPtr->getA(); + auto B = matLoaderPtr->getB(); + /*torch::manual_seed(114514); + //555 + auto A = torch::rand({(long) aRow, (long) aCol}); + auto B = torch::rand({(long) aCol, (long) bCol});*/ + INTELLI_INFO("Generation done, conducting..."); + ThreadPerf pef((int) coreBind); + pef.setPerfList(); + pef.start(); + auto C =module.forward({A, B, (long) sketchDimension}).toTensor(); + pef.end(); + std::string ruName = "default"; - auto resultCsv = pef.resultToConfigMap(); - resultCsv->toFile(ruName + ".csv"); - INTELLI_INFO("Done. here is result"); - std::cout << resultCsv->toString() << endl; + auto resultCsv = pef.resultToConfigMap(); + resultCsv->toFile(ruName + ".csv"); + INTELLI_INFO("Done. here is result"); + std::cout << resultCsv->toString() << endl; } + TEST_CASE("Test the parallelization, thread=2", "[short]") { - int a = 0; - ConfigMapPtr cfg = newConfigMap(); - cfg->edit("ptFile", "torchscripts/RAWMM.pt"); - cfg->edit("threads", (uint64_t) 2); - torch::manual_seed(114514); - auto A = torch::rand({(long) 4, (long) 4}); - auto B = torch::rand({(long) 4, (long) 4}); - AMMBench::BlockPartitionRunner br; - br.setConfig(cfg); - auto C1 = br.runAMM(A, B); - auto C2 = torch::matmul(A, B); - std::cout << "parallel MM" << endl; - std::cout << C1 << std::endl; - std::cout << "raw MM" << endl; - std::cout << C2 << std::endl; - //runSingleThreadTest("scripts/config_CRS.csv"); - // place your test here - REQUIRE(a == 0); + int a = 0; + ConfigMapPtr cfg = newConfigMap(); + cfg->edit("ptFile", "torchscripts/RAWMM.pt"); + cfg->edit("threads", (uint64_t) 2); + torch::manual_seed(114514); + auto A = torch::rand({(long) 4, (long) 4}); + auto B = torch::rand({(long) 4, (long) 4}); + AMMBench::BlockPartitionRunner br; + br.setConfig(cfg); + auto C1 = br.runAMM(A, B); + auto C2 = torch::matmul(A, B); + std::cout << "parallel MM" << endl; + std::cout << C1 << std::endl; + std::cout << "raw MM" << endl; + std::cout << C2 << std::endl; + //runSingleThreadTest("scripts/config_CRS.csv"); + // place your test here + REQUIRE(a == 0); } \ No newline at end of file diff --git a/test/SystemTest/CRSTest.cpp b/test/SystemTest/CRSTest.cpp index f7150d54..a692f349 100644 --- a/test/SystemTest/CRSTest.cpp +++ b/test/SystemTest/CRSTest.cpp @@ -1,96 +1,104 @@ #include #define CATCH_CONFIG_MAIN + #include "catch.hpp" #include + using namespace std; using namespace INTELLI; using namespace torch; + void runSingleThreadTest(std::string configName) { - ConfigMapPtr cfg = newConfigMap(); - cfg->fromFile(configName); - AMMBench::MatrixLoaderTable mLoaderTable; - uint64_t sketchDimension; - sketchDimension = cfg->tryU64("sketchDimension", 50, true); - uint64_t coreBind = cfg->tryU64("coreBind", 0, true); - UtilityFunctions::bind2Core((int) coreBind); - torch::set_num_threads(1); - std::string ptFile = cfg->tryString("ptFile", "torchscripts/FDAMM.pt", true); + ConfigMapPtr cfg = newConfigMap(); + cfg->fromFile(configName); + AMMBench::MatrixLoaderTable mLoaderTable; + uint64_t sketchDimension; + sketchDimension = cfg->tryU64("sketchDimension", 50, true); + uint64_t coreBind = cfg->tryU64("coreBind", 0, true); + UtilityFunctions::bind2Core((int) coreBind); + torch::set_num_threads(1); + std::string ptFile = cfg->tryString("ptFile", "torchscripts/FDAMM.pt", true); - //uint64_t customResultName = cfg->tryU64("customResultName", 0, true); - INTELLI_INFO("Place me at core" + to_string(coreBind)); - INTELLI_INFO( - "with sketch" + to_string(sketchDimension)); - torch::jit::script::Module module; - INTELLI_INFO("Try pt file " + ptFile); - module = torch::jit::load(ptFile); - std::string matrixLoaderTag = cfg->tryString("matrixLoaderTag", "random", true); - auto matLoaderPtr = mLoaderTable.findMatrixLoader(matrixLoaderTag); - assert(matLoaderPtr); - matLoaderPtr->setConfig(cfg); - auto A = matLoaderPtr->getA(); - auto B = matLoaderPtr->getB(); - /*torch::manual_seed(114514); -//555 -auto A = torch::rand({(long) aRow, (long) aCol}); -auto B = torch::rand({(long) aCol, (long) bCol});*/ - INTELLI_INFO("Generation done, conducting..."); - ThreadPerf pef((int) coreBind); - pef.setPerfList(); - pef.start(); - auto C =module.forward({A, B, (long) sketchDimension}).toTensor(); - pef.end(); - std::string ruName = "default"; + //uint64_t customResultName = cfg->tryU64("customResultName", 0, true); + INTELLI_INFO("Place me at core" + to_string(coreBind)); + INTELLI_INFO( + "with sketch" + to_string(sketchDimension)); + torch::jit::script::Module module; + INTELLI_INFO("Try pt file " + ptFile); + module = torch::jit::load(ptFile); + std::string matrixLoaderTag = cfg->tryString("matrixLoaderTag", "random", true); + auto matLoaderPtr = mLoaderTable.findMatrixLoader(matrixLoaderTag); + assert(matLoaderPtr); + matLoaderPtr->setConfig(cfg); + auto A = matLoaderPtr->getA(); + auto B = matLoaderPtr->getB(); + /*torch::manual_seed(114514); + //555 + auto A = torch::rand({(long) aRow, (long) aCol}); + auto B = torch::rand({(long) aCol, (long) bCol});*/ + INTELLI_INFO("Generation done, conducting..."); + ThreadPerf pef((int) coreBind); + pef.setPerfList(); + pef.start(); + auto C =module.forward({A, B, (long) sketchDimension}).toTensor(); + pef.end(); + std::string ruName = "default"; - auto resultCsv = pef.resultToConfigMap(); - resultCsv->toFile(ruName + ".csv"); - INTELLI_INFO("Done. here is result"); - std::cout << resultCsv->toString() << endl; + auto resultCsv = pef.resultToConfigMap(); + resultCsv->toFile(ruName + ".csv"); + INTELLI_INFO("Done. here is result"); + std::cout << resultCsv->toString() << endl; } + TEST_CASE("Test the COLUMN ROW SAMPLINGS", "[short]") { - int a = 0; - runSingleThreadTest("scripts/config_CRS.csv"); - // place your test here - REQUIRE(a == 0); + int a = 0; + runSingleThreadTest("scripts/config_CRS.csv"); + // place your test here + REQUIRE(a == 0); } + TEST_CASE("Test the COLUMN ROW SAMPLINGS, V2", "[short]") { - int a = 0; - runSingleThreadTest("scripts/config_CRSV2.csv"); - // place your test here - REQUIRE(a == 0); + int a = 0; + runSingleThreadTest("scripts/config_CRSV2.csv"); + // place your test here + REQUIRE(a == 0); } + TEST_CASE("Test CRS in cpp", "[short]") { - torch::manual_seed(114514); - AMMBench::CRSCPPAlgo crs; - auto A = torch::rand({400, 400}); - auto B = torch::rand({400, 400}); - auto realC = torch::matmul(A, B); - auto ammC = crs.amm(A, B, 20); - double froError = INTELLI::UtilityFunctions::relativeFrobeniusNorm(realC, ammC); - REQUIRE(froError < 0.5); + torch::manual_seed(114514); + AMMBench::CRSCPPAlgo crs; + auto A = torch::rand({400, 400}); + auto B = torch::rand({400, 400}); + auto realC = torch::matmul(A, B); + auto ammC = crs.amm(A, B, 20); + double froError = INTELLI::UtilityFunctions::relativeFrobeniusNorm(realC, ammC); + REQUIRE(froError < 0.5); } + TEST_CASE("Test CRS v2 in cpp", "[short]") { - torch::manual_seed(114514); - AMMBench::CRSV2CPPAlgo crs; - auto A = torch::rand({400, 400}); - auto B = torch::rand({400, 400}); - auto realC = torch::matmul(A, B); - auto ammC = crs.amm(A, B, 20); - double froError = INTELLI::UtilityFunctions::relativeFrobeniusNorm(realC, ammC); - REQUIRE(froError < 0.5); + torch::manual_seed(114514); + AMMBench::CRSV2CPPAlgo crs; + auto A = torch::rand({400, 400}); + auto B = torch::rand({400, 400}); + auto realC = torch::matmul(A, B); + auto ammC = crs.amm(A, B, 20); + double froError = INTELLI::UtilityFunctions::relativeFrobeniusNorm(realC, ammC); + REQUIRE(froError < 0.5); } + TEST_CASE("Test Bernoulli CRS in cpp", "[short]") { - torch::manual_seed(114514); - AMMBench::BCRSCPPAlgo bcrs; - auto A = torch::rand({400, 400}); - auto B = torch::rand({400, 400}); - auto realC = torch::matmul(A, B); - auto ammC = bcrs.amm(A, B, 20); - double froError = INTELLI::UtilityFunctions::relativeFrobeniusNorm(realC, ammC); - REQUIRE(froError < 0.5); + torch::manual_seed(114514); + AMMBench::BCRSCPPAlgo bcrs; + auto A = torch::rand({400, 400}); + auto B = torch::rand({400, 400}); + auto realC = torch::matmul(A, B); + auto ammC = bcrs.amm(A, B, 20); + double froError = INTELLI::UtilityFunctions::relativeFrobeniusNorm(realC, ammC); + REQUIRE(froError < 0.5); } \ No newline at end of file diff --git a/test/SystemTest/EWSTest.cpp b/test/SystemTest/EWSTest.cpp index 135e132d..e9ef2b5c 100644 --- a/test/SystemTest/EWSTest.cpp +++ b/test/SystemTest/EWSTest.cpp @@ -1,67 +1,72 @@ #include #define CATCH_CONFIG_MAIN + #include "catch.hpp" #include + using namespace std; using namespace INTELLI; using namespace torch; + void runSingleThreadTest(std::string configName) { - ConfigMapPtr cfg = newConfigMap(); - cfg->fromFile(configName); - AMMBench::MatrixLoaderTable mLoaderTable; - uint64_t sketchDimension; - sketchDimension = cfg->tryU64("sketchDimension", 50, true); - uint64_t coreBind = cfg->tryU64("coreBind", 0, true); - UtilityFunctions::bind2Core((int) coreBind); - torch::set_num_threads(1); - std::string ptFile = cfg->tryString("ptFile", "torchscripts/FDAMM.pt", true); + ConfigMapPtr cfg = newConfigMap(); + cfg->fromFile(configName); + AMMBench::MatrixLoaderTable mLoaderTable; + uint64_t sketchDimension; + sketchDimension = cfg->tryU64("sketchDimension", 50, true); + uint64_t coreBind = cfg->tryU64("coreBind", 0, true); + UtilityFunctions::bind2Core((int) coreBind); + torch::set_num_threads(1); + std::string ptFile = cfg->tryString("ptFile", "torchscripts/FDAMM.pt", true); - //uint64_t customResultName = cfg->tryU64("customResultName", 0, true); - INTELLI_INFO("Place me at core" + to_string(coreBind)); - INTELLI_INFO( - "with sketch" + to_string(sketchDimension)); - torch::jit::script::Module module; - INTELLI_INFO("Try pt file " + ptFile); - module = torch::jit::load(ptFile); - std::string matrixLoaderTag = cfg->tryString("matrixLoaderTag", "random", true); - auto matLoaderPtr = mLoaderTable.findMatrixLoader(matrixLoaderTag); - assert(matLoaderPtr); - matLoaderPtr->setConfig(cfg); - auto A = matLoaderPtr->getA(); - auto B = matLoaderPtr->getB(); - /*torch::manual_seed(114514); -//555 -auto A = torch::rand({(long) aRow, (long) aCol}); -auto B = torch::rand({(long) aCol, (long) bCol});*/ - INTELLI_INFO("Generation done, conducting..."); - ThreadPerf pef((int) coreBind); - pef.setPerfList(); - pef.start(); - auto C =module.forward({A, B, (long) sketchDimension}).toTensor(); - pef.end(); - std::string ruName = "default"; + //uint64_t customResultName = cfg->tryU64("customResultName", 0, true); + INTELLI_INFO("Place me at core" + to_string(coreBind)); + INTELLI_INFO( + "with sketch" + to_string(sketchDimension)); + torch::jit::script::Module module; + INTELLI_INFO("Try pt file " + ptFile); + module = torch::jit::load(ptFile); + std::string matrixLoaderTag = cfg->tryString("matrixLoaderTag", "random", true); + auto matLoaderPtr = mLoaderTable.findMatrixLoader(matrixLoaderTag); + assert(matLoaderPtr); + matLoaderPtr->setConfig(cfg); + auto A = matLoaderPtr->getA(); + auto B = matLoaderPtr->getB(); + /*torch::manual_seed(114514); + //555 + auto A = torch::rand({(long) aRow, (long) aCol}); + auto B = torch::rand({(long) aCol, (long) bCol});*/ + INTELLI_INFO("Generation done, conducting..."); + ThreadPerf pef((int) coreBind); + pef.setPerfList(); + pef.start(); + auto C =module.forward({A, B, (long) sketchDimension}).toTensor(); + pef.end(); + std::string ruName = "default"; - auto resultCsv = pef.resultToConfigMap(); - resultCsv->toFile(ruName + ".csv"); - INTELLI_INFO("Done. here is result"); - std::cout << resultCsv->toString() << endl; + auto resultCsv = pef.resultToConfigMap(); + resultCsv->toFile(ruName + ".csv"); + INTELLI_INFO("Done. here is result"); + std::cout << resultCsv->toString() << endl; } + TEST_CASE("Test the COLUMN ROW SAMPLINGS", "[short]") { - int a = 0; - runSingleThreadTest("scripts/config_EWS.csv"); - // place your test here - REQUIRE(a == 0); + int a = 0; + runSingleThreadTest("scripts/config_EWS.csv"); + // place your test here + REQUIRE(a == 0); } + TEST_CASE("Test EWS in cpp", "[short]") { - torch::manual_seed(114514); - AMMBench::EWSCPPAlgo ews; - auto A = torch::rand({400, 400}); - auto B = torch::rand({400, 400}); - auto realC = torch::matmul(A, B); - auto ammC = ews.amm(A, B, 20); - double froError = INTELLI::UtilityFunctions::relativeFrobeniusNorm(realC, ammC); - REQUIRE(froError < 0.5); + torch::manual_seed(114514); + AMMBench::EWSCPPAlgo ews; + auto A = torch::rand({400, 400}); + auto B = torch::rand({400, 400}); + auto realC = torch::matmul(A, B); + auto ammC = ews.amm(A, B, 20); + double froError = INTELLI::UtilityFunctions::relativeFrobeniusNorm(realC, ammC); + REQUIRE(froError < 0.5); } \ No newline at end of file diff --git a/test/SystemTest/INT8Test.cpp b/test/SystemTest/INT8Test.cpp index c24f02d4..47264518 100644 --- a/test/SystemTest/INT8Test.cpp +++ b/test/SystemTest/INT8Test.cpp @@ -4,21 +4,23 @@ #include #define CATCH_CONFIG_MAIN + #include "catch.hpp" #include #include + TEST_CASE("Test int8", "[short]") { - torch::manual_seed(114514); - AMMBench::INT8CPPAlgo int8mm; - auto A = torch::rand({4, 4}); - auto B = torch::rand({4, 4}); - auto realC = torch::matmul(A, B); - auto ammC = int8mm.amm(A, B, 20); - std::cout << "int8:" << std::endl; - std::cout << ammC << std::endl; - std::cout << "fp32:" << std::endl; - std::cout << realC << std::endl; - double froError = INTELLI::UtilityFunctions::relativeFrobeniusNorm(realC, ammC); - REQUIRE(froError < 0.5); + torch::manual_seed(114514); + AMMBench::INT8CPPAlgo int8mm; + auto A = torch::rand({4, 4}); + auto B = torch::rand({4, 4}); + auto realC = torch::matmul(A, B); + auto ammC = int8mm.amm(A, B, 20); + std::cout << "int8:" << std::endl; + std::cout << ammC << std::endl; + std::cout << "fp32:" << std::endl; + std::cout << realC << std::endl; + double froError = INTELLI::UtilityFunctions::relativeFrobeniusNorm(realC, ammC); + REQUIRE(froError < 0.5); } \ No newline at end of file diff --git a/test/SystemTest/PQTest.cpp b/test/SystemTest/PQTest.cpp index 4537a4a6..f008f040 100644 --- a/test/SystemTest/PQTest.cpp +++ b/test/SystemTest/PQTest.cpp @@ -1,22 +1,25 @@ #include #define CATCH_CONFIG_MAIN + #include "catch.hpp" #include + using namespace std; using namespace INTELLI; using namespace torch; TEST_CASE("Test Load PQ", "[short]") { - torch::serialize::InputArchive archive; - archive.load_from("torchscripts/PQ/prototypes.pt"); - torch::Tensor prototypes; - archive.read("prototypes", prototypes); - auto pt_size = prototypes.sizes(); - std::cout<<"prototype size:"+ to_string(pt_size[0])+"x"+to_string(pt_size[1])+"x"+to_string(pt_size[2])< #define CATCH_CONFIG_MAIN + #include "catch.hpp" #include + using namespace std; using namespace INTELLI; using namespace torch; TEST_CASE("Test CRS in cpp", "[short]") { - torch::manual_seed(114514); - AMMBench::SMPPCACPPAlgo smppca; - auto A = torch::rand({600, 400}); - auto B = torch::rand({400, 1000}); - auto realC = torch::matmul(A, B); - auto ammC = smppca.amm(A, B, 20); - double froError = INTELLI::UtilityFunctions::relativeFrobeniusNorm(realC, ammC); - std::cout << froError << std::endl; - // REQUIRE(froError < 0.5); + torch::manual_seed(114514); + AMMBench::SMPPCACPPAlgo smppca; + auto A = torch::rand({600, 400}); + auto B = torch::rand({400, 1000}); + auto realC = torch::matmul(A, B); + auto ammC = smppca.amm(A, B, 20); + double froError = INTELLI::UtilityFunctions::relativeFrobeniusNorm(realC, ammC); + std::cout << froError << std::endl; + // REQUIRE(froError < 0.5); } \ No newline at end of file diff --git a/test/SystemTest/SimpleTest.cpp b/test/SystemTest/SimpleTest.cpp index 5aad51e3..e3378caf 100644 --- a/test/SystemTest/SimpleTest.cpp +++ b/test/SystemTest/SimpleTest.cpp @@ -1,34 +1,37 @@ #include #define CATCH_CONFIG_MAIN + #include "catch.hpp" #include #include + using namespace std; TEST_CASE("Test basic", "[short]") { - int a = 0; - // place your test here - REQUIRE(a == 0); + int a = 0; + // place your test here + REQUIRE(a == 0); } + TEST_CASE("Generate spase matrix", "[short]") { - int a = 0; - INTELLI::ConfigMapPtr cfg = newConfigMap(); - cfg->edit("aRow", (uint64_t) 6); - cfg->edit("aCol", (uint64_t) 6); - cfg->edit("bCol", (uint64_t) 6); - cfg->edit("aDensity", (double) 1.0); - cfg->edit("bDensity", (double) 0.5); - cfg->edit("aReduce", (uint64_t) 1); - cfg->edit("bReduce", (uint64_t) 2); - AMMBench::SparseMatrixLoader sml; - sml.setConfig(cfg); - cout << "sparse A" << endl; - cout << sml.getA() << endl; - cout << "sparse B" << endl; - cout << sml.getB() << endl; - // place your test here - REQUIRE(a == 0); + int a = 0; + INTELLI::ConfigMapPtr cfg = newConfigMap(); + cfg->edit("aRow", (uint64_t) 6); + cfg->edit("aCol", (uint64_t) 6); + cfg->edit("bCol", (uint64_t) 6); + cfg->edit("aDensity", (double) 1.0); + cfg->edit("bDensity", (double) 0.5); + cfg->edit("aReduce", (uint64_t) 1); + cfg->edit("bReduce", (uint64_t) 2); + AMMBench::SparseMatrixLoader sml; + sml.setConfig(cfg); + cout << "sparse A" << endl; + cout << sml.getA() << endl; + cout << "sparse B" << endl; + cout << sml.getB() << endl; + // place your test here + REQUIRE(a == 0); } \ No newline at end of file diff --git a/test/SystemTest/SketchTest.cpp b/test/SystemTest/SketchTest.cpp index b76a2c43..691a2148 100644 --- a/test/SystemTest/SketchTest.cpp +++ b/test/SystemTest/SketchTest.cpp @@ -1,91 +1,97 @@ #include #define CATCH_CONFIG_MAIN + #include "catch.hpp" #include + using namespace std; using namespace INTELLI; using namespace torch; + void runSingleThreadTest(std::string configName) { - ConfigMapPtr cfg = newConfigMap(); - cfg->fromFile(configName); - AMMBench::MatrixLoaderTable mLoaderTable; - uint64_t sketchDimension; - sketchDimension = cfg->tryU64("sketchDimension", 50, true); - uint64_t coreBind = cfg->tryU64("coreBind", 0, true); - UtilityFunctions::bind2Core((int) coreBind); - torch::set_num_threads(1); - std::string ptFile = cfg->tryString("ptFile", "torchscripts/FDAMM.pt", true); + ConfigMapPtr cfg = newConfigMap(); + cfg->fromFile(configName); + AMMBench::MatrixLoaderTable mLoaderTable; + uint64_t sketchDimension; + sketchDimension = cfg->tryU64("sketchDimension", 50, true); + uint64_t coreBind = cfg->tryU64("coreBind", 0, true); + UtilityFunctions::bind2Core((int) coreBind); + torch::set_num_threads(1); + std::string ptFile = cfg->tryString("ptFile", "torchscripts/FDAMM.pt", true); - //uint64_t customResultName = cfg->tryU64("customResultName", 0, true); - INTELLI_INFO("Place me at core" + to_string(coreBind)); - INTELLI_INFO( - "with sketch" + to_string(sketchDimension)); - torch::jit::script::Module module; - INTELLI_INFO("Try pt file " + ptFile); - module = torch::jit::load(ptFile); - std::string matrixLoaderTag = cfg->tryString("matrixLoaderTag", "random", true); - auto matLoaderPtr = mLoaderTable.findMatrixLoader(matrixLoaderTag); - assert(matLoaderPtr); - matLoaderPtr->setConfig(cfg); - auto A = matLoaderPtr->getA(); - auto B = matLoaderPtr->getB(); - /*torch::manual_seed(114514); -//555 -auto A = torch::rand({(long) aRow, (long) aCol}); -auto B = torch::rand({(long) aCol, (long) bCol});*/ - INTELLI_INFO("Generation done, conducting..."); - ThreadPerf pef((int) coreBind); - pef.setPerfList(); - pef.start(); - auto C =module.forward({A, B, (long) sketchDimension}).toTensor(); - pef.end(); - std::string ruName = "default"; + //uint64_t customResultName = cfg->tryU64("customResultName", 0, true); + INTELLI_INFO("Place me at core" + to_string(coreBind)); + INTELLI_INFO( + "with sketch" + to_string(sketchDimension)); + torch::jit::script::Module module; + INTELLI_INFO("Try pt file " + ptFile); + module = torch::jit::load(ptFile); + std::string matrixLoaderTag = cfg->tryString("matrixLoaderTag", "random", true); + auto matLoaderPtr = mLoaderTable.findMatrixLoader(matrixLoaderTag); + assert(matLoaderPtr); + matLoaderPtr->setConfig(cfg); + auto A = matLoaderPtr->getA(); + auto B = matLoaderPtr->getB(); + /*torch::manual_seed(114514); + //555 + auto A = torch::rand({(long) aRow, (long) aCol}); + auto B = torch::rand({(long) aCol, (long) bCol});*/ + INTELLI_INFO("Generation done, conducting..."); + ThreadPerf pef((int) coreBind); + pef.setPerfList(); + pef.start(); + auto C =module.forward({A, B, (long) sketchDimension}).toTensor(); + pef.end(); + std::string ruName = "default"; - auto resultCsv = pef.resultToConfigMap(); - resultCsv->toFile(ruName + ".csv"); - INTELLI_INFO("Done. here is result"); - std::cout << resultCsv->toString() << endl; + auto resultCsv = pef.resultToConfigMap(); + resultCsv->toFile(ruName + ".csv"); + INTELLI_INFO("Done. here is result"); + std::cout << resultCsv->toString() << endl; } + TEST_CASE("Test the counter sketch", "[short]") { - int a = 0; - runSingleThreadTest("scripts/config_countSketch.csv"); - // place your test here - REQUIRE(a == 0); + int a = 0; + runSingleThreadTest("scripts/config_countSketch.csv"); + // place your test here + REQUIRE(a == 0); } TEST_CASE("Test Count Sketch in cpp", "[short]") { - torch::manual_seed(114514); - AMMBench::CountSketchCPPAlgo cs; - auto A = torch::rand({400, 400}); - auto B = torch::rand({400, 400}); - auto realC = torch::matmul(A, B); - auto ammC = cs.amm(A, B, 20); - double froError = INTELLI::UtilityFunctions::relativeFrobeniusNorm(realC, ammC); - REQUIRE(froError < 0.5); + torch::manual_seed(114514); + AMMBench::CountSketchCPPAlgo cs; + auto A = torch::rand({400, 400}); + auto B = torch::rand({400, 400}); + auto realC = torch::matmul(A, B); + auto ammC = cs.amm(A, B, 20); + double froError = INTELLI::UtilityFunctions::relativeFrobeniusNorm(realC, ammC); + REQUIRE(froError < 0.5); } + TEST_CASE("Test Co-Occurring FD in cpp", "[short]") { - torch::manual_seed(114514); - AMMBench::CoOccurringFDCPPAlgo coofd; - auto A = torch::rand({400, 400}); - auto B = torch::rand({400, 400}); - auto realC = torch::matmul(A, B); - auto ammC = coofd.amm(A, B, 20); - double froError = INTELLI::UtilityFunctions::relativeFrobeniusNorm(realC, ammC); - REQUIRE(froError < 0.5); + torch::manual_seed(114514); + AMMBench::CoOccurringFDCPPAlgo coofd; + auto A = torch::rand({400, 400}); + auto B = torch::rand({400, 400}); + auto realC = torch::matmul(A, B); + auto ammC = coofd.amm(A, B, 20); + double froError = INTELLI::UtilityFunctions::relativeFrobeniusNorm(realC, ammC); + REQUIRE(froError < 0.5); } + TEST_CASE("Test Beta-Co-Occurring FD in cpp", "[short]") { - torch::manual_seed(114514); - AMMBench::BetaCoOFDCPPAlgo bcoofd; - auto A = torch::rand({400, 400}); - auto B = torch::rand({400, 400}); - auto realC = torch::matmul(A, B); - auto ammC = bcoofd.amm(A, B, 20); - double froError = INTELLI::UtilityFunctions::relativeFrobeniusNorm(realC, ammC); - REQUIRE(froError < 0.2); + torch::manual_seed(114514); + AMMBench::BetaCoOFDCPPAlgo bcoofd; + auto A = torch::rand({400, 400}); + auto B = torch::rand({400, 400}); + auto realC = torch::matmul(A, B); + auto ammC = bcoofd.amm(A, B, 20); + double froError = INTELLI::UtilityFunctions::relativeFrobeniusNorm(realC, ammC); + REQUIRE(froError < 0.2); } diff --git a/test/SystemTest/StreamingTest.cpp b/test/SystemTest/StreamingTest.cpp index 44b4202c..78443b1e 100644 --- a/test/SystemTest/StreamingTest.cpp +++ b/test/SystemTest/StreamingTest.cpp @@ -1,7 +1,9 @@ #define CATCH_CONFIG_MAIN + #include "catch.hpp" #include #include + using namespace std; using namespace INTELLI; using namespace torch; @@ -13,17 +15,17 @@ TEST_CASE("Test the basic streaming batch 1", "[short]") torch::manual_seed(114514); auto A = torch::rand({(long) 4, (long) 4}); auto B = torch::rand({(long) 4, (long) 4}); - auto rawC=torch::matmul(A, B); + auto rawC = torch::matmul(A, B); AMMBench::SingleThreadStreamer ss; //cfg->edit("",(uint64_t)100); ss.setConfig(cfg); - auto ssC=ss.streamingAmm(A,B); - std::cout<<"raw C:"<edit("batchSize",(uint64_t)2); + cfg->edit("batchSize", (uint64_t) 2); ss.setConfig(cfg); - auto ssC=ss.streamingAmm(A,B); - std::cout<<"raw C:"< + using namespace std; using namespace INTELLI; using namespace torch; + void runSingleThreadTest(std::string configName) { - ConfigMapPtr cfg = newConfigMap(); - cfg->fromFile(configName); - AMMBench::MatrixLoaderTable mLoaderTable; - uint64_t sketchDimension; - sketchDimension = cfg->tryU64("sketchDimension", 50, true); - uint64_t coreBind = cfg->tryU64("coreBind", 0, true); - UtilityFunctions::bind2Core((int) coreBind); - torch::set_num_threads(1); - std::string ptFile = cfg->tryString("ptFile", "torchscripts/FDAMM.pt", true); + ConfigMapPtr cfg = newConfigMap(); + cfg->fromFile(configName); + AMMBench::MatrixLoaderTable mLoaderTable; + uint64_t sketchDimension; + sketchDimension = cfg->tryU64("sketchDimension", 50, true); + uint64_t coreBind = cfg->tryU64("coreBind", 0, true); + UtilityFunctions::bind2Core((int) coreBind); + torch::set_num_threads(1); + std::string ptFile = cfg->tryString("ptFile", "torchscripts/FDAMM.pt", true); - //uint64_t customResultName = cfg->tryU64("customResultName", 0, true); - INTELLI_INFO("Place me at core" + to_string(coreBind)); - INTELLI_INFO( - "with sketch" + to_string(sketchDimension)); - torch::jit::script::Module module; - INTELLI_INFO("Try pt file " + ptFile); - module = torch::jit::load(ptFile); - std::string matrixLoaderTag = cfg->tryString("matrixLoaderTag", "random", true); - auto matLoaderPtr = mLoaderTable.findMatrixLoader(matrixLoaderTag); - assert(matLoaderPtr); - matLoaderPtr->setConfig(cfg); - auto A = matLoaderPtr->getA(); - auto B = matLoaderPtr->getB(); - /*torch::manual_seed(114514); -//555 -auto A = torch::rand({(long) aRow, (long) aCol}); -auto B = torch::rand({(long) aCol, (long) bCol});*/ - INTELLI_INFO("Generation done, conducting..."); - ThreadPerf pef((int) coreBind); - pef.setPerfList(); - pef.start(); - auto C =module.forward({A, B, (long) sketchDimension}).toTensor(); - pef.end(); - std::string ruName = "default"; + //uint64_t customResultName = cfg->tryU64("customResultName", 0, true); + INTELLI_INFO("Place me at core" + to_string(coreBind)); + INTELLI_INFO( + "with sketch" + to_string(sketchDimension)); + torch::jit::script::Module module; + INTELLI_INFO("Try pt file " + ptFile); + module = torch::jit::load(ptFile); + std::string matrixLoaderTag = cfg->tryString("matrixLoaderTag", "random", true); + auto matLoaderPtr = mLoaderTable.findMatrixLoader(matrixLoaderTag); + assert(matLoaderPtr); + matLoaderPtr->setConfig(cfg); + auto A = matLoaderPtr->getA(); + auto B = matLoaderPtr->getB(); + /*torch::manual_seed(114514); + //555 + auto A = torch::rand({(long) aRow, (long) aCol}); + auto B = torch::rand({(long) aCol, (long) bCol});*/ + INTELLI_INFO("Generation done, conducting..."); + ThreadPerf pef((int) coreBind); + pef.setPerfList(); + pef.start(); + auto C =module.forward({A, B, (long) sketchDimension}).toTensor(); + pef.end(); + std::string ruName = "default"; - auto resultCsv = pef.resultToConfigMap(); - resultCsv->toFile(ruName + ".csv"); - INTELLI_INFO("Done. here is result"); - std::cout << resultCsv->toString() << endl; + auto resultCsv = pef.resultToConfigMap(); + resultCsv->toFile(ruName + ".csv"); + INTELLI_INFO("Done. here is result"); + std::cout << resultCsv->toString() << endl; } + TEST_CASE("Test the Tug of War", "[short]") { - int a = 0; - runSingleThreadTest("scripts/config_tugOfWar.csv"); - // place your test here - REQUIRE(a == 0); + int a = 0; + runSingleThreadTest("scripts/config_tugOfWar.csv"); + // place your test here + REQUIRE(a == 0); } + TEST_CASE("Test Tug of War in cpp", "[short]") { - torch::manual_seed(114514); - AMMBench::TugOfWarCPPAlgo tw; - auto A = torch::rand({400, 400}); - auto B = torch::rand({400, 400}); - auto realC = torch::matmul(A, B); - auto ammC = tw.amm(A, B, 20); - double froError = INTELLI::UtilityFunctions::relativeFrobeniusNorm(realC, ammC); - REQUIRE(froError < 0.5); + torch::manual_seed(114514); + AMMBench::TugOfWarCPPAlgo tw; + auto A = torch::rand({400, 400}); + auto B = torch::rand({400, 400}); + auto realC = torch::matmul(A, B); + auto ammC = tw.amm(A, B, 20); + double froError = INTELLI::UtilityFunctions::relativeFrobeniusNorm(realC, ammC); + REQUIRE(froError < 0.5); } diff --git a/test/SystemTest/WeightedCRTest.cpp b/test/SystemTest/WeightedCRTest.cpp index 9c4b915b..213412f9 100644 --- a/test/SystemTest/WeightedCRTest.cpp +++ b/test/SystemTest/WeightedCRTest.cpp @@ -1,59 +1,64 @@ #include #define CATCH_CONFIG_MAIN + #include "catch.hpp" #include + using namespace std; using namespace INTELLI; using namespace torch; + void runSingleThreadTest(std::string configName) { - ConfigMapPtr cfg = newConfigMap(); - cfg->fromFile(configName); - AMMBench::MatrixLoaderTable mLoaderTable; - uint64_t sketchDimension; - sketchDimension = cfg->tryU64("sketchDimension", 50, true); - uint64_t coreBind = cfg->tryU64("coreBind", 0, true); - UtilityFunctions::bind2Core((int) coreBind); - torch::set_num_threads(1); - std::string ptFile = cfg->tryString("ptFile", "torchscripts/WeightedCR.pt", true); + ConfigMapPtr cfg = newConfigMap(); + cfg->fromFile(configName); + AMMBench::MatrixLoaderTable mLoaderTable; + uint64_t sketchDimension; + sketchDimension = cfg->tryU64("sketchDimension", 50, true); + uint64_t coreBind = cfg->tryU64("coreBind", 0, true); + UtilityFunctions::bind2Core((int) coreBind); + torch::set_num_threads(1); + std::string ptFile = cfg->tryString("ptFile", "torchscripts/WeightedCR.pt", true); - //uint64_t customResultName = cfg->tryU64("customResultName", 0, true); - INTELLI_INFO("Place me at core" + to_string(coreBind)); - INTELLI_INFO( - "with sketch" + to_string(sketchDimension)); - torch::jit::script::Module module; - INTELLI_INFO("Try pt file " + ptFile); - module = torch::jit::load(ptFile); - std::string matrixLoaderTag = cfg->tryString("matrixLoaderTag", "random", true); - auto matLoaderPtr = mLoaderTable.findMatrixLoader(matrixLoaderTag); - assert(matLoaderPtr); - matLoaderPtr->setConfig(cfg); - auto A = matLoaderPtr->getA(); - auto B = matLoaderPtr->getB(); - /*torch::manual_seed(114514); -//555 -auto A = torch::rand({(long) aRow, (long) aCol}); -auto B = torch::rand({(long) aCol, (long) bCol});*/ - INTELLI_INFO("Generation done, conducting..."); - ThreadPerf pef((int) coreBind); - pef.setPerfList(); - pef.start(); - auto C =module.forward({A, B, (long) sketchDimension}).toTensor(); - pef.end(); - std::string ruName = "default"; + //uint64_t customResultName = cfg->tryU64("customResultName", 0, true); + INTELLI_INFO("Place me at core" + to_string(coreBind)); + INTELLI_INFO( + "with sketch" + to_string(sketchDimension)); + torch::jit::script::Module module; + INTELLI_INFO("Try pt file " + ptFile); + module = torch::jit::load(ptFile); + std::string matrixLoaderTag = cfg->tryString("matrixLoaderTag", "random", true); + auto matLoaderPtr = mLoaderTable.findMatrixLoader(matrixLoaderTag); + assert(matLoaderPtr); + matLoaderPtr->setConfig(cfg); + auto A = matLoaderPtr->getA(); + auto B = matLoaderPtr->getB(); + /*torch::manual_seed(114514); + //555 + auto A = torch::rand({(long) aRow, (long) aCol}); + auto B = torch::rand({(long) aCol, (long) bCol});*/ + INTELLI_INFO("Generation done, conducting..."); + ThreadPerf pef((int) coreBind); + pef.setPerfList(); + pef.start(); + auto C =module.forward({A, B, (long) sketchDimension}).toTensor(); + pef.end(); + std::string ruName = "default"; - auto resultCsv = pef.resultToConfigMap(); - resultCsv->toFile(ruName + ".csv"); - INTELLI_INFO("Done. here is result"); - std::cout << resultCsv->toString() << endl; + auto resultCsv = pef.resultToConfigMap(); + resultCsv->toFile(ruName + ".csv"); + INTELLI_INFO("Done. here is result"); + std::cout << resultCsv->toString() << endl; } + TEST_CASE("Test the COLUMN ROW SAMPLINGS", "[short]") { - int a = 0; - runSingleThreadTest("scripts/config_WeightedCR.csv"); - // place your test here - REQUIRE(a == 0); + int a = 0; + runSingleThreadTest("scripts/config_WeightedCR.csv"); + // place your test here + REQUIRE(a == 0); } + TEST_CASE("Test Weighted CR in cpp", "[short]") { torch::manual_seed(114514); diff --git a/test/SystemTest/catch.hpp b/test/SystemTest/catch.hpp index 8d31885d..c034636c 100644 --- a/test/SystemTest/catch.hpp +++ b/test/SystemTest/catch.hpp @@ -96,7 +96,7 @@ // start catch_user_interfaces.h namespace Catch { -unsigned int rngSeed(); + unsigned int rngSeed(); } // end catch_user_interfaces.h @@ -327,7 +327,9 @@ unsigned int rngSeed(); // Check if byte is available and usable # if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # include + # if defined(__cpp_lib_byte) && (__cpp_lib_byte > 0) # define CATCH_INTERNAL_CONFIG_CPP17_BYTE # endif @@ -336,7 +338,7 @@ unsigned int rngSeed(); // Check if variant is available and usable # if __has_include() && defined(CATCH_CPP17_OR_GREATER) # if defined(__clang__) && (__clang_major__ < 8) -// work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852 + // work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852 // fix should be in clang 8, workaround in libstdc++ 8.2 # include # if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) @@ -478,67 +480,80 @@ unsigned int rngSeed(); #include // We need a dummy global operator<< so we can bring it into Catch namespace later -struct Catch_global_namespace_dummy {}; +struct Catch_global_namespace_dummy { +}; + std::ostream &operator<<(std::ostream &, Catch_global_namespace_dummy); namespace Catch { -struct CaseSensitive { - enum Choice { - Yes, - No - }; -}; + struct CaseSensitive { + enum Choice { + Yes, + No + }; + }; -class NonCopyable { - NonCopyable(NonCopyable const &) = delete; - NonCopyable(NonCopyable &&) = delete; - NonCopyable &operator=(NonCopyable const &) = delete; - NonCopyable &operator=(NonCopyable &&) = delete; + class NonCopyable { + NonCopyable(NonCopyable const &) = delete; - protected: - NonCopyable(); - virtual ~NonCopyable(); -}; + NonCopyable(NonCopyable &&) = delete; -struct SourceLineInfo { + NonCopyable &operator=(NonCopyable const &) = delete; - SourceLineInfo() = delete; - SourceLineInfo(char const *_file, std::size_t _line) noexcept - : file(_file), - line(_line) {} + NonCopyable &operator=(NonCopyable &&) = delete; - SourceLineInfo(SourceLineInfo const &other) = default; - SourceLineInfo &operator=(SourceLineInfo const &) = default; - SourceLineInfo(SourceLineInfo &&) noexcept = default; - SourceLineInfo &operator=(SourceLineInfo &&) noexcept = default; + protected: + NonCopyable(); - bool empty() const noexcept { return file[0] == '\0'; } - bool operator==(SourceLineInfo const &other) const noexcept; - bool operator<(SourceLineInfo const &other) const noexcept; + virtual ~NonCopyable(); + }; - char const *file; - std::size_t line; -}; + struct SourceLineInfo { + + SourceLineInfo() = delete; + + SourceLineInfo(char const *_file, std::size_t _line) noexcept + : file(_file), + line(_line) {} + + SourceLineInfo(SourceLineInfo const &other) = default; + + SourceLineInfo &operator=(SourceLineInfo const &) = default; + + SourceLineInfo(SourceLineInfo &&) noexcept = default; + + SourceLineInfo &operator=(SourceLineInfo &&) noexcept = default; -std::ostream &operator<<(std::ostream &os, SourceLineInfo const &info); + bool empty() const noexcept { return file[0] == '\0'; } + + bool operator==(SourceLineInfo const &other) const noexcept; + + bool operator<(SourceLineInfo const &other) const noexcept; + + char const *file; + std::size_t line; + }; + + std::ostream &operator<<(std::ostream &os, SourceLineInfo const &info); // Bring in operator<< from global namespace into Catch namespace // This is necessary because the overload of operator<< above makes // lookup stop at namespace Catch -using ::operator<<; + using ::operator<<; // Use this in variadic streaming macros to allow // >> +StreamEndStop // as well as // >> stuff +StreamEndStop -struct StreamEndStop { - std::string operator+() const; -}; -template -T const &operator+(T const &value, StreamEndStop) { - return value; -} + struct StreamEndStop { + std::string operator+() const; + }; + + template + T const &operator+(T const &value, StreamEndStop) { + return value; + } } #define CATCH_INTERNAL_LINEINFO \ @@ -547,9 +562,9 @@ T const &operator+(T const &value, StreamEndStop) { // end catch_common.h namespace Catch { -struct RegistrarForTagAliases { - RegistrarForTagAliases(char const *alias, char const *tag, SourceLineInfo const &lineInfo); -}; + struct RegistrarForTagAliases { + RegistrarForTagAliases(char const *alias, char const *tag, SourceLineInfo const &lineInfo); + }; } // end namespace Catch @@ -568,28 +583,35 @@ struct RegistrarForTagAliases { namespace Catch { -class TestSpec; + class TestSpec; -struct ITestInvoker { - virtual void invoke() const = 0; - virtual ~ITestInvoker(); -}; + struct ITestInvoker { + virtual void invoke() const = 0; -class TestCase; -struct IConfig; + virtual ~ITestInvoker(); + }; -struct ITestCaseRegistry { - virtual ~ITestCaseRegistry(); - virtual std::vector const &getAllTests() const = 0; - virtual std::vector const &getAllTestsSorted(IConfig const &config) const = 0; -}; + class TestCase; -bool isThrowSafe(TestCase const &testCase, IConfig const &config); -bool matchTest(TestCase const &testCase, TestSpec const &testSpec, IConfig const &config); -std::vector filterTests(std::vector const &testCases, - TestSpec const &testSpec, - IConfig const &config); -std::vector const &getAllTestCasesSorted(IConfig const &config); + struct IConfig; + + struct ITestCaseRegistry { + virtual ~ITestCaseRegistry(); + + virtual std::vector const &getAllTests() const = 0; + + virtual std::vector const &getAllTestsSorted(IConfig const &config) const = 0; + }; + + bool isThrowSafe(TestCase const &testCase, IConfig const &config); + + bool matchTest(TestCase const &testCase, TestSpec const &testSpec, IConfig const &config); + + std::vector filterTests(std::vector const &testCases, + TestSpec const &testSpec, + IConfig const &config); + + std::vector const &getAllTestCasesSorted(IConfig const &config); } @@ -606,85 +628,89 @@ namespace Catch { /// A non-owning string class (similar to the forthcoming std::string_view) /// Note that, because a StringRef may be a substring of another string, /// it may not be null terminated. -class StringRef { - public: - using size_type = std::size_t; - using const_iterator = const char *; - - private: - static constexpr char const *const s_empty = ""; - - char const *m_start = s_empty; - size_type m_size = 0; - - public: // construction - constexpr StringRef() noexcept = default; - - StringRef(char const *rawChars) noexcept; - - constexpr StringRef(char const *rawChars, size_type size) noexcept - : m_start(rawChars), - m_size(size) {} - - StringRef(std::string const &stdString) noexcept - : m_start(stdString.c_str()), - m_size(stdString.size()) {} - - explicit operator std::string() const { - return std::string(m_start, m_size); - } - - public: // operators - auto operator==(StringRef const &other) const noexcept -> bool; - auto operator!=(StringRef const &other) const noexcept -> bool { - return !(*this == other); - } - - auto operator[](size_type index) const noexcept -> char { - assert(index < m_size); - return m_start[index]; - } - - public: // named queries - constexpr auto empty() const noexcept -> bool { - return m_size == 0; - } - constexpr auto size() const noexcept -> size_type { - return m_size; - } - - // Returns the current start pointer. If the StringRef is not - // null-terminated, throws std::domain_exception - auto c_str() const -> char const *; - - public: // substrings and searches - // Returns a substring of [start, start + length). - // If start + length > size(), then the substring is [start, size()). - // If start > size(), then the substring is empty. - auto substr(size_type start, size_type length) const noexcept -> StringRef; - - // Returns the current start pointer. May not be null-terminated. - auto data() const noexcept -> char const *; - - constexpr auto isNullTerminated() const noexcept -> bool { - return m_start[m_size] == '\0'; - } - - public: // iterators - constexpr const_iterator begin() const { return m_start; } - constexpr const_iterator end() const { return m_start + m_size; } -}; + class StringRef { + public: + using size_type = std::size_t; + using const_iterator = const char *; -auto operator+=(std::string &lhs, StringRef const &sr) -> std::string &; -auto operator<<(std::ostream &os, StringRef const &sr) -> std::ostream &; + private: + static constexpr char const *const s_empty = ""; -constexpr auto operator "" _sr(char const *rawChars, std::size_t size) noexcept -> StringRef { - return StringRef(rawChars, size); -} + char const *m_start = s_empty; + size_type m_size = 0; + + public: // construction + constexpr StringRef() noexcept = default; + + StringRef(char const *rawChars) noexcept; + + constexpr StringRef(char const *rawChars, size_type size) noexcept + : m_start(rawChars), + m_size(size) {} + + StringRef(std::string const &stdString) noexcept + : m_start(stdString.c_str()), + m_size(stdString.size()) {} + + explicit operator std::string() const { + return std::string(m_start, m_size); + } + + public: // operators + auto operator==(StringRef const &other) const noexcept -> bool; + + auto operator!=(StringRef const &other) const noexcept -> bool { + return !(*this == other); + } + + auto operator[](size_type index) const noexcept -> char { + assert(index < m_size); + return m_start[index]; + } + + public: // named queries + constexpr auto empty() const noexcept -> bool { + return m_size == 0; + } + + constexpr auto size() const noexcept -> size_type { + return m_size; + } + + // Returns the current start pointer. If the StringRef is not + // null-terminated, throws std::domain_exception + auto c_str() const -> char const *; + + public: // substrings and searches + // Returns a substring of [start, start + length). + // If start + length > size(), then the substring is [start, size()). + // If start > size(), then the substring is empty. + auto substr(size_type start, size_type length) const noexcept -> StringRef; + + // Returns the current start pointer. May not be null-terminated. + auto data() const noexcept -> char const *; + + constexpr auto isNullTerminated() const noexcept -> bool { + return m_start[m_size] == '\0'; + } + + public: // iterators + constexpr const_iterator begin() const { return m_start; } + + constexpr const_iterator end() const { return m_start + m_size; } + }; + + auto operator+=(std::string &lhs, StringRef const &sr) -> std::string &; + + auto operator<<(std::ostream &os, StringRef const &sr) -> std::ostream &; + + constexpr auto operator "" _sr(char const *rawChars, std::size_t size) noexcept -> StringRef { + return StringRef(rawChars, size); + } } // namespace Catch constexpr auto operator "" _catch_sr(char const *rawChars, std::size_t size) noexcept -> Catch::StringRef { - return Catch::StringRef(rawChars, size); + return Catch::StringRef(rawChars, size); } // end catch_stringref.h @@ -922,31 +948,36 @@ constexpr auto operator "" _catch_sr(char const *rawChars, std::size_t size) noe #include namespace Catch { -template -struct always_false : std::false_type {}; - -template -struct true_given : std::true_type {}; -struct is_callable_tester { - template - true_given()(std::declval()...))> static test(int); - template - std::false_type static test(...); -}; + template + struct always_false : std::false_type { + }; + + template + struct true_given : std::true_type { + }; -template -struct is_callable; + struct is_callable_tester { + template + true_given()(std::declval()...))> static test(int); + + template + std::false_type static test(...); + }; + + template + struct is_callable; -template -struct is_callable : decltype(is_callable_tester::test(0)) {}; + template + struct is_callable : decltype(is_callable_tester::test(0)) { + }; #if defined(__cpp_lib_is_invocable) && __cpp_lib_is_invocable >= 201703 // std::result_of is deprecated in C++17 and removed in C++20. Hence, it is // replaced with std::invoke_result here. -template -using FunctionReturnType = std::remove_reference_t>>; + template + using FunctionReturnType = std::remove_reference_t>>; #else -// Keep ::type here because we still support C++11 + // Keep ::type here because we still support C++11 template using FunctionReturnType = typename std::remove_reference::type>::type>::type; #endif @@ -954,45 +985,48 @@ using FunctionReturnType = typename std::remove_reference -class TestInvokerAsMethod : public ITestInvoker { - void (C::*m_testAsMethod)(); - public: - TestInvokerAsMethod(void (C::*testAsMethod)()) noexcept: m_testAsMethod(testAsMethod) {} + template + class TestInvokerAsMethod : public ITestInvoker { + void (C::*m_testAsMethod)(); - void invoke() const override { - C obj; - (obj.*m_testAsMethod)(); - } -}; + public: + TestInvokerAsMethod(void (C::*testAsMethod)()) noexcept: m_testAsMethod(testAsMethod) {} -auto makeTestInvoker(void(*testAsFunction)()) noexcept -> ITestInvoker *; + void invoke() const override { + C obj; + (obj.*m_testAsMethod)(); + } + }; -template -auto makeTestInvoker(void (C::*testAsMethod)()) noexcept -> ITestInvoker * { - return new(std::nothrow) - TestInvokerAsMethod(testAsMethod); -} + auto makeTestInvoker(void(*testAsFunction)()) noexcept -> ITestInvoker *; -struct NameAndTags { - NameAndTags(StringRef const &name_ = StringRef(), StringRef const &tags_ = StringRef()) noexcept; - StringRef name; - StringRef tags; -}; + template + auto makeTestInvoker(void (C::*testAsMethod)()) noexcept -> ITestInvoker * { + return new(std::nothrow) + TestInvokerAsMethod(testAsMethod); + } -struct AutoReg : NonCopyable { - AutoReg(ITestInvoker *invoker, - SourceLineInfo const &lineInfo, - StringRef const &classOrMethod, - NameAndTags const &nameAndTags) noexcept; - ~AutoReg(); -}; + struct NameAndTags { + NameAndTags(StringRef const &name_ = StringRef(), StringRef const &tags_ = StringRef()) noexcept; + + StringRef name; + StringRef tags; + }; + + struct AutoReg : NonCopyable { + AutoReg(ITestInvoker *invoker, + SourceLineInfo const &lineInfo, + StringRef const &classOrMethod, + NameAndTags const &nameAndTags) noexcept; + + ~AutoReg(); + }; } // end namespace Catch @@ -1356,63 +1390,66 @@ struct AutoReg : NonCopyable { namespace Catch { // ResultWas::OfType enum -struct ResultWas { - enum OfType { - Unknown = -1, - Ok = 0, - Info = 1, - Warning = 2, + struct ResultWas { + enum OfType { + Unknown = -1, + Ok = 0, + Info = 1, + Warning = 2, - FailureBit = 0x10, + FailureBit = 0x10, - ExpressionFailed = FailureBit | 1, - ExplicitFailure = FailureBit | 2, + ExpressionFailed = FailureBit | 1, + ExplicitFailure = FailureBit | 2, - Exception = 0x100 | FailureBit, + Exception = 0x100 | FailureBit, - ThrewException = Exception | 1, - DidntThrowException = Exception | 2, + ThrewException = Exception | 1, + DidntThrowException = Exception | 2, - FatalErrorCondition = 0x200 | FailureBit + FatalErrorCondition = 0x200 | FailureBit - }; -}; + }; + }; -bool isOk(ResultWas::OfType resultType); -bool isJustInfo(int flags); + bool isOk(ResultWas::OfType resultType); + + bool isJustInfo(int flags); // ResultDisposition::Flags enum -struct ResultDisposition { - enum Flags { - Normal = 0x01, - - ContinueOnFailure = 0x02, // Failures fail test, but execution continues - FalseTest = 0x04, // Prefix expression with ! - SuppressFail = 0x08 // Failures are reported but do not fail the test - }; -}; + struct ResultDisposition { + enum Flags { + Normal = 0x01, + + ContinueOnFailure = 0x02, // Failures fail test, but execution continues + FalseTest = 0x04, // Prefix expression with ! + SuppressFail = 0x08 // Failures are reported but do not fail the test + }; + }; + + ResultDisposition::Flags operator|(ResultDisposition::Flags lhs, ResultDisposition::Flags rhs); -ResultDisposition::Flags operator|(ResultDisposition::Flags lhs, ResultDisposition::Flags rhs); + bool shouldContinueOnFailure(int flags); -bool shouldContinueOnFailure(int flags); -inline bool isFalseTest(int flags) { return (flags & ResultDisposition::FalseTest) != 0; } -bool shouldSuppressFailure(int flags); + inline bool isFalseTest(int flags) { return (flags & ResultDisposition::FalseTest) != 0; } + + bool shouldSuppressFailure(int flags); } // end namespace Catch // end catch_result_type.h namespace Catch { -struct AssertionInfo { - StringRef macroName; - SourceLineInfo lineInfo; - StringRef capturedExpression; - ResultDisposition::Flags resultDisposition; + struct AssertionInfo { + StringRef macroName; + SourceLineInfo lineInfo; + StringRef capturedExpression; + ResultDisposition::Flags resultDisposition; - // We want to delete this constructor but a compiler bug in 4.8 means - // the struct is then treated as non-aggregate - //AssertionInfo() = delete; -}; + // We want to delete this constructor but a compiler bug in 4.8 means + // the struct is then treated as non-aggregate + //AssertionInfo() = delete; + }; } // end namespace Catch @@ -1433,35 +1470,40 @@ struct AssertionInfo { namespace Catch { -std::ostream &cout(); -std::ostream &cerr(); -std::ostream &clog(); + std::ostream &cout(); -class StringRef; + std::ostream &cerr(); -struct IStream { - virtual ~IStream(); - virtual std::ostream &stream() const = 0; -}; + std::ostream &clog(); -auto makeStream(StringRef const &filename) -> IStream const *; + class StringRef; -class ReusableStringStream : NonCopyable { - std::size_t m_index; - std::ostream *m_oss; - public: - ReusableStringStream(); - ~ReusableStringStream(); + struct IStream { + virtual ~IStream(); - auto str() const -> std::string; + virtual std::ostream &stream() const = 0; + }; - template - auto operator<<(T const &value) -> ReusableStringStream & { - *m_oss << value; - return *this; - } - auto get() -> std::ostream & { return *m_oss; } -}; + auto makeStream(StringRef const &filename) -> IStream const *; + + class ReusableStringStream : NonCopyable { + std::size_t m_index; + std::ostream *m_oss; + public: + ReusableStringStream(); + + ~ReusableStringStream(); + + auto str() const -> std::string; + + template + auto operator<<(T const &value) -> ReusableStringStream & { + *m_oss << value; + return *this; + } + + auto get() -> std::ostream & { return *m_oss; } + }; } // end catch_stream.h @@ -1471,41 +1513,43 @@ class ReusableStringStream : NonCopyable { namespace Catch { -namespace Detail { -struct EnumInfo { - StringRef m_name; - std::vector> m_values; + namespace Detail { + struct EnumInfo { + StringRef m_name; + std::vector> m_values; + + ~EnumInfo(); - ~EnumInfo(); + StringRef lookup(int value) const; + }; + } // namespace Detail - StringRef lookup(int value) const; -}; -} // namespace Detail - -struct IMutableEnumValuesRegistry { - virtual ~IMutableEnumValuesRegistry(); - - virtual Detail::EnumInfo const ®isterEnum(StringRef enumName, - StringRef allEnums, - std::vector const &values) = 0; - - template - Detail::EnumInfo const ®isterEnum(StringRef enumName, StringRef allEnums, std::initializer_list values) { - static_assert(sizeof(int) >= sizeof(E), "Cannot serialize enum to int"); - std::vector intValues; - intValues.reserve(values.size()); - for (auto enumValue : values) - intValues.push_back(static_cast( enumValue )); - return registerEnum(enumName, allEnums, intValues); - } -}; + struct IMutableEnumValuesRegistry { + virtual ~IMutableEnumValuesRegistry(); + + virtual Detail::EnumInfo const ®isterEnum(StringRef enumName, + StringRef allEnums, + std::vector const &values) = 0; + + template + Detail::EnumInfo const ®isterEnum(StringRef enumName, StringRef allEnums, std::initializer_list values) { + static_assert(sizeof(int) >= sizeof(E), "Cannot serialize enum to int"); + std::vector intValues; + intValues.reserve(values.size()); + for (auto enumValue: values) + intValues.push_back(static_cast( enumValue )); + return registerEnum(enumName, allEnums, intValues); + } + }; } // Catch // end catch_interfaces_enum_values_registry.h #ifdef CATCH_CONFIG_CPP17_STRING_VIEW + #include + #endif #ifdef __OBJC__ @@ -1560,54 +1604,55 @@ inline id performOptionalSelector( id obj, SEL sel ) { #endif namespace Catch { -namespace Detail { + namespace Detail { -extern const std::string unprintableString; + extern const std::string unprintableString; -std::string rawMemoryToString(const void *object, std::size_t size); + std::string rawMemoryToString(const void *object, std::size_t size); -template -std::string rawMemoryToString(const T &object) { - return rawMemoryToString(&object, sizeof(object)); -} + template + std::string rawMemoryToString(const T &object) { + return rawMemoryToString(&object, sizeof(object)); + } -template -class IsStreamInsertable { - template - static auto test(int) - -> decltype(std::declval() << std::declval(), std::true_type()); + template + class IsStreamInsertable { + template + static auto test(int) + -> decltype(std::declval() << std::declval(), std::true_type()); - template - static auto test(...) -> std::false_type; + template + static auto test(...) -> std::false_type; - public: - static const bool value = decltype(test(0))::value; -}; + public: + static const bool value = decltype(test(0))::value; + }; -template -std::string convertUnknownEnumToString(E e); + template + std::string convertUnknownEnumToString(E e); -template -typename std::enable_if< - !std::is_enum::value && !std::is_base_of::value, - std::string>::type convertUnstreamable(T const &) { - return Detail::unprintableString; -} -template -typename std::enable_if< - !std::is_enum::value && std::is_base_of::value, - std::string>::type convertUnstreamable(T const &ex) { - return ex.what(); -} + template + typename std::enable_if< + !std::is_enum::value && !std::is_base_of::value, + std::string>::type convertUnstreamable(T const &) { + return Detail::unprintableString; + } -template -typename std::enable_if< - std::is_enum::value, std::string>::type convertUnstreamable(T const &value) { - return convertUnknownEnumToString(value); -} + template + typename std::enable_if< + !std::is_enum::value && std::is_base_of::value, + std::string>::type convertUnstreamable(T const &ex) { + return ex.what(); + } + + template + typename std::enable_if< + std::is_enum::value, std::string>::type convertUnstreamable(T const &value) { + return convertUnknownEnumToString(value); + } #if defined(_MANAGED) - //! Convert a CLR string to a utf8 std::string + //! Convert a CLR string to a utf8 std::string template std::string clrReferenceToString( T^ ref ) { if (ref == nullptr) @@ -1618,248 +1663,271 @@ typename std::enable_if< } #endif -} // namespace Detail + } // namespace Detail // If we decide for C++14, change these to enable_if_ts -template -struct StringMaker { - template - static - typename std::enable_if<::Catch::Detail::IsStreamInsertable::value, std::string>::type - convert(const Fake &value) { - ReusableStringStream rss; - // NB: call using the function-like syntax to avoid ambiguity with - // user-defined templated operator<< under clang. - rss.operator<<(value); - return rss.str(); - } - - template - static - typename std::enable_if::value, std::string>::type - convert(const Fake &value) { + template + struct StringMaker { + template + static + typename std::enable_if<::Catch::Detail::IsStreamInsertable::value, std::string>::type + convert(const Fake &value) { + ReusableStringStream rss; + // NB: call using the function-like syntax to avoid ambiguity with + // user-defined templated operator<< under clang. + rss.operator<<(value); + return rss.str(); + } + + template + static + typename std::enable_if::value, std::string>::type + convert(const Fake &value) { #if !defined(CATCH_CONFIG_FALLBACK_STRINGIFIER) - return Detail::convertUnstreamable(value); + return Detail::convertUnstreamable(value); #else - return CATCH_CONFIG_FALLBACK_STRINGIFIER(value); + return CATCH_CONFIG_FALLBACK_STRINGIFIER(value); #endif - } -}; + } + }; -namespace Detail { + namespace Detail { // This function dispatches all stringification requests inside of Catch. // Should be preferably called fully qualified, like ::Catch::Detail::stringify -template -std::string stringify(const T &e) { - return ::Catch::StringMaker::type>::type>::convert(e); -} + template + std::string stringify(const T &e) { + return ::Catch::StringMaker::type>::type>::convert( + e); + } -template -std::string convertUnknownEnumToString(E e) { - return ::Catch::Detail::stringify(static_cast::type>(e)); -} + template + std::string convertUnknownEnumToString(E e) { + return ::Catch::Detail::stringify(static_cast::type>(e)); + } #if defined(_MANAGED) - template + template std::string stringify( T^ e ) { return ::Catch::StringMaker::convert(e); } #endif -} // namespace Detail + } // namespace Detail // Some predefined specializations -template<> -struct StringMaker { - static std::string convert(const std::string &str); -}; + template<> + struct StringMaker { + static std::string convert(const std::string &str); + }; #ifdef CATCH_CONFIG_CPP17_STRING_VIEW -template<> -struct StringMaker { - static std::string convert(std::string_view str); -}; + + template<> + struct StringMaker { + static std::string convert(std::string_view str); + }; + #endif -template<> -struct StringMaker { - static std::string convert(char const *str); -}; -template<> -struct StringMaker { - static std::string convert(char *str); -}; + template<> + struct StringMaker { + static std::string convert(char const *str); + }; + + template<> + struct StringMaker { + static std::string convert(char *str); + }; #ifdef CATCH_CONFIG_WCHAR -template<> -struct StringMaker { - static std::string convert(const std::wstring &wstr); -}; + + template<> + struct StringMaker { + static std::string convert(const std::wstring &wstr); + }; # ifdef CATCH_CONFIG_CPP17_STRING_VIEW -template<> -struct StringMaker { - static std::string convert(std::wstring_view str); -}; + + template<> + struct StringMaker { + static std::string convert(std::wstring_view str); + }; + # endif -template<> -struct StringMaker { - static std::string convert(wchar_t const *str); -}; -template<> -struct StringMaker { - static std::string convert(wchar_t *str); -}; + template<> + struct StringMaker { + static std::string convert(wchar_t const *str); + }; + + template<> + struct StringMaker { + static std::string convert(wchar_t *str); + }; + #endif // TBD: Should we use `strnlen` to ensure that we don't go out of the buffer, // while keeping string semantics? -template -struct StringMaker { - static std::string convert(char const *str) { - return ::Catch::Detail::stringify(std::string{str}); - } -}; -template -struct StringMaker { - static std::string convert(signed char const *str) { - return ::Catch::Detail::stringify(std::string{reinterpret_cast(str)}); - } -}; -template -struct StringMaker { - static std::string convert(unsigned char const *str) { - return ::Catch::Detail::stringify(std::string{reinterpret_cast(str)}); - } -}; + template + struct StringMaker { + static std::string convert(char const *str) { + return ::Catch::Detail::stringify(std::string{str}); + } + }; -#if defined(CATCH_CONFIG_CPP17_BYTE) -template<> -struct StringMaker { - static std::string convert(std::byte value); -}; -#endif // defined(CATCH_CONFIG_CPP17_BYTE) -template<> -struct StringMaker { - static std::string convert(int value); -}; -template<> -struct StringMaker { - static std::string convert(long value); -}; -template<> -struct StringMaker { - static std::string convert(long long value); -}; -template<> -struct StringMaker { - static std::string convert(unsigned int value); -}; -template<> -struct StringMaker { - static std::string convert(unsigned long value); -}; -template<> -struct StringMaker { - static std::string convert(unsigned long long value); -}; + template + struct StringMaker { + static std::string convert(signed char const *str) { + return ::Catch::Detail::stringify(std::string{reinterpret_cast(str)}); + } + }; -template<> -struct StringMaker { - static std::string convert(bool b); -}; + template + struct StringMaker { + static std::string convert(unsigned char const *str) { + return ::Catch::Detail::stringify(std::string{reinterpret_cast(str)}); + } + }; -template<> -struct StringMaker { - static std::string convert(char c); -}; -template<> -struct StringMaker { - static std::string convert(signed char c); -}; -template<> -struct StringMaker { - static std::string convert(unsigned char c); -}; +#if defined(CATCH_CONFIG_CPP17_BYTE) -template<> -struct StringMaker { - static std::string convert(std::nullptr_t); -}; + template<> + struct StringMaker { + static std::string convert(std::byte value); + }; -template<> -struct StringMaker { - static std::string convert(float value); - static int precision; -}; +#endif // defined(CATCH_CONFIG_CPP17_BYTE) -template<> -struct StringMaker { - static std::string convert(double value); - static int precision; -}; + template<> + struct StringMaker { + static std::string convert(int value); + }; -template -struct StringMaker { - template - static std::string convert(U *p) { - if (p) { - return ::Catch::Detail::rawMemoryToString(p); - } else { - return "nullptr"; - } - } -}; + template<> + struct StringMaker { + static std::string convert(long value); + }; -template -struct StringMaker { - static std::string convert(R C::* p) { - if (p) { - return ::Catch::Detail::rawMemoryToString(p); - } else { - return "nullptr"; - } - } -}; + template<> + struct StringMaker { + static std::string convert(long long value); + }; -#if defined(_MANAGED) - template - struct StringMaker { - static std::string convert( T^ ref ) { - return ::Catch::Detail::clrReferenceToString(ref); - } + template<> + struct StringMaker { + static std::string convert(unsigned int value); }; -#endif -namespace Detail { -template -std::string rangeToString(InputIterator first, Sentinel last) { - ReusableStringStream rss; - rss << "{ "; - if (first != last) { - rss << ::Catch::Detail::stringify(*first); - for (++first; first != last; ++first) - rss << ", " << ::Catch::Detail::stringify(*first); - } - rss << " }"; - return rss.str(); -} -} + template<> + struct StringMaker { + static std::string convert(unsigned long value); + }; -#ifdef __OBJC__ - template<> - struct StringMaker { - static std::string convert(NSString * nsstring) { - if (!nsstring) - return "nil"; - return std::string("@") + [nsstring UTF8String]; - } + template<> + struct StringMaker { + static std::string convert(unsigned long long value); }; + template<> - struct StringMaker { + struct StringMaker { + static std::string convert(bool b); + }; + + template<> + struct StringMaker { + static std::string convert(char c); + }; + + template<> + struct StringMaker { + static std::string convert(signed char c); + }; + + template<> + struct StringMaker { + static std::string convert(unsigned char c); + }; + + template<> + struct StringMaker { + static std::string convert(std::nullptr_t); + }; + + template<> + struct StringMaker { + static std::string convert(float value); + + static int precision; + }; + + template<> + struct StringMaker { + static std::string convert(double value); + + static int precision; + }; + + template + struct StringMaker { + template + static std::string convert(U *p) { + if (p) { + return ::Catch::Detail::rawMemoryToString(p); + } else { + return "nullptr"; + } + } + }; + + template + struct StringMaker { + static std::string convert(R C::* p) { + if (p) { + return ::Catch::Detail::rawMemoryToString(p); + } else { + return "nullptr"; + } + } + }; + +#if defined(_MANAGED) + template + struct StringMaker { + static std::string convert( T^ ref ) { + return ::Catch::Detail::clrReferenceToString(ref); + } + }; +#endif + + namespace Detail { + template + std::string rangeToString(InputIterator first, Sentinel last) { + ReusableStringStream rss; + rss << "{ "; + if (first != last) { + rss << ::Catch::Detail::stringify(*first); + for (++first; first != last; ++first) + rss << ", " << ::Catch::Detail::stringify(*first); + } + rss << " }"; + return rss.str(); + } + } + +#ifdef __OBJC__ + template<> + struct StringMaker { + static std::string convert(NSString * nsstring) { + if (!nsstring) + return "nil"; + return std::string("@") + [nsstring UTF8String]; + } + }; + template<> + struct StringMaker { static std::string convert(NSObject* nsObject) { return ::Catch::Detail::stringify([nsObject description]); } @@ -1995,189 +2063,200 @@ namespace Catch { namespace Catch { // Import begin/ end from std here -using std::begin; -using std::end; + using std::begin; + using std::end; -namespace detail { -template -struct void_type { - using type = void; -}; + namespace detail { + template + struct void_type { + using type = void; + }; -template -struct is_range_impl : std::false_type { -}; + template + struct is_range_impl : std::false_type { + }; -template -struct is_range_impl()))>::type> : std::true_type { -}; -} // namespace detail + template + struct is_range_impl()))>::type> : std::true_type { + }; + } // namespace detail -template -struct is_range : detail::is_range_impl { -}; + template + struct is_range : detail::is_range_impl { + }; #if defined(_MANAGED) // Managed types are never ranges - template + template struct is_range { static const bool value = false; }; #endif -template -std::string rangeToString(Range const &range) { - return ::Catch::Detail::rangeToString(begin(range), end(range)); -} + template + std::string rangeToString(Range const &range) { + return ::Catch::Detail::rangeToString(begin(range), end(range)); + } // Handle vector specially -template -std::string rangeToString(std::vector const &v) { - ReusableStringStream rss; - rss << "{ "; - bool first = true; - for (bool b : v) { - if (first) - first = false; - else - rss << ", "; - rss << ::Catch::Detail::stringify(b); - } - rss << " }"; - return rss.str(); -} + template + std::string rangeToString(std::vector const &v) { + ReusableStringStream rss; + rss << "{ "; + bool first = true; + for (bool b: v) { + if (first) + first = false; + else + rss << ", "; + rss << ::Catch::Detail::stringify(b); + } + rss << " }"; + return rss.str(); + } -template -struct StringMaker::value && !::Catch::Detail::IsStreamInsertable::value>::type> { - static std::string convert(R const &range) { - return rangeToString(range); - } -}; + template + struct StringMaker::value && !::Catch::Detail::IsStreamInsertable::value>::type> { + static std::string convert(R const &range) { + return rangeToString(range); + } + }; -template -struct StringMaker { - static std::string convert(T const(&arr)[SZ]) { - return rangeToString(arr); - } -}; + template + struct StringMaker { + static std::string convert(T const(&arr)[SZ]) { + return rangeToString(arr); + } + }; } // namespace Catch // Separate std::chrono::duration specialization #if defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) + #include #include #include namespace Catch { -template -struct ratio_string { - static std::string symbol(); -}; + template + struct ratio_string { + static std::string symbol(); + }; -template -std::string ratio_string::symbol() { - Catch::ReusableStringStream rss; - rss << '[' << Ratio::num << '/' - << Ratio::den << ']'; - return rss.str(); -} -template<> -struct ratio_string { - static std::string symbol(); -}; -template<> -struct ratio_string { - static std::string symbol(); -}; -template<> -struct ratio_string { - static std::string symbol(); -}; -template<> -struct ratio_string { - static std::string symbol(); -}; -template<> -struct ratio_string { - static std::string symbol(); -}; -template<> -struct ratio_string { - static std::string symbol(); -}; + template + std::string ratio_string::symbol() { + Catch::ReusableStringStream rss; + rss << '[' << Ratio::num << '/' + << Ratio::den << ']'; + return rss.str(); + } + + template<> + struct ratio_string { + static std::string symbol(); + }; + + template<> + struct ratio_string { + static std::string symbol(); + }; + + template<> + struct ratio_string { + static std::string symbol(); + }; + + template<> + struct ratio_string { + static std::string symbol(); + }; + + template<> + struct ratio_string { + static std::string symbol(); + }; + + template<> + struct ratio_string { + static std::string symbol(); + }; //////////// // std::chrono::duration specializations -template -struct StringMaker> { - static std::string convert(std::chrono::duration const &duration) { - ReusableStringStream rss; - rss << duration.count() << ' ' << ratio_string::symbol() << 's'; - return rss.str(); - } -}; -template -struct StringMaker>> { - static std::string convert(std::chrono::duration> const &duration) { - ReusableStringStream rss; - rss << duration.count() << " s"; - return rss.str(); - } -}; -template -struct StringMaker>> { - static std::string convert(std::chrono::duration> const &duration) { - ReusableStringStream rss; - rss << duration.count() << " m"; - return rss.str(); - } -}; -template -struct StringMaker>> { - static std::string convert(std::chrono::duration> const &duration) { - ReusableStringStream rss; - rss << duration.count() << " h"; - return rss.str(); - } -}; + template + struct StringMaker> { + static std::string convert(std::chrono::duration const &duration) { + ReusableStringStream rss; + rss << duration.count() << ' ' << ratio_string::symbol() << 's'; + return rss.str(); + } + }; + + template + struct StringMaker>> { + static std::string convert(std::chrono::duration> const &duration) { + ReusableStringStream rss; + rss << duration.count() << " s"; + return rss.str(); + } + }; + + template + struct StringMaker>> { + static std::string convert(std::chrono::duration> const &duration) { + ReusableStringStream rss; + rss << duration.count() << " m"; + return rss.str(); + } + }; + + template + struct StringMaker>> { + static std::string convert(std::chrono::duration> const &duration) { + ReusableStringStream rss; + rss << duration.count() << " h"; + return rss.str(); + } + }; //////////// // std::chrono::time_point specialization // Generic time_point cannot be specialized, only std::chrono::time_point -template -struct StringMaker> { - static std::string convert(std::chrono::time_point const &time_point) { - return ::Catch::Detail::stringify(time_point.time_since_epoch()) + " since epoch"; - } -}; + template + struct StringMaker> { + static std::string convert(std::chrono::time_point const &time_point) { + return ::Catch::Detail::stringify(time_point.time_since_epoch()) + " since epoch"; + } + }; + // std::chrono::time_point specialization -template -struct StringMaker> { - static std::string convert(std::chrono::time_point const &time_point) { - auto converted = std::chrono::system_clock::to_time_t(time_point); + template + struct StringMaker> { + static std::string convert(std::chrono::time_point const &time_point) { + auto converted = std::chrono::system_clock::to_time_t(time_point); #ifdef _MSC_VER - std::tm timeInfo = {}; + std::tm timeInfo = {}; gmtime_s(&timeInfo, &converted); #else - std::tm *timeInfo = std::gmtime(&converted); + std::tm *timeInfo = std::gmtime(&converted); #endif - auto const timeStampSize = sizeof("2017-01-16T17:06:45Z"); - char timeStamp[timeStampSize]; - const char *const fmt = "%Y-%m-%dT%H:%M:%SZ"; + auto const timeStampSize = sizeof("2017-01-16T17:06:45Z"); + char timeStamp[timeStampSize]; + const char *const fmt = "%Y-%m-%dT%H:%M:%SZ"; #ifdef _MSC_VER - std::strftime(timeStamp, timeStampSize, fmt, &timeInfo); + std::strftime(timeStamp, timeStampSize, fmt, &timeInfo); #else - std::strftime(timeStamp, timeStampSize, fmt, timeInfo); + std::strftime(timeStamp, timeStampSize, fmt, timeInfo); #endif - return std::string(timeStamp); - } -}; + return std::string(timeStamp); + } + }; } #endif // CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER @@ -2211,225 +2290,243 @@ namespace Catch { \ namespace Catch { -struct ITransientExpression { - auto isBinaryExpression() const -> bool { return m_isBinaryExpression; } - auto getResult() const -> bool { return m_result; } - virtual void streamReconstructedExpression(std::ostream &os) const = 0; + struct ITransientExpression { + auto isBinaryExpression() const -> bool { return m_isBinaryExpression; } - ITransientExpression(bool isBinaryExpression, bool result) - : m_isBinaryExpression(isBinaryExpression), - m_result(result) {} + auto getResult() const -> bool { return m_result; } - // We don't actually need a virtual destructor, but many static analysers - // complain if it's not here :-( - virtual ~ITransientExpression(); + virtual void streamReconstructedExpression(std::ostream &os) const = 0; - bool m_isBinaryExpression; - bool m_result; + ITransientExpression(bool isBinaryExpression, bool result) + : m_isBinaryExpression(isBinaryExpression), + m_result(result) {} -}; + // We don't actually need a virtual destructor, but many static analysers + // complain if it's not here :-( + virtual ~ITransientExpression(); -void formatReconstructedExpression(std::ostream &os, std::string const &lhs, StringRef op, std::string const &rhs); - -template -class BinaryExpr : public ITransientExpression { - LhsT m_lhs; - StringRef m_op; - RhsT m_rhs; - - void streamReconstructedExpression(std::ostream &os) const override { - formatReconstructedExpression - (os, Catch::Detail::stringify(m_lhs), m_op, Catch::Detail::stringify(m_rhs)); - } - - public: - BinaryExpr(bool comparisonResult, LhsT lhs, StringRef op, RhsT rhs) - : ITransientExpression{true, comparisonResult}, - m_lhs(lhs), - m_op(op), - m_rhs(rhs) {} - - template - auto operator&&(T) const -> BinaryExpr const { - static_assert(always_false::value, - "chained comparisons are not supported inside assertions, " - "wrap the expression inside parentheses, or decompose it"); - } - - template - auto operator||(T) const -> BinaryExpr const { - static_assert(always_false::value, - "chained comparisons are not supported inside assertions, " - "wrap the expression inside parentheses, or decompose it"); - } - - template - auto operator==(T) const -> BinaryExpr const { - static_assert(always_false::value, - "chained comparisons are not supported inside assertions, " - "wrap the expression inside parentheses, or decompose it"); - } - - template - auto operator!=(T) const -> BinaryExpr const { - static_assert(always_false::value, - "chained comparisons are not supported inside assertions, " - "wrap the expression inside parentheses, or decompose it"); - } - - template - auto operator>(T) const -> BinaryExpr const { - static_assert(always_false::value, - "chained comparisons are not supported inside assertions, " - "wrap the expression inside parentheses, or decompose it"); - } - - template - auto operator<(T) const -> BinaryExpr const { - static_assert(always_false::value, - "chained comparisons are not supported inside assertions, " - "wrap the expression inside parentheses, or decompose it"); - } - - template - auto operator>=(T) const -> BinaryExpr const { - static_assert(always_false::value, - "chained comparisons are not supported inside assertions, " - "wrap the expression inside parentheses, or decompose it"); - } - - template - auto operator<=(T) const -> BinaryExpr const { - static_assert(always_false::value, - "chained comparisons are not supported inside assertions, " - "wrap the expression inside parentheses, or decompose it"); - } -}; + bool m_isBinaryExpression; + bool m_result; -template -class UnaryExpr : public ITransientExpression { - LhsT m_lhs; + }; - void streamReconstructedExpression(std::ostream &os) const override { - os << Catch::Detail::stringify(m_lhs); - } + void formatReconstructedExpression(std::ostream &os, std::string const &lhs, StringRef op, std::string const &rhs); - public: - explicit UnaryExpr(LhsT lhs) - : ITransientExpression{false, static_cast(lhs)}, - m_lhs(lhs) {} -}; + template + class BinaryExpr : public ITransientExpression { + LhsT m_lhs; + StringRef m_op; + RhsT m_rhs; + + void streamReconstructedExpression(std::ostream &os) const override { + formatReconstructedExpression + (os, Catch::Detail::stringify(m_lhs), m_op, Catch::Detail::stringify(m_rhs)); + } + + public: + BinaryExpr(bool comparisonResult, LhsT lhs, StringRef op, RhsT rhs) + : ITransientExpression{true, comparisonResult}, + m_lhs(lhs), + m_op(op), + m_rhs(rhs) {} + + template + auto operator&&(T) const -> BinaryExpr const { + static_assert(always_false::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + template + auto operator||(T) const -> BinaryExpr const { + static_assert(always_false::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + template + auto operator==(T) const -> BinaryExpr const { + static_assert(always_false::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + template + auto operator!=(T) const -> BinaryExpr const { + static_assert(always_false::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + template + auto operator>(T) const -> BinaryExpr const { + static_assert(always_false::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + template + auto operator<(T) const -> BinaryExpr const { + static_assert(always_false::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + template + auto operator>=(T) const -> BinaryExpr const { + static_assert(always_false::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + template + auto operator<=(T) const -> BinaryExpr const { + static_assert(always_false::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + }; + + template + class UnaryExpr : public ITransientExpression { + LhsT m_lhs; + + void streamReconstructedExpression(std::ostream &os) const override { + os << Catch::Detail::stringify(m_lhs); + } + + public: + explicit UnaryExpr(LhsT lhs) + : ITransientExpression{false, static_cast(lhs)}, + m_lhs(lhs) {} + }; // Specialised comparison functions to handle equality comparisons between ints and pointers (NULL deduces as an int) -template -auto compareEqual(LhsT const &lhs, RhsT const &rhs) -> bool { return static_cast(lhs == rhs); } -template -auto compareEqual(T *const &lhs, int rhs) -> bool { return lhs == reinterpret_cast( rhs ); } -template -auto compareEqual(T *const &lhs, long rhs) -> bool { return lhs == reinterpret_cast( rhs ); } -template -auto compareEqual(int lhs, T *const &rhs) -> bool { return reinterpret_cast( lhs ) == rhs; } -template -auto compareEqual(long lhs, T *const &rhs) -> bool { return reinterpret_cast( lhs ) == rhs; } - -template -auto compareNotEqual(LhsT const &lhs, RhsT &&rhs) -> bool { return static_cast(lhs != rhs); } -template -auto compareNotEqual(T *const &lhs, int rhs) -> bool { return lhs != reinterpret_cast( rhs ); } -template -auto compareNotEqual(T *const &lhs, long rhs) -> bool { return lhs != reinterpret_cast( rhs ); } -template -auto compareNotEqual(int lhs, T *const &rhs) -> bool { return reinterpret_cast( lhs ) != rhs; } -template -auto compareNotEqual(long lhs, T *const &rhs) -> bool { return reinterpret_cast( lhs ) != rhs; } - -template -class ExprLhs { - LhsT m_lhs; - public: - explicit ExprLhs(LhsT lhs) : m_lhs(lhs) {} - - template - auto operator==(RhsT const &rhs) -> BinaryExpr const { - return {compareEqual(m_lhs, rhs), m_lhs, "==", rhs}; - } - auto operator==(bool rhs) -> BinaryExpr const { - return {m_lhs == rhs, m_lhs, "==", rhs}; - } - - template - auto operator!=(RhsT const &rhs) -> BinaryExpr const { - return {compareNotEqual(m_lhs, rhs), m_lhs, "!=", rhs}; - } - auto operator!=(bool rhs) -> BinaryExpr const { - return {m_lhs != rhs, m_lhs, "!=", rhs}; - } - - template - auto operator>(RhsT const &rhs) -> BinaryExpr const { - return {static_cast(m_lhs > rhs), m_lhs, ">", rhs}; - } - template - auto operator<(RhsT const &rhs) -> BinaryExpr const { - return {static_cast(m_lhs < rhs), m_lhs, "<", rhs}; - } - template - auto operator>=(RhsT const &rhs) -> BinaryExpr const { - return {static_cast(m_lhs >= rhs), m_lhs, ">=", rhs}; - } - template - auto operator<=(RhsT const &rhs) -> BinaryExpr const { - return {static_cast(m_lhs <= rhs), m_lhs, "<=", rhs}; - } - template - auto operator|(RhsT const &rhs) -> BinaryExpr const { - return {static_cast(m_lhs | rhs), m_lhs, "|", rhs}; - } - template - auto operator&(RhsT const &rhs) -> BinaryExpr const { - return {static_cast(m_lhs & rhs), m_lhs, "&", rhs}; - } - template - auto operator^(RhsT const &rhs) -> BinaryExpr const { - return {static_cast(m_lhs ^ rhs), m_lhs, "^", rhs}; - } - - template - auto operator&&(RhsT const &) -> BinaryExpr const { - static_assert(always_false::value, - "operator&& is not supported inside assertions, " - "wrap the expression inside parentheses, or decompose it"); - } - - template - auto operator||(RhsT const &) -> BinaryExpr const { - static_assert(always_false::value, - "operator|| is not supported inside assertions, " - "wrap the expression inside parentheses, or decompose it"); - } - - auto makeUnaryExpr() const -> UnaryExpr { - return UnaryExpr{m_lhs}; - } -}; + template + auto compareEqual(LhsT const &lhs, RhsT const &rhs) -> bool { return static_cast(lhs == rhs); } -void handleExpression(ITransientExpression const &expr); + template + auto compareEqual(T *const &lhs, int rhs) -> bool { return lhs == reinterpret_cast( rhs ); } -template -void handleExpression(ExprLhs const &expr) { - handleExpression(expr.makeUnaryExpr()); -} + template + auto compareEqual(T *const &lhs, long rhs) -> bool { return lhs == reinterpret_cast( rhs ); } -struct Decomposer { - template - auto operator<=(T const &lhs) -> ExprLhs { - return ExprLhs{lhs}; - } + template + auto compareEqual(int lhs, T *const &rhs) -> bool { return reinterpret_cast( lhs ) == rhs; } - auto operator<=(bool value) -> ExprLhs { - return ExprLhs{value}; - } -}; + template + auto compareEqual(long lhs, T *const &rhs) -> bool { return reinterpret_cast( lhs ) == rhs; } + + template + auto compareNotEqual(LhsT const &lhs, RhsT &&rhs) -> bool { return static_cast(lhs != rhs); } + + template + auto compareNotEqual(T *const &lhs, int rhs) -> bool { return lhs != reinterpret_cast( rhs ); } + + template + auto compareNotEqual(T *const &lhs, long rhs) -> bool { return lhs != reinterpret_cast( rhs ); } + + template + auto compareNotEqual(int lhs, T *const &rhs) -> bool { return reinterpret_cast( lhs ) != rhs; } + + template + auto compareNotEqual(long lhs, T *const &rhs) -> bool { return reinterpret_cast( lhs ) != rhs; } + + template + class ExprLhs { + LhsT m_lhs; + public: + explicit ExprLhs(LhsT lhs) : m_lhs(lhs) {} + + template + auto operator==(RhsT const &rhs) -> BinaryExpr const { + return {compareEqual(m_lhs, rhs), m_lhs, "==", rhs}; + } + + auto operator==(bool rhs) -> BinaryExpr const { + return {m_lhs == rhs, m_lhs, "==", rhs}; + } + + template + auto operator!=(RhsT const &rhs) -> BinaryExpr const { + return {compareNotEqual(m_lhs, rhs), m_lhs, "!=", rhs}; + } + + auto operator!=(bool rhs) -> BinaryExpr const { + return {m_lhs != rhs, m_lhs, "!=", rhs}; + } + + template + auto operator>(RhsT const &rhs) -> BinaryExpr const { + return {static_cast(m_lhs > rhs), m_lhs, ">", rhs}; + } + + template + auto operator<(RhsT const &rhs) -> BinaryExpr const { + return {static_cast(m_lhs < rhs), m_lhs, "<", rhs}; + } + + template + auto operator>=(RhsT const &rhs) -> BinaryExpr const { + return {static_cast(m_lhs >= rhs), m_lhs, ">=", rhs}; + } + + template + auto operator<=(RhsT const &rhs) -> BinaryExpr const { + return {static_cast(m_lhs <= rhs), m_lhs, "<=", rhs}; + } + + template + auto operator|(RhsT const &rhs) -> BinaryExpr const { + return {static_cast(m_lhs | rhs), m_lhs, "|", rhs}; + } + + template + auto operator&(RhsT const &rhs) -> BinaryExpr const { + return {static_cast(m_lhs & rhs), m_lhs, "&", rhs}; + } + + template + auto operator^(RhsT const &rhs) -> BinaryExpr const { + return {static_cast(m_lhs ^ rhs), m_lhs, "^", rhs}; + } + + template + auto operator&&(RhsT const &) -> BinaryExpr const { + static_assert(always_false::value, + "operator&& is not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + template + auto operator||(RhsT const &) -> BinaryExpr const { + static_assert(always_false::value, + "operator|| is not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + auto makeUnaryExpr() const -> UnaryExpr { + return UnaryExpr{m_lhs}; + } + }; + + void handleExpression(ITransientExpression const &expr); + + template + void handleExpression(ExprLhs const &expr) { + handleExpression(expr.makeUnaryExpr()); + } + + struct Decomposer { + template + auto operator<=(T const &lhs) -> ExprLhs { + return ExprLhs{lhs}; + } + + auto operator<=(bool value) -> ExprLhs { + return ExprLhs{value}; + } + }; } // end namespace Catch @@ -2445,241 +2542,272 @@ struct Decomposer { namespace Catch { -class AssertionResult; -struct AssertionInfo; -struct SectionInfo; -struct SectionEndInfo; -struct MessageInfo; -struct MessageBuilder; -struct Counts; -struct AssertionReaction; -struct SourceLineInfo; + class AssertionResult; -struct ITransientExpression; -struct IGeneratorTracker; + struct AssertionInfo; + struct SectionInfo; + struct SectionEndInfo; + struct MessageInfo; + struct MessageBuilder; + struct Counts; + struct AssertionReaction; + struct SourceLineInfo; + + struct ITransientExpression; + struct IGeneratorTracker; #if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) - struct BenchmarkInfo; + struct BenchmarkInfo; template > struct BenchmarkStats; #endif // CATCH_CONFIG_ENABLE_BENCHMARKING -struct IResultCapture { + struct IResultCapture { + + virtual ~IResultCapture(); - virtual ~IResultCapture(); + virtual bool sectionStarted(SectionInfo const §ionInfo, + Counts &assertions) = 0; - virtual bool sectionStarted(SectionInfo const §ionInfo, - Counts &assertions) = 0; - virtual void sectionEnded(SectionEndInfo const &endInfo) = 0; - virtual void sectionEndedEarly(SectionEndInfo const &endInfo) = 0; + virtual void sectionEnded(SectionEndInfo const &endInfo) = 0; - virtual auto acquireGeneratorTracker(StringRef generatorName, - SourceLineInfo const &lineInfo) -> IGeneratorTracker & = 0; + virtual void sectionEndedEarly(SectionEndInfo const &endInfo) = 0; + + virtual auto acquireGeneratorTracker(StringRef generatorName, + SourceLineInfo const &lineInfo) -> IGeneratorTracker & = 0; #if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) - virtual void benchmarkPreparing( std::string const& name ) = 0; + virtual void benchmarkPreparing( std::string const& name ) = 0; virtual void benchmarkStarting( BenchmarkInfo const& info ) = 0; virtual void benchmarkEnded( BenchmarkStats<> const& stats ) = 0; virtual void benchmarkFailed( std::string const& error ) = 0; #endif // CATCH_CONFIG_ENABLE_BENCHMARKING - virtual void pushScopedMessage(MessageInfo const &message) = 0; - virtual void popScopedMessage(MessageInfo const &message) = 0; - - virtual void emplaceUnscopedMessage(MessageBuilder const &builder) = 0; - - virtual void handleFatalErrorCondition(StringRef message) = 0; - - virtual void handleExpr - (AssertionInfo const &info, - ITransientExpression const &expr, - AssertionReaction &reaction) = 0; - virtual void handleMessage - (AssertionInfo const &info, - ResultWas::OfType resultType, - StringRef const &message, - AssertionReaction &reaction) = 0; - virtual void handleUnexpectedExceptionNotThrown - (AssertionInfo const &info, - AssertionReaction &reaction) = 0; - virtual void handleUnexpectedInflightException - (AssertionInfo const &info, - std::string const &message, - AssertionReaction &reaction) = 0; - virtual void handleIncomplete - (AssertionInfo const &info) = 0; - virtual void handleNonExpr - (AssertionInfo const &info, - ResultWas::OfType resultType, - AssertionReaction &reaction) = 0; - - virtual bool lastAssertionPassed() = 0; - virtual void assertionPassed() = 0; - - // Deprecated, do not use: - virtual std::string getCurrentTestName() const = 0; - virtual const AssertionResult *getLastResult() const = 0; - virtual void exceptionEarlyReported() = 0; -}; + virtual void pushScopedMessage(MessageInfo const &message) = 0; -IResultCapture &getResultCapture(); -} + virtual void popScopedMessage(MessageInfo const &message) = 0; -// end catch_interfaces_capture.h -namespace Catch { + virtual void emplaceUnscopedMessage(MessageBuilder const &builder) = 0; -struct TestFailureException {}; -struct AssertionResultData; -struct IResultCapture; -class RunContext; + virtual void handleFatalErrorCondition(StringRef message) = 0; -class LazyExpression { - friend class AssertionHandler; - friend struct AssertionStats; - friend class RunContext; + virtual void handleExpr + (AssertionInfo const &info, + ITransientExpression const &expr, + AssertionReaction &reaction) = 0; - ITransientExpression const *m_transientExpression = nullptr; - bool m_isNegated; - public: - LazyExpression(bool isNegated); - LazyExpression(LazyExpression const &other); - LazyExpression &operator=(LazyExpression const &) = delete; + virtual void handleMessage + (AssertionInfo const &info, + ResultWas::OfType resultType, + StringRef const &message, + AssertionReaction &reaction) = 0; - explicit operator bool() const; + virtual void handleUnexpectedExceptionNotThrown + (AssertionInfo const &info, + AssertionReaction &reaction) = 0; - friend auto operator<<(std::ostream &os, LazyExpression const &lazyExpr) -> std::ostream &; -}; + virtual void handleUnexpectedInflightException + (AssertionInfo const &info, + std::string const &message, + AssertionReaction &reaction) = 0; -struct AssertionReaction { - bool shouldDebugBreak = false; - bool shouldThrow = false; -}; + virtual void handleIncomplete + (AssertionInfo const &info) = 0; -class AssertionHandler { - AssertionInfo m_assertionInfo; - AssertionReaction m_reaction; - bool m_completed = false; - IResultCapture &m_resultCapture; - - public: - AssertionHandler - (StringRef const ¯oName, - SourceLineInfo const &lineInfo, - StringRef capturedExpression, - ResultDisposition::Flags resultDisposition); - ~AssertionHandler() { - if (!m_completed) { - m_resultCapture.handleIncomplete(m_assertionInfo); - } - } - - template - void handleExpr(ExprLhs const &expr) { - handleExpr(expr.makeUnaryExpr()); - } - void handleExpr(ITransientExpression const &expr); - - void handleMessage(ResultWas::OfType resultType, StringRef const &message); - - void handleExceptionThrownAsExpected(); - void handleUnexpectedExceptionNotThrown(); - void handleExceptionNotThrownAsExpected(); - void handleThrowingCallSkipped(); - void handleUnexpectedInflightException(); - - void complete(); - void setCompleted(); - - // query - auto allowThrows() const -> bool; -}; + virtual void handleNonExpr + (AssertionInfo const &info, + ResultWas::OfType resultType, + AssertionReaction &reaction) = 0; -void handleExceptionMatchExpr(AssertionHandler &handler, std::string const &str, StringRef const &matcherString); + virtual bool lastAssertionPassed() = 0; -} // namespace Catch + virtual void assertionPassed() = 0; -// end catch_assertionhandler.h -// start catch_message.h + // Deprecated, do not use: + virtual std::string getCurrentTestName() const = 0; -#include -#include + virtual const AssertionResult *getLastResult() const = 0; -namespace Catch { + virtual void exceptionEarlyReported() = 0; + }; -struct MessageInfo { - MessageInfo(StringRef const &_macroName, - SourceLineInfo const &_lineInfo, - ResultWas::OfType _type); - - StringRef macroName; - std::string message; - SourceLineInfo lineInfo; - ResultWas::OfType type; - unsigned int sequence; - - bool operator==(MessageInfo const &other) const; - bool operator<(MessageInfo const &other) const; - private: - static unsigned int globalCount; -}; + IResultCapture &getResultCapture(); +} + +// end catch_interfaces_capture.h +namespace Catch { -struct MessageStream { + struct TestFailureException { + }; + struct AssertionResultData; + struct IResultCapture; - template - MessageStream &operator<<(T const &value) { - m_stream << value; - return *this; - } + class RunContext; - ReusableStringStream m_stream; -}; + class LazyExpression { + friend class AssertionHandler; + + friend struct AssertionStats; + + friend class RunContext; + + ITransientExpression const *m_transientExpression = nullptr; + bool m_isNegated; + public: + LazyExpression(bool isNegated); + + LazyExpression(LazyExpression const &other); + + LazyExpression &operator=(LazyExpression const &) = delete; + + explicit operator bool() const; + + friend auto operator<<(std::ostream &os, LazyExpression const &lazyExpr) -> std::ostream &; + }; + + struct AssertionReaction { + bool shouldDebugBreak = false; + bool shouldThrow = false; + }; + + class AssertionHandler { + AssertionInfo m_assertionInfo; + AssertionReaction m_reaction; + bool m_completed = false; + IResultCapture &m_resultCapture; -struct MessageBuilder : MessageStream { - MessageBuilder(StringRef const ¯oName, + public: + AssertionHandler + (StringRef const ¯oName, SourceLineInfo const &lineInfo, - ResultWas::OfType type); + StringRef capturedExpression, + ResultDisposition::Flags resultDisposition); - template - MessageBuilder &operator<<(T const &value) { - m_stream << value; - return *this; - } + ~AssertionHandler() { + if (!m_completed) { + m_resultCapture.handleIncomplete(m_assertionInfo); + } + } - MessageInfo m_info; -}; + template + void handleExpr(ExprLhs const &expr) { + handleExpr(expr.makeUnaryExpr()); + } -class ScopedMessage { - public: - explicit ScopedMessage(MessageBuilder const &builder); - ScopedMessage(ScopedMessage &duplicate) = delete; - ScopedMessage(ScopedMessage &&old); - ~ScopedMessage(); + void handleExpr(ITransientExpression const &expr); - MessageInfo m_info; - bool m_moved; -}; + void handleMessage(ResultWas::OfType resultType, StringRef const &message); -class Capturer { - std::vector m_messages; - IResultCapture &m_resultCapture = getResultCapture(); - size_t m_captured = 0; - public: - Capturer(StringRef macroName, SourceLineInfo const &lineInfo, ResultWas::OfType resultType, StringRef names); - ~Capturer(); - - void captureValue(size_t index, std::string const &value); - - template - void captureValues(size_t index, T const &value) { - captureValue(index, Catch::Detail::stringify(value)); - } - - template - void captureValues(size_t index, T const &value, Ts const &... values) { - captureValue(index, Catch::Detail::stringify(value)); - captureValues(index + 1, values...); - } -}; + void handleExceptionThrownAsExpected(); + + void handleUnexpectedExceptionNotThrown(); + + void handleExceptionNotThrownAsExpected(); + + void handleThrowingCallSkipped(); + + void handleUnexpectedInflightException(); + + void complete(); + + void setCompleted(); + + // query + auto allowThrows() const -> bool; + }; + + void handleExceptionMatchExpr(AssertionHandler &handler, std::string const &str, StringRef const &matcherString); + +} // namespace Catch + +// end catch_assertionhandler.h +// start catch_message.h + +#include +#include + +namespace Catch { + + struct MessageInfo { + MessageInfo(StringRef const &_macroName, + SourceLineInfo const &_lineInfo, + ResultWas::OfType _type); + + StringRef macroName; + std::string message; + SourceLineInfo lineInfo; + ResultWas::OfType type; + unsigned int sequence; + + bool operator==(MessageInfo const &other) const; + + bool operator<(MessageInfo const &other) const; + + private: + static unsigned int globalCount; + }; + + struct MessageStream { + + template + MessageStream &operator<<(T const &value) { + m_stream << value; + return *this; + } + + ReusableStringStream m_stream; + }; + + struct MessageBuilder : MessageStream { + MessageBuilder(StringRef const ¯oName, + SourceLineInfo const &lineInfo, + ResultWas::OfType type); + + template + MessageBuilder &operator<<(T const &value) { + m_stream << value; + return *this; + } + + MessageInfo m_info; + }; + + class ScopedMessage { + public: + explicit ScopedMessage(MessageBuilder const &builder); + + ScopedMessage(ScopedMessage &duplicate) = delete; + + ScopedMessage(ScopedMessage &&old); + + ~ScopedMessage(); + + MessageInfo m_info; + bool m_moved; + }; + + class Capturer { + std::vector m_messages; + IResultCapture &m_resultCapture = getResultCapture(); + size_t m_captured = 0; + public: + Capturer(StringRef macroName, SourceLineInfo const &lineInfo, ResultWas::OfType resultType, StringRef names); + + ~Capturer(); + + void captureValue(size_t index, std::string const &value); + + template + void captureValues(size_t index, T const &value) { + captureValue(index, Catch::Detail::stringify(value)); + } + + template + void captureValues(size_t index, T const &value, Ts const &... values) { + captureValue(index, Catch::Detail::stringify(value)); + captureValues(index + 1, values...); + } + }; } // end namespace Catch @@ -2836,30 +2964,34 @@ class Capturer { namespace Catch { -struct Counts { - Counts operator-(Counts const &other) const; - Counts &operator+=(Counts const &other); + struct Counts { + Counts operator-(Counts const &other) const; - std::size_t total() const; - bool allPassed() const; - bool allOk() const; + Counts &operator+=(Counts const &other); - std::size_t passed = 0; - std::size_t failed = 0; - std::size_t failedButOk = 0; -}; + std::size_t total() const; -struct Totals { + bool allPassed() const; - Totals operator-(Totals const &other) const; - Totals &operator+=(Totals const &other); + bool allOk() const; - Totals delta(Totals const &prevTotals) const; + std::size_t passed = 0; + std::size_t failed = 0; + std::size_t failedButOk = 0; + }; - int error = 0; - Counts assertions; - Counts testCases; -}; + struct Totals { + + Totals operator-(Totals const &other) const; + + Totals &operator+=(Totals const &other); + + Totals delta(Totals const &prevTotals) const; + + int error = 0; + Counts assertions; + Counts testCases; + }; } // end catch_totals.h @@ -2867,27 +2999,27 @@ struct Totals { namespace Catch { -struct SectionInfo { - SectionInfo - (SourceLineInfo const &_lineInfo, - std::string const &_name); + struct SectionInfo { + SectionInfo + (SourceLineInfo const &_lineInfo, + std::string const &_name); - // Deprecated - SectionInfo - (SourceLineInfo const &_lineInfo, - std::string const &_name, - std::string const &) : SectionInfo(_lineInfo, _name) {} + // Deprecated + SectionInfo + (SourceLineInfo const &_lineInfo, + std::string const &_name, + std::string const &) : SectionInfo(_lineInfo, _name) {} - std::string name; - std::string description; // !Deprecated: this will always be empty - SourceLineInfo lineInfo; -}; + std::string name; + std::string description; // !Deprecated: this will always be empty + SourceLineInfo lineInfo; + }; -struct SectionEndInfo { - SectionInfo sectionInfo; - Counts prevAssertions; - double durationInSeconds; -}; + struct SectionEndInfo { + SectionInfo sectionInfo; + Counts prevAssertions; + double durationInSeconds; + }; } // end namespace Catch @@ -2898,18 +3030,23 @@ struct SectionEndInfo { namespace Catch { -auto getCurrentNanosecondsSinceEpoch() -> uint64_t; -auto getEstimatedClockResolution() -> uint64_t; - -class Timer { - uint64_t m_nanoseconds = 0; - public: - void start(); - auto getElapsedNanoseconds() const -> uint64_t; - auto getElapsedMicroseconds() const -> uint64_t; - auto getElapsedMilliseconds() const -> unsigned int; - auto getElapsedSeconds() const -> double; -}; + auto getCurrentNanosecondsSinceEpoch() -> uint64_t; + + auto getEstimatedClockResolution() -> uint64_t; + + class Timer { + uint64_t m_nanoseconds = 0; + public: + void start(); + + auto getElapsedNanoseconds() const -> uint64_t; + + auto getElapsedMicroseconds() const -> uint64_t; + + auto getElapsedMilliseconds() const -> unsigned int; + + auto getElapsedSeconds() const -> double; + }; } // namespace Catch @@ -2918,22 +3055,23 @@ class Timer { namespace Catch { -class Section : NonCopyable { - public: - Section(SectionInfo const &info); - ~Section(); + class Section : NonCopyable { + public: + Section(SectionInfo const &info); + + ~Section(); - // This indicates whether the section should be executed or not - explicit operator bool() const; + // This indicates whether the section should be executed or not + explicit operator bool() const; - private: - SectionInfo m_info; + private: + SectionInfo m_info; - std::string m_name; - Counts m_assertions; - bool m_sectionIncluded; - Timer m_timer; -}; + std::string m_name; + Counts m_assertions; + bool m_sectionIncluded; + Timer m_timer; + }; } // end namespace Catch @@ -2959,45 +3097,60 @@ class Section : NonCopyable { namespace Catch { -class TestCase; -struct ITestCaseRegistry; -struct IExceptionTranslatorRegistry; -struct IExceptionTranslator; -struct IReporterRegistry; -struct IReporterFactory; -struct ITagAliasRegistry; -struct IMutableEnumValuesRegistry; + class TestCase; -class StartupExceptionRegistry; + struct ITestCaseRegistry; + struct IExceptionTranslatorRegistry; + struct IExceptionTranslator; + struct IReporterRegistry; + struct IReporterFactory; + struct ITagAliasRegistry; + struct IMutableEnumValuesRegistry; -using IReporterFactoryPtr = std::shared_ptr; + class StartupExceptionRegistry; -struct IRegistryHub { - virtual ~IRegistryHub(); + using IReporterFactoryPtr = std::shared_ptr; - virtual IReporterRegistry const &getReporterRegistry() const = 0; - virtual ITestCaseRegistry const &getTestCaseRegistry() const = 0; - virtual ITagAliasRegistry const &getTagAliasRegistry() const = 0; - virtual IExceptionTranslatorRegistry const &getExceptionTranslatorRegistry() const = 0; + struct IRegistryHub { + virtual ~IRegistryHub(); - virtual StartupExceptionRegistry const &getStartupExceptionRegistry() const = 0; -}; + virtual IReporterRegistry const &getReporterRegistry() const = 0; -struct IMutableRegistryHub { - virtual ~IMutableRegistryHub(); - virtual void registerReporter(std::string const &name, IReporterFactoryPtr const &factory) = 0; - virtual void registerListener(IReporterFactoryPtr const &factory) = 0; - virtual void registerTest(TestCase const &testInfo) = 0; - virtual void registerTranslator(const IExceptionTranslator *translator) = 0; - virtual void registerTagAlias(std::string const &alias, std::string const &tag, SourceLineInfo const &lineInfo) = 0; - virtual void registerStartupException() noexcept = 0; - virtual IMutableEnumValuesRegistry &getMutableEnumValuesRegistry() = 0; -}; + virtual ITestCaseRegistry const &getTestCaseRegistry() const = 0; + + virtual ITagAliasRegistry const &getTagAliasRegistry() const = 0; + + virtual IExceptionTranslatorRegistry const &getExceptionTranslatorRegistry() const = 0; -IRegistryHub const &getRegistryHub(); -IMutableRegistryHub &getMutableRegistryHub(); -void cleanUp(); -std::string translateActiveException(); + virtual StartupExceptionRegistry const &getStartupExceptionRegistry() const = 0; + }; + + struct IMutableRegistryHub { + virtual ~IMutableRegistryHub(); + + virtual void registerReporter(std::string const &name, IReporterFactoryPtr const &factory) = 0; + + virtual void registerListener(IReporterFactoryPtr const &factory) = 0; + + virtual void registerTest(TestCase const &testInfo) = 0; + + virtual void registerTranslator(const IExceptionTranslator *translator) = 0; + + virtual void + registerTagAlias(std::string const &alias, std::string const &tag, SourceLineInfo const &lineInfo) = 0; + + virtual void registerStartupException() noexcept = 0; + + virtual IMutableEnumValuesRegistry &getMutableEnumValuesRegistry() = 0; + }; + + IRegistryHub const &getRegistryHub(); + + IMutableRegistryHub &getMutableRegistryHub(); + + void cleanUp(); + + std::string translateActiveException(); } @@ -3012,59 +3165,60 @@ std::string translateActiveException(); #include namespace Catch { -using exceptionTranslateFunction = std::string(*)(); + using exceptionTranslateFunction = std::string(*)(); -struct IExceptionTranslator; -using ExceptionTranslators = std::vector>; + struct IExceptionTranslator; + using ExceptionTranslators = std::vector>; -struct IExceptionTranslator { - virtual ~IExceptionTranslator(); - virtual std::string translate(ExceptionTranslators::const_iterator it, - ExceptionTranslators::const_iterator itEnd) const = 0; -}; + struct IExceptionTranslator { + virtual ~IExceptionTranslator(); + + virtual std::string translate(ExceptionTranslators::const_iterator it, + ExceptionTranslators::const_iterator itEnd) const = 0; + }; -struct IExceptionTranslatorRegistry { - virtual ~IExceptionTranslatorRegistry(); + struct IExceptionTranslatorRegistry { + virtual ~IExceptionTranslatorRegistry(); - virtual std::string translateActiveException() const = 0; -}; + virtual std::string translateActiveException() const = 0; + }; -class ExceptionTranslatorRegistrar { - template - class ExceptionTranslator : public IExceptionTranslator { - public: + class ExceptionTranslatorRegistrar { + template + class ExceptionTranslator : public IExceptionTranslator { + public: - ExceptionTranslator(std::string(*translateFunction)(T &)) - : m_translateFunction(translateFunction) {} + ExceptionTranslator(std::string(*translateFunction)(T &)) + : m_translateFunction(translateFunction) {} - std::string translate(ExceptionTranslators::const_iterator it, - ExceptionTranslators::const_iterator itEnd) const override { + std::string translate(ExceptionTranslators::const_iterator it, + ExceptionTranslators::const_iterator itEnd) const override { #if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) - return ""; + return ""; #else - try { - if (it == itEnd) - std::rethrow_exception(std::current_exception()); - else - return (*it)->translate(it + 1, itEnd); - } - catch (T &ex) { - return m_translateFunction(ex); - } + try { + if (it == itEnd) + std::rethrow_exception(std::current_exception()); + else + return (*it)->translate(it + 1, itEnd); + } + catch (T &ex) { + return m_translateFunction(ex); + } #endif - } + } - protected: - std::string (*m_translateFunction)(T &); - }; + protected: + std::string (*m_translateFunction)(T &); + }; - public: - template - ExceptionTranslatorRegistrar(std::string(*translateFunction)(T &)) { - getMutableRegistryHub().registerTranslator - (new ExceptionTranslator(translateFunction)); - } -}; + public: + template + ExceptionTranslatorRegistrar(std::string(*translateFunction)(T &)) { + getMutableRegistryHub().registerTranslator + (new ExceptionTranslator(translateFunction)); + } + }; } /////////////////////////////////////////////////////////////////////////////// @@ -3084,117 +3238,120 @@ class ExceptionTranslatorRegistrar { #include namespace Catch { -namespace Detail { - -class Approx { - private: - bool equalityComparisonImpl(double other) const; - // Validates the new margin (margin >= 0) - // out-of-line to avoid including stdexcept in the header - void setMargin(double margin); - // Validates the new epsilon (0 < epsilon < 1) - // out-of-line to avoid including stdexcept in the header - void setEpsilon(double epsilon); - - public: - explicit Approx(double value); - - static Approx custom(); - - Approx operator-() const; - - template::value>::type> - Approx operator()(T const &value) const { - Approx approx(static_cast(value)); - approx.m_epsilon = m_epsilon; - approx.m_margin = m_margin; - approx.m_scale = m_scale; - return approx; - } - - template::value>::type> - explicit Approx(T const &value): Approx(static_cast(value)) {} - - template::value>::type> - friend bool operator==(const T &lhs, Approx const &rhs) { - auto lhs_v = static_cast(lhs); - return rhs.equalityComparisonImpl(lhs_v); - } - - template::value>::type> - friend bool operator==(Approx const &lhs, const T &rhs) { - return operator==(rhs, lhs); - } - - template::value>::type> - friend bool operator!=(T const &lhs, Approx const &rhs) { - return !operator==(lhs, rhs); - } - - template::value>::type> - friend bool operator!=(Approx const &lhs, T const &rhs) { - return !operator==(rhs, lhs); - } - - template::value>::type> - friend bool operator<=(T const &lhs, Approx const &rhs) { - return static_cast(lhs) < rhs.m_value || lhs == rhs; - } - - template::value>::type> - friend bool operator<=(Approx const &lhs, T const &rhs) { - return lhs.m_value < static_cast(rhs) || lhs == rhs; - } - - template::value>::type> - friend bool operator>=(T const &lhs, Approx const &rhs) { - return static_cast(lhs) > rhs.m_value || lhs == rhs; - } - - template::value>::type> - friend bool operator>=(Approx const &lhs, T const &rhs) { - return lhs.m_value > static_cast(rhs) || lhs == rhs; - } - - template::value>::type> - Approx &epsilon(T const &newEpsilon) { - double epsilonAsDouble = static_cast(newEpsilon); - setEpsilon(epsilonAsDouble); - return *this; - } - - template::value>::type> - Approx &margin(T const &newMargin) { - double marginAsDouble = static_cast(newMargin); - setMargin(marginAsDouble); - return *this; - } - - template::value>::type> - Approx &scale(T const &newScale) { - m_scale = static_cast(newScale); - return *this; - } - - std::string toString() const; - - private: - double m_epsilon; - double m_margin; - double m_scale; - double m_value; -}; -} // end namespace Detail + namespace Detail { + + class Approx { + private: + bool equalityComparisonImpl(double other) const; -namespace literals { -Detail::Approx operator "" _a(long double val); -Detail::Approx operator "" _a(unsigned long long val); -} // end namespace literals + // Validates the new margin (margin >= 0) + // out-of-line to avoid including stdexcept in the header + void setMargin(double margin); -template<> -struct StringMaker { - static std::string convert(Catch::Detail::Approx const &value); -}; + // Validates the new epsilon (0 < epsilon < 1) + // out-of-line to avoid including stdexcept in the header + void setEpsilon(double epsilon); + + public: + explicit Approx(double value); + + static Approx custom(); + + Approx operator-() const; + + template::value>::type> + Approx operator()(T const &value) const { + Approx approx(static_cast(value)); + approx.m_epsilon = m_epsilon; + approx.m_margin = m_margin; + approx.m_scale = m_scale; + return approx; + } + + template::value>::type> + explicit Approx(T const &value): Approx(static_cast(value)) {} + + template::value>::type> + friend bool operator==(const T &lhs, Approx const &rhs) { + auto lhs_v = static_cast(lhs); + return rhs.equalityComparisonImpl(lhs_v); + } + + template::value>::type> + friend bool operator==(Approx const &lhs, const T &rhs) { + return operator==(rhs, lhs); + } + + template::value>::type> + friend bool operator!=(T const &lhs, Approx const &rhs) { + return !operator==(lhs, rhs); + } + + template::value>::type> + friend bool operator!=(Approx const &lhs, T const &rhs) { + return !operator==(rhs, lhs); + } + + template::value>::type> + friend bool operator<=(T const &lhs, Approx const &rhs) { + return static_cast(lhs) < rhs.m_value || lhs == rhs; + } + + template::value>::type> + friend bool operator<=(Approx const &lhs, T const &rhs) { + return lhs.m_value < static_cast(rhs) || lhs == rhs; + } + + template::value>::type> + friend bool operator>=(T const &lhs, Approx const &rhs) { + return static_cast(lhs) > rhs.m_value || lhs == rhs; + } + + template::value>::type> + friend bool operator>=(Approx const &lhs, T const &rhs) { + return lhs.m_value > static_cast(rhs) || lhs == rhs; + } + + template::value>::type> + Approx &epsilon(T const &newEpsilon) { + double epsilonAsDouble = static_cast(newEpsilon); + setEpsilon(epsilonAsDouble); + return *this; + } + + template::value>::type> + Approx &margin(T const &newMargin) { + double marginAsDouble = static_cast(newMargin); + setMargin(marginAsDouble); + return *this; + } + + template::value>::type> + Approx &scale(T const &newScale) { + m_scale = static_cast(newScale); + return *this; + } + + std::string toString() const; + + private: + double m_epsilon; + double m_margin; + double m_scale; + double m_value; + }; + } // end namespace Detail + + namespace literals { + Detail::Approx operator "" _a(long double val); + + Detail::Approx operator "" _a(unsigned long long val); + } // end namespace literals + + template<> + struct StringMaker { + static std::string convert(Catch::Detail::Approx const &value); + }; } // end namespace Catch @@ -3207,30 +3364,39 @@ struct StringMaker { namespace Catch { -bool startsWith(std::string const &s, std::string const &prefix); -bool startsWith(std::string const &s, char prefix); -bool endsWith(std::string const &s, std::string const &suffix); -bool endsWith(std::string const &s, char suffix); -bool contains(std::string const &s, std::string const &infix); -void toLowerInPlace(std::string &s); -std::string toLower(std::string const &s); + bool startsWith(std::string const &s, std::string const &prefix); + + bool startsWith(std::string const &s, char prefix); + + bool endsWith(std::string const &s, std::string const &suffix); + + bool endsWith(std::string const &s, char suffix); + + bool contains(std::string const &s, std::string const &infix); + + void toLowerInPlace(std::string &s); + + std::string toLower(std::string const &s); + //! Returns a new string without whitespace at the start/end -std::string trim(std::string const &str); + std::string trim(std::string const &str); + //! Returns a substring of the original ref without whitespace. Beware lifetimes! -StringRef trim(StringRef ref); + StringRef trim(StringRef ref); // !!! Be aware, returns refs into original string - make sure original string outlives them -std::vector splitStringRef(StringRef str, char delimiter); -bool replaceInPlace(std::string &str, std::string const &replaceThis, std::string const &withThis); + std::vector splitStringRef(StringRef str, char delimiter); -struct pluralise { - pluralise(std::size_t count, std::string const &label); + bool replaceInPlace(std::string &str, std::string const &replaceThis, std::string const &withThis); - friend std::ostream &operator<<(std::ostream &os, pluralise const &pluraliser); + struct pluralise { + pluralise(std::size_t count, std::string const &label); - std::size_t m_count; - std::string m_label; -}; + friend std::ostream &operator<<(std::ostream &os, pluralise const &pluraliser); + + std::size_t m_count; + std::string m_label; + }; } // end catch_string_manip.h @@ -3243,41 +3409,46 @@ struct pluralise { #include namespace Catch { -namespace Matchers { -namespace Impl { - -template -struct MatchAllOf; -template -struct MatchAnyOf; -template -struct MatchNotOf; - -class MatcherUntypedBase { - public: - MatcherUntypedBase() = default; - MatcherUntypedBase(MatcherUntypedBase const &) = default; - MatcherUntypedBase &operator=(MatcherUntypedBase const &) = delete; - std::string toString() const; - - protected: - virtual ~MatcherUntypedBase(); - virtual std::string describe() const = 0; - mutable std::string m_cachedToString; -}; + namespace Matchers { + namespace Impl { + + template + struct MatchAllOf; + template + struct MatchAnyOf; + template + struct MatchNotOf; + + class MatcherUntypedBase { + public: + MatcherUntypedBase() = default; + + MatcherUntypedBase(MatcherUntypedBase const &) = default; + + MatcherUntypedBase &operator=(MatcherUntypedBase const &) = delete; + + std::string toString() const; + + protected: + virtual ~MatcherUntypedBase(); + + virtual std::string describe() const = 0; + + mutable std::string m_cachedToString; + }; #ifdef __clang__ - # pragma clang diagnostic push + # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wnon-virtual-dtor" #endif -template -struct MatcherMethod { - virtual bool match(ObjectT const &arg) const = 0; -}; + template + struct MatcherMethod { + virtual bool match(ObjectT const &arg) const = 0; + }; #if defined(__OBJC__) - // Hack to fix Catch GH issue #1661. Could use id for generic Object support. + // Hack to fix Catch GH issue #1661. Could use id for generic Object support. // use of const for Object pointers is very uncommon and under ARC it causes some kind of signature mismatch that breaks compilation template<> struct MatcherMethod { @@ -3289,173 +3460,187 @@ struct MatcherMethod { # pragma clang diagnostic pop #endif -template -struct MatcherBase : MatcherUntypedBase, MatcherMethod { + template + struct MatcherBase : MatcherUntypedBase, MatcherMethod { - MatchAllOf operator&&(MatcherBase const &other) const; - MatchAnyOf operator||(MatcherBase const &other) const; - MatchNotOf operator!() const; -}; + MatchAllOf operator&&(MatcherBase const &other) const; -template -struct MatchAllOf : MatcherBase { - bool match(ArgT const &arg) const override { - for (auto matcher : m_matchers) { - if (!matcher->match(arg)) - return false; - } - return true; - } - std::string describe() const override { - std::string description; - description.reserve(4 + m_matchers.size() * 32); - description += "( "; - bool first = true; - for (auto matcher : m_matchers) { - if (first) - first = false; - else - description += " and "; - description += matcher->toString(); - } - description += " )"; - return description; - } - - MatchAllOf operator&&(MatcherBase const &other) { - auto copy(*this); - copy.m_matchers.push_back(&other); - return copy; - } - - std::vector const *> m_matchers; -}; -template -struct MatchAnyOf : MatcherBase { + MatchAnyOf operator||(MatcherBase const &other) const; - bool match(ArgT const &arg) const override { - for (auto matcher : m_matchers) { - if (matcher->match(arg)) - return true; - } - return false; - } - std::string describe() const override { - std::string description; - description.reserve(4 + m_matchers.size() * 32); - description += "( "; - bool first = true; - for (auto matcher : m_matchers) { - if (first) - first = false; - else - description += " or "; - description += matcher->toString(); - } - description += " )"; - return description; - } - - MatchAnyOf operator||(MatcherBase const &other) { - auto copy(*this); - copy.m_matchers.push_back(&other); - return copy; - } - - std::vector const *> m_matchers; -}; + MatchNotOf operator!() const; + }; -template -struct MatchNotOf : MatcherBase { + template + struct MatchAllOf : MatcherBase { + bool match(ArgT const &arg) const override { + for (auto matcher: m_matchers) { + if (!matcher->match(arg)) + return false; + } + return true; + } - MatchNotOf(MatcherBase const &underlyingMatcher) : m_underlyingMatcher(underlyingMatcher) {} + std::string describe() const override { + std::string description; + description.reserve(4 + m_matchers.size() * 32); + description += "( "; + bool first = true; + for (auto matcher: m_matchers) { + if (first) + first = false; + else + description += " and "; + description += matcher->toString(); + } + description += " )"; + return description; + } - bool match(ArgT const &arg) const override { - return !m_underlyingMatcher.match(arg); - } + MatchAllOf operator&&(MatcherBase const &other) { + auto copy(*this); + copy.m_matchers.push_back(&other); + return copy; + } - std::string describe() const override { - return "not " + m_underlyingMatcher.toString(); - } - MatcherBase const &m_underlyingMatcher; -}; + std::vector const *> m_matchers; + }; -template -MatchAllOf MatcherBase::operator&&(MatcherBase const &other) const { - return MatchAllOf() && *this && other; -} -template -MatchAnyOf MatcherBase::operator||(MatcherBase const &other) const { - return MatchAnyOf() || *this || other; -} -template -MatchNotOf MatcherBase::operator!() const { - return MatchNotOf(*this); -} + template + struct MatchAnyOf : MatcherBase { -} // namespace Impl + bool match(ArgT const &arg) const override { + for (auto matcher: m_matchers) { + if (matcher->match(arg)) + return true; + } + return false; + } -} // namespace Matchers + std::string describe() const override { + std::string description; + description.reserve(4 + m_matchers.size() * 32); + description += "( "; + bool first = true; + for (auto matcher: m_matchers) { + if (first) + first = false; + else + description += " or "; + description += matcher->toString(); + } + description += " )"; + return description; + } -using namespace Matchers; -using Matchers::Impl::MatcherBase; + MatchAnyOf operator||(MatcherBase const &other) { + auto copy(*this); + copy.m_matchers.push_back(&other); + return copy; + } -} // namespace Catch + std::vector const *> m_matchers; + }; -// end catch_matchers.h -// start catch_matchers_exception.hpp + template + struct MatchNotOf : MatcherBase { -namespace Catch { -namespace Matchers { -namespace Exception { + MatchNotOf(MatcherBase const &underlyingMatcher) : m_underlyingMatcher(underlyingMatcher) {} -class ExceptionMessageMatcher : public MatcherBase { - std::string m_message; - public: + bool match(ArgT const &arg) const override { + return !m_underlyingMatcher.match(arg); + } - ExceptionMessageMatcher(std::string const &message) : - m_message(message) {} + std::string describe() const override { + return "not " + m_underlyingMatcher.toString(); + } - bool match(std::exception const &ex) const override; + MatcherBase const &m_underlyingMatcher; + }; - std::string describe() const override; -}; + template + MatchAllOf MatcherBase::operator&&(MatcherBase const &other) const { + return MatchAllOf() && *this && other; + } -} // namespace Exception + template + MatchAnyOf MatcherBase::operator||(MatcherBase const &other) const { + return MatchAnyOf() || *this || other; + } -Exception::ExceptionMessageMatcher Message(std::string const &message); + template + MatchNotOf MatcherBase::operator!() const { + return MatchNotOf(*this); + } + + } // namespace Impl + + } // namespace Matchers + + using namespace Matchers; + using Matchers::Impl::MatcherBase; + +} // namespace Catch + +// end catch_matchers.h +// start catch_matchers_exception.hpp + +namespace Catch { + namespace Matchers { + namespace Exception { + + class ExceptionMessageMatcher : public MatcherBase { + std::string m_message; + public: + + ExceptionMessageMatcher(std::string const &message) : + m_message(message) {} + + bool match(std::exception const &ex) const override; + + std::string describe() const override; + }; + + } // namespace Exception -} // namespace Matchers + Exception::ExceptionMessageMatcher Message(std::string const &message); + + } // namespace Matchers } // namespace Catch // end catch_matchers_exception.hpp // start catch_matchers_floating.h namespace Catch { -namespace Matchers { + namespace Matchers { -namespace Floating { + namespace Floating { -enum class FloatingPointKind : uint8_t; + enum class FloatingPointKind : uint8_t; -struct WithinAbsMatcher : MatcherBase { - WithinAbsMatcher(double target, double margin); - bool match(double const &matchee) const override; - std::string describe() const override; - private: - double m_target; - double m_margin; -}; + struct WithinAbsMatcher : MatcherBase { + WithinAbsMatcher(double target, double margin); -struct WithinUlpsMatcher : MatcherBase { - WithinUlpsMatcher(double target, uint64_t ulps, FloatingPointKind baseType); - bool match(double const &matchee) const override; - std::string describe() const override; - private: - double m_target; - uint64_t m_ulps; - FloatingPointKind m_type; -}; + bool match(double const &matchee) const override; + + std::string describe() const override; + + private: + double m_target; + double m_margin; + }; + + struct WithinUlpsMatcher : MatcherBase { + WithinUlpsMatcher(double target, uint64_t ulps, FloatingPointKind baseType); + + bool match(double const &matchee) const override; + + std::string describe() const override; + + private: + double m_target; + uint64_t m_ulps; + FloatingPointKind m_type; + }; // Given IEEE-754 format for floats and doubles, we can assume // that float -> double promotion is lossless. Given this, we can @@ -3463,30 +3648,39 @@ struct WithinUlpsMatcher : MatcherBase { // |lhs - rhs| <= epsilon * max(fabs(lhs), fabs(rhs)), then we get // the same result if we do this for floats, as if we do this for // doubles that were promoted from floats. -struct WithinRelMatcher : MatcherBase { - WithinRelMatcher(double target, double epsilon); - bool match(double const &matchee) const override; - std::string describe() const override; - private: - double m_target; - double m_epsilon; -}; + struct WithinRelMatcher : MatcherBase { + WithinRelMatcher(double target, double epsilon); + + bool match(double const &matchee) const override; + + std::string describe() const override; -} // namespace Floating + private: + double m_target; + double m_epsilon; + }; + + } // namespace Floating // The following functions create the actual matcher objects. // This allows the types to be inferred -Floating::WithinUlpsMatcher WithinULP(double target, uint64_t maxUlpDiff); -Floating::WithinUlpsMatcher WithinULP(float target, uint64_t maxUlpDiff); -Floating::WithinAbsMatcher WithinAbs(double target, double margin); -Floating::WithinRelMatcher WithinRel(double target, double eps); + Floating::WithinUlpsMatcher WithinULP(double target, uint64_t maxUlpDiff); + + Floating::WithinUlpsMatcher WithinULP(float target, uint64_t maxUlpDiff); + + Floating::WithinAbsMatcher WithinAbs(double target, double margin); + + Floating::WithinRelMatcher WithinRel(double target, double eps); + // defaults epsilon to 100*numeric_limits::epsilon() -Floating::WithinRelMatcher WithinRel(double target); -Floating::WithinRelMatcher WithinRel(float target, float eps); + Floating::WithinRelMatcher WithinRel(double target); + + Floating::WithinRelMatcher WithinRel(float target, float eps); + // defaults epsilon to 100*numeric_limits::epsilon() -Floating::WithinRelMatcher WithinRel(float target); + Floating::WithinRelMatcher WithinRel(float target); -} // namespace Matchers + } // namespace Matchers } // namespace Catch // end catch_matchers_floating.h @@ -3496,45 +3690,45 @@ Floating::WithinRelMatcher WithinRel(float target); #include namespace Catch { -namespace Matchers { -namespace Generic { + namespace Matchers { + namespace Generic { -namespace Detail { -std::string finalizeDescription(const std::string &desc); -} + namespace Detail { + std::string finalizeDescription(const std::string &desc); + } -template -class PredicateMatcher : public MatcherBase { - std::function m_predicate; - std::string m_description; - public: + template + class PredicateMatcher : public MatcherBase { + std::function m_predicate; + std::string m_description; + public: - PredicateMatcher(std::function const &elem, std::string const &descr) - : m_predicate(std::move(elem)), - m_description(Detail::finalizeDescription(descr)) {} + PredicateMatcher(std::function const &elem, std::string const &descr) + : m_predicate(std::move(elem)), + m_description(Detail::finalizeDescription(descr)) {} - bool match(T const &item) const override { - return m_predicate(item); - } + bool match(T const &item) const override { + return m_predicate(item); + } - std::string describe() const override { - return m_description; - } -}; + std::string describe() const override { + return m_description; + } + }; -} // namespace Generic + } // namespace Generic // The following functions create the actual matcher objects. // The user has to explicitly specify type to the function, because // inferring std::function is hard (but possible) and // requires a lot of TMP. -template -Generic::PredicateMatcher Predicate(std::function const &predicate, - std::string const &description = "") { - return Generic::PredicateMatcher(predicate, description); -} + template + Generic::PredicateMatcher Predicate(std::function const &predicate, + std::string const &description = "") { + return Generic::PredicateMatcher(predicate, description); + } -} // namespace Matchers + } // namespace Matchers } // namespace Catch // end catch_matchers_generic.hpp @@ -3543,67 +3737,87 @@ Generic::PredicateMatcher Predicate(std::function const &pre #include namespace Catch { -namespace Matchers { + namespace Matchers { -namespace StdString { + namespace StdString { -struct CasedString { - CasedString(std::string const &str, CaseSensitive::Choice caseSensitivity); - std::string adjustString(std::string const &str) const; - std::string caseSensitivitySuffix() const; + struct CasedString { + CasedString(std::string const &str, CaseSensitive::Choice caseSensitivity); - CaseSensitive::Choice m_caseSensitivity; - std::string m_str; -}; + std::string adjustString(std::string const &str) const; -struct StringMatcherBase : MatcherBase { - StringMatcherBase(std::string const &operation, CasedString const &comparator); - std::string describe() const override; + std::string caseSensitivitySuffix() const; - CasedString m_comparator; - std::string m_operation; -}; + CaseSensitive::Choice m_caseSensitivity; + std::string m_str; + }; -struct EqualsMatcher : StringMatcherBase { - EqualsMatcher(CasedString const &comparator); - bool match(std::string const &source) const override; -}; -struct ContainsMatcher : StringMatcherBase { - ContainsMatcher(CasedString const &comparator); - bool match(std::string const &source) const override; -}; -struct StartsWithMatcher : StringMatcherBase { - StartsWithMatcher(CasedString const &comparator); - bool match(std::string const &source) const override; -}; -struct EndsWithMatcher : StringMatcherBase { - EndsWithMatcher(CasedString const &comparator); - bool match(std::string const &source) const override; -}; + struct StringMatcherBase : MatcherBase { + StringMatcherBase(std::string const &operation, CasedString const &comparator); -struct RegexMatcher : MatcherBase { - RegexMatcher(std::string regex, CaseSensitive::Choice caseSensitivity); - bool match(std::string const &matchee) const override; - std::string describe() const override; + std::string describe() const override; - private: - std::string m_regex; - CaseSensitive::Choice m_caseSensitivity; -}; + CasedString m_comparator; + std::string m_operation; + }; + + struct EqualsMatcher : StringMatcherBase { + EqualsMatcher(CasedString const &comparator); + + bool match(std::string const &source) const override; + }; + + struct ContainsMatcher : StringMatcherBase { + ContainsMatcher(CasedString const &comparator); + + bool match(std::string const &source) const override; + }; + + struct StartsWithMatcher : StringMatcherBase { + StartsWithMatcher(CasedString const &comparator); -} // namespace StdString + bool match(std::string const &source) const override; + }; + + struct EndsWithMatcher : StringMatcherBase { + EndsWithMatcher(CasedString const &comparator); + + bool match(std::string const &source) const override; + }; + + struct RegexMatcher : MatcherBase { + RegexMatcher(std::string regex, CaseSensitive::Choice caseSensitivity); + + bool match(std::string const &matchee) const override; + + std::string describe() const override; + + private: + std::string m_regex; + CaseSensitive::Choice m_caseSensitivity; + }; + + } // namespace StdString // The following functions create the actual matcher objects. // This allows the types to be inferred -StdString::EqualsMatcher Equals(std::string const &str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes); -StdString::ContainsMatcher Contains(std::string const &str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes); -StdString::EndsWithMatcher EndsWith(std::string const &str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes); -StdString::StartsWithMatcher StartsWith(std::string const &str, - CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes); -StdString::RegexMatcher Matches(std::string const ®ex, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes); + StdString::EqualsMatcher + Equals(std::string const &str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes); + + StdString::ContainsMatcher + Contains(std::string const &str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes); -} // namespace Matchers + StdString::EndsWithMatcher + EndsWith(std::string const &str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes); + + StdString::StartsWithMatcher StartsWith(std::string const &str, + CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes); + + StdString::RegexMatcher + Matches(std::string const ®ex, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes); + + } // namespace Matchers } // namespace Catch // end catch_matchers_string.h @@ -3612,203 +3826,214 @@ StdString::RegexMatcher Matches(std::string const ®ex, CaseSensitive::Choice #include namespace Catch { -namespace Matchers { + namespace Matchers { -namespace Vector { -template -struct ContainsElementMatcher : MatcherBase> { + namespace Vector { + template + struct ContainsElementMatcher : MatcherBase> { - ContainsElementMatcher(T const &comparator) : m_comparator(comparator) {} + ContainsElementMatcher(T const &comparator) : m_comparator(comparator) {} - bool match(std::vector const &v) const override { - for (auto const &el : v) { - if (el == m_comparator) { - return true; - } - } - return false; - } + bool match(std::vector const &v) const override { + for (auto const &el: v) { + if (el == m_comparator) { + return true; + } + } + return false; + } - std::string describe() const override { - return "Contains: " + ::Catch::Detail::stringify(m_comparator); - } + std::string describe() const override { + return "Contains: " + ::Catch::Detail::stringify(m_comparator); + } - T const &m_comparator; -}; + T const &m_comparator; + }; -template -struct ContainsMatcher : MatcherBase> { + template + struct ContainsMatcher : MatcherBase> { + + ContainsMatcher(std::vector const &comparator) : m_comparator(comparator) {} + + bool match(std::vector const &v) const override { + // !TBD: see note in EqualsMatcher + if (m_comparator.size() > v.size()) + return false; + for (auto const &comparator: m_comparator) { + auto present = false; + for (const auto &el: v) { + if (el == comparator) { + present = true; + break; + } + } + if (!present) { + return false; + } + } + return true; + } - ContainsMatcher(std::vector const &comparator) : m_comparator(comparator) {} + std::string describe() const override { + return "Contains: " + ::Catch::Detail::stringify(m_comparator); + } - bool match(std::vector const &v) const override { - // !TBD: see note in EqualsMatcher - if (m_comparator.size() > v.size()) - return false; - for (auto const &comparator : m_comparator) { - auto present = false; - for (const auto &el : v) { - if (el == comparator) { - present = true; - break; - } - } - if (!present) { - return false; - } - } - return true; - } - std::string describe() const override { - return "Contains: " + ::Catch::Detail::stringify(m_comparator); - } + std::vector const &m_comparator; + }; - std::vector const &m_comparator; -}; + template + struct EqualsMatcher : MatcherBase> { + + EqualsMatcher(std::vector const &comparator) : m_comparator(comparator) {} + + bool match(std::vector const &v) const override { + // !TBD: This currently works if all elements can be compared using != + // - a more general approach would be via a compare template that defaults + // to using !=. but could be specialised for, e.g. std::vector etc + // - then just call that directly + if (m_comparator.size() != v.size()) + return false; + for (std::size_t i = 0; i < v.size(); ++i) + if (m_comparator[i] != v[i]) + return false; + return true; + } -template -struct EqualsMatcher : MatcherBase> { + std::string describe() const override { + return "Equals: " + ::Catch::Detail::stringify(m_comparator); + } - EqualsMatcher(std::vector const &comparator) : m_comparator(comparator) {} + std::vector const &m_comparator; + }; - bool match(std::vector const &v) const override { - // !TBD: This currently works if all elements can be compared using != - // - a more general approach would be via a compare template that defaults - // to using !=. but could be specialised for, e.g. std::vector etc - // - then just call that directly - if (m_comparator.size() != v.size()) - return false; - for (std::size_t i = 0; i < v.size(); ++i) - if (m_comparator[i] != v[i]) - return false; - return true; - } - std::string describe() const override { - return "Equals: " + ::Catch::Detail::stringify(m_comparator); - } - std::vector const &m_comparator; -}; + template + struct ApproxMatcher : MatcherBase> { -template -struct ApproxMatcher : MatcherBase> { + ApproxMatcher(std::vector const &comparator) : m_comparator(comparator) {} - ApproxMatcher(std::vector const &comparator) : m_comparator(comparator) {} + bool match(std::vector const &v) const override { + if (m_comparator.size() != v.size()) + return false; + for (std::size_t i = 0; i < v.size(); ++i) + if (m_comparator[i] != approx(v[i])) + return false; + return true; + } - bool match(std::vector const &v) const override { - if (m_comparator.size() != v.size()) - return false; - for (std::size_t i = 0; i < v.size(); ++i) - if (m_comparator[i] != approx(v[i])) - return false; - return true; - } - std::string describe() const override { - return "is approx: " + ::Catch::Detail::stringify(m_comparator); - } - template::value>::type> - ApproxMatcher &epsilon(T const &newEpsilon) { - approx.epsilon(newEpsilon); - return *this; - } - template::value>::type> - ApproxMatcher &margin(T const &newMargin) { - approx.margin(newMargin); - return *this; - } - template::value>::type> - ApproxMatcher &scale(T const &newScale) { - approx.scale(newScale); - return *this; - } - - std::vector const &m_comparator; - mutable Catch::Detail::Approx approx = Catch::Detail::Approx::custom(); -}; + std::string describe() const override { + return "is approx: " + ::Catch::Detail::stringify(m_comparator); + } -template -struct UnorderedEqualsMatcher : MatcherBase> { - UnorderedEqualsMatcher(std::vector const &target) : m_target(target) {} - bool match(std::vector const &vec) const override { - if (m_target.size() != vec.size()) { - return false; - } - return std::is_permutation(m_target.begin(), m_target.end(), vec.begin()); - } - - std::string describe() const override { - return "UnorderedEquals: " + ::Catch::Detail::stringify(m_target); - } - private: - std::vector const &m_target; -}; + template::value>::type> + ApproxMatcher &epsilon(T const &newEpsilon) { + approx.epsilon(newEpsilon); + return *this; + } + + template::value>::type> + ApproxMatcher &margin(T const &newMargin) { + approx.margin(newMargin); + return *this; + } + + template::value>::type> + ApproxMatcher &scale(T const &newScale) { + approx.scale(newScale); + return *this; + } + + std::vector const &m_comparator; + mutable Catch::Detail::Approx approx = Catch::Detail::Approx::custom(); + }; + + template + struct UnorderedEqualsMatcher : MatcherBase> { + UnorderedEqualsMatcher(std::vector const &target) : m_target(target) {} + + bool match(std::vector const &vec) const override { + if (m_target.size() != vec.size()) { + return false; + } + return std::is_permutation(m_target.begin(), m_target.end(), vec.begin()); + } -} // namespace Vector + std::string describe() const override { + return "UnorderedEquals: " + ::Catch::Detail::stringify(m_target); + } + + private: + std::vector const &m_target; + }; + + } // namespace Vector // The following functions create the actual matcher objects. // This allows the types to be inferred -template, typename AllocMatch = AllocComp> -Vector::ContainsMatcher Contains(std::vector const &comparator) { - return Vector::ContainsMatcher(comparator); -} + template, typename AllocMatch = AllocComp> + Vector::ContainsMatcher Contains(std::vector const &comparator) { + return Vector::ContainsMatcher(comparator); + } -template> -Vector::ContainsElementMatcher VectorContains(T const &comparator) { - return Vector::ContainsElementMatcher(comparator); -} + template> + Vector::ContainsElementMatcher VectorContains(T const &comparator) { + return Vector::ContainsElementMatcher(comparator); + } -template, typename AllocMatch = AllocComp> -Vector::EqualsMatcher Equals(std::vector const &comparator) { - return Vector::EqualsMatcher(comparator); -} + template, typename AllocMatch = AllocComp> + Vector::EqualsMatcher Equals(std::vector const &comparator) { + return Vector::EqualsMatcher(comparator); + } -template, typename AllocMatch = AllocComp> -Vector::ApproxMatcher Approx(std::vector const &comparator) { - return Vector::ApproxMatcher(comparator); -} + template, typename AllocMatch = AllocComp> + Vector::ApproxMatcher Approx(std::vector const &comparator) { + return Vector::ApproxMatcher(comparator); + } -template, typename AllocMatch = AllocComp> -Vector::UnorderedEqualsMatcher UnorderedEquals(std::vector const &target) { - return Vector::UnorderedEqualsMatcher(target); -} + template, typename AllocMatch = AllocComp> + Vector::UnorderedEqualsMatcher + UnorderedEquals(std::vector const &target) { + return Vector::UnorderedEqualsMatcher(target); + } -} // namespace Matchers + } // namespace Matchers } // namespace Catch // end catch_matchers_vector.h namespace Catch { -template -class MatchExpr : public ITransientExpression { - ArgT const &m_arg; - MatcherT m_matcher; - StringRef m_matcherString; - public: - MatchExpr(ArgT const &arg, MatcherT const &matcher, StringRef const &matcherString) - : ITransientExpression{true, matcher.match(arg)}, - m_arg(arg), - m_matcher(matcher), - m_matcherString(matcherString) {} - - void streamReconstructedExpression(std::ostream &os) const override { - auto matcherAsString = m_matcher.toString(); - os << Catch::Detail::stringify(m_arg) << ' '; - if (matcherAsString == Detail::unprintableString) - os << m_matcherString; - else - os << matcherAsString; - } -}; + template + class MatchExpr : public ITransientExpression { + ArgT const &m_arg; + MatcherT m_matcher; + StringRef m_matcherString; + public: + MatchExpr(ArgT const &arg, MatcherT const &matcher, StringRef const &matcherString) + : ITransientExpression{true, matcher.match(arg)}, + m_arg(arg), + m_matcher(matcher), + m_matcherString(matcherString) {} + + void streamReconstructedExpression(std::ostream &os) const override { + auto matcherAsString = m_matcher.toString(); + os << Catch::Detail::stringify(m_arg) << ' '; + if (matcherAsString == Detail::unprintableString) + os << m_matcherString; + else + os << matcherAsString; + } + }; -using StringMatcher = Matchers::Impl::MatcherBase; + using StringMatcher = Matchers::Impl::MatcherBase; -void handleExceptionMatchExpr(AssertionHandler &handler, StringMatcher const &matcher, StringRef const &matcherString); + void + handleExceptionMatchExpr(AssertionHandler &handler, StringMatcher const &matcher, StringRef const &matcherString); -template -auto makeMatchExpr(ArgT const &arg, MatcherT const &matcher, StringRef const &matcherString) -> MatchExpr { - return MatchExpr(arg, matcher, matcherString); -} + template + auto makeMatchExpr(ArgT const &arg, MatcherT const &matcher, StringRef const &matcherString) -> MatchExpr { + return MatchExpr(arg, matcher, matcherString); + } } // namespace Catch @@ -3853,27 +4078,33 @@ auto makeMatchExpr(ArgT const &arg, MatcherT const &matcher, StringRef const &ma namespace Catch { -namespace Generators { -class GeneratorUntypedBase { - public: - GeneratorUntypedBase() = default; - virtual ~GeneratorUntypedBase(); - // Attempts to move the generator to the next element - // - // Returns true iff the move succeeded (and a valid element - // can be retrieved). - virtual bool next() = 0; -}; -using GeneratorBasePtr = std::unique_ptr; + namespace Generators { + class GeneratorUntypedBase { + public: + GeneratorUntypedBase() = default; -} // namespace Generators + virtual ~GeneratorUntypedBase(); -struct IGeneratorTracker { - virtual ~IGeneratorTracker(); - virtual auto hasGenerator() const -> bool = 0; - virtual auto getGenerator() const -> Generators::GeneratorBasePtr const & = 0; - virtual void setGenerator(Generators::GeneratorBasePtr &&generator) = 0; -}; + // Attempts to move the generator to the next element + // + // Returns true iff the move succeeded (and a valid element + // can be retrieved). + virtual bool next() = 0; + }; + + using GeneratorBasePtr = std::unique_ptr; + + } // namespace Generators + + struct IGeneratorTracker { + virtual ~IGeneratorTracker(); + + virtual auto hasGenerator() const -> bool = 0; + + virtual auto getGenerator() const -> Generators::GeneratorBasePtr const & = 0; + + virtual void setGenerator(Generators::GeneratorBasePtr &&generator) = 0; + }; } // namespace Catch @@ -3884,22 +4115,26 @@ struct IGeneratorTracker { namespace Catch { #if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) -template -[[noreturn]] -void throw_exception(Ex const &e) { - throw e; -} + + template + [[noreturn]] + void throw_exception(Ex const &e) { + throw e; + } + #else // ^^ Exceptions are enabled // Exceptions are disabled vv - [[noreturn]] + [[noreturn]] void throw_exception(std::exception const& e); #endif -[[noreturn]] -void throw_logic_error(std::string const &msg); -[[noreturn]] -void throw_domain_error(std::string const &msg); -[[noreturn]] -void throw_runtime_error(std::string const &msg); + [[noreturn]] + void throw_logic_error(std::string const &msg); + + [[noreturn]] + void throw_domain_error(std::string const &msg); + + [[noreturn]] + void throw_runtime_error(std::string const &msg); } // namespace Catch; @@ -3928,423 +4163,444 @@ void throw_runtime_error(std::string const &msg); namespace Catch { -class GeneratorException : public std::exception { - const char *const m_msg = ""; + class GeneratorException : public std::exception { + const char *const m_msg = ""; - public: - GeneratorException(const char *msg) : - m_msg(msg) {} + public: + GeneratorException(const char *msg) : + m_msg(msg) {} - const char *what() const noexcept override final; -}; + const char *what() const noexcept override final; + }; -namespace Generators { + namespace Generators { // !TBD move this into its own location? -namespace pf { -template -std::unique_ptr make_unique(Args &&... args) { - return std::unique_ptr(new T(std::forward(args)...)); -} -} + namespace pf { + template + std::unique_ptr make_unique(Args &&... args) { + return std::unique_ptr(new T(std::forward(args)...)); + } + } -template -struct IGenerator : GeneratorUntypedBase { - virtual ~IGenerator() = default; + template + struct IGenerator : GeneratorUntypedBase { + virtual ~IGenerator() = default; - // Returns the current element of the generator - // - // \Precondition The generator is either freshly constructed, - // or the last call to `next()` returned true - virtual T const &get() const = 0; - using type = T; -}; + // Returns the current element of the generator + // + // \Precondition The generator is either freshly constructed, + // or the last call to `next()` returned true + virtual T const &get() const = 0; -template -class SingleValueGenerator final : public IGenerator { - T m_value; - public: - SingleValueGenerator(T &&value) : m_value(std::move(value)) {} - - T const &get() const override { - return m_value; - } - bool next() override { - return false; - } -}; + using type = T; + }; -template -class FixedValuesGenerator final : public IGenerator { - static_assert(!std::is_same::value, - "FixedValuesGenerator does not support bools because of std::vector" - "specialization, use SingleValue Generator instead."); - std::vector m_values; - size_t m_idx = 0; - public: - FixedValuesGenerator(std::initializer_list values) : m_values(values) {} - - T const &get() const override { - return m_values[m_idx]; - } - bool next() override { - ++m_idx; - return m_idx < m_values.size(); - } -}; + template + class SingleValueGenerator final : public IGenerator { + T m_value; + public: + SingleValueGenerator(T &&value) : m_value(std::move(value)) {} -template -class GeneratorWrapper final { - std::unique_ptr> m_generator; - public: - GeneratorWrapper(std::unique_ptr> generator) : - m_generator(std::move(generator)) {} - T const &get() const { - return m_generator->get(); - } - bool next() { - return m_generator->next(); - } -}; + T const &get() const override { + return m_value; + } -template -GeneratorWrapper value(T &&value) { - return GeneratorWrapper(pf::make_unique>(std::forward(value))); -} -template -GeneratorWrapper values(std::initializer_list values) { - return GeneratorWrapper(pf::make_unique>(values)); -} + bool next() override { + return false; + } + }; -template -class Generators : public IGenerator { - std::vector> m_generators; - size_t m_current = 0; - - void populate(GeneratorWrapper &&generator) { - m_generators.emplace_back(std::move(generator)); - } - void populate(T &&val) { - m_generators.emplace_back(value(std::forward(val))); - } - template - void populate(U &&val) { - populate(T(std::forward(val))); - } - template - void populate(U &&valueOrGenerator, Gs &&... moreGenerators) { - populate(std::forward(valueOrGenerator)); - populate(std::forward(moreGenerators)...); - } - - public: - template - Generators(Gs &&... moreGenerators) { - m_generators.reserve(sizeof...(Gs)); - populate(std::forward(moreGenerators)...); - } - - T const &get() const override { - return m_generators[m_current].get(); - } - - bool next() override { - if (m_current >= m_generators.size()) { - return false; - } - const bool current_status = m_generators[m_current].next(); - if (!current_status) { - ++m_current; - } - return m_current < m_generators.size(); - } -}; + template + class FixedValuesGenerator final : public IGenerator { + static_assert(!std::is_same::value, + "FixedValuesGenerator does not support bools because of std::vector" + "specialization, use SingleValue Generator instead."); + std::vector m_values; + size_t m_idx = 0; + public: + FixedValuesGenerator(std::initializer_list values) : m_values(values) {} -template -GeneratorWrapper> table(std::initializer_list::type...>> tuples) { - return values>(tuples); -} + T const &get() const override { + return m_values[m_idx]; + } -// Tag type to signal that a generator sequence should convert arguments to a specific type -template -struct as {}; + bool next() override { + ++m_idx; + return m_idx < m_values.size(); + } + }; -template -auto makeGenerators(GeneratorWrapper &&generator, Gs &&... moreGenerators) -> Generators { - return Generators(std::move(generator), std::forward(moreGenerators)...); -} -template -auto makeGenerators(GeneratorWrapper &&generator) -> Generators { - return Generators(std::move(generator)); -} -template -auto makeGenerators(T &&val, Gs &&... moreGenerators) -> Generators { - return makeGenerators(value(std::forward(val)), std::forward(moreGenerators)...); -} -template -auto makeGenerators(as, U &&val, Gs &&... moreGenerators) -> Generators { - return makeGenerators(value(T(std::forward(val))), std::forward(moreGenerators)...); -} + template + class GeneratorWrapper final { + std::unique_ptr> m_generator; + public: + GeneratorWrapper(std::unique_ptr> generator) : + m_generator(std::move(generator)) {} -auto acquireGeneratorTracker(StringRef generatorName, SourceLineInfo const &lineInfo) -> IGeneratorTracker &; + T const &get() const { + return m_generator->get(); + } -template -// Note: The type after -> is weird, because VS2015 cannot parse -// the expression used in the typedef inside, when it is in -// return type. Yeah. -auto generate(StringRef generatorName, - SourceLineInfo const &lineInfo, - L const &generatorExpression) -> decltype(std::declval().get()) { - using UnderlyingType = typename decltype(generatorExpression())::type; - - IGeneratorTracker &tracker = acquireGeneratorTracker(generatorName, lineInfo); - if (!tracker.hasGenerator()) { - tracker.setGenerator(pf::make_unique>(generatorExpression())); - } - - auto const &generator = static_cast const &>( *tracker.getGenerator()); - return generator.get(); -} + bool next() { + return m_generator->next(); + } + }; -} // namespace Generators -} // namespace Catch + template + GeneratorWrapper value(T &&value) { + return GeneratorWrapper(pf::make_unique>(std::forward(value))); + } -#define GENERATE(...) \ - Catch::Generators::generate( INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \ - CATCH_INTERNAL_LINEINFO, \ - [ ]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace) -#define GENERATE_COPY(...) \ - Catch::Generators::generate( INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \ - CATCH_INTERNAL_LINEINFO, \ - [=]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace) -#define GENERATE_REF(...) \ - Catch::Generators::generate( INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \ - CATCH_INTERNAL_LINEINFO, \ - [&]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace) + template + GeneratorWrapper values(std::initializer_list values) { + return GeneratorWrapper(pf::make_unique>(values)); + } -// end catch_generators.hpp -// start catch_generators_generic.hpp + template + class Generators : public IGenerator { + std::vector> m_generators; + size_t m_current = 0; -namespace Catch { -namespace Generators { - -template -class TakeGenerator : public IGenerator { - GeneratorWrapper m_generator; - size_t m_returned = 0; - size_t m_target; - public: - TakeGenerator(size_t target, GeneratorWrapper &&generator) : - m_generator(std::move(generator)), - m_target(target) { - assert(target != 0 && "Empty generators are not allowed"); - } - T const &get() const override { - return m_generator.get(); - } - bool next() override { - ++m_returned; - if (m_returned >= m_target) { - return false; - } - - const auto success = m_generator.next(); - // If the underlying generator does not contain enough values - // then we cut short as well - if (!success) { - m_returned = m_target; - } - return success; - } -}; + void populate(GeneratorWrapper &&generator) { + m_generators.emplace_back(std::move(generator)); + } -template -GeneratorWrapper take(size_t target, GeneratorWrapper &&generator) { - return GeneratorWrapper(pf::make_unique>(target, std::move(generator))); -} + void populate(T &&val) { + m_generators.emplace_back(value(std::forward(val))); + } -template -class FilterGenerator : public IGenerator { - GeneratorWrapper m_generator; - Predicate m_predicate; - public: - template - FilterGenerator(P &&pred, GeneratorWrapper &&generator): - m_generator(std::move(generator)), - m_predicate(std::forward

(pred)) { - if (!m_predicate(m_generator.get())) { - // It might happen that there are no values that pass the - // filter. In that case we throw an exception. - auto has_initial_value = nextImpl(); - if (!has_initial_value) { - Catch::throw_exception(GeneratorException("No valid value found in filtered generator")); - } - } - } - - T const &get() const override { - return m_generator.get(); - } - - bool next() override { - return nextImpl(); - } - - private: - bool nextImpl() { - bool success = m_generator.next(); - if (!success) { - return false; - } - while (!m_predicate(m_generator.get()) && (success = m_generator.next()) == true); - return success; - } -}; + template + void populate(U &&val) { + populate(T(std::forward(val))); + } -template -GeneratorWrapper filter(Predicate &&pred, GeneratorWrapper &&generator) { - return GeneratorWrapper(std::unique_ptr>(pf::make_unique>(std::forward< - Predicate>(pred), std::move(generator)))); -} + template + void populate(U &&valueOrGenerator, Gs &&... moreGenerators) { + populate(std::forward(valueOrGenerator)); + populate(std::forward(moreGenerators)...); + } -template -class RepeatGenerator : public IGenerator { - static_assert(!std::is_same::value, - "RepeatGenerator currently does not support bools" - "because of std::vector specialization"); - GeneratorWrapper m_generator; - mutable std::vector m_returned; - size_t m_target_repeats; - size_t m_current_repeat = 0; - size_t m_repeat_index = 0; - public: - RepeatGenerator(size_t repeats, GeneratorWrapper &&generator) : - m_generator(std::move(generator)), - m_target_repeats(repeats) { - assert(m_target_repeats > 0 && "Repeat generator must repeat at least once"); - } - - T const &get() const override { - if (m_current_repeat == 0) { - m_returned.push_back(m_generator.get()); - return m_returned.back(); - } - return m_returned[m_repeat_index]; - } - - bool next() override { - // There are 2 basic cases: - // 1) We are still reading the generator - // 2) We are reading our own cache - - // In the first case, we need to poke the underlying generator. - // If it happily moves, we are left in that state, otherwise it is time to start reading from our cache - if (m_current_repeat == 0) { - const auto success = m_generator.next(); - if (!success) { - ++m_current_repeat; - } - return m_current_repeat < m_target_repeats; - } - - // In the second case, we need to move indices forward and check that we haven't run up against the end - ++m_repeat_index; - if (m_repeat_index == m_returned.size()) { - m_repeat_index = 0; - ++m_current_repeat; - } - return m_current_repeat < m_target_repeats; - } -}; + public: + template + Generators(Gs &&... moreGenerators) { + m_generators.reserve(sizeof...(Gs)); + populate(std::forward(moreGenerators)...); + } -template -GeneratorWrapper repeat(size_t repeats, GeneratorWrapper &&generator) { - return GeneratorWrapper(pf::make_unique>(repeats, std::move(generator))); -} + T const &get() const override { + return m_generators[m_current].get(); + } -template -class MapGenerator : public IGenerator { - // TBD: provide static assert for mapping function, for friendly error message - GeneratorWrapper m_generator; - Func m_function; - // To avoid returning dangling reference, we have to save the values - T m_cache; - public: - template - MapGenerator(F2 &&function, GeneratorWrapper &&generator) : - m_generator(std::move(generator)), - m_function(std::forward(function)), - m_cache(m_function(m_generator.get())) {} - - T const &get() const override { - return m_cache; - } - bool next() override { - const auto success = m_generator.next(); - if (success) { - m_cache = m_function(m_generator.get()); - } - return success; - } -}; + bool next() override { + if (m_current >= m_generators.size()) { + return false; + } + const bool current_status = m_generators[m_current].next(); + if (!current_status) { + ++m_current; + } + return m_current < m_generators.size(); + } + }; -template> -GeneratorWrapper map(Func &&function, GeneratorWrapper &&generator) { - return GeneratorWrapper( - pf::make_unique>(std::forward(function), std::move(generator)) - ); -} + template + GeneratorWrapper> + table(std::initializer_list::type...>> tuples) { + return values>(tuples); + } -template -GeneratorWrapper map(Func &&function, GeneratorWrapper &&generator) { - return GeneratorWrapper( - pf::make_unique>(std::forward(function), std::move(generator)) - ); -} +// Tag type to signal that a generator sequence should convert arguments to a specific type + template + struct as { + }; -template -class ChunkGenerator final : public IGenerator> { - std::vector m_chunk; - size_t m_chunk_size; - GeneratorWrapper m_generator; - bool m_used_up = false; - public: - ChunkGenerator(size_t size, GeneratorWrapper generator) : - m_chunk_size(size), m_generator(std::move(generator)) { - m_chunk.reserve(m_chunk_size); - if (m_chunk_size != 0) { - m_chunk.push_back(m_generator.get()); - for (size_t i = 1; i < m_chunk_size; ++i) { - if (!m_generator.next()) { - Catch::throw_exception(GeneratorException("Not enough values to initialize the first chunk")); - } - m_chunk.push_back(m_generator.get()); - } - } - } - std::vector const &get() const override { - return m_chunk; - } - bool next() override { - m_chunk.clear(); - for (size_t idx = 0; idx < m_chunk_size; ++idx) { - if (!m_generator.next()) { - return false; - } - m_chunk.push_back(m_generator.get()); - } - return true; - } -}; + template + auto makeGenerators(GeneratorWrapper &&generator, Gs &&... moreGenerators) -> Generators { + return Generators(std::move(generator), std::forward(moreGenerators)...); + } -template -GeneratorWrapper> chunk(size_t size, GeneratorWrapper &&generator) { - return GeneratorWrapper>( - pf::make_unique>(size, std::move(generator)) - ); -} + template + auto makeGenerators(GeneratorWrapper &&generator) -> Generators { + return Generators(std::move(generator)); + } -} // namespace Generators + template + auto makeGenerators(T &&val, Gs &&... moreGenerators) -> Generators { + return makeGenerators(value(std::forward(val)), std::forward(moreGenerators)...); + } + + template + auto makeGenerators(as, U &&val, Gs &&... moreGenerators) -> Generators { + return makeGenerators(value(T(std::forward(val))), std::forward(moreGenerators)...); + } + + auto acquireGeneratorTracker(StringRef generatorName, SourceLineInfo const &lineInfo) -> IGeneratorTracker &; + + template +// Note: The type after -> is weird, because VS2015 cannot parse +// the expression used in the typedef inside, when it is in +// return type. Yeah. + auto generate(StringRef generatorName, + SourceLineInfo const &lineInfo, + L const &generatorExpression) -> decltype(std::declval().get()) { + using UnderlyingType = typename decltype(generatorExpression())::type; + + IGeneratorTracker &tracker = acquireGeneratorTracker(generatorName, lineInfo); + if (!tracker.hasGenerator()) { + tracker.setGenerator(pf::make_unique>(generatorExpression())); + } + + auto const &generator = static_cast const &>( *tracker.getGenerator()); + return generator.get(); + } + + } // namespace Generators +} // namespace Catch + +#define GENERATE(...) \ + Catch::Generators::generate( INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \ + CATCH_INTERNAL_LINEINFO, \ + [ ]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace) +#define GENERATE_COPY(...) \ + Catch::Generators::generate( INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \ + CATCH_INTERNAL_LINEINFO, \ + [=]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace) +#define GENERATE_REF(...) \ + Catch::Generators::generate( INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \ + CATCH_INTERNAL_LINEINFO, \ + [&]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace) + +// end catch_generators.hpp +// start catch_generators_generic.hpp + +namespace Catch { + namespace Generators { + + template + class TakeGenerator : public IGenerator { + GeneratorWrapper m_generator; + size_t m_returned = 0; + size_t m_target; + public: + TakeGenerator(size_t target, GeneratorWrapper &&generator) : + m_generator(std::move(generator)), + m_target(target) { + assert(target != 0 && "Empty generators are not allowed"); + } + + T const &get() const override { + return m_generator.get(); + } + + bool next() override { + ++m_returned; + if (m_returned >= m_target) { + return false; + } + + const auto success = m_generator.next(); + // If the underlying generator does not contain enough values + // then we cut short as well + if (!success) { + m_returned = m_target; + } + return success; + } + }; + + template + GeneratorWrapper take(size_t target, GeneratorWrapper &&generator) { + return GeneratorWrapper(pf::make_unique>(target, std::move(generator))); + } + + template + class FilterGenerator : public IGenerator { + GeneratorWrapper m_generator; + Predicate m_predicate; + public: + template + FilterGenerator(P &&pred, GeneratorWrapper &&generator): + m_generator(std::move(generator)), + m_predicate(std::forward

(pred)) { + if (!m_predicate(m_generator.get())) { + // It might happen that there are no values that pass the + // filter. In that case we throw an exception. + auto has_initial_value = nextImpl(); + if (!has_initial_value) { + Catch::throw_exception(GeneratorException("No valid value found in filtered generator")); + } + } + } + + T const &get() const override { + return m_generator.get(); + } + + bool next() override { + return nextImpl(); + } + + private: + bool nextImpl() { + bool success = m_generator.next(); + if (!success) { + return false; + } + while (!m_predicate(m_generator.get()) && (success = m_generator.next()) == true); + return success; + } + }; + + template + GeneratorWrapper filter(Predicate &&pred, GeneratorWrapper &&generator) { + return GeneratorWrapper( + std::unique_ptr>(pf::make_unique>(std::forward< + Predicate>(pred), std::move(generator)))); + } + + template + class RepeatGenerator : public IGenerator { + static_assert(!std::is_same::value, + "RepeatGenerator currently does not support bools" + "because of std::vector specialization"); + GeneratorWrapper m_generator; + mutable std::vector m_returned; + size_t m_target_repeats; + size_t m_current_repeat = 0; + size_t m_repeat_index = 0; + public: + RepeatGenerator(size_t repeats, GeneratorWrapper &&generator) : + m_generator(std::move(generator)), + m_target_repeats(repeats) { + assert(m_target_repeats > 0 && "Repeat generator must repeat at least once"); + } + + T const &get() const override { + if (m_current_repeat == 0) { + m_returned.push_back(m_generator.get()); + return m_returned.back(); + } + return m_returned[m_repeat_index]; + } + + bool next() override { + // There are 2 basic cases: + // 1) We are still reading the generator + // 2) We are reading our own cache + + // In the first case, we need to poke the underlying generator. + // If it happily moves, we are left in that state, otherwise it is time to start reading from our cache + if (m_current_repeat == 0) { + const auto success = m_generator.next(); + if (!success) { + ++m_current_repeat; + } + return m_current_repeat < m_target_repeats; + } + + // In the second case, we need to move indices forward and check that we haven't run up against the end + ++m_repeat_index; + if (m_repeat_index == m_returned.size()) { + m_repeat_index = 0; + ++m_current_repeat; + } + return m_current_repeat < m_target_repeats; + } + }; + + template + GeneratorWrapper repeat(size_t repeats, GeneratorWrapper &&generator) { + return GeneratorWrapper(pf::make_unique>(repeats, std::move(generator))); + } + + template + class MapGenerator : public IGenerator { + // TBD: provide static assert for mapping function, for friendly error message + GeneratorWrapper m_generator; + Func m_function; + // To avoid returning dangling reference, we have to save the values + T m_cache; + public: + template + MapGenerator(F2 &&function, GeneratorWrapper &&generator) : + m_generator(std::move(generator)), + m_function(std::forward(function)), + m_cache(m_function(m_generator.get())) {} + + T const &get() const override { + return m_cache; + } + + bool next() override { + const auto success = m_generator.next(); + if (success) { + m_cache = m_function(m_generator.get()); + } + return success; + } + }; + + template> + GeneratorWrapper map(Func &&function, GeneratorWrapper &&generator) { + return GeneratorWrapper( + pf::make_unique>(std::forward(function), std::move(generator)) + ); + } + + template + GeneratorWrapper map(Func &&function, GeneratorWrapper &&generator) { + return GeneratorWrapper( + pf::make_unique>(std::forward(function), std::move(generator)) + ); + } + + template + class ChunkGenerator final : public IGenerator> { + std::vector m_chunk; + size_t m_chunk_size; + GeneratorWrapper m_generator; + bool m_used_up = false; + public: + ChunkGenerator(size_t size, GeneratorWrapper generator) : + m_chunk_size(size), m_generator(std::move(generator)) { + m_chunk.reserve(m_chunk_size); + if (m_chunk_size != 0) { + m_chunk.push_back(m_generator.get()); + for (size_t i = 1; i < m_chunk_size; ++i) { + if (!m_generator.next()) { + Catch::throw_exception( + GeneratorException("Not enough values to initialize the first chunk")); + } + m_chunk.push_back(m_generator.get()); + } + } + } + + std::vector const &get() const override { + return m_chunk; + } + + bool next() override { + m_chunk.clear(); + for (size_t idx = 0; idx < m_chunk_size; ++idx) { + if (!m_generator.next()) { + return false; + } + m_chunk.push_back(m_generator.get()); + } + return true; + } + }; + + template + GeneratorWrapper> chunk(size_t size, GeneratorWrapper &&generator) { + return GeneratorWrapper>( + pf::make_unique>(size, std::move(generator)) + ); + } + + } // namespace Generators } // namespace Catch // end catch_generators_generic.hpp @@ -4356,49 +4612,58 @@ GeneratorWrapper> chunk(size_t size, GeneratorWrapper &&genera namespace Catch { -struct IResultCapture; -struct IRunner; -struct IConfig; -struct IMutableContext; + struct IResultCapture; + struct IRunner; + struct IConfig; + struct IMutableContext; -using IConfigPtr = std::shared_ptr; + using IConfigPtr = std::shared_ptr; -struct IContext { - virtual ~IContext(); + struct IContext { + virtual ~IContext(); - virtual IResultCapture *getResultCapture() = 0; - virtual IRunner *getRunner() = 0; - virtual IConfigPtr const &getConfig() const = 0; -}; + virtual IResultCapture *getResultCapture() = 0; -struct IMutableContext : IContext { - virtual ~IMutableContext(); - virtual void setResultCapture(IResultCapture *resultCapture) = 0; - virtual void setRunner(IRunner *runner) = 0; - virtual void setConfig(IConfigPtr const &config) = 0; - - private: - static IMutableContext *currentContext; - friend IMutableContext &getCurrentMutableContext(); - friend void cleanUpContext(); - static void createContext(); -}; + virtual IRunner *getRunner() = 0; -inline IMutableContext &getCurrentMutableContext() { - if (!IMutableContext::currentContext) - IMutableContext::createContext(); - // NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn) - return *IMutableContext::currentContext; -} + virtual IConfigPtr const &getConfig() const = 0; + }; -inline IContext &getCurrentContext() { - return getCurrentMutableContext(); -} + struct IMutableContext : IContext { + virtual ~IMutableContext(); + + virtual void setResultCapture(IResultCapture *resultCapture) = 0; + + virtual void setRunner(IRunner *runner) = 0; -void cleanUpContext(); + virtual void setConfig(IConfigPtr const &config) = 0; -class SimplePcg32; -SimplePcg32 &rng(); + private: + static IMutableContext *currentContext; + + friend IMutableContext &getCurrentMutableContext(); + + friend void cleanUpContext(); + + static void createContext(); + }; + + inline IMutableContext &getCurrentMutableContext() { + if (!IMutableContext::currentContext) + IMutableContext::createContext(); + // NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn) + return *IMutableContext::currentContext; + } + + inline IContext &getCurrentContext() { + return getCurrentMutableContext(); + } + + void cleanUpContext(); + + class SimplePcg32; + + SimplePcg32 &rng(); } // end catch_context.h @@ -4409,60 +4674,68 @@ SimplePcg32 &rng(); namespace Catch { // An optional type -template -class Option { - public: - Option() : nullableValue(nullptr) {} - Option(T const &_value) - : nullableValue(new(storage) T(_value)) {} - Option(Option const &_other) - : nullableValue(_other ? new(storage) T(*_other) : nullptr) {} - - ~Option() { - reset(); - } - - Option &operator=(Option const &_other) { - if (&_other != this) { - reset(); - if (_other) - nullableValue = new(storage) T(*_other); - } - return *this; - } - Option &operator=(T const &_value) { - reset(); - nullableValue = new(storage) T(_value); - return *this; - } - - void reset() { - if (nullableValue) - nullableValue->~T(); - nullableValue = nullptr; - } - - T &operator*() { return *nullableValue; } - T const &operator*() const { return *nullableValue; } - T *operator->() { return nullableValue; } - const T *operator->() const { return nullableValue; } - - T valueOr(T const &defaultValue) const { - return nullableValue ? *nullableValue : defaultValue; - } - - bool some() const { return nullableValue != nullptr; } - bool none() const { return nullableValue == nullptr; } - - bool operator!() const { return nullableValue == nullptr; } - explicit operator bool() const { - return some(); - } - - private: - T *nullableValue; - alignas(alignof(T)) char storage[sizeof(T)]; -}; + template + class Option { + public: + Option() : nullableValue(nullptr) {} + + Option(T const &_value) + : nullableValue(new(storage) T(_value)) {} + + Option(Option const &_other) + : nullableValue(_other ? new(storage) T(*_other) : nullptr) {} + + ~Option() { + reset(); + } + + Option &operator=(Option const &_other) { + if (&_other != this) { + reset(); + if (_other) + nullableValue = new(storage) T(*_other); + } + return *this; + } + + Option &operator=(T const &_value) { + reset(); + nullableValue = new(storage) T(_value); + return *this; + } + + void reset() { + if (nullableValue) + nullableValue->~T(); + nullableValue = nullptr; + } + + T &operator*() { return *nullableValue; } + + T const &operator*() const { return *nullableValue; } + + T *operator->() { return nullableValue; } + + const T *operator->() const { return nullableValue; } + + T valueOr(T const &defaultValue) const { + return nullableValue ? *nullableValue : defaultValue; + } + + bool some() const { return nullableValue != nullptr; } + + bool none() const { return nullableValue == nullptr; } + + bool operator!() const { return nullableValue == nullptr; } + + explicit operator bool() const { + return some(); + } + + private: + T *nullableValue; + alignas(alignof(T)) char storage[sizeof(T)]; + }; } // end namespace Catch @@ -4475,84 +4748,106 @@ class Option { namespace Catch { -enum class Verbosity { - Quiet = 0, - Normal, - High -}; + enum class Verbosity { + Quiet = 0, + Normal, + High + }; -struct WarnAbout { - enum What { - Nothing = 0x00, - NoAssertions = 0x01, - NoTests = 0x02 - }; -}; + struct WarnAbout { + enum What { + Nothing = 0x00, + NoAssertions = 0x01, + NoTests = 0x02 + }; + }; -struct ShowDurations { - enum OrNot { - DefaultForReporter, - Always, - Never - }; -}; -struct RunTests { - enum InWhatOrder { - InDeclarationOrder, - InLexicographicalOrder, - InRandomOrder - }; -}; -struct UseColour { - enum YesOrNo { - Auto, - Yes, - No - }; -}; -struct WaitForKeypress { - enum When { - Never, - BeforeStart = 1, - BeforeExit = 2, - BeforeStartAndExit = BeforeStart | BeforeExit - }; -}; + struct ShowDurations { + enum OrNot { + DefaultForReporter, + Always, + Never + }; + }; + struct RunTests { + enum InWhatOrder { + InDeclarationOrder, + InLexicographicalOrder, + InRandomOrder + }; + }; + struct UseColour { + enum YesOrNo { + Auto, + Yes, + No + }; + }; + struct WaitForKeypress { + enum When { + Never, + BeforeStart = 1, + BeforeExit = 2, + BeforeStartAndExit = BeforeStart | BeforeExit + }; + }; -class TestSpec; - -struct IConfig : NonCopyable { - - virtual ~IConfig(); - - virtual bool allowThrows() const = 0; - virtual std::ostream &stream() const = 0; - virtual std::string name() const = 0; - virtual bool includeSuccessfulResults() const = 0; - virtual bool shouldDebugBreak() const = 0; - virtual bool warnAboutMissingAssertions() const = 0; - virtual bool warnAboutNoTests() const = 0; - virtual int abortAfter() const = 0; - virtual bool showInvisibles() const = 0; - virtual ShowDurations::OrNot showDurations() const = 0; - virtual double minDuration() const = 0; - virtual TestSpec const &testSpec() const = 0; - virtual bool hasTestFilters() const = 0; - virtual std::vector const &getTestsOrTags() const = 0; - virtual RunTests::InWhatOrder runOrder() const = 0; - virtual unsigned int rngSeed() const = 0; - virtual UseColour::YesOrNo useColour() const = 0; - virtual std::vector const &getSectionsToRun() const = 0; - virtual Verbosity verbosity() const = 0; - - virtual bool benchmarkNoAnalysis() const = 0; - virtual int benchmarkSamples() const = 0; - virtual double benchmarkConfidenceInterval() const = 0; - virtual unsigned int benchmarkResamples() const = 0; - virtual std::chrono::milliseconds benchmarkWarmupTime() const = 0; -}; + class TestSpec; + + struct IConfig : NonCopyable { + + virtual ~IConfig(); + + virtual bool allowThrows() const = 0; + + virtual std::ostream &stream() const = 0; + + virtual std::string name() const = 0; + + virtual bool includeSuccessfulResults() const = 0; + + virtual bool shouldDebugBreak() const = 0; + + virtual bool warnAboutMissingAssertions() const = 0; + + virtual bool warnAboutNoTests() const = 0; + + virtual int abortAfter() const = 0; + + virtual bool showInvisibles() const = 0; + + virtual ShowDurations::OrNot showDurations() const = 0; + + virtual double minDuration() const = 0; -using IConfigPtr = std::shared_ptr; + virtual TestSpec const &testSpec() const = 0; + + virtual bool hasTestFilters() const = 0; + + virtual std::vector const &getTestsOrTags() const = 0; + + virtual RunTests::InWhatOrder runOrder() const = 0; + + virtual unsigned int rngSeed() const = 0; + + virtual UseColour::YesOrNo useColour() const = 0; + + virtual std::vector const &getSectionsToRun() const = 0; + + virtual Verbosity verbosity() const = 0; + + virtual bool benchmarkNoAnalysis() const = 0; + + virtual int benchmarkSamples() const = 0; + + virtual double benchmarkConfidenceInterval() const = 0; + + virtual unsigned int benchmarkResamples() const = 0; + + virtual std::chrono::milliseconds benchmarkWarmupTime() const = 0; + }; + + using IConfigPtr = std::shared_ptr; } // end catch_interfaces_config.h @@ -4567,41 +4862,45 @@ namespace Catch { // does not use it, but it should behave as expected inside stdlib's // distributions. // The implementation is based on the PCG family (http://pcg-random.org) -class SimplePcg32 { - using state_type = std::uint64_t; - public: - using result_type = std::uint32_t; - static constexpr result_type (min)() { - return 0; - } - static constexpr result_type (max)() { - return static_cast(-1); - } - - // Provide some default initial state for the default constructor - SimplePcg32() : SimplePcg32(0xed743cc4U) {} - - explicit SimplePcg32(result_type seed_); - - void seed(result_type seed_); - void discard(uint64_t skip); - - result_type operator()(); - - private: - friend bool operator==(SimplePcg32 const &lhs, SimplePcg32 const &rhs); - friend bool operator!=(SimplePcg32 const &lhs, SimplePcg32 const &rhs); - - // In theory we also need operator<< and operator>> - // In practice we do not use them, so we will skip them for now - - std::uint64_t m_state; - // This part of the state determines which "stream" of the numbers - // is chosen -- we take it as a constant for Catch2, so we only - // need to deal with seeding the main state. - // Picked by reading 8 bytes from `/dev/random` :-) - static const std::uint64_t s_inc = (0x13ed0cc53f939476ULL << 1ULL) | 1ULL; -}; + class SimplePcg32 { + using state_type = std::uint64_t; + public: + using result_type = std::uint32_t; + + static constexpr result_type (min)() { + return 0; + } + + static constexpr result_type (max)() { + return static_cast(-1); + } + + // Provide some default initial state for the default constructor + SimplePcg32() : SimplePcg32(0xed743cc4U) {} + + explicit SimplePcg32(result_type seed_); + + void seed(result_type seed_); + + void discard(uint64_t skip); + + result_type operator()(); + + private: + friend bool operator==(SimplePcg32 const &lhs, SimplePcg32 const &rhs); + + friend bool operator!=(SimplePcg32 const &lhs, SimplePcg32 const &rhs); + + // In theory we also need operator<< and operator>> + // In practice we do not use them, so we will skip them for now + + std::uint64_t m_state; + // This part of the state determines which "stream" of the numbers + // is chosen -- we take it as a constant for Catch2, so we only + // need to deal with seeding the main state. + // Picked by reading 8 bytes from `/dev/random` :-) + static const std::uint64_t s_inc = (0x13ed0cc53f939476ULL << 1ULL) | 1ULL; + }; } // end namespace Catch @@ -4609,155 +4908,158 @@ class SimplePcg32 { #include namespace Catch { -namespace Generators { - -template -class RandomFloatingGenerator final : public IGenerator { - Catch::SimplePcg32 &m_rng; - std::uniform_real_distribution m_dist; - Float m_current_number; - public: - - RandomFloatingGenerator(Float a, Float b) : - m_rng(rng()), - m_dist(a, b) { - static_cast(next()); - } - - Float const &get() const override { - return m_current_number; - } - bool next() override { - m_current_number = m_dist(m_rng); - return true; - } -}; + namespace Generators { -template -class RandomIntegerGenerator final : public IGenerator { - Catch::SimplePcg32 &m_rng; - std::uniform_int_distribution m_dist; - Integer m_current_number; - public: - - RandomIntegerGenerator(Integer a, Integer b) : - m_rng(rng()), - m_dist(a, b) { - static_cast(next()); - } - - Integer const &get() const override { - return m_current_number; - } - bool next() override { - m_current_number = m_dist(m_rng); - return true; - } -}; + template + class RandomFloatingGenerator final : public IGenerator { + Catch::SimplePcg32 &m_rng; + std::uniform_real_distribution m_dist; + Float m_current_number; + public: + + RandomFloatingGenerator(Float a, Float b) : + m_rng(rng()), + m_dist(a, b) { + static_cast(next()); + } + + Float const &get() const override { + return m_current_number; + } + + bool next() override { + m_current_number = m_dist(m_rng); + return true; + } + }; + + template + class RandomIntegerGenerator final : public IGenerator { + Catch::SimplePcg32 &m_rng; + std::uniform_int_distribution m_dist; + Integer m_current_number; + public: + + RandomIntegerGenerator(Integer a, Integer b) : + m_rng(rng()), + m_dist(a, b) { + static_cast(next()); + } + + Integer const &get() const override { + return m_current_number; + } + + bool next() override { + m_current_number = m_dist(m_rng); + return true; + } + }; // TODO: Ideally this would be also constrained against the various char types, // but I don't expect users to run into that in practice. -template -typename std::enable_if::value && !std::is_same::value, - GeneratorWrapper>::type -random(T a, T b) { - return GeneratorWrapper( - pf::make_unique>(a, b) - ); -} + template + typename std::enable_if::value && !std::is_same::value, + GeneratorWrapper>::type + random(T a, T b) { + return GeneratorWrapper( + pf::make_unique>(a, b) + ); + } -template -typename std::enable_if::value, - GeneratorWrapper>::type -random(T a, T b) { - return GeneratorWrapper( - pf::make_unique>(a, b) - ); -} + template + typename std::enable_if::value, + GeneratorWrapper>::type + random(T a, T b) { + return GeneratorWrapper( + pf::make_unique>(a, b) + ); + } -template -class RangeGenerator final : public IGenerator { - T m_current; - T m_end; - T m_step; - bool m_positive; - - public: - RangeGenerator(T const &start, T const &end, T const &step) : - m_current(start), - m_end(end), - m_step(step), - m_positive(m_step > T(0)) { - assert(m_current != m_end && "Range start and end cannot be equal"); - assert(m_step != T(0) && "Step size cannot be zero"); - assert(((m_positive && m_current <= m_end) || (!m_positive && m_current >= m_end)) && "Step moves away from end"); - } - - RangeGenerator(T const &start, T const &end) : - RangeGenerator(start, end, (start < end) ? T(1) : T(-1)) {} - - T const &get() const override { - return m_current; - } - - bool next() override { - m_current += m_step; - return (m_positive) ? (m_current < m_end) : (m_current > m_end); - } -}; + template + class RangeGenerator final : public IGenerator { + T m_current; + T m_end; + T m_step; + bool m_positive; -template -GeneratorWrapper range(T const &start, T const &end, T const &step) { - static_assert(std::is_arithmetic::value && !std::is_same::value, "Type must be numeric"); - return GeneratorWrapper(pf::make_unique>(start, end, step)); -} + public: + RangeGenerator(T const &start, T const &end, T const &step) : + m_current(start), + m_end(end), + m_step(step), + m_positive(m_step > T(0)) { + assert(m_current != m_end && "Range start and end cannot be equal"); + assert(m_step != T(0) && "Step size cannot be zero"); + assert(((m_positive && m_current <= m_end) || (!m_positive && m_current >= m_end)) && + "Step moves away from end"); + } -template -GeneratorWrapper range(T const &start, T const &end) { - static_assert(std::is_integral::value && !std::is_same::value, "Type must be an integer"); - return GeneratorWrapper(pf::make_unique>(start, end)); -} + RangeGenerator(T const &start, T const &end) : + RangeGenerator(start, end, (start < end) ? T(1) : T(-1)) {} -template -class IteratorGenerator final : public IGenerator { - static_assert(!std::is_same::value, - "IteratorGenerator currently does not support bools" - "because of std::vector specialization"); - - std::vector m_elems; - size_t m_current = 0; - public: - template - IteratorGenerator(InputIterator first, InputSentinel last):m_elems(first, last) { - if (m_elems.empty()) { - Catch::throw_exception(GeneratorException("IteratorGenerator received no valid values")); - } - } - - T const &get() const override { - return m_elems[m_current]; - } - - bool next() override { - ++m_current; - return m_current != m_elems.size(); - } -}; + T const &get() const override { + return m_current; + } -template::value_type> -GeneratorWrapper from_range(InputIterator from, InputSentinel to) { - return GeneratorWrapper(pf::make_unique>(from, to)); -} + bool next() override { + m_current += m_step; + return (m_positive) ? (m_current < m_end) : (m_current > m_end); + } + }; -template -GeneratorWrapper from_range(Container const &cnt) { - return GeneratorWrapper(pf::make_unique>(cnt.begin(), cnt.end())); -} + template + GeneratorWrapper range(T const &start, T const &end, T const &step) { + static_assert(std::is_arithmetic::value && !std::is_same::value, "Type must be numeric"); + return GeneratorWrapper(pf::make_unique>(start, end, step)); + } + + template + GeneratorWrapper range(T const &start, T const &end) { + static_assert(std::is_integral::value && !std::is_same::value, "Type must be an integer"); + return GeneratorWrapper(pf::make_unique>(start, end)); + } + + template + class IteratorGenerator final : public IGenerator { + static_assert(!std::is_same::value, + "IteratorGenerator currently does not support bools" + "because of std::vector specialization"); + + std::vector m_elems; + size_t m_current = 0; + public: + template + IteratorGenerator(InputIterator first, InputSentinel last):m_elems(first, last) { + if (m_elems.empty()) { + Catch::throw_exception(GeneratorException("IteratorGenerator received no valid values")); + } + } + + T const &get() const override { + return m_elems[m_current]; + } -} // namespace Generators + bool next() override { + ++m_current; + return m_current != m_elems.size(); + } + }; + + template::value_type> + GeneratorWrapper from_range(InputIterator from, InputSentinel to) { + return GeneratorWrapper(pf::make_unique>(from, to)); + } + + template + GeneratorWrapper from_range(Container const &cnt) { + return GeneratorWrapper(pf::make_unique>(cnt.begin(), cnt.end())); + } + + } // namespace Generators } // namespace Catch // end catch_generators_specific.hpp @@ -4777,65 +5079,69 @@ GeneratorWrapper from_range(Container const &cnt) { namespace Catch { -struct ITestInvoker; - -struct TestCaseInfo { - enum SpecialProperties { - None = 0, - IsHidden = 1 << 1, - ShouldFail = 1 << 2, - MayFail = 1 << 3, - Throws = 1 << 4, - NonPortable = 1 << 5, - Benchmark = 1 << 6 - }; - - TestCaseInfo(std::string const &_name, - std::string const &_className, - std::string const &_description, - std::vector const &_tags, - SourceLineInfo const &_lineInfo); - - friend void setTags(TestCaseInfo &testCaseInfo, std::vector tags); - - bool isHidden() const; - bool throws() const; - bool okToFail() const; - bool expectedToFail() const; - - std::string tagsAsString() const; - - std::string name; - std::string className; - std::string description; - std::vector tags; - std::vector lcaseTags; - SourceLineInfo lineInfo; - SpecialProperties properties; -}; + struct ITestInvoker; + + struct TestCaseInfo { + enum SpecialProperties { + None = 0, + IsHidden = 1 << 1, + ShouldFail = 1 << 2, + MayFail = 1 << 3, + Throws = 1 << 4, + NonPortable = 1 << 5, + Benchmark = 1 << 6 + }; -class TestCase : public TestCaseInfo { - public: + TestCaseInfo(std::string const &_name, + std::string const &_className, + std::string const &_description, + std::vector const &_tags, + SourceLineInfo const &_lineInfo); - TestCase(ITestInvoker *testCase, TestCaseInfo &&info); + friend void setTags(TestCaseInfo &testCaseInfo, std::vector tags); - TestCase withName(std::string const &_newName) const; + bool isHidden() const; - void invoke() const; + bool throws() const; - TestCaseInfo const &getTestCaseInfo() const; + bool okToFail() const; - bool operator==(TestCase const &other) const; - bool operator<(TestCase const &other) const; + bool expectedToFail() const; - private: - std::shared_ptr test; -}; + std::string tagsAsString() const; -TestCase makeTestCase(ITestInvoker *testCase, - std::string const &className, - NameAndTags const &nameAndTags, - SourceLineInfo const &lineInfo); + std::string name; + std::string className; + std::string description; + std::vector tags; + std::vector lcaseTags; + SourceLineInfo lineInfo; + SpecialProperties properties; + }; + + class TestCase : public TestCaseInfo { + public: + + TestCase(ITestInvoker *testCase, TestCaseInfo &&info); + + TestCase withName(std::string const &_newName) const; + + void invoke() const; + + TestCaseInfo const &getTestCaseInfo() const; + + bool operator==(TestCase const &other) const; + + bool operator<(TestCase const &other) const; + + private: + std::shared_ptr test; + }; + + TestCase makeTestCase(ITestInvoker *testCase, + std::string const &className, + NameAndTags const &nameAndTags, + SourceLineInfo const &lineInfo); } #ifdef __clang__ @@ -4847,10 +5153,11 @@ TestCase makeTestCase(ITestInvoker *testCase, namespace Catch { -struct IRunner { - virtual ~IRunner(); - virtual bool aborting() const = 0; -}; + struct IRunner { + virtual ~IRunner(); + + virtual bool aborting() const = 0; + }; } // end catch_interfaces_runner.h @@ -5086,98 +5393,117 @@ return @ desc; \ // start catch_wildcard_pattern.h namespace Catch { -class WildcardPattern { - enum WildcardPosition { - NoWildcard = 0, - WildcardAtStart = 1, - WildcardAtEnd = 2, - WildcardAtBothEnds = WildcardAtStart | WildcardAtEnd - }; - - public: - - WildcardPattern(std::string const &pattern, CaseSensitive::Choice caseSensitivity); - virtual ~WildcardPattern() = default; - virtual bool matches(std::string const &str) const; - - private: - std::string normaliseString(std::string const &str) const; - CaseSensitive::Choice m_caseSensitivity; - WildcardPosition m_wildcard = NoWildcard; - std::string m_pattern; -}; + class WildcardPattern { + enum WildcardPosition { + NoWildcard = 0, + WildcardAtStart = 1, + WildcardAtEnd = 2, + WildcardAtBothEnds = WildcardAtStart | WildcardAtEnd + }; + + public: + + WildcardPattern(std::string const &pattern, CaseSensitive::Choice caseSensitivity); + + virtual ~WildcardPattern() = default; + + virtual bool matches(std::string const &str) const; + + private: + std::string normaliseString(std::string const &str) const; + + CaseSensitive::Choice m_caseSensitivity; + WildcardPosition m_wildcard = NoWildcard; + std::string m_pattern; + }; } -// end catch_wildcard_pattern.h -#include -#include -#include +// end catch_wildcard_pattern.h +#include +#include +#include + +namespace Catch { + + struct IConfig; + + class TestSpec { + class Pattern { + public: + explicit Pattern(std::string const &name); + + virtual ~Pattern(); + + virtual bool matches(TestCaseInfo const &testCase) const = 0; + + std::string const &name() const; + + private: + std::string const m_name; + }; + + using PatternPtr = std::shared_ptr; + + class NamePattern : public Pattern { + public: + explicit NamePattern(std::string const &name, std::string const &filterString); + + bool matches(TestCaseInfo const &testCase) const override; + + private: + WildcardPattern m_wildcardPattern; + }; + + class TagPattern : public Pattern { + public: + explicit TagPattern(std::string const &tag, std::string const &filterString); + + bool matches(TestCaseInfo const &testCase) const override; + + private: + std::string m_tag; + }; + + class ExcludedPattern : public Pattern { + public: + explicit ExcludedPattern(PatternPtr const &underlyingPattern); + + bool matches(TestCaseInfo const &testCase) const override; + + private: + PatternPtr m_underlyingPattern; + }; + + struct Filter { + std::vector m_patterns; + + bool matches(TestCaseInfo const &testCase) const; + + std::string name() const; + }; + + public: + struct FilterMatch { + std::string name; + std::vector tests; + }; + using Matches = std::vector; + using vectorStrings = std::vector; + + bool hasFilters() const; + + bool matches(TestCaseInfo const &testCase) const; -namespace Catch { + Matches matchesByFilter(std::vector const &testCases, IConfig const &config) const; -struct IConfig; - -class TestSpec { - class Pattern { - public: - explicit Pattern(std::string const &name); - virtual ~Pattern(); - virtual bool matches(TestCaseInfo const &testCase) const = 0; - std::string const &name() const; - private: - std::string const m_name; - }; - using PatternPtr = std::shared_ptr; - - class NamePattern : public Pattern { - public: - explicit NamePattern(std::string const &name, std::string const &filterString); - bool matches(TestCaseInfo const &testCase) const override; - private: - WildcardPattern m_wildcardPattern; - }; - - class TagPattern : public Pattern { - public: - explicit TagPattern(std::string const &tag, std::string const &filterString); - bool matches(TestCaseInfo const &testCase) const override; - private: - std::string m_tag; - }; - - class ExcludedPattern : public Pattern { - public: - explicit ExcludedPattern(PatternPtr const &underlyingPattern); - bool matches(TestCaseInfo const &testCase) const override; - private: - PatternPtr m_underlyingPattern; - }; - - struct Filter { - std::vector m_patterns; - - bool matches(TestCaseInfo const &testCase) const; - std::string name() const; - }; - - public: - struct FilterMatch { - std::string name; - std::vector tests; - }; - using Matches = std::vector; - using vectorStrings = std::vector; - - bool hasFilters() const; - bool matches(TestCaseInfo const &testCase) const; - Matches matchesByFilter(std::vector const &testCases, IConfig const &config) const; - const vectorStrings &getInvalidArgs() const; - - private: - std::vector m_filters; - std::vector m_invalidArgs; - friend class TestSpecParser; -}; + const vectorStrings &getInvalidArgs() const; + + private: + std::vector m_filters; + std::vector m_invalidArgs; + + friend class TestSpecParser; + }; } #ifdef __clang__ @@ -5191,72 +5517,91 @@ class TestSpec { namespace Catch { -struct TagAlias; + struct TagAlias; -struct ITagAliasRegistry { - virtual ~ITagAliasRegistry(); - // Nullptr if not present - virtual TagAlias const *find(std::string const &alias) const = 0; - virtual std::string expandAliases(std::string const &unexpandedTestSpec) const = 0; + struct ITagAliasRegistry { + virtual ~ITagAliasRegistry(); - static ITagAliasRegistry const &get(); -}; + // Nullptr if not present + virtual TagAlias const *find(std::string const &alias) const = 0; + + virtual std::string expandAliases(std::string const &unexpandedTestSpec) const = 0; + + static ITagAliasRegistry const &get(); + }; } // end namespace Catch // end catch_interfaces_tag_alias_registry.h namespace Catch { -class TestSpecParser { - enum Mode { None, Name, QuotedName, Tag, EscapedName }; - Mode m_mode = None; - Mode lastMode = None; - bool m_exclusion = false; - std::size_t m_pos = 0; - std::size_t m_realPatternPos = 0; - std::string m_arg; - std::string m_substring; - std::string m_patternName; - std::vector m_escapeChars; - TestSpec::Filter m_currentFilter; - TestSpec m_testSpec; - ITagAliasRegistry const *m_tagAliases = nullptr; - - public: - TestSpecParser(ITagAliasRegistry const &tagAliases); - - TestSpecParser &parse(std::string const &arg); - TestSpec testSpec(); - - private: - bool visitChar(char c); - void startNewMode(Mode mode); - bool processNoneChar(char c); - void processNameChar(char c); - bool processOtherChar(char c); - void endMode(); - void escape(); - bool isControlChar(char c) const; - void saveLastMode(); - void revertBackToLastMode(); - void addFilter(); - bool separate(); - - // Handles common preprocessing of the pattern for name/tag patterns - std::string preprocessPattern(); - // Adds the current pattern as a test name - void addNamePattern(); - // Adds the current pattern as a tag - void addTagPattern(); - - inline void addCharToPattern(char c) { - m_substring += c; - m_patternName += c; - m_realPatternPos++; - } + class TestSpecParser { + enum Mode { + None, Name, QuotedName, Tag, EscapedName + }; + Mode m_mode = None; + Mode lastMode = None; + bool m_exclusion = false; + std::size_t m_pos = 0; + std::size_t m_realPatternPos = 0; + std::string m_arg; + std::string m_substring; + std::string m_patternName; + std::vector m_escapeChars; + TestSpec::Filter m_currentFilter; + TestSpec m_testSpec; + ITagAliasRegistry const *m_tagAliases = nullptr; -}; -TestSpec parseTestSpec(std::string const &arg); + public: + TestSpecParser(ITagAliasRegistry const &tagAliases); + + TestSpecParser &parse(std::string const &arg); + + TestSpec testSpec(); + + private: + bool visitChar(char c); + + void startNewMode(Mode mode); + + bool processNoneChar(char c); + + void processNameChar(char c); + + bool processOtherChar(char c); + + void endMode(); + + void escape(); + + bool isControlChar(char c) const; + + void saveLastMode(); + + void revertBackToLastMode(); + + void addFilter(); + + bool separate(); + + // Handles common preprocessing of the pattern for name/tag patterns + std::string preprocessPattern(); + + // Adds the current pattern as a test name + void addNamePattern(); + + // Adds the current pattern as a tag + void addTagPattern(); + + inline void addCharToPattern(char c) { + m_substring += c; + m_patternName += c; + m_realPatternPos++; + } + + }; + + TestSpec parseTestSpec(std::string const &arg); } // namespace Catch @@ -5277,108 +5622,136 @@ TestSpec parseTestSpec(std::string const &arg); namespace Catch { -struct IStream; - -struct ConfigData { - bool listTests = false; - bool listTags = false; - bool listReporters = false; - bool listTestNamesOnly = false; - - bool showSuccessfulTests = false; - bool shouldDebugBreak = false; - bool noThrow = false; - bool showHelp = false; - bool showInvisibles = false; - bool filenamesAsTags = false; - bool libIdentify = false; - - int abortAfter = -1; - unsigned int rngSeed = 0; - - bool benchmarkNoAnalysis = false; - unsigned int benchmarkSamples = 100; - double benchmarkConfidenceInterval = 0.95; - unsigned int benchmarkResamples = 100000; - std::chrono::milliseconds::rep benchmarkWarmupTime = 100; - - Verbosity verbosity = Verbosity::Normal; - WarnAbout::What warnings = WarnAbout::Nothing; - ShowDurations::OrNot showDurations = ShowDurations::DefaultForReporter; - double minDuration = -1; - RunTests::InWhatOrder runOrder = RunTests::InDeclarationOrder; - UseColour::YesOrNo useColour = UseColour::Auto; - WaitForKeypress::When waitForKeypress = WaitForKeypress::Never; - - std::string outputFilename; - std::string name; - std::string processName; + struct IStream; + + struct ConfigData { + bool listTests = false; + bool listTags = false; + bool listReporters = false; + bool listTestNamesOnly = false; + + bool showSuccessfulTests = false; + bool shouldDebugBreak = false; + bool noThrow = false; + bool showHelp = false; + bool showInvisibles = false; + bool filenamesAsTags = false; + bool libIdentify = false; + + int abortAfter = -1; + unsigned int rngSeed = 0; + + bool benchmarkNoAnalysis = false; + unsigned int benchmarkSamples = 100; + double benchmarkConfidenceInterval = 0.95; + unsigned int benchmarkResamples = 100000; + std::chrono::milliseconds::rep benchmarkWarmupTime = 100; + + Verbosity verbosity = Verbosity::Normal; + WarnAbout::What warnings = WarnAbout::Nothing; + ShowDurations::OrNot showDurations = ShowDurations::DefaultForReporter; + double minDuration = -1; + RunTests::InWhatOrder runOrder = RunTests::InDeclarationOrder; + UseColour::YesOrNo useColour = UseColour::Auto; + WaitForKeypress::When waitForKeypress = WaitForKeypress::Never; + + std::string outputFilename; + std::string name; + std::string processName; #ifndef CATCH_CONFIG_DEFAULT_REPORTER #define CATCH_CONFIG_DEFAULT_REPORTER "console" #endif - std::string reporterName = CATCH_CONFIG_DEFAULT_REPORTER; + std::string reporterName = CATCH_CONFIG_DEFAULT_REPORTER; #undef CATCH_CONFIG_DEFAULT_REPORTER - std::vector testsOrTags; - std::vector sectionsToRun; -}; + std::vector testsOrTags; + std::vector sectionsToRun; + }; -class Config : public IConfig { - public: - - Config() = default; - Config(ConfigData const &data); - virtual ~Config() = default; - - std::string const &getFilename() const; - - bool listTests() const; - bool listTestNamesOnly() const; - bool listTags() const; - bool listReporters() const; - - std::string getProcessName() const; - std::string const &getReporterName() const; - - std::vector const &getTestsOrTags() const override; - std::vector const &getSectionsToRun() const override; - - TestSpec const &testSpec() const override; - bool hasTestFilters() const override; - - bool showHelp() const; - - // IConfig interface - bool allowThrows() const override; - std::ostream &stream() const override; - std::string name() const override; - bool includeSuccessfulResults() const override; - bool warnAboutMissingAssertions() const override; - bool warnAboutNoTests() const override; - ShowDurations::OrNot showDurations() const override; - double minDuration() const override; - RunTests::InWhatOrder runOrder() const override; - unsigned int rngSeed() const override; - UseColour::YesOrNo useColour() const override; - bool shouldDebugBreak() const override; - int abortAfter() const override; - bool showInvisibles() const override; - Verbosity verbosity() const override; - bool benchmarkNoAnalysis() const override; - int benchmarkSamples() const override; - double benchmarkConfidenceInterval() const override; - unsigned int benchmarkResamples() const override; - std::chrono::milliseconds benchmarkWarmupTime() const override; - - private: - - IStream const *openStream(); - ConfigData m_data; - - std::unique_ptr m_stream; - TestSpec m_testSpec; - bool m_hasTestFilters = false; -}; + class Config : public IConfig { + public: + + Config() = default; + + Config(ConfigData const &data); + + virtual ~Config() = default; + + std::string const &getFilename() const; + + bool listTests() const; + + bool listTestNamesOnly() const; + + bool listTags() const; + + bool listReporters() const; + + std::string getProcessName() const; + + std::string const &getReporterName() const; + + std::vector const &getTestsOrTags() const override; + + std::vector const &getSectionsToRun() const override; + + TestSpec const &testSpec() const override; + + bool hasTestFilters() const override; + + bool showHelp() const; + + // IConfig interface + bool allowThrows() const override; + + std::ostream &stream() const override; + + std::string name() const override; + + bool includeSuccessfulResults() const override; + + bool warnAboutMissingAssertions() const override; + + bool warnAboutNoTests() const override; + + ShowDurations::OrNot showDurations() const override; + + double minDuration() const override; + + RunTests::InWhatOrder runOrder() const override; + + unsigned int rngSeed() const override; + + UseColour::YesOrNo useColour() const override; + + bool shouldDebugBreak() const override; + + int abortAfter() const override; + + bool showInvisibles() const override; + + Verbosity verbosity() const override; + + bool benchmarkNoAnalysis() const override; + + int benchmarkSamples() const override; + + double benchmarkConfidenceInterval() const override; + + unsigned int benchmarkResamples() const override; + + std::chrono::milliseconds benchmarkWarmupTime() const override; + + private: + + IStream const *openStream(); + + ConfigData m_data; + + std::unique_ptr m_stream; + TestSpec m_testSpec; + bool m_hasTestFilters = false; + }; } // end namespace Catch @@ -5389,41 +5762,53 @@ class Config : public IConfig { namespace Catch { -struct AssertionResultData { - AssertionResultData() = delete; + struct AssertionResultData { + AssertionResultData() = delete; - AssertionResultData(ResultWas::OfType _resultType, LazyExpression const &_lazyExpression); + AssertionResultData(ResultWas::OfType _resultType, LazyExpression const &_lazyExpression); - std::string message; - mutable std::string reconstructedExpression; - LazyExpression lazyExpression; - ResultWas::OfType resultType; + std::string message; + mutable std::string reconstructedExpression; + LazyExpression lazyExpression; + ResultWas::OfType resultType; - std::string reconstructExpression() const; -}; + std::string reconstructExpression() const; + }; -class AssertionResult { - public: - AssertionResult() = delete; - AssertionResult(AssertionInfo const &info, AssertionResultData const &data); - - bool isOk() const; - bool succeeded() const; - ResultWas::OfType getResultType() const; - bool hasExpression() const; - bool hasMessage() const; - std::string getExpression() const; - std::string getExpressionInMacro() const; - bool hasExpandedExpression() const; - std::string getExpandedExpression() const; - std::string getMessage() const; - SourceLineInfo getSourceInfo() const; - StringRef getTestMacroName() const; - - //protected: - AssertionInfo m_info; - AssertionResultData m_resultData; -}; + class AssertionResult { + public: + AssertionResult() = delete; + + AssertionResult(AssertionInfo const &info, AssertionResultData const &data); + + bool isOk() const; + + bool succeeded() const; + + ResultWas::OfType getResultType() const; + + bool hasExpression() const; + + bool hasMessage() const; + + std::string getExpression() const; + + std::string getExpressionInMacro() const; + + bool hasExpandedExpression() const; + + std::string getExpandedExpression() const; + + std::string getMessage() const; + + SourceLineInfo getSourceInfo() const; + + StringRef getTestMacroName() const; + + //protected: + AssertionInfo m_info; + AssertionResultData m_resultData; + }; } // end namespace Catch @@ -5486,140 +5871,167 @@ namespace Catch { namespace Catch { -struct ReporterConfig { - explicit ReporterConfig(IConfigPtr const &_fullConfig); + struct ReporterConfig { + explicit ReporterConfig(IConfigPtr const &_fullConfig); - ReporterConfig(IConfigPtr const &_fullConfig, std::ostream &_stream); + ReporterConfig(IConfigPtr const &_fullConfig, std::ostream &_stream); - std::ostream &stream() const; - IConfigPtr fullConfig() const; + std::ostream &stream() const; - private: - std::ostream *m_stream; - IConfigPtr m_fullConfig; -}; + IConfigPtr fullConfig() const; -struct ReporterPreferences { - bool shouldRedirectStdOut = false; - bool shouldReportAllAssertions = false; -}; + private: + std::ostream *m_stream; + IConfigPtr m_fullConfig; + }; -template -struct LazyStat : Option { - LazyStat &operator=(T const &_value) { - Option::operator=(_value); - used = false; - return *this; - } - void reset() { - Option::reset(); - used = false; - } - bool used = false; -}; + struct ReporterPreferences { + bool shouldRedirectStdOut = false; + bool shouldReportAllAssertions = false; + }; -struct TestRunInfo { - TestRunInfo(std::string const &_name); - std::string name; -}; -struct GroupInfo { - GroupInfo(std::string const &_name, - std::size_t _groupIndex, - std::size_t _groupsCount); - - std::string name; - std::size_t groupIndex; - std::size_t groupsCounts; -}; + template + struct LazyStat : Option { + LazyStat &operator=(T const &_value) { + Option::operator=(_value); + used = false; + return *this; + } -struct AssertionStats { - AssertionStats(AssertionResult const &_assertionResult, - std::vector const &_infoMessages, - Totals const &_totals); + void reset() { + Option::reset(); + used = false; + } - AssertionStats(AssertionStats const &) = default; - AssertionStats(AssertionStats &&) = default; - AssertionStats &operator=(AssertionStats const &) = delete; - AssertionStats &operator=(AssertionStats &&) = delete; - virtual ~AssertionStats(); + bool used = false; + }; - AssertionResult assertionResult; - std::vector infoMessages; - Totals totals; -}; + struct TestRunInfo { + TestRunInfo(std::string const &_name); -struct SectionStats { - SectionStats(SectionInfo const &_sectionInfo, - Counts const &_assertions, - double _durationInSeconds, - bool _missingAssertions); - SectionStats(SectionStats const &) = default; - SectionStats(SectionStats &&) = default; - SectionStats &operator=(SectionStats const &) = default; - SectionStats &operator=(SectionStats &&) = default; - virtual ~SectionStats(); - - SectionInfo sectionInfo; - Counts assertions; - double durationInSeconds; - bool missingAssertions; -}; + std::string name; + }; -struct TestCaseStats { - TestCaseStats(TestCaseInfo const &_testInfo, - Totals const &_totals, - std::string const &_stdOut, - std::string const &_stdErr, - bool _aborting); - - TestCaseStats(TestCaseStats const &) = default; - TestCaseStats(TestCaseStats &&) = default; - TestCaseStats &operator=(TestCaseStats const &) = default; - TestCaseStats &operator=(TestCaseStats &&) = default; - virtual ~TestCaseStats(); - - TestCaseInfo testInfo; - Totals totals; - std::string stdOut; - std::string stdErr; - bool aborting; -}; + struct GroupInfo { + GroupInfo(std::string const &_name, + std::size_t _groupIndex, + std::size_t _groupsCount); -struct TestGroupStats { - TestGroupStats(GroupInfo const &_groupInfo, - Totals const &_totals, - bool _aborting); - TestGroupStats(GroupInfo const &_groupInfo); - - TestGroupStats(TestGroupStats const &) = default; - TestGroupStats(TestGroupStats &&) = default; - TestGroupStats &operator=(TestGroupStats const &) = default; - TestGroupStats &operator=(TestGroupStats &&) = default; - virtual ~TestGroupStats(); - - GroupInfo groupInfo; - Totals totals; - bool aborting; -}; + std::string name; + std::size_t groupIndex; + std::size_t groupsCounts; + }; -struct TestRunStats { - TestRunStats(TestRunInfo const &_runInfo, - Totals const &_totals, - bool _aborting); + struct AssertionStats { + AssertionStats(AssertionResult const &_assertionResult, + std::vector const &_infoMessages, + Totals const &_totals); - TestRunStats(TestRunStats const &) = default; - TestRunStats(TestRunStats &&) = default; - TestRunStats &operator=(TestRunStats const &) = default; - TestRunStats &operator=(TestRunStats &&) = default; - virtual ~TestRunStats(); + AssertionStats(AssertionStats const &) = default; - TestRunInfo runInfo; - Totals totals; - bool aborting; -}; + AssertionStats(AssertionStats &&) = default; + + AssertionStats &operator=(AssertionStats const &) = delete; + + AssertionStats &operator=(AssertionStats &&) = delete; + + virtual ~AssertionStats(); + + AssertionResult assertionResult; + std::vector infoMessages; + Totals totals; + }; + + struct SectionStats { + SectionStats(SectionInfo const &_sectionInfo, + Counts const &_assertions, + double _durationInSeconds, + bool _missingAssertions); + + SectionStats(SectionStats const &) = default; + + SectionStats(SectionStats &&) = default; + + SectionStats &operator=(SectionStats const &) = default; + + SectionStats &operator=(SectionStats &&) = default; + + virtual ~SectionStats(); + + SectionInfo sectionInfo; + Counts assertions; + double durationInSeconds; + bool missingAssertions; + }; + + struct TestCaseStats { + TestCaseStats(TestCaseInfo const &_testInfo, + Totals const &_totals, + std::string const &_stdOut, + std::string const &_stdErr, + bool _aborting); + + TestCaseStats(TestCaseStats const &) = default; + + TestCaseStats(TestCaseStats &&) = default; + + TestCaseStats &operator=(TestCaseStats const &) = default; + + TestCaseStats &operator=(TestCaseStats &&) = default; + + virtual ~TestCaseStats(); + + TestCaseInfo testInfo; + Totals totals; + std::string stdOut; + std::string stdErr; + bool aborting; + }; + + struct TestGroupStats { + TestGroupStats(GroupInfo const &_groupInfo, + Totals const &_totals, + bool _aborting); + + TestGroupStats(GroupInfo const &_groupInfo); + + TestGroupStats(TestGroupStats const &) = default; + + TestGroupStats(TestGroupStats &&) = default; + + TestGroupStats &operator=(TestGroupStats const &) = default; + + TestGroupStats &operator=(TestGroupStats &&) = default; + + virtual ~TestGroupStats(); + + GroupInfo groupInfo; + Totals totals; + bool aborting; + }; + + struct TestRunStats { + TestRunStats(TestRunInfo const &_runInfo, + Totals const &_totals, + bool _aborting); + + TestRunStats(TestRunStats const &) = default; + + TestRunStats(TestRunStats &&) = default; + + TestRunStats &operator=(TestRunStats const &) = default; + + TestRunStats &operator=(TestRunStats &&) = default; + + virtual ~TestRunStats(); + + TestRunInfo runInfo; + Totals totals; + bool aborting; + }; #if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) - struct BenchmarkInfo { + struct BenchmarkInfo { std::string name; double estimatedDuration; int iterations; @@ -5656,333 +6068,364 @@ struct TestRunStats { }; #endif // CATCH_CONFIG_ENABLE_BENCHMARKING -struct IStreamingReporter { - virtual ~IStreamingReporter() = default; + struct IStreamingReporter { + virtual ~IStreamingReporter() = default; + + // Implementing class must also provide the following static methods: + // static std::string getDescription(); + // static std::set getSupportedVerbosities() + + virtual ReporterPreferences getPreferences() const = 0; + + virtual void noMatchingTestCases(std::string const &spec) = 0; + + virtual void reportInvalidArguments(std::string const &) {} + + virtual void testRunStarting(TestRunInfo const &testRunInfo) = 0; + + virtual void testGroupStarting(GroupInfo const &groupInfo) = 0; + + virtual void testCaseStarting(TestCaseInfo const &testInfo) = 0; + + virtual void sectionStarting(SectionInfo const §ionInfo) = 0; + +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) + virtual void benchmarkPreparing( std::string const& ) {} + virtual void benchmarkStarting( BenchmarkInfo const& ) {} + virtual void benchmarkEnded( BenchmarkStats<> const& ) {} + virtual void benchmarkFailed( std::string const& ) {} +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING + + virtual void assertionStarting(AssertionInfo const &assertionInfo) = 0; + + // The return value indicates if the messages buffer should be cleared: + virtual bool assertionEnded(AssertionStats const &assertionStats) = 0; + + virtual void sectionEnded(SectionStats const §ionStats) = 0; + + virtual void testCaseEnded(TestCaseStats const &testCaseStats) = 0; + + virtual void testGroupEnded(TestGroupStats const &testGroupStats) = 0; + + virtual void testRunEnded(TestRunStats const &testRunStats) = 0; + + virtual void skipTest(TestCaseInfo const &testInfo) = 0; + + // Default empty implementation provided + virtual void fatalErrorEncountered(StringRef name); + + virtual bool isMulti() const; + }; + + using IStreamingReporterPtr = std::unique_ptr; + + struct IReporterFactory { + virtual ~IReporterFactory(); + + virtual IStreamingReporterPtr create(ReporterConfig const &config) const = 0; + + virtual std::string getDescription() const = 0; + }; + + using IReporterFactoryPtr = std::shared_ptr; + + struct IReporterRegistry { + using FactoryMap = std::map; + using Listeners = std::vector; + + virtual ~IReporterRegistry(); + + virtual IStreamingReporterPtr create(std::string const &name, IConfigPtr const &config) const = 0; + + virtual FactoryMap const &getFactories() const = 0; + + virtual Listeners const &getListeners() const = 0; + }; + +} // end namespace Catch + +// end catch_interfaces_reporter.h +#include +#include +#include +#include +#include +#include +#include + +namespace Catch { + void prepareExpandedExpression(AssertionResult &result); + +// Returns double formatted as %.3f (format expected on output) + std::string getFormattedDuration(double duration); + +//! Should the reporter show + bool shouldShowDuration(IConfig const &config, double duration); + + std::string serializeFilters(std::vector const &container); + + template + struct StreamingReporterBase : IStreamingReporter { + + StreamingReporterBase(ReporterConfig const &_config) + : m_config(_config.fullConfig()), + stream(_config.stream()) { + m_reporterPrefs.shouldRedirectStdOut = false; + if (!DerivedT::getSupportedVerbosities().count(m_config->verbosity())) + CATCH_ERROR("Verbosity level not supported by this reporter"); + } + + ReporterPreferences getPreferences() const override { + return m_reporterPrefs; + } + + static std::set getSupportedVerbosities() { + return {Verbosity::Normal}; + } + + ~StreamingReporterBase() override = default; + + void noMatchingTestCases(std::string const &) override {} + + void reportInvalidArguments(std::string const &) override {} + + void testRunStarting(TestRunInfo const &_testRunInfo) override { + currentTestRunInfo = _testRunInfo; + } + + void testGroupStarting(GroupInfo const &_groupInfo) override { + currentGroupInfo = _groupInfo; + } + + void testCaseStarting(TestCaseInfo const &_testInfo) override { + currentTestCaseInfo = _testInfo; + } + + void sectionStarting(SectionInfo const &_sectionInfo) override { + m_sectionStack.push_back(_sectionInfo); + } + + void sectionEnded(SectionStats const & /* _sectionStats */) override { + m_sectionStack.pop_back(); + } + + void testCaseEnded(TestCaseStats const & /* _testCaseStats */) override { + currentTestCaseInfo.reset(); + } + + void testGroupEnded(TestGroupStats const & /* _testGroupStats */) override { + currentGroupInfo.reset(); + } + + void testRunEnded(TestRunStats const & /* _testRunStats */) override { + currentTestCaseInfo.reset(); + currentGroupInfo.reset(); + currentTestRunInfo.reset(); + } + + void skipTest(TestCaseInfo const &) override { + // Don't do anything with this by default. + // It can optionally be overridden in the derived class. + } + + IConfigPtr m_config; + std::ostream &stream; + + LazyStat currentTestRunInfo; + LazyStat currentGroupInfo; + LazyStat currentTestCaseInfo; + + std::vector m_sectionStack; + ReporterPreferences m_reporterPrefs; + }; + + template + struct CumulativeReporterBase : IStreamingReporter { + template + struct Node { + explicit Node(T const &_value) : value(_value) {} + + virtual ~Node() {} + + using ChildNodes = std::vector>; + T value; + ChildNodes children; + }; + + struct SectionNode { + explicit SectionNode(SectionStats const &_stats) : stats(_stats) {} + + virtual ~SectionNode() = default; - // Implementing class must also provide the following static methods: - // static std::string getDescription(); - // static std::set getSupportedVerbosities() + bool operator==(SectionNode const &other) const { + return stats.sectionInfo.lineInfo == other.stats.sectionInfo.lineInfo; + } - virtual ReporterPreferences getPreferences() const = 0; + bool operator==(std::shared_ptr const &other) const { + return operator==(*other); + } - virtual void noMatchingTestCases(std::string const &spec) = 0; + SectionStats stats; + using ChildSections = std::vector>; + using Assertions = std::vector; + ChildSections childSections; + Assertions assertions; + std::string stdOut; + std::string stdErr; + }; - virtual void reportInvalidArguments(std::string const &) {} + struct BySectionInfo { + BySectionInfo(SectionInfo const &other) : m_other(other) {} - virtual void testRunStarting(TestRunInfo const &testRunInfo) = 0; - virtual void testGroupStarting(GroupInfo const &groupInfo) = 0; + BySectionInfo(BySectionInfo const &other) : m_other(other.m_other) {} - virtual void testCaseStarting(TestCaseInfo const &testInfo) = 0; - virtual void sectionStarting(SectionInfo const §ionInfo) = 0; + bool operator()(std::shared_ptr const &node) const { + return ((node->stats.sectionInfo.name == m_other.name) && + (node->stats.sectionInfo.lineInfo == m_other.lineInfo)); + } -#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) - virtual void benchmarkPreparing( std::string const& ) {} - virtual void benchmarkStarting( BenchmarkInfo const& ) {} - virtual void benchmarkEnded( BenchmarkStats<> const& ) {} - virtual void benchmarkFailed( std::string const& ) {} -#endif // CATCH_CONFIG_ENABLE_BENCHMARKING + void operator=(BySectionInfo const &) = delete; - virtual void assertionStarting(AssertionInfo const &assertionInfo) = 0; + private: + SectionInfo const &m_other; + }; - // The return value indicates if the messages buffer should be cleared: - virtual bool assertionEnded(AssertionStats const &assertionStats) = 0; + using TestCaseNode = Node; + using TestGroupNode = Node; + using TestRunNode = Node; - virtual void sectionEnded(SectionStats const §ionStats) = 0; - virtual void testCaseEnded(TestCaseStats const &testCaseStats) = 0; - virtual void testGroupEnded(TestGroupStats const &testGroupStats) = 0; - virtual void testRunEnded(TestRunStats const &testRunStats) = 0; + CumulativeReporterBase(ReporterConfig const &_config) + : m_config(_config.fullConfig()), + stream(_config.stream()) { + m_reporterPrefs.shouldRedirectStdOut = false; + if (!DerivedT::getSupportedVerbosities().count(m_config->verbosity())) + CATCH_ERROR("Verbosity level not supported by this reporter"); + } - virtual void skipTest(TestCaseInfo const &testInfo) = 0; + ~CumulativeReporterBase() override = default; - // Default empty implementation provided - virtual void fatalErrorEncountered(StringRef name); + ReporterPreferences getPreferences() const override { + return m_reporterPrefs; + } - virtual bool isMulti() const; -}; -using IStreamingReporterPtr = std::unique_ptr; + static std::set getSupportedVerbosities() { + return {Verbosity::Normal}; + } -struct IReporterFactory { - virtual ~IReporterFactory(); - virtual IStreamingReporterPtr create(ReporterConfig const &config) const = 0; - virtual std::string getDescription() const = 0; -}; -using IReporterFactoryPtr = std::shared_ptr; + void testRunStarting(TestRunInfo const &) override {} -struct IReporterRegistry { - using FactoryMap = std::map; - using Listeners = std::vector; + void testGroupStarting(GroupInfo const &) override {} - virtual ~IReporterRegistry(); - virtual IStreamingReporterPtr create(std::string const &name, IConfigPtr const &config) const = 0; - virtual FactoryMap const &getFactories() const = 0; - virtual Listeners const &getListeners() const = 0; -}; + void testCaseStarting(TestCaseInfo const &) override {} -} // end namespace Catch + void sectionStarting(SectionInfo const §ionInfo) override { + SectionStats incompleteStats(sectionInfo, Counts(), 0, false); + std::shared_ptr node; + if (m_sectionStack.empty()) { + if (!m_rootSection) + m_rootSection = std::make_shared(incompleteStats); + node = m_rootSection; + } else { + SectionNode &parentNode = *m_sectionStack.back(); + auto it = + std::find_if(parentNode.childSections.begin(), + parentNode.childSections.end(), + BySectionInfo(sectionInfo)); + if (it == parentNode.childSections.end()) { + node = std::make_shared(incompleteStats); + parentNode.childSections.push_back(node); + } else + node = *it; + } + m_sectionStack.push_back(node); + m_deepestSection = std::move(node); + } -// end catch_interfaces_reporter.h -#include -#include -#include -#include -#include -#include -#include + void assertionStarting(AssertionInfo const &) override {} + + bool assertionEnded(AssertionStats const &assertionStats) override { + assert(!m_sectionStack.empty()); + // AssertionResult holds a pointer to a temporary DecomposedExpression, + // which getExpandedExpression() calls to build the expression string. + // Our section stack copy of the assertionResult will likely outlive the + // temporary, so it must be expanded or discarded now to avoid calling + // a destroyed object later. + prepareExpandedExpression(const_cast( assertionStats.assertionResult )); + SectionNode §ionNode = *m_sectionStack.back(); + sectionNode.assertions.push_back(assertionStats); + return true; + } -namespace Catch { -void prepareExpandedExpression(AssertionResult &result); + void sectionEnded(SectionStats const §ionStats) override { + assert(!m_sectionStack.empty()); + SectionNode &node = *m_sectionStack.back(); + node.stats = sectionStats; + m_sectionStack.pop_back(); + } -// Returns double formatted as %.3f (format expected on output) -std::string getFormattedDuration(double duration); + void testCaseEnded(TestCaseStats const &testCaseStats) override { + auto node = std::make_shared(testCaseStats); + assert(m_sectionStack.size() == 0); + node->children.push_back(m_rootSection); + m_testCases.push_back(node); + m_rootSection.reset(); -//! Should the reporter show -bool shouldShowDuration(IConfig const &config, double duration); - -std::string serializeFilters(std::vector const &container); - -template -struct StreamingReporterBase : IStreamingReporter { - - StreamingReporterBase(ReporterConfig const &_config) - : m_config(_config.fullConfig()), - stream(_config.stream()) { - m_reporterPrefs.shouldRedirectStdOut = false; - if (!DerivedT::getSupportedVerbosities().count(m_config->verbosity())) - CATCH_ERROR("Verbosity level not supported by this reporter"); - } - - ReporterPreferences getPreferences() const override { - return m_reporterPrefs; - } - - static std::set getSupportedVerbosities() { - return {Verbosity::Normal}; - } - - ~StreamingReporterBase() override = default; - - void noMatchingTestCases(std::string const &) override {} - - void reportInvalidArguments(std::string const &) override {} - - void testRunStarting(TestRunInfo const &_testRunInfo) override { - currentTestRunInfo = _testRunInfo; - } - - void testGroupStarting(GroupInfo const &_groupInfo) override { - currentGroupInfo = _groupInfo; - } - - void testCaseStarting(TestCaseInfo const &_testInfo) override { - currentTestCaseInfo = _testInfo; - } - void sectionStarting(SectionInfo const &_sectionInfo) override { - m_sectionStack.push_back(_sectionInfo); - } - - void sectionEnded(SectionStats const & /* _sectionStats */) override { - m_sectionStack.pop_back(); - } - void testCaseEnded(TestCaseStats const & /* _testCaseStats */) override { - currentTestCaseInfo.reset(); - } - void testGroupEnded(TestGroupStats const & /* _testGroupStats */) override { - currentGroupInfo.reset(); - } - void testRunEnded(TestRunStats const & /* _testRunStats */) override { - currentTestCaseInfo.reset(); - currentGroupInfo.reset(); - currentTestRunInfo.reset(); - } - - void skipTest(TestCaseInfo const &) override { - // Don't do anything with this by default. - // It can optionally be overridden in the derived class. - } - - IConfigPtr m_config; - std::ostream &stream; - - LazyStat currentTestRunInfo; - LazyStat currentGroupInfo; - LazyStat currentTestCaseInfo; - - std::vector m_sectionStack; - ReporterPreferences m_reporterPrefs; -}; + assert(m_deepestSection); + m_deepestSection->stdOut = testCaseStats.stdOut; + m_deepestSection->stdErr = testCaseStats.stdErr; + } -template -struct CumulativeReporterBase : IStreamingReporter { - template - struct Node { - explicit Node(T const &_value) : value(_value) {} - virtual ~Node() {} - - using ChildNodes = std::vector>; - T value; - ChildNodes children; - }; - struct SectionNode { - explicit SectionNode(SectionStats const &_stats) : stats(_stats) {} - virtual ~SectionNode() = default; - - bool operator==(SectionNode const &other) const { - return stats.sectionInfo.lineInfo == other.stats.sectionInfo.lineInfo; - } - bool operator==(std::shared_ptr const &other) const { - return operator==(*other); - } - - SectionStats stats; - using ChildSections = std::vector>; - using Assertions = std::vector; - ChildSections childSections; - Assertions assertions; - std::string stdOut; - std::string stdErr; - }; - - struct BySectionInfo { - BySectionInfo(SectionInfo const &other) : m_other(other) {} - BySectionInfo(BySectionInfo const &other) : m_other(other.m_other) {} - bool operator()(std::shared_ptr const &node) const { - return ((node->stats.sectionInfo.name == m_other.name) && - (node->stats.sectionInfo.lineInfo == m_other.lineInfo)); - } - void operator=(BySectionInfo const &) = delete; - - private: - SectionInfo const &m_other; - }; - - using TestCaseNode = Node; - using TestGroupNode = Node; - using TestRunNode = Node; - - CumulativeReporterBase(ReporterConfig const &_config) - : m_config(_config.fullConfig()), - stream(_config.stream()) { - m_reporterPrefs.shouldRedirectStdOut = false; - if (!DerivedT::getSupportedVerbosities().count(m_config->verbosity())) - CATCH_ERROR("Verbosity level not supported by this reporter"); - } - ~CumulativeReporterBase() override = default; - - ReporterPreferences getPreferences() const override { - return m_reporterPrefs; - } - - static std::set getSupportedVerbosities() { - return {Verbosity::Normal}; - } - - void testRunStarting(TestRunInfo const &) override {} - void testGroupStarting(GroupInfo const &) override {} - - void testCaseStarting(TestCaseInfo const &) override {} - - void sectionStarting(SectionInfo const §ionInfo) override { - SectionStats incompleteStats(sectionInfo, Counts(), 0, false); - std::shared_ptr node; - if (m_sectionStack.empty()) { - if (!m_rootSection) - m_rootSection = std::make_shared(incompleteStats); - node = m_rootSection; - } else { - SectionNode &parentNode = *m_sectionStack.back(); - auto it = - std::find_if(parentNode.childSections.begin(), - parentNode.childSections.end(), - BySectionInfo(sectionInfo)); - if (it == parentNode.childSections.end()) { - node = std::make_shared(incompleteStats); - parentNode.childSections.push_back(node); - } else - node = *it; - } - m_sectionStack.push_back(node); - m_deepestSection = std::move(node); - } - - void assertionStarting(AssertionInfo const &) override {} - - bool assertionEnded(AssertionStats const &assertionStats) override { - assert(!m_sectionStack.empty()); - // AssertionResult holds a pointer to a temporary DecomposedExpression, - // which getExpandedExpression() calls to build the expression string. - // Our section stack copy of the assertionResult will likely outlive the - // temporary, so it must be expanded or discarded now to avoid calling - // a destroyed object later. - prepareExpandedExpression(const_cast( assertionStats.assertionResult )); - SectionNode §ionNode = *m_sectionStack.back(); - sectionNode.assertions.push_back(assertionStats); - return true; - } - void sectionEnded(SectionStats const §ionStats) override { - assert(!m_sectionStack.empty()); - SectionNode &node = *m_sectionStack.back(); - node.stats = sectionStats; - m_sectionStack.pop_back(); - } - void testCaseEnded(TestCaseStats const &testCaseStats) override { - auto node = std::make_shared(testCaseStats); - assert(m_sectionStack.size() == 0); - node->children.push_back(m_rootSection); - m_testCases.push_back(node); - m_rootSection.reset(); - - assert(m_deepestSection); - m_deepestSection->stdOut = testCaseStats.stdOut; - m_deepestSection->stdErr = testCaseStats.stdErr; - } - void testGroupEnded(TestGroupStats const &testGroupStats) override { - auto node = std::make_shared(testGroupStats); - node->children.swap(m_testCases); - m_testGroups.push_back(node); - } - void testRunEnded(TestRunStats const &testRunStats) override { - auto node = std::make_shared(testRunStats); - node->children.swap(m_testGroups); - m_testRuns.push_back(node); - testRunEndedCumulative(); - } - virtual void testRunEndedCumulative() = 0; - - void skipTest(TestCaseInfo const &) override {} - - IConfigPtr m_config; - std::ostream &stream; - std::vector m_assertions; - std::vector>> m_sections; - std::vector> m_testCases; - std::vector> m_testGroups; - - std::vector> m_testRuns; - - std::shared_ptr m_rootSection; - std::shared_ptr m_deepestSection; - std::vector> m_sectionStack; - ReporterPreferences m_reporterPrefs; -}; + void testGroupEnded(TestGroupStats const &testGroupStats) override { + auto node = std::make_shared(testGroupStats); + node->children.swap(m_testCases); + m_testGroups.push_back(node); + } -template -char const *getLineOfChars() { - static char line[CATCH_CONFIG_CONSOLE_WIDTH] = {0}; - if (!*line) { - std::memset(line, C, CATCH_CONFIG_CONSOLE_WIDTH - 1); - line[CATCH_CONFIG_CONSOLE_WIDTH - 1] = 0; - } - return line; -} + void testRunEnded(TestRunStats const &testRunStats) override { + auto node = std::make_shared(testRunStats); + node->children.swap(m_testGroups); + m_testRuns.push_back(node); + testRunEndedCumulative(); + } -struct TestEventListenerBase : StreamingReporterBase { - TestEventListenerBase(ReporterConfig const &_config); + virtual void testRunEndedCumulative() = 0; - static std::set getSupportedVerbosities(); + void skipTest(TestCaseInfo const &) override {} - void assertionStarting(AssertionInfo const &) override; - bool assertionEnded(AssertionStats const &) override; -}; + IConfigPtr m_config; + std::ostream &stream; + std::vector m_assertions; + std::vector>> m_sections; + std::vector> m_testCases; + std::vector> m_testGroups; + + std::vector> m_testRuns; + + std::shared_ptr m_rootSection; + std::shared_ptr m_deepestSection; + std::vector> m_sectionStack; + ReporterPreferences m_reporterPrefs; + }; + + template + char const *getLineOfChars() { + static char line[CATCH_CONFIG_CONSOLE_WIDTH] = {0}; + if (!*line) { + std::memset(line, C, CATCH_CONFIG_CONSOLE_WIDTH - 1); + line[CATCH_CONFIG_CONSOLE_WIDTH - 1] = 0; + } + return line; + } + + struct TestEventListenerBase : StreamingReporterBase { + TestEventListenerBase(ReporterConfig const &_config); + + static std::set getSupportedVerbosities(); + + void assertionStarting(AssertionInfo const &) override; + + bool assertionEnded(AssertionStats const &) override; + }; } // end namespace Catch @@ -5991,57 +6434,60 @@ struct TestEventListenerBase : StreamingReporterBase { namespace Catch { -struct Colour { - enum Code { - None = 0, - - White, - Red, - Green, - Blue, - Cyan, - Yellow, - Grey, - - Bright = 0x10, - - BrightRed = Bright | Red, - BrightGreen = Bright | Green, - LightGrey = Bright | Grey, - BrightWhite = Bright | White, - BrightYellow = Bright | Yellow, - - // By intention - FileName = LightGrey, - Warning = BrightYellow, - ResultError = BrightRed, - ResultSuccess = BrightGreen, - ResultExpectedFailure = Warning, - - Error = BrightRed, - Success = Green, - - OriginalExpression = Cyan, - ReconstructedExpression = BrightYellow, - - SecondaryText = LightGrey, - Headers = White - }; - - // Use constructed object for RAII guard - Colour(Code _colourCode); - Colour(Colour &&other) noexcept; - Colour &operator=(Colour &&other) noexcept; - ~Colour(); - - // Use static method for one-shot changes - static void use(Code _colourCode); - - private: - bool m_moved = false; -}; + struct Colour { + enum Code { + None = 0, + + White, + Red, + Green, + Blue, + Cyan, + Yellow, + Grey, + + Bright = 0x10, + + BrightRed = Bright | Red, + BrightGreen = Bright | Green, + LightGrey = Bright | Grey, + BrightWhite = Bright | White, + BrightYellow = Bright | Yellow, + + // By intention + FileName = LightGrey, + Warning = BrightYellow, + ResultError = BrightRed, + ResultSuccess = BrightGreen, + ResultExpectedFailure = Warning, + + Error = BrightRed, + Success = Green, + + OriginalExpression = Cyan, + ReconstructedExpression = BrightYellow, + + SecondaryText = LightGrey, + Headers = White + }; + + // Use constructed object for RAII guard + Colour(Code _colourCode); -std::ostream &operator<<(std::ostream &os, Colour const &); + Colour(Colour &&other) noexcept; + + Colour &operator=(Colour &&other) noexcept; + + ~Colour(); + + // Use static method for one-shot changes + static void use(Code _colourCode); + + private: + bool m_moved = false; + }; + + std::ostream &operator<<(std::ostream &os, Colour const &); } // end namespace Catch @@ -6051,46 +6497,47 @@ std::ostream &operator<<(std::ostream &os, Colour const &); namespace Catch { -template -class ReporterRegistrar { + template + class ReporterRegistrar { + + class ReporterFactory : public IReporterFactory { - class ReporterFactory : public IReporterFactory { + IStreamingReporterPtr create(ReporterConfig const &config) const override { + return std::unique_ptr(new T(config)); + } - IStreamingReporterPtr create(ReporterConfig const &config) const override { - return std::unique_ptr(new T(config)); - } + std::string getDescription() const override { + return T::getDescription(); + } + }; - std::string getDescription() const override { - return T::getDescription(); - } - }; + public: - public: + explicit ReporterRegistrar(std::string const &name) { + getMutableRegistryHub().registerReporter(name, std::make_shared()); + } + }; - explicit ReporterRegistrar(std::string const &name) { - getMutableRegistryHub().registerReporter(name, std::make_shared()); - } -}; + template + class ListenerRegistrar { -template -class ListenerRegistrar { + class ListenerFactory : public IReporterFactory { - class ListenerFactory : public IReporterFactory { + IStreamingReporterPtr create(ReporterConfig const &config) const override { + return std::unique_ptr(new T(config)); + } - IStreamingReporterPtr create(ReporterConfig const &config) const override { - return std::unique_ptr(new T(config)); - } - std::string getDescription() const override { - return std::string(); - } - }; + std::string getDescription() const override { + return std::string(); + } + }; - public: + public: - ListenerRegistrar() { - getMutableRegistryHub().registerListener(std::make_shared()); - } -}; + ListenerRegistrar() { + getMutableRegistryHub().registerListener(std::make_shared()); + } + }; } #if !defined(CATCH_CONFIG_DISABLE) @@ -6119,25 +6566,25 @@ class ListenerRegistrar { namespace Catch { -struct CompactReporter : StreamingReporterBase { + struct CompactReporter : StreamingReporterBase { - using StreamingReporterBase::StreamingReporterBase; + using StreamingReporterBase::StreamingReporterBase; - ~CompactReporter() override; + ~CompactReporter() override; - static std::string getDescription(); + static std::string getDescription(); - void noMatchingTestCases(std::string const &spec) override; + void noMatchingTestCases(std::string const &spec) override; - void assertionStarting(AssertionInfo const &) override; + void assertionStarting(AssertionInfo const &) override; - bool assertionEnded(AssertionStats const &_assertionStats) override; + bool assertionEnded(AssertionStats const &_assertionStats) override; - void sectionEnded(SectionStats const &_sectionStats) override; + void sectionEnded(SectionStats const &_sectionStats) override; - void testRunEnded(TestRunStats const &_testRunStats) override; + void testRunEnded(TestRunStats const &_testRunStats) override; -}; + }; } // end namespace Catch @@ -6153,64 +6600,79 @@ struct CompactReporter : StreamingReporterBase { namespace Catch { // Fwd decls -struct SummaryColumn; -class TablePrinter; + struct SummaryColumn; + + class TablePrinter; + + struct ConsoleReporter : StreamingReporterBase { + std::unique_ptr m_tablePrinter; -struct ConsoleReporter : StreamingReporterBase { - std::unique_ptr m_tablePrinter; + ConsoleReporter(ReporterConfig const &config); - ConsoleReporter(ReporterConfig const &config); - ~ConsoleReporter() override; - static std::string getDescription(); + ~ConsoleReporter() override; - void noMatchingTestCases(std::string const &spec) override; + static std::string getDescription(); - void reportInvalidArguments(std::string const &arg) override; + void noMatchingTestCases(std::string const &spec) override; - void assertionStarting(AssertionInfo const &) override; + void reportInvalidArguments(std::string const &arg) override; - bool assertionEnded(AssertionStats const &_assertionStats) override; + void assertionStarting(AssertionInfo const &) override; - void sectionStarting(SectionInfo const &_sectionInfo) override; - void sectionEnded(SectionStats const &_sectionStats) override; + bool assertionEnded(AssertionStats const &_assertionStats) override; + + void sectionStarting(SectionInfo const &_sectionInfo) override; + + void sectionEnded(SectionStats const &_sectionStats) override; #if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) - void benchmarkPreparing(std::string const& name) override; + void benchmarkPreparing(std::string const& name) override; void benchmarkStarting(BenchmarkInfo const& info) override; void benchmarkEnded(BenchmarkStats<> const& stats) override; void benchmarkFailed(std::string const& error) override; #endif // CATCH_CONFIG_ENABLE_BENCHMARKING - void testCaseEnded(TestCaseStats const &_testCaseStats) override; - void testGroupEnded(TestGroupStats const &_testGroupStats) override; - void testRunEnded(TestRunStats const &_testRunStats) override; - void testRunStarting(TestRunInfo const &_testRunInfo) override; - private: + void testCaseEnded(TestCaseStats const &_testCaseStats) override; - void lazyPrint(); + void testGroupEnded(TestGroupStats const &_testGroupStats) override; - void lazyPrintWithoutClosingBenchmarkTable(); - void lazyPrintRunInfo(); - void lazyPrintGroupInfo(); - void printTestCaseAndSectionHeader(); + void testRunEnded(TestRunStats const &_testRunStats) override; - void printClosedHeader(std::string const &_name); - void printOpenHeader(std::string const &_name); + void testRunStarting(TestRunInfo const &_testRunInfo) override; - // if string has a : in first line will set indent to follow it on - // subsequent lines - void printHeaderString(std::string const &_string, std::size_t indent = 0); + private: - void printTotals(Totals const &totals); - void printSummaryRow(std::string const &label, std::vector const &cols, std::size_t row); + void lazyPrint(); - void printTotalsDivider(Totals const &totals); - void printSummaryDivider(); - void printTestFilters(); + void lazyPrintWithoutClosingBenchmarkTable(); - private: - bool m_headerPrinted = false; -}; + void lazyPrintRunInfo(); + + void lazyPrintGroupInfo(); + + void printTestCaseAndSectionHeader(); + + void printClosedHeader(std::string const &_name); + + void printOpenHeader(std::string const &_name); + + // if string has a : in first line will set indent to follow it on + // subsequent lines + void printHeaderString(std::string const &_string, std::size_t indent = 0); + + void printTotals(Totals const &totals); + + void printSummaryRow(std::string const &label, std::vector const &cols, std::size_t row); + + void printTotalsDivider(Totals const &totals); + + void printSummaryDivider(); + + void printTestFilters(); + + private: + bool m_headerPrinted = false; + }; } // end namespace Catch @@ -6226,152 +6688,163 @@ struct ConsoleReporter : StreamingReporterBase { #include namespace Catch { -enum class XmlFormatting { - None = 0x00, - Indent = 0x01, - Newline = 0x02, -}; + enum class XmlFormatting { + None = 0x00, + Indent = 0x01, + Newline = 0x02, + }; + + XmlFormatting operator|(XmlFormatting lhs, XmlFormatting rhs); -XmlFormatting operator|(XmlFormatting lhs, XmlFormatting rhs); -XmlFormatting operator&(XmlFormatting lhs, XmlFormatting rhs); + XmlFormatting operator&(XmlFormatting lhs, XmlFormatting rhs); -class XmlEncode { - public: - enum ForWhat { ForTextNodes, ForAttributes }; + class XmlEncode { + public: + enum ForWhat { + ForTextNodes, ForAttributes + }; - XmlEncode(std::string const &str, ForWhat forWhat = ForTextNodes); + XmlEncode(std::string const &str, ForWhat forWhat = ForTextNodes); - void encodeTo(std::ostream &os) const; + void encodeTo(std::ostream &os) const; - friend std::ostream &operator<<(std::ostream &os, XmlEncode const &xmlEncode); + friend std::ostream &operator<<(std::ostream &os, XmlEncode const &xmlEncode); - private: - std::string m_str; - ForWhat m_forWhat; -}; + private: + std::string m_str; + ForWhat m_forWhat; + }; -class XmlWriter { - public: + class XmlWriter { + public: - class ScopedElement { - public: - ScopedElement(XmlWriter *writer, XmlFormatting fmt); + class ScopedElement { + public: + ScopedElement(XmlWriter *writer, XmlFormatting fmt); - ScopedElement(ScopedElement &&other) noexcept; - ScopedElement &operator=(ScopedElement &&other) noexcept; + ScopedElement(ScopedElement &&other) noexcept; - ~ScopedElement(); + ScopedElement &operator=(ScopedElement &&other) noexcept; - ScopedElement &writeText(std::string const &text, - XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent); + ~ScopedElement(); - template - ScopedElement &writeAttribute(std::string const &name, T const &attribute) { - m_writer->writeAttribute(name, attribute); - return *this; - } + ScopedElement &writeText(std::string const &text, + XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent); + + template + ScopedElement &writeAttribute(std::string const &name, T const &attribute) { + m_writer->writeAttribute(name, attribute); + return *this; + } - private: - mutable XmlWriter *m_writer = nullptr; - XmlFormatting m_fmt; - }; + private: + mutable XmlWriter *m_writer = nullptr; + XmlFormatting m_fmt; + }; - XmlWriter(std::ostream &os = Catch::cout()); - ~XmlWriter(); + XmlWriter(std::ostream &os = Catch::cout()); - XmlWriter(XmlWriter const &) = delete; - XmlWriter &operator=(XmlWriter const &) = delete; + ~XmlWriter(); - XmlWriter &startElement(std::string const &name, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent); + XmlWriter(XmlWriter const &) = delete; - ScopedElement scopedElement(std::string const &name, - XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent); + XmlWriter &operator=(XmlWriter const &) = delete; - XmlWriter &endElement(XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent); + XmlWriter & + startElement(std::string const &name, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent); - XmlWriter &writeAttribute(std::string const &name, std::string const &attribute); + ScopedElement scopedElement(std::string const &name, + XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent); - XmlWriter &writeAttribute(std::string const &name, bool attribute); + XmlWriter &endElement(XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent); - template - XmlWriter &writeAttribute(std::string const &name, T const &attribute) { - ReusableStringStream rss; - rss << attribute; - return writeAttribute(name, rss.str()); - } + XmlWriter &writeAttribute(std::string const &name, std::string const &attribute); - XmlWriter &writeText(std::string const &text, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent); + XmlWriter &writeAttribute(std::string const &name, bool attribute); + + template + XmlWriter &writeAttribute(std::string const &name, T const &attribute) { + ReusableStringStream rss; + rss << attribute; + return writeAttribute(name, rss.str()); + } - XmlWriter &writeComment(std::string const &text, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent); + XmlWriter & + writeText(std::string const &text, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent); - void writeStylesheetRef(std::string const &url); + XmlWriter & + writeComment(std::string const &text, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent); - XmlWriter &writeBlankLine(); + void writeStylesheetRef(std::string const &url); - void ensureTagClosed(); + XmlWriter &writeBlankLine(); - private: + void ensureTagClosed(); - void applyFormatting(XmlFormatting fmt); + private: - void writeDeclaration(); + void applyFormatting(XmlFormatting fmt); - void newlineIfNecessary(); + void writeDeclaration(); - bool m_tagIsOpen = false; - bool m_needsNewline = false; - std::vector m_tags; - std::string m_indent; - std::ostream &m_os; -}; + void newlineIfNecessary(); + + bool m_tagIsOpen = false; + bool m_needsNewline = false; + std::vector m_tags; + std::string m_indent; + std::ostream &m_os; + }; } // end catch_xmlwriter.h namespace Catch { -class JunitReporter : public CumulativeReporterBase { - public: - JunitReporter(ReporterConfig const &_config); + class JunitReporter : public CumulativeReporterBase { + public: + JunitReporter(ReporterConfig const &_config); - ~JunitReporter() override; + ~JunitReporter() override; - static std::string getDescription(); + static std::string getDescription(); - void noMatchingTestCases(std::string const & /*spec*/) override; + void noMatchingTestCases(std::string const & /*spec*/) override; - void testRunStarting(TestRunInfo const &runInfo) override; + void testRunStarting(TestRunInfo const &runInfo) override; - void testGroupStarting(GroupInfo const &groupInfo) override; + void testGroupStarting(GroupInfo const &groupInfo) override; - void testCaseStarting(TestCaseInfo const &testCaseInfo) override; - bool assertionEnded(AssertionStats const &assertionStats) override; + void testCaseStarting(TestCaseInfo const &testCaseInfo) override; - void testCaseEnded(TestCaseStats const &testCaseStats) override; + bool assertionEnded(AssertionStats const &assertionStats) override; - void testGroupEnded(TestGroupStats const &testGroupStats) override; + void testCaseEnded(TestCaseStats const &testCaseStats) override; - void testRunEndedCumulative() override; + void testGroupEnded(TestGroupStats const &testGroupStats) override; - void writeGroup(TestGroupNode const &groupNode, double suiteTime); + void testRunEndedCumulative() override; - void writeTestCase(TestCaseNode const &testCaseNode); + void writeGroup(TestGroupNode const &groupNode, double suiteTime); - void writeSection(std::string const &className, - std::string const &rootName, - SectionNode const §ionNode, - bool testOkToFail); + void writeTestCase(TestCaseNode const &testCaseNode); - void writeAssertions(SectionNode const §ionNode); - void writeAssertion(AssertionStats const &stats); + void writeSection(std::string const &className, + std::string const &rootName, + SectionNode const §ionNode, + bool testOkToFail); - XmlWriter xml; - Timer suiteTimer; - std::string stdOutForSuite; - std::string stdErrForSuite; - unsigned int unexpectedExceptions = 0; - bool m_okToFail = false; -}; + void writeAssertions(SectionNode const §ionNode); + + void writeAssertion(AssertionStats const &stats); + + XmlWriter xml; + Timer suiteTimer; + std::string stdOutForSuite; + std::string stdErrForSuite; + unsigned int unexpectedExceptions = 0; + bool m_okToFail = false; + }; } // end namespace Catch @@ -6379,54 +6852,54 @@ class JunitReporter : public CumulativeReporterBase { // start catch_reporter_xml.h namespace Catch { -class XmlReporter : public StreamingReporterBase { - public: - XmlReporter(ReporterConfig const &_config); + class XmlReporter : public StreamingReporterBase { + public: + XmlReporter(ReporterConfig const &_config); - ~XmlReporter() override; + ~XmlReporter() override; - static std::string getDescription(); + static std::string getDescription(); - virtual std::string getStylesheetRef() const; + virtual std::string getStylesheetRef() const; - void writeSourceInfo(SourceLineInfo const &sourceInfo); + void writeSourceInfo(SourceLineInfo const &sourceInfo); - public: // StreamingReporterBase + public: // StreamingReporterBase - void noMatchingTestCases(std::string const &s) override; + void noMatchingTestCases(std::string const &s) override; - void testRunStarting(TestRunInfo const &testInfo) override; + void testRunStarting(TestRunInfo const &testInfo) override; - void testGroupStarting(GroupInfo const &groupInfo) override; + void testGroupStarting(GroupInfo const &groupInfo) override; - void testCaseStarting(TestCaseInfo const &testInfo) override; + void testCaseStarting(TestCaseInfo const &testInfo) override; - void sectionStarting(SectionInfo const §ionInfo) override; + void sectionStarting(SectionInfo const §ionInfo) override; - void assertionStarting(AssertionInfo const &) override; + void assertionStarting(AssertionInfo const &) override; - bool assertionEnded(AssertionStats const &assertionStats) override; + bool assertionEnded(AssertionStats const &assertionStats) override; - void sectionEnded(SectionStats const §ionStats) override; + void sectionEnded(SectionStats const §ionStats) override; - void testCaseEnded(TestCaseStats const &testCaseStats) override; + void testCaseEnded(TestCaseStats const &testCaseStats) override; - void testGroupEnded(TestGroupStats const &testGroupStats) override; + void testGroupEnded(TestGroupStats const &testGroupStats) override; - void testRunEnded(TestRunStats const &testRunStats) override; + void testRunEnded(TestRunStats const &testRunStats) override; #if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) - void benchmarkPreparing(std::string const& name) override; + void benchmarkPreparing(std::string const& name) override; void benchmarkStarting(BenchmarkInfo const&) override; void benchmarkEnded(BenchmarkStats<> const&) override; void benchmarkFailed(std::string const&) override; #endif // CATCH_CONFIG_ENABLE_BENCHMARKING - private: - Timer m_testCaseTimer; - XmlWriter m_xml; - int m_sectionDepth = 0; -}; + private: + Timer m_testCaseTimer; + XmlWriter m_xml; + int m_sectionDepth = 0; + }; } // end namespace Catch @@ -7477,161 +7950,184 @@ namespace Catch { #include namespace Catch { -namespace TestCaseTracking { + namespace TestCaseTracking { + + struct NameAndLocation { + std::string name; + SourceLineInfo location; -struct NameAndLocation { - std::string name; - SourceLineInfo location; + NameAndLocation(std::string const &_name, SourceLineInfo const &_location); - NameAndLocation(std::string const &_name, SourceLineInfo const &_location); - friend bool operator==(NameAndLocation const &lhs, NameAndLocation const &rhs) { - return lhs.name == rhs.name - && lhs.location == rhs.location; - } -}; + friend bool operator==(NameAndLocation const &lhs, NameAndLocation const &rhs) { + return lhs.name == rhs.name + && lhs.location == rhs.location; + } + }; -class ITracker; + class ITracker; -using ITrackerPtr = std::shared_ptr; + using ITrackerPtr = std::shared_ptr; -class ITracker { - NameAndLocation m_nameAndLocation; + class ITracker { + NameAndLocation m_nameAndLocation; - public: - ITracker(NameAndLocation const &nameAndLoc) : - m_nameAndLocation(nameAndLoc) {} + public: + ITracker(NameAndLocation const &nameAndLoc) : + m_nameAndLocation(nameAndLoc) {} + + // static queries + NameAndLocation const &nameAndLocation() const { + return m_nameAndLocation; + } - // static queries - NameAndLocation const &nameAndLocation() const { - return m_nameAndLocation; - } + virtual ~ITracker(); - virtual ~ITracker(); + // dynamic queries + virtual bool isComplete() const = 0; // Successfully completed or failed + virtual bool isSuccessfullyCompleted() const = 0; - // dynamic queries - virtual bool isComplete() const = 0; // Successfully completed or failed - virtual bool isSuccessfullyCompleted() const = 0; - virtual bool isOpen() const = 0; // Started but not complete - virtual bool hasChildren() const = 0; - virtual bool hasStarted() const = 0; + virtual bool isOpen() const = 0; // Started but not complete + virtual bool hasChildren() const = 0; - virtual ITracker &parent() = 0; + virtual bool hasStarted() const = 0; - // actions - virtual void close() = 0; // Successfully complete - virtual void fail() = 0; - virtual void markAsNeedingAnotherRun() = 0; + virtual ITracker &parent() = 0; - virtual void addChild(ITrackerPtr const &child) = 0; - virtual ITrackerPtr findChild(NameAndLocation const &nameAndLocation) = 0; - virtual void openChild() = 0; + // actions + virtual void close() = 0; // Successfully complete + virtual void fail() = 0; - // Debug/ checking - virtual bool isSectionTracker() const = 0; - virtual bool isGeneratorTracker() const = 0; -}; + virtual void markAsNeedingAnotherRun() = 0; -class TrackerContext { + virtual void addChild(ITrackerPtr const &child) = 0; - enum RunState { - NotStarted, - Executing, - CompletedCycle - }; + virtual ITrackerPtr findChild(NameAndLocation const &nameAndLocation) = 0; - ITrackerPtr m_rootTracker; - ITracker *m_currentTracker = nullptr; - RunState m_runState = NotStarted; + virtual void openChild() = 0; - public: + // Debug/ checking + virtual bool isSectionTracker() const = 0; - ITracker &startRun(); - void endRun(); + virtual bool isGeneratorTracker() const = 0; + }; - void startCycle(); - void completeCycle(); + class TrackerContext { - bool completedCycle() const; - ITracker ¤tTracker(); - void setCurrentTracker(ITracker *tracker); -}; + enum RunState { + NotStarted, + Executing, + CompletedCycle + }; -class TrackerBase : public ITracker { - protected: - enum CycleState { - NotStarted, - Executing, - ExecutingChildren, - NeedsAnotherRun, - CompletedSuccessfully, - Failed - }; - - using Children = std::vector; - TrackerContext &m_ctx; - ITracker *m_parent; - Children m_children; - CycleState m_runState = NotStarted; - - public: - TrackerBase(NameAndLocation const &nameAndLocation, TrackerContext &ctx, ITracker *parent); - - bool isComplete() const override; - bool isSuccessfullyCompleted() const override; - bool isOpen() const override; - bool hasChildren() const override; - bool hasStarted() const override { - return m_runState != NotStarted; - } - - void addChild(ITrackerPtr const &child) override; - - ITrackerPtr findChild(NameAndLocation const &nameAndLocation) override; - ITracker &parent() override; - - void openChild() override; - - bool isSectionTracker() const override; - bool isGeneratorTracker() const override; - - void open(); - - void close() override; - void fail() override; - void markAsNeedingAnotherRun() override; - - private: - void moveToParent(); - void moveToThis(); -}; + ITrackerPtr m_rootTracker; + ITracker *m_currentTracker = nullptr; + RunState m_runState = NotStarted; -class SectionTracker : public TrackerBase { - std::vector m_filters; - std::string m_trimmed_name; - public: - SectionTracker(NameAndLocation const &nameAndLocation, TrackerContext &ctx, ITracker *parent); + public: - bool isSectionTracker() const override; + ITracker &startRun(); - bool isComplete() const override; + void endRun(); - static SectionTracker &acquire(TrackerContext &ctx, NameAndLocation const &nameAndLocation); + void startCycle(); - void tryOpen(); + void completeCycle(); - void addInitialFilters(std::vector const &filters); - void addNextFilters(std::vector const &filters); - //! Returns filters active in this tracker - std::vector const &getFilters() const; - //! Returns whitespace-trimmed name of the tracked section - std::string const &trimmedName() const; -}; + bool completedCycle() const; + + ITracker ¤tTracker(); + + void setCurrentTracker(ITracker *tracker); + }; + + class TrackerBase : public ITracker { + protected: + enum CycleState { + NotStarted, + Executing, + ExecutingChildren, + NeedsAnotherRun, + CompletedSuccessfully, + Failed + }; + + using Children = std::vector; + TrackerContext &m_ctx; + ITracker *m_parent; + Children m_children; + CycleState m_runState = NotStarted; + + public: + TrackerBase(NameAndLocation const &nameAndLocation, TrackerContext &ctx, ITracker *parent); + + bool isComplete() const override; + + bool isSuccessfullyCompleted() const override; + + bool isOpen() const override; + + bool hasChildren() const override; + + bool hasStarted() const override { + return m_runState != NotStarted; + } + + void addChild(ITrackerPtr const &child) override; + + ITrackerPtr findChild(NameAndLocation const &nameAndLocation) override; + + ITracker &parent() override; + + void openChild() override; + + bool isSectionTracker() const override; + + bool isGeneratorTracker() const override; + + void open(); + + void close() override; + + void fail() override; + + void markAsNeedingAnotherRun() override; + + private: + void moveToParent(); + + void moveToThis(); + }; + + class SectionTracker : public TrackerBase { + std::vector m_filters; + std::string m_trimmed_name; + public: + SectionTracker(NameAndLocation const &nameAndLocation, TrackerContext &ctx, ITracker *parent); + + bool isSectionTracker() const override; + + bool isComplete() const override; + + static SectionTracker &acquire(TrackerContext &ctx, NameAndLocation const &nameAndLocation); + + void tryOpen(); + + void addInitialFilters(std::vector const &filters); + + void addNextFilters(std::vector const &filters); + + //! Returns filters active in this tracker + std::vector const &getFilters() const; + + //! Returns whitespace-trimmed name of the tracked section + std::string const &trimmedName() const; + }; -} // namespace TestCaseTracking + } // namespace TestCaseTracking -using TestCaseTracking::ITracker; -using TestCaseTracking::TrackerContext; -using TestCaseTracking::SectionTracker; + using TestCaseTracking::ITracker; + using TestCaseTracking::TrackerContext; + using TestCaseTracking::SectionTracker; } // namespace Catch @@ -7641,10 +8137,11 @@ using TestCaseTracking::SectionTracker; namespace Catch { -struct LeakDetector { - LeakDetector(); - ~LeakDetector(); -}; + struct LeakDetector { + LeakDetector(); + + ~LeakDetector(); + }; } // end catch_leak_detector.h @@ -7869,72 +8366,74 @@ namespace { // Performs equivalent check of std::fabs(lhs - rhs) <= margin // But without the subtraction to allow for INFINITY in comparison -bool marginComparison(double lhs, double rhs, double margin) { - return (lhs + margin >= rhs) && (rhs + margin >= lhs); -} + bool marginComparison(double lhs, double rhs, double margin) { + return (lhs + margin >= rhs) && (rhs + margin >= lhs); + } } namespace Catch { -namespace Detail { + namespace Detail { -Approx::Approx(double value) - : m_epsilon(std::numeric_limits::epsilon() * 100), - m_margin(0.0), - m_scale(0.0), - m_value(value) {} + Approx::Approx(double value) + : m_epsilon(std::numeric_limits::epsilon() * 100), + m_margin(0.0), + m_scale(0.0), + m_value(value) {} -Approx Approx::custom() { - return Approx(0); -} + Approx Approx::custom() { + return Approx(0); + } -Approx Approx::operator-() const { - auto temp(*this); - temp.m_value = -temp.m_value; - return temp; -} + Approx Approx::operator-() const { + auto temp(*this); + temp.m_value = -temp.m_value; + return temp; + } -std::string Approx::toString() const { - ReusableStringStream rss; - rss << "Approx( " << ::Catch::Detail::stringify(m_value) << " )"; - return rss.str(); -} + std::string Approx::toString() const { + ReusableStringStream rss; + rss << "Approx( " << ::Catch::Detail::stringify(m_value) << " )"; + return rss.str(); + } -bool Approx::equalityComparisonImpl(const double other) const { - // First try with fixed margin, then compute margin based on epsilon, scale and Approx's value - // Thanks to Richard Harris for his help refining the scaled margin value - return marginComparison(m_value, other, m_margin) - || marginComparison(m_value, other, m_epsilon * (m_scale + std::fabs(std::isinf(m_value) ? 0 : m_value))); -} + bool Approx::equalityComparisonImpl(const double other) const { + // First try with fixed margin, then compute margin based on epsilon, scale and Approx's value + // Thanks to Richard Harris for his help refining the scaled margin value + return marginComparison(m_value, other, m_margin) + || marginComparison(m_value, other, + m_epsilon * (m_scale + std::fabs(std::isinf(m_value) ? 0 : m_value))); + } -void Approx::setMargin(double newMargin) { - CATCH_ENFORCE(newMargin >= 0, - "Invalid Approx::margin: " << newMargin << '.' - << " Approx::Margin has to be non-negative."); - m_margin = newMargin; -} + void Approx::setMargin(double newMargin) { + CATCH_ENFORCE(newMargin >= 0, + "Invalid Approx::margin: " << newMargin << '.' + << " Approx::Margin has to be non-negative."); + m_margin = newMargin; + } -void Approx::setEpsilon(double newEpsilon) { - CATCH_ENFORCE(newEpsilon >= 0 && newEpsilon <= 1.0, - "Invalid Approx::epsilon: " << newEpsilon << '.' - << " Approx::epsilon has to be in [0, 1]"); - m_epsilon = newEpsilon; -} + void Approx::setEpsilon(double newEpsilon) { + CATCH_ENFORCE(newEpsilon >= 0 && newEpsilon <= 1.0, + "Invalid Approx::epsilon: " << newEpsilon << '.' + << " Approx::epsilon has to be in [0, 1]"); + m_epsilon = newEpsilon; + } -} // end namespace Detail + } // end namespace Detail -namespace literals { -Detail::Approx operator "" _a(long double val) { - return Detail::Approx(val); -} -Detail::Approx operator "" _a(unsigned long long val) { - return Detail::Approx(val); -} -} // end namespace literals + namespace literals { + Detail::Approx operator "" _a(long double val) { + return Detail::Approx(val); + } -std::string StringMaker::convert(Catch::Detail::Approx const &value) { - return value.toString(); -} + Detail::Approx operator "" _a(unsigned long long val) { + return Detail::Approx(val); + } + } // end namespace literals + + std::string StringMaker::convert(Catch::Detail::Approx const &value) { + return value.toString(); + } } // end namespace Catch // end catch_approx.cpp @@ -7943,7 +8442,7 @@ std::string StringMaker::convert(Catch::Detail::Approx co // start catch_debugger.h namespace Catch { -bool isDebuggerActive(); + bool isDebuggerActive(); } #ifdef CATCH_PLATFORM_MAC @@ -8010,44 +8509,48 @@ namespace Catch { // // Can only be instantiated once, and assumes that once a signal // is caught, the binary will end up terminating. Thus, there -class FatalConditionHandler { - bool m_started = false; - - // Install/disengage implementation for specific platform. - // Should be if-defed to work on current platform, can assume - // engage-disengage 1:1 pairing. - void engage_platform(); - void disengage_platform(); - public: - // Should also have platform-specific implementations as needed - FatalConditionHandler(); - ~FatalConditionHandler(); - - void engage() { - assert(!m_started && "Handler cannot be installed twice."); - m_started = true; - engage_platform(); - } - - void disengage() { - assert(m_started && "Handler cannot be uninstalled without being installed first"); - m_started = false; - disengage_platform(); - } -}; + class FatalConditionHandler { + bool m_started = false; + + // Install/disengage implementation for specific platform. + // Should be if-defed to work on current platform, can assume + // engage-disengage 1:1 pairing. + void engage_platform(); + + void disengage_platform(); + + public: + // Should also have platform-specific implementations as needed + FatalConditionHandler(); + + ~FatalConditionHandler(); + + void engage() { + assert(!m_started && "Handler cannot be installed twice."); + m_started = true; + engage_platform(); + } + + void disengage() { + assert(m_started && "Handler cannot be uninstalled without being installed first"); + m_started = false; + disengage_platform(); + } + }; //! Simple RAII guard for (dis)engaging the FatalConditionHandler -class FatalConditionHandlerGuard { - FatalConditionHandler *m_handler; - public: - FatalConditionHandlerGuard(FatalConditionHandler *handler) : - m_handler(handler) { - m_handler->engage(); - } - ~FatalConditionHandlerGuard() { - m_handler->disengage(); - } -}; + class FatalConditionHandlerGuard { + FatalConditionHandler *m_handler; + public: + FatalConditionHandlerGuard(FatalConditionHandler *handler) : + m_handler(handler) { + m_handler->engage(); + } + + ~FatalConditionHandlerGuard() { + m_handler->disengage(); + } + }; } // end namespace Catch @@ -8056,334 +8559,354 @@ class FatalConditionHandlerGuard { namespace Catch { -struct IMutableContext; + struct IMutableContext; /////////////////////////////////////////////////////////////////////////// -class RunContext : public IResultCapture, public IRunner { + class RunContext : public IResultCapture, public IRunner { + + public: + RunContext(RunContext const &) = delete; + + RunContext &operator=(RunContext const &) = delete; - public: - RunContext(RunContext const &) = delete; - RunContext &operator=(RunContext const &) = delete; + explicit RunContext(IConfigPtr const &_config, IStreamingReporterPtr &&reporter); - explicit RunContext(IConfigPtr const &_config, IStreamingReporterPtr &&reporter); + ~RunContext() override; - ~RunContext() override; + void testGroupStarting(std::string const &testSpec, std::size_t groupIndex, std::size_t groupsCount); - void testGroupStarting(std::string const &testSpec, std::size_t groupIndex, std::size_t groupsCount); - void testGroupEnded(std::string const &testSpec, - Totals const &totals, - std::size_t groupIndex, - std::size_t groupsCount); + void testGroupEnded(std::string const &testSpec, + Totals const &totals, + std::size_t groupIndex, + std::size_t groupsCount); - Totals runTest(TestCase const &testCase); + Totals runTest(TestCase const &testCase); - IConfigPtr config() const; - IStreamingReporter &reporter() const; + IConfigPtr config() const; - public: // IResultCapture + IStreamingReporter &reporter() const; - // Assertion handlers - void handleExpr - (AssertionInfo const &info, - ITransientExpression const &expr, - AssertionReaction &reaction) override; - void handleMessage - (AssertionInfo const &info, - ResultWas::OfType resultType, - StringRef const &message, - AssertionReaction &reaction) override; - void handleUnexpectedExceptionNotThrown - (AssertionInfo const &info, - AssertionReaction &reaction) override; - void handleUnexpectedInflightException - (AssertionInfo const &info, - std::string const &message, - AssertionReaction &reaction) override; - void handleIncomplete - (AssertionInfo const &info) override; - void handleNonExpr - (AssertionInfo const &info, - ResultWas::OfType resultType, - AssertionReaction &reaction) override; + public: // IResultCapture - bool sectionStarted(SectionInfo const §ionInfo, Counts &assertions) override; + // Assertion handlers + void handleExpr + (AssertionInfo const &info, + ITransientExpression const &expr, + AssertionReaction &reaction) override; - void sectionEnded(SectionEndInfo const &endInfo) override; - void sectionEndedEarly(SectionEndInfo const &endInfo) override; + void handleMessage + (AssertionInfo const &info, + ResultWas::OfType resultType, + StringRef const &message, + AssertionReaction &reaction) override; - auto acquireGeneratorTracker(StringRef generatorName, SourceLineInfo const &lineInfo) -> IGeneratorTracker & override; + void handleUnexpectedExceptionNotThrown + (AssertionInfo const &info, + AssertionReaction &reaction) override; + + void handleUnexpectedInflightException + (AssertionInfo const &info, + std::string const &message, + AssertionReaction &reaction) override; + + void handleIncomplete + (AssertionInfo const &info) override; + + void handleNonExpr + (AssertionInfo const &info, + ResultWas::OfType resultType, + AssertionReaction &reaction) override; + + bool sectionStarted(SectionInfo const §ionInfo, Counts &assertions) override; + + void sectionEnded(SectionEndInfo const &endInfo) override; + + void sectionEndedEarly(SectionEndInfo const &endInfo) override; + + auto acquireGeneratorTracker(StringRef generatorName, + SourceLineInfo const &lineInfo) -> IGeneratorTracker & override; #if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) - void benchmarkPreparing( std::string const& name ) override; + void benchmarkPreparing( std::string const& name ) override; void benchmarkStarting( BenchmarkInfo const& info ) override; void benchmarkEnded( BenchmarkStats<> const& stats ) override; void benchmarkFailed( std::string const& error ) override; #endif // CATCH_CONFIG_ENABLE_BENCHMARKING - void pushScopedMessage(MessageInfo const &message) override; - void popScopedMessage(MessageInfo const &message) override; + void pushScopedMessage(MessageInfo const &message) override; + + void popScopedMessage(MessageInfo const &message) override; - void emplaceUnscopedMessage(MessageBuilder const &builder) override; + void emplaceUnscopedMessage(MessageBuilder const &builder) override; - std::string getCurrentTestName() const override; + std::string getCurrentTestName() const override; - const AssertionResult *getLastResult() const override; + const AssertionResult *getLastResult() const override; - void exceptionEarlyReported() override; + void exceptionEarlyReported() override; - void handleFatalErrorCondition(StringRef message) override; + void handleFatalErrorCondition(StringRef message) override; - bool lastAssertionPassed() override; + bool lastAssertionPassed() override; - void assertionPassed() override; + void assertionPassed() override; - public: - // !TBD We need to do this another way! - bool aborting() const final; + public: + // !TBD We need to do this another way! + bool aborting() const final; - private: + private: - void runCurrentTest(std::string &redirectedCout, std::string &redirectedCerr); - void invokeActiveTestCase(); + void runCurrentTest(std::string &redirectedCout, std::string &redirectedCerr); - void resetAssertionInfo(); - bool testForMissingAssertions(Counts &assertions); + void invokeActiveTestCase(); - void assertionEnded(AssertionResult const &result); - void reportExpr - (AssertionInfo const &info, - ResultWas::OfType resultType, - ITransientExpression const *expr, - bool negated); + void resetAssertionInfo(); - void populateReaction(AssertionReaction &reaction); + bool testForMissingAssertions(Counts &assertions); - private: + void assertionEnded(AssertionResult const &result); - void handleUnfinishedSections(); + void reportExpr + (AssertionInfo const &info, + ResultWas::OfType resultType, + ITransientExpression const *expr, + bool negated); - TestRunInfo m_runInfo; - IMutableContext &m_context; - TestCase const *m_activeTestCase = nullptr; - ITracker *m_testCaseTracker = nullptr; - Option m_lastResult; + void populateReaction(AssertionReaction &reaction); - IConfigPtr m_config; - Totals m_totals; - IStreamingReporterPtr m_reporter; - std::vector m_messages; - std::vector m_messageScopes; /* Keeps owners of so-called unscoped messages. */ - AssertionInfo m_lastAssertionInfo; - std::vector m_unfinishedSections; - std::vector m_activeSections; - TrackerContext m_trackerContext; - FatalConditionHandler m_fatalConditionhandler; - bool m_lastAssertionPassed = false; - bool m_shouldReportUnexpected = true; - bool m_includeSuccessfulResults; -}; + private: -void seedRng(IConfig const &config); -unsigned int rngSeed(); + void handleUnfinishedSections(); + + TestRunInfo m_runInfo; + IMutableContext &m_context; + TestCase const *m_activeTestCase = nullptr; + ITracker *m_testCaseTracker = nullptr; + Option m_lastResult; + + IConfigPtr m_config; + Totals m_totals; + IStreamingReporterPtr m_reporter; + std::vector m_messages; + std::vector m_messageScopes; /* Keeps owners of so-called unscoped messages. */ + AssertionInfo m_lastAssertionInfo; + std::vector m_unfinishedSections; + std::vector m_activeSections; + TrackerContext m_trackerContext; + FatalConditionHandler m_fatalConditionhandler; + bool m_lastAssertionPassed = false; + bool m_shouldReportUnexpected = true; + bool m_includeSuccessfulResults; + }; + + void seedRng(IConfig const &config); + + unsigned int rngSeed(); } // end namespace Catch // end catch_run_context.h namespace Catch { -namespace { -auto operator<<(std::ostream &os, ITransientExpression const &expr) -> std::ostream & { - expr.streamReconstructedExpression(os); - return os; -} -} + namespace { + auto operator<<(std::ostream &os, ITransientExpression const &expr) -> std::ostream & { + expr.streamReconstructedExpression(os); + return os; + } + } + + LazyExpression::LazyExpression(bool isNegated) + : m_isNegated(isNegated) {} -LazyExpression::LazyExpression(bool isNegated) - : m_isNegated(isNegated) {} + LazyExpression::LazyExpression(LazyExpression const &other) : m_isNegated(other.m_isNegated) {} -LazyExpression::LazyExpression(LazyExpression const &other) : m_isNegated(other.m_isNegated) {} + LazyExpression::operator bool() const { + return m_transientExpression != nullptr; + } -LazyExpression::operator bool() const { - return m_transientExpression != nullptr; -} + auto operator<<(std::ostream &os, LazyExpression const &lazyExpr) -> std::ostream & { + if (lazyExpr.m_isNegated) + os << "!"; -auto operator<<(std::ostream &os, LazyExpression const &lazyExpr) -> std::ostream & { - if (lazyExpr.m_isNegated) - os << "!"; + if (lazyExpr) { + if (lazyExpr.m_isNegated && lazyExpr.m_transientExpression->isBinaryExpression()) + os << "(" << *lazyExpr.m_transientExpression << ")"; + else + os << *lazyExpr.m_transientExpression; + } else { + os << "{** error - unchecked empty expression requested **}"; + } + return os; + } - if (lazyExpr) { - if (lazyExpr.m_isNegated && lazyExpr.m_transientExpression->isBinaryExpression()) - os << "(" << *lazyExpr.m_transientExpression << ")"; - else - os << *lazyExpr.m_transientExpression; - } else { - os << "{** error - unchecked empty expression requested **}"; - } - return os; -} + AssertionHandler::AssertionHandler + (StringRef const ¯oName, + SourceLineInfo const &lineInfo, + StringRef capturedExpression, + ResultDisposition::Flags resultDisposition) + : m_assertionInfo{macroName, lineInfo, capturedExpression, resultDisposition}, + m_resultCapture(getResultCapture()) {} -AssertionHandler::AssertionHandler - (StringRef const ¯oName, - SourceLineInfo const &lineInfo, - StringRef capturedExpression, - ResultDisposition::Flags resultDisposition) - : m_assertionInfo{macroName, lineInfo, capturedExpression, resultDisposition}, - m_resultCapture(getResultCapture()) {} + void AssertionHandler::handleExpr(ITransientExpression const &expr) { + m_resultCapture.handleExpr(m_assertionInfo, expr, m_reaction); + } -void AssertionHandler::handleExpr(ITransientExpression const &expr) { - m_resultCapture.handleExpr(m_assertionInfo, expr, m_reaction); -} -void AssertionHandler::handleMessage(ResultWas::OfType resultType, StringRef const &message) { - m_resultCapture.handleMessage(m_assertionInfo, resultType, message, m_reaction); -} + void AssertionHandler::handleMessage(ResultWas::OfType resultType, StringRef const &message) { + m_resultCapture.handleMessage(m_assertionInfo, resultType, message, m_reaction); + } -auto AssertionHandler::allowThrows() const -> bool { - return getCurrentContext().getConfig()->allowThrows(); -} + auto AssertionHandler::allowThrows() const -> bool { + return getCurrentContext().getConfig()->allowThrows(); + } -void AssertionHandler::complete() { - setCompleted(); - if (m_reaction.shouldDebugBreak) { + void AssertionHandler::complete() { + setCompleted(); + if (m_reaction.shouldDebugBreak) { - // If you find your debugger stopping you here then go one level up on the - // call-stack for the code that caused it (typically a failed assertion) + // If you find your debugger stopping you here then go one level up on the + // call-stack for the code that caused it (typically a failed assertion) - // (To go back to the test and change execution, jump over the throw, next) - CATCH_BREAK_INTO_DEBUGGER(); - } - if (m_reaction.shouldThrow) { + // (To go back to the test and change execution, jump over the throw, next) + CATCH_BREAK_INTO_DEBUGGER(); + } + if (m_reaction.shouldThrow) { #if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) - throw Catch::TestFailureException(); + throw Catch::TestFailureException(); #else - CATCH_ERROR( "Test failure requires aborting test!" ); + CATCH_ERROR( "Test failure requires aborting test!" ); #endif - } -} -void AssertionHandler::setCompleted() { - m_completed = true; -} + } + } -void AssertionHandler::handleUnexpectedInflightException() { - m_resultCapture.handleUnexpectedInflightException(m_assertionInfo, Catch::translateActiveException(), m_reaction); -} + void AssertionHandler::setCompleted() { + m_completed = true; + } -void AssertionHandler::handleExceptionThrownAsExpected() { - m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction); -} -void AssertionHandler::handleExceptionNotThrownAsExpected() { - m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction); -} + void AssertionHandler::handleUnexpectedInflightException() { + m_resultCapture.handleUnexpectedInflightException(m_assertionInfo, Catch::translateActiveException(), + m_reaction); + } -void AssertionHandler::handleUnexpectedExceptionNotThrown() { - m_resultCapture.handleUnexpectedExceptionNotThrown(m_assertionInfo, m_reaction); -} + void AssertionHandler::handleExceptionThrownAsExpected() { + m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction); + } -void AssertionHandler::handleThrowingCallSkipped() { - m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction); -} + void AssertionHandler::handleExceptionNotThrownAsExpected() { + m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction); + } + + void AssertionHandler::handleUnexpectedExceptionNotThrown() { + m_resultCapture.handleUnexpectedExceptionNotThrown(m_assertionInfo, m_reaction); + } + + void AssertionHandler::handleThrowingCallSkipped() { + m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction); + } // This is the overload that takes a string and infers the Equals matcher from it // The more general overload, that takes any string matcher, is in catch_capture_matchers.cpp -void handleExceptionMatchExpr(AssertionHandler &handler, std::string const &str, StringRef const &matcherString) { - handleExceptionMatchExpr(handler, Matchers::Equals(str), matcherString); -} + void handleExceptionMatchExpr(AssertionHandler &handler, std::string const &str, StringRef const &matcherString) { + handleExceptionMatchExpr(handler, Matchers::Equals(str), matcherString); + } } // namespace Catch // end catch_assertionhandler.cpp // start catch_assertionresult.cpp namespace Catch { -AssertionResultData::AssertionResultData(ResultWas::OfType _resultType, LazyExpression const &_lazyExpression) : - lazyExpression(_lazyExpression), - resultType(_resultType) {} + AssertionResultData::AssertionResultData(ResultWas::OfType _resultType, LazyExpression const &_lazyExpression) : + lazyExpression(_lazyExpression), + resultType(_resultType) {} -std::string AssertionResultData::reconstructExpression() const { + std::string AssertionResultData::reconstructExpression() const { - if (reconstructedExpression.empty()) { - if (lazyExpression) { - ReusableStringStream rss; - rss << lazyExpression; - reconstructedExpression = rss.str(); + if (reconstructedExpression.empty()) { + if (lazyExpression) { + ReusableStringStream rss; + rss << lazyExpression; + reconstructedExpression = rss.str(); + } + } + return reconstructedExpression; } - } - return reconstructedExpression; -} -AssertionResult::AssertionResult(AssertionInfo const &info, AssertionResultData const &data) - : m_info(info), - m_resultData(data) {} + AssertionResult::AssertionResult(AssertionInfo const &info, AssertionResultData const &data) + : m_info(info), + m_resultData(data) {} // Result was a success -bool AssertionResult::succeeded() const { - return Catch::isOk(m_resultData.resultType); -} + bool AssertionResult::succeeded() const { + return Catch::isOk(m_resultData.resultType); + } // Result was a success, or failure is suppressed -bool AssertionResult::isOk() const { - return Catch::isOk(m_resultData.resultType) || shouldSuppressFailure(m_info.resultDisposition); -} + bool AssertionResult::isOk() const { + return Catch::isOk(m_resultData.resultType) || shouldSuppressFailure(m_info.resultDisposition); + } -ResultWas::OfType AssertionResult::getResultType() const { - return m_resultData.resultType; -} + ResultWas::OfType AssertionResult::getResultType() const { + return m_resultData.resultType; + } -bool AssertionResult::hasExpression() const { - return !m_info.capturedExpression.empty(); -} + bool AssertionResult::hasExpression() const { + return !m_info.capturedExpression.empty(); + } -bool AssertionResult::hasMessage() const { - return !m_resultData.message.empty(); -} + bool AssertionResult::hasMessage() const { + return !m_resultData.message.empty(); + } -std::string AssertionResult::getExpression() const { - // Possibly overallocating by 3 characters should be basically free - std::string expr; - expr.reserve(m_info.capturedExpression.size() + 3); - if (isFalseTest(m_info.resultDisposition)) { - expr += "!("; - } - expr += m_info.capturedExpression; - if (isFalseTest(m_info.resultDisposition)) { - expr += ')'; - } - return expr; -} + std::string AssertionResult::getExpression() const { + // Possibly overallocating by 3 characters should be basically free + std::string expr; + expr.reserve(m_info.capturedExpression.size() + 3); + if (isFalseTest(m_info.resultDisposition)) { + expr += "!("; + } + expr += m_info.capturedExpression; + if (isFalseTest(m_info.resultDisposition)) { + expr += ')'; + } + return expr; + } -std::string AssertionResult::getExpressionInMacro() const { - std::string expr; - if (m_info.macroName.empty()) - expr = static_cast(m_info.capturedExpression); - else { - expr.reserve(m_info.macroName.size() + m_info.capturedExpression.size() + 4); - expr += m_info.macroName; - expr += "( "; - expr += m_info.capturedExpression; - expr += " )"; - } - return expr; -} + std::string AssertionResult::getExpressionInMacro() const { + std::string expr; + if (m_info.macroName.empty()) + expr = static_cast(m_info.capturedExpression); + else { + expr.reserve(m_info.macroName.size() + m_info.capturedExpression.size() + 4); + expr += m_info.macroName; + expr += "( "; + expr += m_info.capturedExpression; + expr += " )"; + } + return expr; + } -bool AssertionResult::hasExpandedExpression() const { - return hasExpression() && getExpandedExpression() != getExpression(); -} + bool AssertionResult::hasExpandedExpression() const { + return hasExpression() && getExpandedExpression() != getExpression(); + } -std::string AssertionResult::getExpandedExpression() const { - std::string expr = m_resultData.reconstructExpression(); - return expr.empty() - ? getExpression() - : expr; -} + std::string AssertionResult::getExpandedExpression() const { + std::string expr = m_resultData.reconstructExpression(); + return expr.empty() + ? getExpression() + : expr; + } -std::string AssertionResult::getMessage() const { - return m_resultData.message; -} -SourceLineInfo AssertionResult::getSourceInfo() const { - return m_info.lineInfo; -} + std::string AssertionResult::getMessage() const { + return m_resultData.message; + } -StringRef AssertionResult::getTestMacroName() const { - return m_info.macroName; -} + SourceLineInfo AssertionResult::getSourceInfo() const { + return m_info.lineInfo; + } + + StringRef AssertionResult::getTestMacroName() const { + return m_info.macroName; + } } // end namespace Catch // end catch_assertionresult.cpp @@ -8391,16 +8914,17 @@ StringRef AssertionResult::getTestMacroName() const { namespace Catch { -using StringMatcher = Matchers::Impl::MatcherBase; + using StringMatcher = Matchers::Impl::MatcherBase; // This is the general overload that takes a any string matcher // There is another overload, in catch_assertionhandler.h/.cpp, that only takes a string and infers // the Equals matcher (so the header does not mention matchers) -void handleExceptionMatchExpr(AssertionHandler &handler, StringMatcher const &matcher, StringRef const &matcherString) { - std::string exceptionMessage = Catch::translateActiveException(); - MatchExpr expr(exceptionMessage, matcher, matcherString); - handler.handleExpr(expr); -} + void + handleExceptionMatchExpr(AssertionHandler &handler, StringMatcher const &matcher, StringRef const &matcherString) { + std::string exceptionMessage = Catch::translateActiveException(); + MatchExpr expr(exceptionMessage, matcher, matcherString); + handler.handleExpr(expr); + } } // namespace Catch // end catch_capture_matchers.cpp @@ -8446,1235 +8970,1298 @@ void handleExceptionMatchExpr(AssertionHandler &handler, StringMatcher const &ma #ifndef CLARA_CONFIG_OPTIONAL_TYPE #ifdef __has_include #if __has_include() && __cplusplus >= 201703L + #include + #define CLARA_CONFIG_OPTIONAL_TYPE std::optional #endif #endif #endif -// ----------- #included from clara_textflow.hpp ----------- +// ----------- #included from clara_textflow.hpp ----------- + +// TextFlowCpp +// +// A single-header library for wrapping and laying out basic text, by Phil Nash +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// This project is hosted at https://github.com/philsquared/textflowcpp + + +#include +#include +#include +#include + +#ifndef CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH +#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH 80 +#endif + +namespace Catch { + namespace clara { + namespace TextFlow { + + inline auto isWhitespace(char c) -> bool { + static std::string chars = " \t\n\r"; + return chars.find(c) != std::string::npos; + } + + inline auto isBreakableBefore(char c) -> bool { + static std::string chars = "[({<|"; + return chars.find(c) != std::string::npos; + } + + inline auto isBreakableAfter(char c) -> bool { + static std::string chars = "])}>.,:;*+-=&/\\"; + return chars.find(c) != std::string::npos; + } + + class Columns; + + class Column { + std::vector m_strings; + size_t m_width = CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH; + size_t m_indent = 0; + size_t m_initialIndent = std::string::npos; + + public: + class iterator { + friend Column; + + Column const &m_column; + size_t m_stringIndex = 0; + size_t m_pos = 0; + + size_t m_len = 0; + size_t m_end = 0; + bool m_suffix = false; + + iterator(Column const &column, size_t stringIndex) + : m_column(column), + m_stringIndex(stringIndex) {} + + auto line() const -> std::string const & { return m_column.m_strings[m_stringIndex]; } + + auto isBoundary(size_t at) const -> bool { + assert(at > 0); + assert(at <= line().size()); + + return at == line().size() || + (isWhitespace(line()[at]) && !isWhitespace(line()[at - 1])) || + isBreakableBefore(line()[at]) || + isBreakableAfter(line()[at - 1]); + } + + void calcLength() { + assert(m_stringIndex < m_column.m_strings.size()); + + m_suffix = false; + auto width = m_column.m_width - indent(); + m_end = m_pos; + if (line()[m_pos] == '\n') { + ++m_end; + } + while (m_end < line().size() && line()[m_end] != '\n') + ++m_end; + + if (m_end < m_pos + width) { + m_len = m_end - m_pos; + } else { + size_t len = width; + while (len > 0 && !isBoundary(m_pos + len)) + --len; + while (len > 0 && isWhitespace(line()[m_pos + len - 1])) + --len; + + if (len > 0) { + m_len = len; + } else { + m_suffix = true; + m_len = width - 1; + } + } + } + + auto indent() const -> size_t { + auto initial = m_pos == 0 && m_stringIndex == 0 ? m_column.m_initialIndent : std::string::npos; + return initial == std::string::npos ? m_column.m_indent : initial; + } + + auto addIndentAndSuffix(std::string const &plain) const -> std::string { + return std::string(indent(), ' ') + (m_suffix ? plain + "-" : plain); + } + + public: + using difference_type = std::ptrdiff_t; + using value_type = std::string; + using pointer = value_type *; + using reference = value_type &; + using iterator_category = std::forward_iterator_tag; + + explicit iterator(Column const &column) : m_column(column) { + assert(m_column.m_width > m_column.m_indent); + assert(m_column.m_initialIndent == std::string::npos || + m_column.m_width > m_column.m_initialIndent); + calcLength(); + if (m_len == 0) + m_stringIndex++; // Empty string + } + + auto operator*() const -> std::string { + assert(m_stringIndex < m_column.m_strings.size()); + assert(m_pos <= m_end); + return addIndentAndSuffix(line().substr(m_pos, m_len)); + } + + auto operator++() -> iterator & { + m_pos += m_len; + if (m_pos < line().size() && line()[m_pos] == '\n') + m_pos += 1; + else + while (m_pos < line().size() && isWhitespace(line()[m_pos])) + ++m_pos; + + if (m_pos == line().size()) { + m_pos = 0; + ++m_stringIndex; + } + if (m_stringIndex < m_column.m_strings.size()) + calcLength(); + return *this; + } + + auto operator++(int) -> iterator { + iterator prev(*this); + operator++(); + return prev; + } + + auto operator==(iterator const &other) const -> bool { + return + m_pos == other.m_pos && + m_stringIndex == other.m_stringIndex && + &m_column == &other.m_column; + } + + auto operator!=(iterator const &other) const -> bool { + return !operator==(other); + } + }; + + using const_iterator = iterator; + + explicit Column(std::string const &text) { m_strings.push_back(text); } + + auto width(size_t newWidth) -> Column & { + assert(newWidth > 0); + m_width = newWidth; + return *this; + } + + auto indent(size_t newIndent) -> Column & { + m_indent = newIndent; + return *this; + } + + auto initialIndent(size_t newIndent) -> Column & { + m_initialIndent = newIndent; + return *this; + } + + auto width() const -> size_t { return m_width; } + + auto begin() const -> iterator { return iterator(*this); } + + auto end() const -> iterator { return {*this, m_strings.size()}; } + + inline friend std::ostream &operator<<(std::ostream &os, Column const &col) { + bool first = true; + for (auto line: col) { + if (first) + first = false; + else + os << "\n"; + os << line; + } + return os; + } + + auto operator+(Column const &other) -> Columns; + + auto toString() const -> std::string { + std::ostringstream oss; + oss << *this; + return oss.str(); + } + }; + + class Spacer : public Column { + + public: + explicit Spacer(size_t spaceWidth) : Column("") { + width(spaceWidth); + } + }; + + class Columns { + std::vector m_columns; + + public: + + class iterator { + friend Columns; + struct EndTag { + }; + + std::vector const &m_columns; + std::vector m_iterators; + size_t m_activeIterators; + + iterator(Columns const &columns, EndTag) + : m_columns(columns.m_columns), + m_activeIterators(0) { + m_iterators.reserve(m_columns.size()); + + for (auto const &col: m_columns) + m_iterators.push_back(col.end()); + } + + public: + using difference_type = std::ptrdiff_t; + using value_type = std::string; + using pointer = value_type *; + using reference = value_type &; + using iterator_category = std::forward_iterator_tag; + + explicit iterator(Columns const &columns) + : m_columns(columns.m_columns), + m_activeIterators(m_columns.size()) { + m_iterators.reserve(m_columns.size()); + + for (auto const &col: m_columns) + m_iterators.push_back(col.begin()); + } + + auto operator==(iterator const &other) const -> bool { + return m_iterators == other.m_iterators; + } + + auto operator!=(iterator const &other) const -> bool { + return m_iterators != other.m_iterators; + } + + auto operator*() const -> std::string { + std::string row, padding; + + for (size_t i = 0; i < m_columns.size(); ++i) { + auto width = m_columns[i].width(); + if (m_iterators[i] != m_columns[i].end()) { + std::string col = *m_iterators[i]; + row += padding + col; + if (col.size() < width) + padding = std::string(width - col.size(), ' '); + else + padding = ""; + } else { + padding += std::string(width, ' '); + } + } + return row; + } + + auto operator++() -> iterator & { + for (size_t i = 0; i < m_columns.size(); ++i) { + if (m_iterators[i] != m_columns[i].end()) + ++m_iterators[i]; + } + return *this; + } + + auto operator++(int) -> iterator { + iterator prev(*this); + operator++(); + return prev; + } + }; + + using const_iterator = iterator; + + auto begin() const -> iterator { return iterator(*this); } + + auto end() const -> iterator { return {*this, iterator::EndTag()}; } + + auto operator+=(Column const &col) -> Columns & { + m_columns.push_back(col); + return *this; + } + + auto operator+(Column const &col) -> Columns { + Columns combined = *this; + combined += col; + return combined; + } + + inline friend std::ostream &operator<<(std::ostream &os, Columns const &cols) { + + bool first = true; + for (auto line: cols) { + if (first) + first = false; + else + os << "\n"; + os << line; + } + return os; + } + + auto toString() const -> std::string { + std::ostringstream oss; + oss << *this; + return oss.str(); + } + }; + + inline auto Column::operator+(Column const &other) -> Columns { + Columns cols; + cols += *this; + cols += other; + return cols; + } + } + + } +} + +// ----------- end of #include from clara_textflow.hpp ----------- +// ........... back in clara.hpp + +#include +#include +#include +#include +#include + +#if !defined(CATCH_PLATFORM_WINDOWS) && (defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER)) +#define CATCH_PLATFORM_WINDOWS +#endif + +namespace Catch { + namespace clara { + namespace detail { + +// Traits for extracting arg and return type of lambdas (for single argument lambdas) + template + struct UnaryLambdaTraits : UnaryLambdaTraits { + }; + + template + struct UnaryLambdaTraits { + static const bool isValid = false; + }; + + template + struct UnaryLambdaTraits { + static const bool isValid = true; + using ArgType = typename std::remove_const::type>::type; + using ReturnType = ReturnT; + }; + + class TokenStream; + +// Transport for raw args (copied from main args, or supplied via init list for testing) + class Args { + friend TokenStream; + std::string m_exeName; + std::vector m_args; + + public: + Args(int argc, char const *const *argv) + : m_exeName(argv[0]), + m_args(argv + 1, argv + argc) {} + + Args(std::initializer_list args) + : m_exeName(*args.begin()), + m_args(args.begin() + 1, args.end()) {} + + auto exeName() const -> std::string { + return m_exeName; + } + }; + +// Wraps a token coming from a token stream. These may not directly correspond to strings as a single string +// may encode an option + its argument if the : or = form is used + enum class TokenType { + Option, Argument + }; + struct Token { + TokenType type; + std::string token; + }; + + inline auto isOptPrefix(char c) -> bool { + return c == '-' +#ifdef CATCH_PLATFORM_WINDOWS + || c == '/' +#endif + ; + } + +// Abstracts iterators into args as a stream of tokens, with option arguments uniformly handled + class TokenStream { + using Iterator = std::vector::const_iterator; + Iterator it; + Iterator itEnd; + std::vector m_tokenBuffer; + + void loadBuffer() { + m_tokenBuffer.resize(0); + + // Skip any empty strings + while (it != itEnd && it->empty()) + ++it; + + if (it != itEnd) { + auto const &next = *it; + if (isOptPrefix(next[0])) { + auto delimiterPos = next.find_first_of(" :="); + if (delimiterPos != std::string::npos) { + m_tokenBuffer.push_back({TokenType::Option, next.substr(0, delimiterPos)}); + m_tokenBuffer.push_back({TokenType::Argument, next.substr(delimiterPos + 1)}); + } else { + if (next[1] != '-' && next.size() > 2) { + std::string opt = "- "; + for (size_t i = 1; i < next.size(); ++i) { + opt[1] = next[i]; + m_tokenBuffer.push_back({TokenType::Option, opt}); + } + } else { + m_tokenBuffer.push_back({TokenType::Option, next}); + } + } + } else { + m_tokenBuffer.push_back({TokenType::Argument, next}); + } + } + } + + public: + explicit TokenStream(Args const &args) : TokenStream(args.m_args.begin(), args.m_args.end()) {} + + TokenStream(Iterator it, Iterator itEnd) : it(it), itEnd(itEnd) { + loadBuffer(); + } -// TextFlowCpp -// -// A single-header library for wrapping and laying out basic text, by Phil Nash -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// This project is hosted at https://github.com/philsquared/textflowcpp + explicit operator bool() const { + return !m_tokenBuffer.empty() || it != itEnd; + } + auto count() const -> size_t { return m_tokenBuffer.size() + (itEnd - it); } -#include -#include -#include -#include + auto operator*() const -> Token { + assert(!m_tokenBuffer.empty()); + return m_tokenBuffer.front(); + } -#ifndef CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH -#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH 80 -#endif + auto operator->() const -> Token const * { + assert(!m_tokenBuffer.empty()); + return &m_tokenBuffer.front(); + } -namespace Catch { -namespace clara { -namespace TextFlow { + auto operator++() -> TokenStream & { + if (m_tokenBuffer.size() >= 2) { + m_tokenBuffer.erase(m_tokenBuffer.begin()); + } else { + if (it != itEnd) + ++it; + loadBuffer(); + } + return *this; + } + }; -inline auto isWhitespace(char c) -> bool { - static std::string chars = " \t\n\r"; - return chars.find(c) != std::string::npos; -} -inline auto isBreakableBefore(char c) -> bool { - static std::string chars = "[({<|"; - return chars.find(c) != std::string::npos; -} -inline auto isBreakableAfter(char c) -> bool { - static std::string chars = "])}>.,:;*+-=&/\\"; - return chars.find(c) != std::string::npos; -} + class ResultBase { + public: + enum Type { + Ok, LogicError, RuntimeError + }; -class Columns; - -class Column { - std::vector m_strings; - size_t m_width = CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH; - size_t m_indent = 0; - size_t m_initialIndent = std::string::npos; - - public: - class iterator { - friend Column; - - Column const &m_column; - size_t m_stringIndex = 0; - size_t m_pos = 0; - - size_t m_len = 0; - size_t m_end = 0; - bool m_suffix = false; - - iterator(Column const &column, size_t stringIndex) - : m_column(column), - m_stringIndex(stringIndex) {} - - auto line() const -> std::string const & { return m_column.m_strings[m_stringIndex]; } - - auto isBoundary(size_t at) const -> bool { - assert(at > 0); - assert(at <= line().size()); - - return at == line().size() || - (isWhitespace(line()[at]) && !isWhitespace(line()[at - 1])) || - isBreakableBefore(line()[at]) || - isBreakableAfter(line()[at - 1]); - } - - void calcLength() { - assert(m_stringIndex < m_column.m_strings.size()); - - m_suffix = false; - auto width = m_column.m_width - indent(); - m_end = m_pos; - if (line()[m_pos] == '\n') { - ++m_end; - } - while (m_end < line().size() && line()[m_end] != '\n') - ++m_end; - - if (m_end < m_pos + width) { - m_len = m_end - m_pos; - } else { - size_t len = width; - while (len > 0 && !isBoundary(m_pos + len)) - --len; - while (len > 0 && isWhitespace(line()[m_pos + len - 1])) - --len; - - if (len > 0) { - m_len = len; - } else { - m_suffix = true; - m_len = width - 1; - } - } - } - - auto indent() const -> size_t { - auto initial = m_pos == 0 && m_stringIndex == 0 ? m_column.m_initialIndent : std::string::npos; - return initial == std::string::npos ? m_column.m_indent : initial; - } - - auto addIndentAndSuffix(std::string const &plain) const -> std::string { - return std::string(indent(), ' ') + (m_suffix ? plain + "-" : plain); - } - - public: - using difference_type = std::ptrdiff_t; - using value_type = std::string; - using pointer = value_type *; - using reference = value_type &; - using iterator_category = std::forward_iterator_tag; - - explicit iterator(Column const &column) : m_column(column) { - assert(m_column.m_width > m_column.m_indent); - assert(m_column.m_initialIndent == std::string::npos || m_column.m_width > m_column.m_initialIndent); - calcLength(); - if (m_len == 0) - m_stringIndex++; // Empty string - } - - auto operator*() const -> std::string { - assert(m_stringIndex < m_column.m_strings.size()); - assert(m_pos <= m_end); - return addIndentAndSuffix(line().substr(m_pos, m_len)); - } - - auto operator++() -> iterator & { - m_pos += m_len; - if (m_pos < line().size() && line()[m_pos] == '\n') - m_pos += 1; - else - while (m_pos < line().size() && isWhitespace(line()[m_pos])) - ++m_pos; - - if (m_pos == line().size()) { - m_pos = 0; - ++m_stringIndex; - } - if (m_stringIndex < m_column.m_strings.size()) - calcLength(); - return *this; - } - auto operator++(int) -> iterator { - iterator prev(*this); - operator++(); - return prev; - } - - auto operator==(iterator const &other) const -> bool { - return - m_pos == other.m_pos && - m_stringIndex == other.m_stringIndex && - &m_column == &other.m_column; - } - auto operator!=(iterator const &other) const -> bool { - return !operator==(other); - } - }; - using const_iterator = iterator; - - explicit Column(std::string const &text) { m_strings.push_back(text); } - - auto width(size_t newWidth) -> Column & { - assert(newWidth > 0); - m_width = newWidth; - return *this; - } - auto indent(size_t newIndent) -> Column & { - m_indent = newIndent; - return *this; - } - auto initialIndent(size_t newIndent) -> Column & { - m_initialIndent = newIndent; - return *this; - } - - auto width() const -> size_t { return m_width; } - auto begin() const -> iterator { return iterator(*this); } - auto end() const -> iterator { return {*this, m_strings.size()}; } - - inline friend std::ostream &operator<<(std::ostream &os, Column const &col) { - bool first = true; - for (auto line : col) { - if (first) - first = false; - else - os << "\n"; - os << line; - } - return os; - } - - auto operator+(Column const &other) -> Columns; - - auto toString() const -> std::string { - std::ostringstream oss; - oss << *this; - return oss.str(); - } -}; + protected: + ResultBase(Type type) : m_type(type) {} -class Spacer : public Column { + virtual ~ResultBase() = default; - public: - explicit Spacer(size_t spaceWidth) : Column("") { - width(spaceWidth); - } -}; + virtual void enforceOk() const = 0; -class Columns { - std::vector m_columns; + Type m_type; + }; - public: + template + class ResultValueBase : public ResultBase { + public: + auto value() const -> T const & { + enforceOk(); + return m_value; + } - class iterator { - friend Columns; - struct EndTag {}; + protected: + ResultValueBase(Type type) : ResultBase(type) {} - std::vector const &m_columns; - std::vector m_iterators; - size_t m_activeIterators; + ResultValueBase(ResultValueBase const &other) : ResultBase(other) { + if (m_type == ResultBase::Ok) + new(&m_value) T(other.m_value); + } - iterator(Columns const &columns, EndTag) - : m_columns(columns.m_columns), - m_activeIterators(0) { - m_iterators.reserve(m_columns.size()); + ResultValueBase(Type, T const &value) : ResultBase(Ok) { + new(&m_value) T(value); + } - for (auto const &col : m_columns) - m_iterators.push_back(col.end()); - } + auto operator=(ResultValueBase const &other) -> ResultValueBase & { + if (m_type == ResultBase::Ok) + m_value.~T(); + ResultBase::operator=(other); + if (m_type == ResultBase::Ok) + new(&m_value) T(other.m_value); + return *this; + } - public: - using difference_type = std::ptrdiff_t; - using value_type = std::string; - using pointer = value_type *; - using reference = value_type &; - using iterator_category = std::forward_iterator_tag; + ~ResultValueBase() override { + if (m_type == Ok) + m_value.~T(); + } - explicit iterator(Columns const &columns) - : m_columns(columns.m_columns), - m_activeIterators(m_columns.size()) { - m_iterators.reserve(m_columns.size()); + union { + T m_value; + }; + }; - for (auto const &col : m_columns) - m_iterators.push_back(col.begin()); - } + template<> + class ResultValueBase : public ResultBase { + protected: + using ResultBase::ResultBase; + }; - auto operator==(iterator const &other) const -> bool { - return m_iterators == other.m_iterators; - } - auto operator!=(iterator const &other) const -> bool { - return m_iterators != other.m_iterators; - } - auto operator*() const -> std::string { - std::string row, padding; + template + class BasicResult : public ResultValueBase { + public: + template + explicit BasicResult(BasicResult const &other) + : ResultValueBase(other.type()), + m_errorMessage(other.errorMessage()) { + assert(type() != ResultBase::Ok); + } - for (size_t i = 0; i < m_columns.size(); ++i) { - auto width = m_columns[i].width(); - if (m_iterators[i] != m_columns[i].end()) { - std::string col = *m_iterators[i]; - row += padding + col; - if (col.size() < width) - padding = std::string(width - col.size(), ' '); - else - padding = ""; - } else { - padding += std::string(width, ' '); - } - } - return row; - } - auto operator++() -> iterator & { - for (size_t i = 0; i < m_columns.size(); ++i) { - if (m_iterators[i] != m_columns[i].end()) - ++m_iterators[i]; - } - return *this; - } - auto operator++(int) -> iterator { - iterator prev(*this); - operator++(); - return prev; - } - }; - using const_iterator = iterator; - - auto begin() const -> iterator { return iterator(*this); } - auto end() const -> iterator { return {*this, iterator::EndTag()}; } - - auto operator+=(Column const &col) -> Columns & { - m_columns.push_back(col); - return *this; - } - auto operator+(Column const &col) -> Columns { - Columns combined = *this; - combined += col; - return combined; - } - - inline friend std::ostream &operator<<(std::ostream &os, Columns const &cols) { - - bool first = true; - for (auto line : cols) { - if (first) - first = false; - else - os << "\n"; - os << line; - } - return os; - } - - auto toString() const -> std::string { - std::ostringstream oss; - oss << *this; - return oss.str(); - } -}; + template + static auto ok(U const &value) -> BasicResult { return {ResultBase::Ok, value}; } -inline auto Column::operator+(Column const &other) -> Columns { - Columns cols; - cols += *this; - cols += other; - return cols; -} -} + static auto ok() -> BasicResult { return {ResultBase::Ok}; } -} -} + static auto logicError(std::string const &message) -> BasicResult { + return {ResultBase::LogicError, message}; + } -// ----------- end of #include from clara_textflow.hpp ----------- -// ........... back in clara.hpp + static auto runtimeError(std::string const &message) -> BasicResult { + return {ResultBase::RuntimeError, message}; + } -#include -#include -#include -#include -#include + explicit operator bool() const { return m_type == ResultBase::Ok; } -#if !defined(CATCH_PLATFORM_WINDOWS) && (defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER)) -#define CATCH_PLATFORM_WINDOWS -#endif + auto type() const -> ResultBase::Type { return m_type; } -namespace Catch { -namespace clara { -namespace detail { + auto errorMessage() const -> std::string { return m_errorMessage; } -// Traits for extracting arg and return type of lambdas (for single argument lambdas) -template -struct UnaryLambdaTraits : UnaryLambdaTraits {}; + protected: + void enforceOk() const override { -template -struct UnaryLambdaTraits { - static const bool isValid = false; -}; + // Errors shouldn't reach this point, but if they do + // the actual error message will be in m_errorMessage + assert(m_type != ResultBase::LogicError); + assert(m_type != ResultBase::RuntimeError); + if (m_type != ResultBase::Ok) + std::abort(); + } -template -struct UnaryLambdaTraits { - static const bool isValid = true; - using ArgType = typename std::remove_const::type>::type; - using ReturnType = ReturnT; -}; + std::string m_errorMessage; // Only populated if resultType is an error -class TokenStream; + BasicResult(ResultBase::Type type, std::string const &message) + : ResultValueBase(type), + m_errorMessage(message) { + assert(m_type != ResultBase::Ok); + } -// Transport for raw args (copied from main args, or supplied via init list for testing) -class Args { - friend TokenStream; - std::string m_exeName; - std::vector m_args; - - public: - Args(int argc, char const *const *argv) - : m_exeName(argv[0]), - m_args(argv + 1, argv + argc) {} - - Args(std::initializer_list args) - : m_exeName(*args.begin()), - m_args(args.begin() + 1, args.end()) {} - - auto exeName() const -> std::string { - return m_exeName; - } -}; + using ResultValueBase::ResultValueBase; + using ResultBase::m_type; + }; -// Wraps a token coming from a token stream. These may not directly correspond to strings as a single string -// may encode an option + its argument if the : or = form is used -enum class TokenType { - Option, Argument -}; -struct Token { - TokenType type; - std::string token; -}; + enum class ParseResultType { + Matched, NoMatch, ShortCircuitAll, ShortCircuitSame + }; -inline auto isOptPrefix(char c) -> bool { - return c == '-' -#ifdef CATCH_PLATFORM_WINDOWS - || c == '/' -#endif - ; -} + class ParseState { + public: -// Abstracts iterators into args as a stream of tokens, with option arguments uniformly handled -class TokenStream { - using Iterator = std::vector::const_iterator; - Iterator it; - Iterator itEnd; - std::vector m_tokenBuffer; - - void loadBuffer() { - m_tokenBuffer.resize(0); - - // Skip any empty strings - while (it != itEnd && it->empty()) - ++it; - - if (it != itEnd) { - auto const &next = *it; - if (isOptPrefix(next[0])) { - auto delimiterPos = next.find_first_of(" :="); - if (delimiterPos != std::string::npos) { - m_tokenBuffer.push_back({TokenType::Option, next.substr(0, delimiterPos)}); - m_tokenBuffer.push_back({TokenType::Argument, next.substr(delimiterPos + 1)}); - } else { - if (next[1] != '-' && next.size() > 2) { - std::string opt = "- "; - for (size_t i = 1; i < next.size(); ++i) { - opt[1] = next[i]; - m_tokenBuffer.push_back({TokenType::Option, opt}); - } - } else { - m_tokenBuffer.push_back({TokenType::Option, next}); - } - } - } else { - m_tokenBuffer.push_back({TokenType::Argument, next}); - } - } - } + ParseState(ParseResultType type, TokenStream const &remainingTokens) + : m_type(type), + m_remainingTokens(remainingTokens) {} - public: - explicit TokenStream(Args const &args) : TokenStream(args.m_args.begin(), args.m_args.end()) {} + auto type() const -> ParseResultType { return m_type; } - TokenStream(Iterator it, Iterator itEnd) : it(it), itEnd(itEnd) { - loadBuffer(); - } + auto remainingTokens() const -> TokenStream { return m_remainingTokens; } - explicit operator bool() const { - return !m_tokenBuffer.empty() || it != itEnd; - } + private: + ParseResultType m_type; + TokenStream m_remainingTokens; + }; - auto count() const -> size_t { return m_tokenBuffer.size() + (itEnd - it); } + using Result = BasicResult; + using ParserResult = BasicResult; + using InternalParseResult = BasicResult; - auto operator*() const -> Token { - assert(!m_tokenBuffer.empty()); - return m_tokenBuffer.front(); - } + struct HelpColumns { + std::string left; + std::string right; + }; - auto operator->() const -> Token const * { - assert(!m_tokenBuffer.empty()); - return &m_tokenBuffer.front(); - } + template + inline auto convertInto(std::string const &source, T &target) -> ParserResult { + std::stringstream ss; + ss << source; + ss >> target; + if (ss.fail()) + return ParserResult::runtimeError("Unable to convert '" + source + "' to destination type"); + else + return ParserResult::ok(ParseResultType::Matched); + } - auto operator++() -> TokenStream & { - if (m_tokenBuffer.size() >= 2) { - m_tokenBuffer.erase(m_tokenBuffer.begin()); - } else { - if (it != itEnd) - ++it; - loadBuffer(); - } - return *this; - } -}; + inline auto convertInto(std::string const &source, std::string &target) -> ParserResult { + target = source; + return ParserResult::ok(ParseResultType::Matched); + } -class ResultBase { - public: - enum Type { - Ok, LogicError, RuntimeError - }; + inline auto convertInto(std::string const &source, bool &target) -> ParserResult { + std::string srcLC = source; + std::transform(srcLC.begin(), + srcLC.end(), + srcLC.begin(), + [](unsigned char c) { return static_cast( std::tolower(c)); }); + if (srcLC == "y" || srcLC == "1" || srcLC == "true" || srcLC == "yes" || srcLC == "on") + target = true; + else if (srcLC == "n" || srcLC == "0" || srcLC == "false" || srcLC == "no" || srcLC == "off") + target = false; + else + return ParserResult::runtimeError( + "Expected a boolean value but did not recognise: '" + source + "'"); + return ParserResult::ok(ParseResultType::Matched); + } - protected: - ResultBase(Type type) : m_type(type) {} - virtual ~ResultBase() = default; +#ifdef CLARA_CONFIG_OPTIONAL_TYPE - virtual void enforceOk() const = 0; + template + inline auto convertInto(std::string const &source, CLARA_CONFIG_OPTIONAL_TYPE &target) -> ParserResult { + T temp; + auto result = convertInto(source, temp); + if (result) + target = std::move(temp); + return result; + } - Type m_type; -}; +#endif // CLARA_CONFIG_OPTIONAL_TYPE -template -class ResultValueBase : public ResultBase { - public: - auto value() const -> T const & { - enforceOk(); - return m_value; - } - - protected: - ResultValueBase(Type type) : ResultBase(type) {} - - ResultValueBase(ResultValueBase const &other) : ResultBase(other) { - if (m_type == ResultBase::Ok) - new(&m_value) T(other.m_value); - } - - ResultValueBase(Type, T const &value) : ResultBase(Ok) { - new(&m_value) T(value); - } - - auto operator=(ResultValueBase const &other) -> ResultValueBase & { - if (m_type == ResultBase::Ok) - m_value.~T(); - ResultBase::operator=(other); - if (m_type == ResultBase::Ok) - new(&m_value) T(other.m_value); - return *this; - } - - ~ResultValueBase() override { - if (m_type == Ok) - m_value.~T(); - } - - union { - T m_value; - }; -}; + struct NonCopyable { + NonCopyable() = default; -template<> -class ResultValueBase : public ResultBase { - protected: - using ResultBase::ResultBase; -}; + NonCopyable(NonCopyable const &) = delete; -template -class BasicResult : public ResultValueBase { - public: - template - explicit BasicResult(BasicResult const &other) - : ResultValueBase(other.type()), - m_errorMessage(other.errorMessage()) { - assert(type() != ResultBase::Ok); - } - - template - static auto ok(U const &value) -> BasicResult { return {ResultBase::Ok, value}; } - static auto ok() -> BasicResult { return {ResultBase::Ok}; } - static auto logicError(std::string const &message) -> BasicResult { return {ResultBase::LogicError, message}; } - static auto runtimeError(std::string const &message) -> BasicResult { return {ResultBase::RuntimeError, message}; } - - explicit operator bool() const { return m_type == ResultBase::Ok; } - auto type() const -> ResultBase::Type { return m_type; } - auto errorMessage() const -> std::string { return m_errorMessage; } - - protected: - void enforceOk() const override { - - // Errors shouldn't reach this point, but if they do - // the actual error message will be in m_errorMessage - assert(m_type != ResultBase::LogicError); - assert(m_type != ResultBase::RuntimeError); - if (m_type != ResultBase::Ok) - std::abort(); - } - - std::string m_errorMessage; // Only populated if resultType is an error - - BasicResult(ResultBase::Type type, std::string const &message) - : ResultValueBase(type), - m_errorMessage(message) { - assert(m_type != ResultBase::Ok); - } - - using ResultValueBase::ResultValueBase; - using ResultBase::m_type; -}; + NonCopyable(NonCopyable &&) = delete; -enum class ParseResultType { - Matched, NoMatch, ShortCircuitAll, ShortCircuitSame -}; + NonCopyable &operator=(NonCopyable const &) = delete; -class ParseState { - public: + NonCopyable &operator=(NonCopyable &&) = delete; + }; - ParseState(ParseResultType type, TokenStream const &remainingTokens) - : m_type(type), - m_remainingTokens(remainingTokens) {} + struct BoundRef : NonCopyable { + virtual ~BoundRef() = default; - auto type() const -> ParseResultType { return m_type; } - auto remainingTokens() const -> TokenStream { return m_remainingTokens; } + virtual auto isContainer() const -> bool { return false; } - private: - ParseResultType m_type; - TokenStream m_remainingTokens; -}; + virtual auto isFlag() const -> bool { return false; } + }; -using Result = BasicResult; -using ParserResult = BasicResult; -using InternalParseResult = BasicResult; + struct BoundValueRefBase : BoundRef { + virtual auto setValue(std::string const &arg) -> ParserResult = 0; + }; -struct HelpColumns { - std::string left; - std::string right; -}; + struct BoundFlagRefBase : BoundRef { + virtual auto setFlag(bool flag) -> ParserResult = 0; -template -inline auto convertInto(std::string const &source, T &target) -> ParserResult { - std::stringstream ss; - ss << source; - ss >> target; - if (ss.fail()) - return ParserResult::runtimeError("Unable to convert '" + source + "' to destination type"); - else - return ParserResult::ok(ParseResultType::Matched); -} -inline auto convertInto(std::string const &source, std::string &target) -> ParserResult { - target = source; - return ParserResult::ok(ParseResultType::Matched); -} -inline auto convertInto(std::string const &source, bool &target) -> ParserResult { - std::string srcLC = source; - std::transform(srcLC.begin(), - srcLC.end(), - srcLC.begin(), - [](unsigned char c) { return static_cast( std::tolower(c)); }); - if (srcLC == "y" || srcLC == "1" || srcLC == "true" || srcLC == "yes" || srcLC == "on") - target = true; - else if (srcLC == "n" || srcLC == "0" || srcLC == "false" || srcLC == "no" || srcLC == "off") - target = false; - else - return ParserResult::runtimeError("Expected a boolean value but did not recognise: '" + source + "'"); - return ParserResult::ok(ParseResultType::Matched); -} -#ifdef CLARA_CONFIG_OPTIONAL_TYPE -template -inline auto convertInto(std::string const &source, CLARA_CONFIG_OPTIONAL_TYPE &target) -> ParserResult { - T temp; - auto result = convertInto(source, temp); - if (result) - target = std::move(temp); - return result; -} -#endif // CLARA_CONFIG_OPTIONAL_TYPE + virtual auto isFlag() const -> bool { return true; } + }; -struct NonCopyable { - NonCopyable() = default; - NonCopyable(NonCopyable const &) = delete; - NonCopyable(NonCopyable &&) = delete; - NonCopyable &operator=(NonCopyable const &) = delete; - NonCopyable &operator=(NonCopyable &&) = delete; -}; + template + struct BoundValueRef : BoundValueRefBase { + T &m_ref; -struct BoundRef : NonCopyable { - virtual ~BoundRef() = default; - virtual auto isContainer() const -> bool { return false; } - virtual auto isFlag() const -> bool { return false; } -}; -struct BoundValueRefBase : BoundRef { - virtual auto setValue(std::string const &arg) -> ParserResult = 0; -}; -struct BoundFlagRefBase : BoundRef { - virtual auto setFlag(bool flag) -> ParserResult = 0; - virtual auto isFlag() const -> bool { return true; } -}; + explicit BoundValueRef(T &ref) : m_ref(ref) {} -template -struct BoundValueRef : BoundValueRefBase { - T &m_ref; + auto setValue(std::string const &arg) -> ParserResult override { + return convertInto(arg, m_ref); + } + }; - explicit BoundValueRef(T &ref) : m_ref(ref) {} + template + struct BoundValueRef> : BoundValueRefBase { + std::vector &m_ref; - auto setValue(std::string const &arg) -> ParserResult override { - return convertInto(arg, m_ref); - } -}; + explicit BoundValueRef(std::vector &ref) : m_ref(ref) {} -template -struct BoundValueRef> : BoundValueRefBase { - std::vector &m_ref; + auto isContainer() const -> bool override { return true; } - explicit BoundValueRef(std::vector &ref) : m_ref(ref) {} + auto setValue(std::string const &arg) -> ParserResult override { + T temp; + auto result = convertInto(arg, temp); + if (result) + m_ref.push_back(temp); + return result; + } + }; - auto isContainer() const -> bool override { return true; } + struct BoundFlagRef : BoundFlagRefBase { + bool &m_ref; - auto setValue(std::string const &arg) -> ParserResult override { - T temp; - auto result = convertInto(arg, temp); - if (result) - m_ref.push_back(temp); - return result; - } -}; + explicit BoundFlagRef(bool &ref) : m_ref(ref) {} -struct BoundFlagRef : BoundFlagRefBase { - bool &m_ref; + auto setFlag(bool flag) -> ParserResult override { + m_ref = flag; + return ParserResult::ok(ParseResultType::Matched); + } + }; - explicit BoundFlagRef(bool &ref) : m_ref(ref) {} + template + struct LambdaInvoker { + static_assert(std::is_same::value, + "Lambda must return void or clara::ParserResult"); - auto setFlag(bool flag) -> ParserResult override { - m_ref = flag; - return ParserResult::ok(ParseResultType::Matched); - } -}; + template + static auto invoke(L const &lambda, ArgType const &arg) -> ParserResult { + return lambda(arg); + } + }; -template -struct LambdaInvoker { - static_assert(std::is_same::value, "Lambda must return void or clara::ParserResult"); + template<> + struct LambdaInvoker { + template + static auto invoke(L const &lambda, ArgType const &arg) -> ParserResult { + lambda(arg); + return ParserResult::ok(ParseResultType::Matched); + } + }; - template - static auto invoke(L const &lambda, ArgType const &arg) -> ParserResult { - return lambda(arg); - } -}; + template + inline auto invokeLambda(L const &lambda, std::string const &arg) -> ParserResult { + ArgType temp{}; + auto result = convertInto(arg, temp); + return !result + ? result + : LambdaInvoker::ReturnType>::invoke(lambda, temp); + } -template<> -struct LambdaInvoker { - template - static auto invoke(L const &lambda, ArgType const &arg) -> ParserResult { - lambda(arg); - return ParserResult::ok(ParseResultType::Matched); - } -}; + template + struct BoundLambda : BoundValueRefBase { + L m_lambda; -template -inline auto invokeLambda(L const &lambda, std::string const &arg) -> ParserResult { - ArgType temp{}; - auto result = convertInto(arg, temp); - return !result - ? result - : LambdaInvoker::ReturnType>::invoke(lambda, temp); -} + static_assert(UnaryLambdaTraits::isValid, "Supplied lambda must take exactly one argument"); -template -struct BoundLambda : BoundValueRefBase { - L m_lambda; + explicit BoundLambda(L const &lambda) : m_lambda(lambda) {} - static_assert(UnaryLambdaTraits::isValid, "Supplied lambda must take exactly one argument"); - explicit BoundLambda(L const &lambda) : m_lambda(lambda) {} + auto setValue(std::string const &arg) -> ParserResult override { + return invokeLambda::ArgType>(m_lambda, arg); + } + }; - auto setValue(std::string const &arg) -> ParserResult override { - return invokeLambda::ArgType>(m_lambda, arg); - } -}; + template + struct BoundFlagLambda : BoundFlagRefBase { + L m_lambda; + + static_assert(UnaryLambdaTraits::isValid, "Supplied lambda must take exactly one argument"); + static_assert(std::is_same::ArgType, bool>::value, + "flags must be boolean"); -template -struct BoundFlagLambda : BoundFlagRefBase { - L m_lambda; + explicit BoundFlagLambda(L const &lambda) : m_lambda(lambda) {} - static_assert(UnaryLambdaTraits::isValid, "Supplied lambda must take exactly one argument"); - static_assert(std::is_same::ArgType, bool>::value, "flags must be boolean"); + auto setFlag(bool flag) -> ParserResult override { + return LambdaInvoker::ReturnType>::invoke(m_lambda, flag); + } + }; - explicit BoundFlagLambda(L const &lambda) : m_lambda(lambda) {} + enum class Optionality { + Optional, Required + }; - auto setFlag(bool flag) -> ParserResult override { - return LambdaInvoker::ReturnType>::invoke(m_lambda, flag); - } -}; + struct Parser; -enum class Optionality { Optional, Required }; + class ParserBase { + public: + virtual ~ParserBase() = default; -struct Parser; + virtual auto validate() const -> Result { return Result::ok(); } -class ParserBase { - public: - virtual ~ParserBase() = default; - virtual auto validate() const -> Result { return Result::ok(); } - virtual auto parse(std::string const &exeName, TokenStream const &tokens) const -> InternalParseResult = 0; - virtual auto cardinality() const -> size_t { return 1; } + virtual auto + parse(std::string const &exeName, TokenStream const &tokens) const -> InternalParseResult = 0; - auto parse(Args const &args) const -> InternalParseResult { - return parse(args.exeName(), TokenStream(args)); - } -}; + virtual auto cardinality() const -> size_t { return 1; } + + auto parse(Args const &args) const -> InternalParseResult { + return parse(args.exeName(), TokenStream(args)); + } + }; -template -class ComposableParserImpl : public ParserBase { - public: - template - auto operator|(T const &other) const -> Parser; + template + class ComposableParserImpl : public ParserBase { + public: + template + auto operator|(T const &other) const -> Parser; - template - auto operator+(T const &other) const -> Parser; -}; + template + auto operator+(T const &other) const -> Parser; + }; // Common code and state for Args and Opts -template -class ParserRefImpl : public ComposableParserImpl { - protected: - Optionality m_optionality = Optionality::Optional; - std::shared_ptr m_ref; - std::string m_hint; - std::string m_description; - - explicit ParserRefImpl(std::shared_ptr const &ref) : m_ref(ref) {} - - public: - template - ParserRefImpl(T &ref, std::string const &hint) - : m_ref(std::make_shared>(ref)), - m_hint(hint) {} - - template - ParserRefImpl(LambdaT const &ref, std::string const &hint) - : m_ref(std::make_shared>(ref)), - m_hint(hint) {} - - auto operator()(std::string const &description) -> DerivedT & { - m_description = description; - return static_cast( *this ); - } - - auto optional() -> DerivedT & { - m_optionality = Optionality::Optional; - return static_cast( *this ); - }; - - auto required() -> DerivedT & { - m_optionality = Optionality::Required; - return static_cast( *this ); - }; - - auto isOptional() const -> bool { - return m_optionality == Optionality::Optional; - } - - auto cardinality() const -> size_t override { - if (m_ref->isContainer()) - return 0; - else - return 1; - } + template + class ParserRefImpl : public ComposableParserImpl { + protected: + Optionality m_optionality = Optionality::Optional; + std::shared_ptr m_ref; + std::string m_hint; + std::string m_description; - auto hint() const -> std::string { return m_hint; } -}; + explicit ParserRefImpl(std::shared_ptr const &ref) : m_ref(ref) {} + + public: + template + ParserRefImpl(T &ref, std::string const &hint) + : m_ref(std::make_shared>(ref)), + m_hint(hint) {} + + template + ParserRefImpl(LambdaT const &ref, std::string const &hint) + : m_ref(std::make_shared>(ref)), + m_hint(hint) {} + + auto operator()(std::string const &description) -> DerivedT & { + m_description = description; + return static_cast( *this ); + } + + auto optional() -> DerivedT & { + m_optionality = Optionality::Optional; + return static_cast( *this ); + }; -class ExeName : public ComposableParserImpl { - std::shared_ptr m_name; - std::shared_ptr m_ref; + auto required() -> DerivedT & { + m_optionality = Optionality::Required; + return static_cast( *this ); + }; - template - static auto makeRef(LambdaT const &lambda) -> std::shared_ptr { - return std::make_shared>(lambda); - } + auto isOptional() const -> bool { + return m_optionality == Optionality::Optional; + } - public: - ExeName() : m_name(std::make_shared("")) {} + auto cardinality() const -> size_t override { + if (m_ref->isContainer()) + return 0; + else + return 1; + } - explicit ExeName(std::string &ref) : ExeName() { - m_ref = std::make_shared>(ref); - } + auto hint() const -> std::string { return m_hint; } + }; - template - explicit ExeName(LambdaT const &lambda) : ExeName() { - m_ref = std::make_shared>(lambda); - } + class ExeName : public ComposableParserImpl { + std::shared_ptr m_name; + std::shared_ptr m_ref; - // The exe name is not parsed out of the normal tokens, but is handled specially - auto parse(std::string const &, TokenStream const &tokens) const -> InternalParseResult override { - return InternalParseResult::ok(ParseState(ParseResultType::NoMatch, tokens)); - } + template + static auto makeRef(LambdaT const &lambda) -> std::shared_ptr { + return std::make_shared>(lambda); + } - auto name() const -> std::string { return *m_name; } - auto set(std::string const &newName) -> ParserResult { + public: + ExeName() : m_name(std::make_shared("")) {} - auto lastSlash = newName.find_last_of("\\/"); - auto filename = (lastSlash == std::string::npos) - ? newName - : newName.substr(lastSlash + 1); + explicit ExeName(std::string &ref) : ExeName() { + m_ref = std::make_shared>(ref); + } - *m_name = filename; - if (m_ref) - return m_ref->setValue(filename); - else - return ParserResult::ok(ParseResultType::Matched); - } -}; + template + explicit ExeName(LambdaT const &lambda) : ExeName() { + m_ref = std::make_shared>(lambda); + } -class Arg : public ParserRefImpl { - public: - using ParserRefImpl::ParserRefImpl; + // The exe name is not parsed out of the normal tokens, but is handled specially + auto parse(std::string const &, TokenStream const &tokens) const -> InternalParseResult override { + return InternalParseResult::ok(ParseState(ParseResultType::NoMatch, tokens)); + } - auto parse(std::string const &, TokenStream const &tokens) const -> InternalParseResult override { - auto validationResult = validate(); - if (!validationResult) - return InternalParseResult(validationResult); + auto name() const -> std::string { return *m_name; } - auto remainingTokens = tokens; - auto const &token = *remainingTokens; - if (token.type != TokenType::Argument) - return InternalParseResult::ok(ParseState(ParseResultType::NoMatch, remainingTokens)); + auto set(std::string const &newName) -> ParserResult { - assert(!m_ref->isFlag()); - auto valueRef = static_cast( m_ref.get()); + auto lastSlash = newName.find_last_of("\\/"); + auto filename = (lastSlash == std::string::npos) + ? newName + : newName.substr(lastSlash + 1); - auto result = valueRef->setValue(remainingTokens->token); - if (!result) - return InternalParseResult(result); - else - return InternalParseResult::ok(ParseState(ParseResultType::Matched, ++remainingTokens)); - } -}; + *m_name = filename; + if (m_ref) + return m_ref->setValue(filename); + else + return ParserResult::ok(ParseResultType::Matched); + } + }; + + class Arg : public ParserRefImpl { + public: + using ParserRefImpl::ParserRefImpl; + + auto parse(std::string const &, TokenStream const &tokens) const -> InternalParseResult override { + auto validationResult = validate(); + if (!validationResult) + return InternalParseResult(validationResult); + + auto remainingTokens = tokens; + auto const &token = *remainingTokens; + if (token.type != TokenType::Argument) + return InternalParseResult::ok(ParseState(ParseResultType::NoMatch, remainingTokens)); + + assert(!m_ref->isFlag()); + auto valueRef = static_cast( m_ref.get()); + + auto result = valueRef->setValue(remainingTokens->token); + if (!result) + return InternalParseResult(result); + else + return InternalParseResult::ok(ParseState(ParseResultType::Matched, ++remainingTokens)); + } + }; -inline auto normaliseOpt(std::string const &optName) -> std::string { + inline auto normaliseOpt(std::string const &optName) -> std::string { #ifdef CATCH_PLATFORM_WINDOWS - if( optName[0] == '/' ) + if( optName[0] == '/' ) return "-" + optName.substr( 1 ); else #endif - return optName; -} + return optName; + } -class Opt : public ParserRefImpl { - protected: - std::vector m_optNames; - - public: - template - explicit Opt(LambdaT const &ref) : ParserRefImpl(std::make_shared>(ref)) {} - - explicit Opt(bool &ref) : ParserRefImpl(std::make_shared(ref)) {} - - template - Opt(LambdaT const &ref, std::string const &hint) : ParserRefImpl(ref, hint) {} - - template - Opt(T &ref, std::string const &hint) : ParserRefImpl(ref, hint) {} - - auto operator[](std::string const &optName) -> Opt & { - m_optNames.push_back(optName); - return *this; - } - - auto getHelpColumns() const -> std::vector { - std::ostringstream oss; - bool first = true; - for (auto const &opt : m_optNames) { - if (first) - first = false; - else - oss << ", "; - oss << opt; - } - if (!m_hint.empty()) - oss << " <" << m_hint << ">"; - return {{oss.str(), m_description}}; - } - - auto isMatch(std::string const &optToken) const -> bool { - auto normalisedToken = normaliseOpt(optToken); - for (auto const &name : m_optNames) { - if (normaliseOpt(name) == normalisedToken) - return true; - } - return false; - } - - using ParserBase::parse; - - auto parse(std::string const &, TokenStream const &tokens) const -> InternalParseResult override { - auto validationResult = validate(); - if (!validationResult) - return InternalParseResult(validationResult); - - auto remainingTokens = tokens; - if (remainingTokens && remainingTokens->type == TokenType::Option) { - auto const &token = *remainingTokens; - if (isMatch(token.token)) { - if (m_ref->isFlag()) { - auto flagRef = static_cast( m_ref.get()); - auto result = flagRef->setFlag(true); - if (!result) - return InternalParseResult(result); - if (result.value() == ParseResultType::ShortCircuitAll) - return InternalParseResult::ok(ParseState(result.value(), remainingTokens)); - } else { - auto valueRef = static_cast( m_ref.get()); - ++remainingTokens; - if (!remainingTokens) - return InternalParseResult::runtimeError("Expected argument following " + token.token); - auto const &argToken = *remainingTokens; - if (argToken.type != TokenType::Argument) - return InternalParseResult::runtimeError("Expected argument following " + token.token); - auto result = valueRef->setValue(argToken.token); - if (!result) - return InternalParseResult(result); - if (result.value() == ParseResultType::ShortCircuitAll) - return InternalParseResult::ok(ParseState(result.value(), remainingTokens)); - } - return InternalParseResult::ok(ParseState(ParseResultType::Matched, ++remainingTokens)); - } - } - return InternalParseResult::ok(ParseState(ParseResultType::NoMatch, remainingTokens)); - } - - auto validate() const -> Result override { - if (m_optNames.empty()) - return Result::logicError("No options supplied to Opt"); - for (auto const &name : m_optNames) { - if (name.empty()) - return Result::logicError("Option name cannot be empty"); + class Opt : public ParserRefImpl { + protected: + std::vector m_optNames; + + public: + template + explicit Opt(LambdaT const &ref) : ParserRefImpl(std::make_shared>(ref)) {} + + explicit Opt(bool &ref) : ParserRefImpl(std::make_shared(ref)) {} + + template + Opt(LambdaT const &ref, std::string const &hint) : ParserRefImpl(ref, hint) {} + + template + Opt(T &ref, std::string const &hint) : ParserRefImpl(ref, hint) {} + + auto operator[](std::string const &optName) -> Opt & { + m_optNames.push_back(optName); + return *this; + } + + auto getHelpColumns() const -> std::vector { + std::ostringstream oss; + bool first = true; + for (auto const &opt: m_optNames) { + if (first) + first = false; + else + oss << ", "; + oss << opt; + } + if (!m_hint.empty()) + oss << " <" << m_hint << ">"; + return {{oss.str(), m_description}}; + } + + auto isMatch(std::string const &optToken) const -> bool { + auto normalisedToken = normaliseOpt(optToken); + for (auto const &name: m_optNames) { + if (normaliseOpt(name) == normalisedToken) + return true; + } + return false; + } + + using ParserBase::parse; + + auto parse(std::string const &, TokenStream const &tokens) const -> InternalParseResult override { + auto validationResult = validate(); + if (!validationResult) + return InternalParseResult(validationResult); + + auto remainingTokens = tokens; + if (remainingTokens && remainingTokens->type == TokenType::Option) { + auto const &token = *remainingTokens; + if (isMatch(token.token)) { + if (m_ref->isFlag()) { + auto flagRef = static_cast( m_ref.get()); + auto result = flagRef->setFlag(true); + if (!result) + return InternalParseResult(result); + if (result.value() == ParseResultType::ShortCircuitAll) + return InternalParseResult::ok(ParseState(result.value(), remainingTokens)); + } else { + auto valueRef = static_cast( m_ref.get()); + ++remainingTokens; + if (!remainingTokens) + return InternalParseResult::runtimeError( + "Expected argument following " + token.token); + auto const &argToken = *remainingTokens; + if (argToken.type != TokenType::Argument) + return InternalParseResult::runtimeError( + "Expected argument following " + token.token); + auto result = valueRef->setValue(argToken.token); + if (!result) + return InternalParseResult(result); + if (result.value() == ParseResultType::ShortCircuitAll) + return InternalParseResult::ok(ParseState(result.value(), remainingTokens)); + } + return InternalParseResult::ok(ParseState(ParseResultType::Matched, ++remainingTokens)); + } + } + return InternalParseResult::ok(ParseState(ParseResultType::NoMatch, remainingTokens)); + } + + auto validate() const -> Result override { + if (m_optNames.empty()) + return Result::logicError("No options supplied to Opt"); + for (auto const &name: m_optNames) { + if (name.empty()) + return Result::logicError("Option name cannot be empty"); #ifdef CATCH_PLATFORM_WINDOWS - if( name[0] != '-' && name[0] != '/' ) + if( name[0] != '-' && name[0] != '/' ) return Result::logicError( "Option name must begin with '-' or '/'" ); #else - if (name[0] != '-') - return Result::logicError("Option name must begin with '-'"); + if (name[0] != '-') + return Result::logicError("Option name must begin with '-'"); #endif - } - return ParserRefImpl::validate(); - } -}; + } + return ParserRefImpl::validate(); + } + }; -struct Help : Opt { - Help(bool &showHelpFlag) - : Opt([&](bool flag) { - showHelpFlag = flag; - return ParserResult::ok(ParseResultType::ShortCircuitAll); - }) { - static_cast( *this ) - ("display usage information") - ["-?"]["-h"]["--help"] - .optional(); - } -}; + struct Help : Opt { + Help(bool &showHelpFlag) + : Opt([&](bool flag) { + showHelpFlag = flag; + return ParserResult::ok(ParseResultType::ShortCircuitAll); + }) { + static_cast( *this ) + ("display usage information") + ["-?"]["-h"]["--help"] + .optional(); + } + }; -struct Parser : ParserBase { - - mutable ExeName m_exeName; - std::vector m_options; - std::vector m_args; - - auto operator|=(ExeName const &exeName) -> Parser & { - m_exeName = exeName; - return *this; - } - - auto operator|=(Arg const &arg) -> Parser & { - m_args.push_back(arg); - return *this; - } - - auto operator|=(Opt const &opt) -> Parser & { - m_options.push_back(opt); - return *this; - } - - auto operator|=(Parser const &other) -> Parser & { - m_options.insert(m_options.end(), other.m_options.begin(), other.m_options.end()); - m_args.insert(m_args.end(), other.m_args.begin(), other.m_args.end()); - return *this; - } - - template - auto operator|(T const &other) const -> Parser { - return Parser(*this) |= other; - } - - // Forward deprecated interface with '+' instead of '|' - template - auto operator+=(T const &other) -> Parser & { return operator|=(other); } - template - auto operator+(T const &other) const -> Parser { return operator|(other); } - - auto getHelpColumns() const -> std::vector { - std::vector cols; - for (auto const &o : m_options) { - auto childCols = o.getHelpColumns(); - cols.insert(cols.end(), childCols.begin(), childCols.end()); - } - return cols; - } - - void writeToStream(std::ostream &os) const { - if (!m_exeName.name().empty()) { - os << "usage:\n" << " " << m_exeName.name() << " "; - bool required = true, first = true; - for (auto const &arg : m_args) { - if (first) - first = false; - else - os << " "; - if (arg.isOptional() && required) { - os << "["; - required = false; - } - os << "<" << arg.hint() << ">"; - if (arg.cardinality() == 0) - os << " ... "; - } - if (!required) - os << "]"; - if (!m_options.empty()) - os << " options"; - os << "\n\nwhere options are:" << std::endl; - } - - auto rows = getHelpColumns(); - size_t consoleWidth = CATCH_CLARA_CONFIG_CONSOLE_WIDTH; - size_t optWidth = 0; - for (auto const &cols : rows) - optWidth = (std::max) (optWidth, cols.left.size() + 2); - - optWidth = (std::min) (optWidth, consoleWidth / 2); - - for (auto const &cols : rows) { - auto row = - TextFlow::Column(cols.left).width(optWidth).indent(2) + - TextFlow::Spacer(4) + - TextFlow::Column(cols.right).width(consoleWidth - 7 - optWidth); - os << row << std::endl; - } - } - - friend auto operator<<(std::ostream &os, Parser const &parser) -> std::ostream & { - parser.writeToStream(os); - return os; - } - - auto validate() const -> Result override { - for (auto const &opt : m_options) { - auto result = opt.validate(); - if (!result) - return result; - } - for (auto const &arg : m_args) { - auto result = arg.validate(); - if (!result) - return result; - } - return Result::ok(); - } - - using ParserBase::parse; - - auto parse(std::string const &exeName, TokenStream const &tokens) const -> InternalParseResult override { - - struct ParserInfo { - ParserBase const *parser = nullptr; - size_t count = 0; - }; - const size_t totalParsers = m_options.size() + m_args.size(); - assert(totalParsers < 512); - // ParserInfo parseInfos[totalParsers]; // <-- this is what we really want to do - ParserInfo parseInfos[512]; + struct Parser : ParserBase { - { - size_t i = 0; - for (auto const &opt : m_options) parseInfos[i++].parser = &opt; - for (auto const &arg : m_args) parseInfos[i++].parser = &arg; - } + mutable ExeName m_exeName; + std::vector m_options; + std::vector m_args; + + auto operator|=(ExeName const &exeName) -> Parser & { + m_exeName = exeName; + return *this; + } + + auto operator|=(Arg const &arg) -> Parser & { + m_args.push_back(arg); + return *this; + } + + auto operator|=(Opt const &opt) -> Parser & { + m_options.push_back(opt); + return *this; + } + + auto operator|=(Parser const &other) -> Parser & { + m_options.insert(m_options.end(), other.m_options.begin(), other.m_options.end()); + m_args.insert(m_args.end(), other.m_args.begin(), other.m_args.end()); + return *this; + } + + template + auto operator|(T const &other) const -> Parser { + return Parser(*this) |= other; + } + + // Forward deprecated interface with '+' instead of '|' + template + auto operator+=(T const &other) -> Parser & { return operator|=(other); } + + template + auto operator+(T const &other) const -> Parser { return operator|(other); } + + auto getHelpColumns() const -> std::vector { + std::vector cols; + for (auto const &o: m_options) { + auto childCols = o.getHelpColumns(); + cols.insert(cols.end(), childCols.begin(), childCols.end()); + } + return cols; + } + + void writeToStream(std::ostream &os) const { + if (!m_exeName.name().empty()) { + os << "usage:\n" << " " << m_exeName.name() << " "; + bool required = true, first = true; + for (auto const &arg: m_args) { + if (first) + first = false; + else + os << " "; + if (arg.isOptional() && required) { + os << "["; + required = false; + } + os << "<" << arg.hint() << ">"; + if (arg.cardinality() == 0) + os << " ... "; + } + if (!required) + os << "]"; + if (!m_options.empty()) + os << " options"; + os << "\n\nwhere options are:" << std::endl; + } + + auto rows = getHelpColumns(); + size_t consoleWidth = CATCH_CLARA_CONFIG_CONSOLE_WIDTH; + size_t optWidth = 0; + for (auto const &cols: rows) + optWidth = (std::max) (optWidth, cols.left.size() + 2); + + optWidth = (std::min) (optWidth, consoleWidth / 2); + + for (auto const &cols: rows) { + auto row = + TextFlow::Column(cols.left).width(optWidth).indent(2) + + TextFlow::Spacer(4) + + TextFlow::Column(cols.right).width(consoleWidth - 7 - optWidth); + os << row << std::endl; + } + } + + friend auto operator<<(std::ostream &os, Parser const &parser) -> std::ostream & { + parser.writeToStream(os); + return os; + } + + auto validate() const -> Result override { + for (auto const &opt: m_options) { + auto result = opt.validate(); + if (!result) + return result; + } + for (auto const &arg: m_args) { + auto result = arg.validate(); + if (!result) + return result; + } + return Result::ok(); + } - m_exeName.set(exeName); + using ParserBase::parse; - auto result = InternalParseResult::ok(ParseState(ParseResultType::NoMatch, tokens)); - while (result.value().remainingTokens()) { - bool tokenParsed = false; + auto + parse(std::string const &exeName, TokenStream const &tokens) const -> InternalParseResult override { - for (size_t i = 0; i < totalParsers; ++i) { - auto &parseInfo = parseInfos[i]; - if (parseInfo.parser->cardinality() == 0 || parseInfo.count < parseInfo.parser->cardinality()) { - result = parseInfo.parser->parse(exeName, result.value().remainingTokens()); - if (!result) - return result; - if (result.value().type() != ParseResultType::NoMatch) { - tokenParsed = true; - ++parseInfo.count; - break; - } - } - } + struct ParserInfo { + ParserBase const *parser = nullptr; + size_t count = 0; + }; + const size_t totalParsers = m_options.size() + m_args.size(); + assert(totalParsers < 512); + // ParserInfo parseInfos[totalParsers]; // <-- this is what we really want to do + ParserInfo parseInfos[512]; + + { + size_t i = 0; + for (auto const &opt: m_options) parseInfos[i++].parser = &opt; + for (auto const &arg: m_args) parseInfos[i++].parser = &arg; + } - if (result.value().type() == ParseResultType::ShortCircuitAll) - return result; - if (!tokenParsed) - return InternalParseResult::runtimeError("Unrecognised token: " + result.value().remainingTokens()->token); - } - // !TBD Check missing required options - return result; - } -}; + m_exeName.set(exeName); + + auto result = InternalParseResult::ok(ParseState(ParseResultType::NoMatch, tokens)); + while (result.value().remainingTokens()) { + bool tokenParsed = false; + + for (size_t i = 0; i < totalParsers; ++i) { + auto &parseInfo = parseInfos[i]; + if (parseInfo.parser->cardinality() == 0 || + parseInfo.count < parseInfo.parser->cardinality()) { + result = parseInfo.parser->parse(exeName, result.value().remainingTokens()); + if (!result) + return result; + if (result.value().type() != ParseResultType::NoMatch) { + tokenParsed = true; + ++parseInfo.count; + break; + } + } + } -template -template -auto ComposableParserImpl::operator|(T const &other) const -> Parser { - return Parser() | static_cast( *this ) | other; -} -} // namespace detail + if (result.value().type() == ParseResultType::ShortCircuitAll) + return result; + if (!tokenParsed) + return InternalParseResult::runtimeError( + "Unrecognised token: " + result.value().remainingTokens()->token); + } + // !TBD Check missing required options + return result; + } + }; + + template + template + auto ComposableParserImpl::operator|(T const &other) const -> Parser { + return Parser() | static_cast( *this ) | other; + } + } // namespace detail // A Combined parser -using detail::Parser; + using detail::Parser; // A parser for options -using detail::Opt; + using detail::Opt; // A parser for arguments -using detail::Arg; + using detail::Arg; // Wrapper for argc, argv from main() -using detail::Args; + using detail::Args; // Specifies the name of the executable -using detail::ExeName; + using detail::ExeName; // Convenience wrapper for option parser that specifies the help option -using detail::Help; + using detail::Help; // enum of result types from a parse -using detail::ParseResultType; + using detail::ParseResultType; // Result type for parser operation -using detail::ParserResult; + using detail::ParserResult; -} + } } // namespace Catch::clara // end clara.hpp @@ -9691,7 +10278,7 @@ using detail::ParserResult; // end catch_clara.h namespace Catch { -clara::Parser makeCommandLineParser(ConfigData &config); + clara::Parser makeCommandLineParser(ConfigData &config); } // end namespace Catch @@ -9701,218 +10288,219 @@ clara::Parser makeCommandLineParser(ConfigData &config); namespace Catch { -clara::Parser makeCommandLineParser(ConfigData &config) { - - using namespace clara; - - auto const setWarning = [&](std::string const &warning) { - auto warningSet = [&]() { - if (warning == "NoAssertions") - return WarnAbout::NoAssertions; - - if (warning == "NoTests") - return WarnAbout::NoTests; - - return WarnAbout::Nothing; - }(); - - if (warningSet == WarnAbout::Nothing) - return ParserResult::runtimeError("Unrecognised warning: '" + warning + "'"); - config.warnings = static_cast( config.warnings | warningSet ); - return ParserResult::ok(ParseResultType::Matched); - }; - auto const loadTestNamesFromFile = [&](std::string const &filename) { - std::ifstream f(filename.c_str()); - if (!f.is_open()) - return ParserResult::runtimeError("Unable to load input file: '" + filename + "'"); - - std::string line; - while (std::getline(f, line)) { - line = trim(line); - if (!line.empty() && !startsWith(line, '#')) { - if (!startsWith(line, '"')) - line = '"' + line + '"'; - config.testsOrTags.push_back(line); - config.testsOrTags.emplace_back(","); - } - } - //Remove comma in the end - if (!config.testsOrTags.empty()) - config.testsOrTags.erase(config.testsOrTags.end() - 1); - - return ParserResult::ok(ParseResultType::Matched); - }; - auto const setTestOrder = [&](std::string const &order) { - if (startsWith("declared", order)) - config.runOrder = RunTests::InDeclarationOrder; - else if (startsWith("lexical", order)) - config.runOrder = RunTests::InLexicographicalOrder; - else if (startsWith("random", order)) - config.runOrder = RunTests::InRandomOrder; - else - return clara::ParserResult::runtimeError("Unrecognised ordering: '" + order + "'"); - return ParserResult::ok(ParseResultType::Matched); - }; - auto const setRngSeed = [&](std::string const &seed) { - if (seed != "time") - return clara::detail::convertInto(seed, config.rngSeed); - config.rngSeed = static_cast( std::time(nullptr)); - return ParserResult::ok(ParseResultType::Matched); - }; - auto const setColourUsage = [&](std::string const &useColour) { - auto mode = toLower(useColour); - - if (mode == "yes") - config.useColour = UseColour::Yes; - else if (mode == "no") - config.useColour = UseColour::No; - else if (mode == "auto") - config.useColour = UseColour::Auto; - else - return ParserResult::runtimeError( - "colour mode must be one of: auto, yes or no. '" + useColour + "' not recognised"); - return ParserResult::ok(ParseResultType::Matched); - }; - auto const setWaitForKeypress = [&](std::string const &keypress) { - auto keypressLc = toLower(keypress); - if (keypressLc == "never") - config.waitForKeypress = WaitForKeypress::Never; - else if (keypressLc == "start") - config.waitForKeypress = WaitForKeypress::BeforeStart; - else if (keypressLc == "exit") - config.waitForKeypress = WaitForKeypress::BeforeExit; - else if (keypressLc == "both") - config.waitForKeypress = WaitForKeypress::BeforeStartAndExit; - else - return ParserResult::runtimeError( - "keypress argument must be one of: never, start, exit or both. '" + keypress + "' not recognised"); - return ParserResult::ok(ParseResultType::Matched); - }; - auto const setVerbosity = [&](std::string const &verbosity) { - auto lcVerbosity = toLower(verbosity); - if (lcVerbosity == "quiet") - config.verbosity = Verbosity::Quiet; - else if (lcVerbosity == "normal") - config.verbosity = Verbosity::Normal; - else if (lcVerbosity == "high") - config.verbosity = Verbosity::High; - else - return ParserResult::runtimeError("Unrecognised verbosity, '" + verbosity + "'"); - return ParserResult::ok(ParseResultType::Matched); - }; - auto const setReporter = [&](std::string const &reporter) { - IReporterRegistry::FactoryMap const &factories = getRegistryHub().getReporterRegistry().getFactories(); + clara::Parser makeCommandLineParser(ConfigData &config) { - auto lcReporter = toLower(reporter); - auto result = factories.find(lcReporter); + using namespace clara; - if (factories.end() != result) - config.reporterName = lcReporter; - else - return ParserResult::runtimeError( - "Unrecognized reporter, '" + reporter + "'. Check available with --list-reporters"); - return ParserResult::ok(ParseResultType::Matched); - }; - - auto cli - = ExeName(config.processName) - | Help(config.showHelp) - | Opt(config.listTests) - ["-l"]["--list-tests"] - ("list all/matching test cases") - | Opt(config.listTags) - ["-t"]["--list-tags"] - ("list all/matching tags") - | Opt(config.showSuccessfulTests) - ["-s"]["--success"] - ("include successful tests in output") - | Opt(config.shouldDebugBreak) - ["-b"]["--break"] - ("break into debugger on failure") - | Opt(config.noThrow) - ["-e"]["--nothrow"] - ("skip exception tests") - | Opt(config.showInvisibles) - ["-i"]["--invisibles"] - ("show invisibles (tabs, newlines)") - | Opt(config.outputFilename, "filename") - ["-o"]["--out"] - ("output filename") - | Opt(setReporter, "name") - ["-r"]["--reporter"] - ("reporter to use (defaults to console)") - | Opt(config.name, "name") - ["-n"]["--name"] - ("suite name") - | Opt([&](bool) { config.abortAfter = 1; }) - ["-a"]["--abort"] - ("abort at first failure") - | Opt([&](int x) { config.abortAfter = x; }, "no. failures") - ["-x"]["--abortx"] - ("abort after x failures") - | Opt(setWarning, "warning name") - ["-w"]["--warn"] - ("enable warnings") - | Opt([&](bool flag) { config.showDurations = flag ? ShowDurations::Always : ShowDurations::Never; }, - "yes|no") - ["-d"]["--durations"] - ("show test durations") - | Opt(config.minDuration, "seconds") - ["-D"]["--min-duration"] - ("show test durations for tests taking at least the given number of seconds") - | Opt(loadTestNamesFromFile, "filename") - ["-f"]["--input-file"] - ("load test names to run from a file") - | Opt(config.filenamesAsTags) - ["-#"]["--filenames-as-tags"] - ("adds a tag for the filename") - | Opt(config.sectionsToRun, "section name") - ["-c"]["--section"] - ("specify section to run") - | Opt(setVerbosity, "quiet|normal|high") - ["-v"]["--verbosity"] - ("set output verbosity") - | Opt(config.listTestNamesOnly) - ["--list-test-names-only"] - ("list all/matching test cases names only") - | Opt(config.listReporters) - ["--list-reporters"] - ("list all reporters") - | Opt(setTestOrder, "decl|lex|rand") - ["--order"] - ("test case order (defaults to decl)") - | Opt(setRngSeed, "'time'|number") - ["--rng-seed"] - ("set a specific seed for random numbers") - | Opt(setColourUsage, "yes|no") - ["--use-colour"] - ("should output be colourised") - | Opt(config.libIdentify) - ["--libidentify"] - ("report name and version according to libidentify standard") - | Opt(setWaitForKeypress, "never|start|exit|both") - ["--wait-for-keypress"] - ("waits for a keypress before exiting") - | Opt(config.benchmarkSamples, "samples") - ["--benchmark-samples"] - ("number of samples to collect (default: 100)") - | Opt(config.benchmarkResamples, "resamples") - ["--benchmark-resamples"] - ("number of resamples for the bootstrap (default: 100000)") - | Opt(config.benchmarkConfidenceInterval, "confidence interval") - ["--benchmark-confidence-interval"] - ("confidence interval for the bootstrap (between 0 and 1, default: 0.95)") - | Opt(config.benchmarkNoAnalysis) - ["--benchmark-no-analysis"] - ("perform only measurements; do not perform any analysis") - | Opt(config.benchmarkWarmupTime, "benchmarkWarmupTime") - ["--benchmark-warmup-time"] - ("amount of time in milliseconds spent on warming up each test (default: 100)") - | Arg(config.testsOrTags, "test name|pattern|tags") - ("which test or tests to use"); - - return cli; -} + auto const setWarning = [&](std::string const &warning) { + auto warningSet = [&]() { + if (warning == "NoAssertions") + return WarnAbout::NoAssertions; + + if (warning == "NoTests") + return WarnAbout::NoTests; + + return WarnAbout::Nothing; + }(); + + if (warningSet == WarnAbout::Nothing) + return ParserResult::runtimeError("Unrecognised warning: '" + warning + "'"); + config.warnings = static_cast( config.warnings | warningSet ); + return ParserResult::ok(ParseResultType::Matched); + }; + auto const loadTestNamesFromFile = [&](std::string const &filename) { + std::ifstream f(filename.c_str()); + if (!f.is_open()) + return ParserResult::runtimeError("Unable to load input file: '" + filename + "'"); + + std::string line; + while (std::getline(f, line)) { + line = trim(line); + if (!line.empty() && !startsWith(line, '#')) { + if (!startsWith(line, '"')) + line = '"' + line + '"'; + config.testsOrTags.push_back(line); + config.testsOrTags.emplace_back(","); + } + } + //Remove comma in the end + if (!config.testsOrTags.empty()) + config.testsOrTags.erase(config.testsOrTags.end() - 1); + + return ParserResult::ok(ParseResultType::Matched); + }; + auto const setTestOrder = [&](std::string const &order) { + if (startsWith("declared", order)) + config.runOrder = RunTests::InDeclarationOrder; + else if (startsWith("lexical", order)) + config.runOrder = RunTests::InLexicographicalOrder; + else if (startsWith("random", order)) + config.runOrder = RunTests::InRandomOrder; + else + return clara::ParserResult::runtimeError("Unrecognised ordering: '" + order + "'"); + return ParserResult::ok(ParseResultType::Matched); + }; + auto const setRngSeed = [&](std::string const &seed) { + if (seed != "time") + return clara::detail::convertInto(seed, config.rngSeed); + config.rngSeed = static_cast( std::time(nullptr)); + return ParserResult::ok(ParseResultType::Matched); + }; + auto const setColourUsage = [&](std::string const &useColour) { + auto mode = toLower(useColour); + + if (mode == "yes") + config.useColour = UseColour::Yes; + else if (mode == "no") + config.useColour = UseColour::No; + else if (mode == "auto") + config.useColour = UseColour::Auto; + else + return ParserResult::runtimeError( + "colour mode must be one of: auto, yes or no. '" + useColour + "' not recognised"); + return ParserResult::ok(ParseResultType::Matched); + }; + auto const setWaitForKeypress = [&](std::string const &keypress) { + auto keypressLc = toLower(keypress); + if (keypressLc == "never") + config.waitForKeypress = WaitForKeypress::Never; + else if (keypressLc == "start") + config.waitForKeypress = WaitForKeypress::BeforeStart; + else if (keypressLc == "exit") + config.waitForKeypress = WaitForKeypress::BeforeExit; + else if (keypressLc == "both") + config.waitForKeypress = WaitForKeypress::BeforeStartAndExit; + else + return ParserResult::runtimeError( + "keypress argument must be one of: never, start, exit or both. '" + keypress + + "' not recognised"); + return ParserResult::ok(ParseResultType::Matched); + }; + auto const setVerbosity = [&](std::string const &verbosity) { + auto lcVerbosity = toLower(verbosity); + if (lcVerbosity == "quiet") + config.verbosity = Verbosity::Quiet; + else if (lcVerbosity == "normal") + config.verbosity = Verbosity::Normal; + else if (lcVerbosity == "high") + config.verbosity = Verbosity::High; + else + return ParserResult::runtimeError("Unrecognised verbosity, '" + verbosity + "'"); + return ParserResult::ok(ParseResultType::Matched); + }; + auto const setReporter = [&](std::string const &reporter) { + IReporterRegistry::FactoryMap const &factories = getRegistryHub().getReporterRegistry().getFactories(); + + auto lcReporter = toLower(reporter); + auto result = factories.find(lcReporter); + + if (factories.end() != result) + config.reporterName = lcReporter; + else + return ParserResult::runtimeError( + "Unrecognized reporter, '" + reporter + "'. Check available with --list-reporters"); + return ParserResult::ok(ParseResultType::Matched); + }; + + auto cli + = ExeName(config.processName) + | Help(config.showHelp) + | Opt(config.listTests) + ["-l"]["--list-tests"] + ("list all/matching test cases") + | Opt(config.listTags) + ["-t"]["--list-tags"] + ("list all/matching tags") + | Opt(config.showSuccessfulTests) + ["-s"]["--success"] + ("include successful tests in output") + | Opt(config.shouldDebugBreak) + ["-b"]["--break"] + ("break into debugger on failure") + | Opt(config.noThrow) + ["-e"]["--nothrow"] + ("skip exception tests") + | Opt(config.showInvisibles) + ["-i"]["--invisibles"] + ("show invisibles (tabs, newlines)") + | Opt(config.outputFilename, "filename") + ["-o"]["--out"] + ("output filename") + | Opt(setReporter, "name") + ["-r"]["--reporter"] + ("reporter to use (defaults to console)") + | Opt(config.name, "name") + ["-n"]["--name"] + ("suite name") + | Opt([&](bool) { config.abortAfter = 1; }) + ["-a"]["--abort"] + ("abort at first failure") + | Opt([&](int x) { config.abortAfter = x; }, "no. failures") + ["-x"]["--abortx"] + ("abort after x failures") + | Opt(setWarning, "warning name") + ["-w"]["--warn"] + ("enable warnings") + | Opt([&](bool flag) { config.showDurations = flag ? ShowDurations::Always : ShowDurations::Never; }, + "yes|no") + ["-d"]["--durations"] + ("show test durations") + | Opt(config.minDuration, "seconds") + ["-D"]["--min-duration"] + ("show test durations for tests taking at least the given number of seconds") + | Opt(loadTestNamesFromFile, "filename") + ["-f"]["--input-file"] + ("load test names to run from a file") + | Opt(config.filenamesAsTags) + ["-#"]["--filenames-as-tags"] + ("adds a tag for the filename") + | Opt(config.sectionsToRun, "section name") + ["-c"]["--section"] + ("specify section to run") + | Opt(setVerbosity, "quiet|normal|high") + ["-v"]["--verbosity"] + ("set output verbosity") + | Opt(config.listTestNamesOnly) + ["--list-test-names-only"] + ("list all/matching test cases names only") + | Opt(config.listReporters) + ["--list-reporters"] + ("list all reporters") + | Opt(setTestOrder, "decl|lex|rand") + ["--order"] + ("test case order (defaults to decl)") + | Opt(setRngSeed, "'time'|number") + ["--rng-seed"] + ("set a specific seed for random numbers") + | Opt(setColourUsage, "yes|no") + ["--use-colour"] + ("should output be colourised") + | Opt(config.libIdentify) + ["--libidentify"] + ("report name and version according to libidentify standard") + | Opt(setWaitForKeypress, "never|start|exit|both") + ["--wait-for-keypress"] + ("waits for a keypress before exiting") + | Opt(config.benchmarkSamples, "samples") + ["--benchmark-samples"] + ("number of samples to collect (default: 100)") + | Opt(config.benchmarkResamples, "resamples") + ["--benchmark-resamples"] + ("number of resamples for the bootstrap (default: 100000)") + | Opt(config.benchmarkConfidenceInterval, "confidence interval") + ["--benchmark-confidence-interval"] + ("confidence interval for the bootstrap (between 0 and 1, default: 0.95)") + | Opt(config.benchmarkNoAnalysis) + ["--benchmark-no-analysis"] + ("perform only measurements; do not perform any analysis") + | Opt(config.benchmarkWarmupTime, "benchmarkWarmupTime") + ["--benchmark-warmup-time"] + ("amount of time in milliseconds spent on warming up each test (default: 100)") + | Arg(config.testsOrTags, "test name|pattern|tags") + ("which test or tests to use"); + + return cli; + } } // end namespace Catch // end catch_commandline.cpp @@ -9923,30 +10511,32 @@ clara::Parser makeCommandLineParser(ConfigData &config) { namespace Catch { -bool SourceLineInfo::operator==(SourceLineInfo const &other) const noexcept { - return line == other.line && (file == other.file || std::strcmp(file, other.file) == 0); -} -bool SourceLineInfo::operator<(SourceLineInfo const &other) const noexcept { - // We can assume that the same file will usually have the same pointer. - // Thus, if the pointers are the same, there is no point in calling the strcmp - return line < other.line || (line == other.line && file != other.file && (std::strcmp(file, other.file) < 0)); -} + bool SourceLineInfo::operator==(SourceLineInfo const &other) const noexcept { + return line == other.line && (file == other.file || std::strcmp(file, other.file) == 0); + } + + bool SourceLineInfo::operator<(SourceLineInfo const &other) const noexcept { + // We can assume that the same file will usually have the same pointer. + // Thus, if the pointers are the same, there is no point in calling the strcmp + return line < other.line || (line == other.line && file != other.file && (std::strcmp(file, other.file) < 0)); + } -std::ostream &operator<<(std::ostream &os, SourceLineInfo const &info) { + std::ostream &operator<<(std::ostream &os, SourceLineInfo const &info) { #ifndef __GNUG__ - os << info.file << '(' << info.line << ')'; + os << info.file << '(' << info.line << ')'; #else - os << info.file << ':' << info.line; + os << info.file << ':' << info.line; #endif - return os; -} + return os; + } -std::string StreamEndStop::operator+() const { - return std::string(); -} + std::string StreamEndStop::operator+() const { + return std::string(); + } + + NonCopyable::NonCopyable() = default; -NonCopyable::NonCopyable() = default; -NonCopyable::~NonCopyable() = default; + NonCopyable::~NonCopyable() = default; } // end catch_common.cpp @@ -9954,76 +10544,102 @@ NonCopyable::~NonCopyable() = default; namespace Catch { -Config::Config(ConfigData const &data) - : m_data(data), - m_stream(openStream()) { - // We need to trim filter specs to avoid trouble with superfluous - // whitespace (esp. important for bdd macros, as those are manually - // aligned with whitespace). - - for (auto &elem : m_data.testsOrTags) { - elem = trim(elem); - } - for (auto &elem : m_data.sectionsToRun) { - elem = trim(elem); - } - - TestSpecParser parser(ITagAliasRegistry::get()); - if (!m_data.testsOrTags.empty()) { - m_hasTestFilters = true; - for (auto const &testOrTags : m_data.testsOrTags) { - parser.parse(testOrTags); - } - } - m_testSpec = parser.testSpec(); -} + Config::Config(ConfigData const &data) + : m_data(data), + m_stream(openStream()) { + // We need to trim filter specs to avoid trouble with superfluous + // whitespace (esp. important for bdd macros, as those are manually + // aligned with whitespace). -std::string const &Config::getFilename() const { - return m_data.outputFilename; -} + for (auto &elem: m_data.testsOrTags) { + elem = trim(elem); + } + for (auto &elem: m_data.sectionsToRun) { + elem = trim(elem); + } + + TestSpecParser parser(ITagAliasRegistry::get()); + if (!m_data.testsOrTags.empty()) { + m_hasTestFilters = true; + for (auto const &testOrTags: m_data.testsOrTags) { + parser.parse(testOrTags); + } + } + m_testSpec = parser.testSpec(); + } + + std::string const &Config::getFilename() const { + return m_data.outputFilename; + } + + bool Config::listTests() const { return m_data.listTests; } + + bool Config::listTestNamesOnly() const { return m_data.listTestNamesOnly; } + + bool Config::listTags() const { return m_data.listTags; } + + bool Config::listReporters() const { return m_data.listReporters; } + + std::string Config::getProcessName() const { return m_data.processName; } + + std::string const &Config::getReporterName() const { return m_data.reporterName; } -bool Config::listTests() const { return m_data.listTests; } -bool Config::listTestNamesOnly() const { return m_data.listTestNamesOnly; } -bool Config::listTags() const { return m_data.listTags; } -bool Config::listReporters() const { return m_data.listReporters; } + std::vector const &Config::getTestsOrTags() const { return m_data.testsOrTags; } -std::string Config::getProcessName() const { return m_data.processName; } -std::string const &Config::getReporterName() const { return m_data.reporterName; } + std::vector const &Config::getSectionsToRun() const { return m_data.sectionsToRun; } -std::vector const &Config::getTestsOrTags() const { return m_data.testsOrTags; } -std::vector const &Config::getSectionsToRun() const { return m_data.sectionsToRun; } + TestSpec const &Config::testSpec() const { return m_testSpec; } -TestSpec const &Config::testSpec() const { return m_testSpec; } -bool Config::hasTestFilters() const { return m_hasTestFilters; } + bool Config::hasTestFilters() const { return m_hasTestFilters; } -bool Config::showHelp() const { return m_data.showHelp; } + bool Config::showHelp() const { return m_data.showHelp; } // IConfig interface -bool Config::allowThrows() const { return !m_data.noThrow; } -std::ostream &Config::stream() const { return m_stream->stream(); } -std::string Config::name() const { return m_data.name.empty() ? m_data.processName : m_data.name; } -bool Config::includeSuccessfulResults() const { return m_data.showSuccessfulTests; } -bool Config::warnAboutMissingAssertions() const { return !!(m_data.warnings & WarnAbout::NoAssertions); } -bool Config::warnAboutNoTests() const { return !!(m_data.warnings & WarnAbout::NoTests); } -ShowDurations::OrNot Config::showDurations() const { return m_data.showDurations; } -double Config::minDuration() const { return m_data.minDuration; } -RunTests::InWhatOrder Config::runOrder() const { return m_data.runOrder; } -unsigned int Config::rngSeed() const { return m_data.rngSeed; } -UseColour::YesOrNo Config::useColour() const { return m_data.useColour; } -bool Config::shouldDebugBreak() const { return m_data.shouldDebugBreak; } -int Config::abortAfter() const { return m_data.abortAfter; } -bool Config::showInvisibles() const { return m_data.showInvisibles; } -Verbosity Config::verbosity() const { return m_data.verbosity; } - -bool Config::benchmarkNoAnalysis() const { return m_data.benchmarkNoAnalysis; } -int Config::benchmarkSamples() const { return m_data.benchmarkSamples; } -double Config::benchmarkConfidenceInterval() const { return m_data.benchmarkConfidenceInterval; } -unsigned int Config::benchmarkResamples() const { return m_data.benchmarkResamples; } -std::chrono::milliseconds Config::benchmarkWarmupTime() const { return std::chrono::milliseconds(m_data.benchmarkWarmupTime); } - -IStream const *Config::openStream() { - return Catch::makeStream(m_data.outputFilename); -} + bool Config::allowThrows() const { return !m_data.noThrow; } + + std::ostream &Config::stream() const { return m_stream->stream(); } + + std::string Config::name() const { return m_data.name.empty() ? m_data.processName : m_data.name; } + + bool Config::includeSuccessfulResults() const { return m_data.showSuccessfulTests; } + + bool Config::warnAboutMissingAssertions() const { return !!(m_data.warnings & WarnAbout::NoAssertions); } + + bool Config::warnAboutNoTests() const { return !!(m_data.warnings & WarnAbout::NoTests); } + + ShowDurations::OrNot Config::showDurations() const { return m_data.showDurations; } + + double Config::minDuration() const { return m_data.minDuration; } + + RunTests::InWhatOrder Config::runOrder() const { return m_data.runOrder; } + + unsigned int Config::rngSeed() const { return m_data.rngSeed; } + + UseColour::YesOrNo Config::useColour() const { return m_data.useColour; } + + bool Config::shouldDebugBreak() const { return m_data.shouldDebugBreak; } + + int Config::abortAfter() const { return m_data.abortAfter; } + + bool Config::showInvisibles() const { return m_data.showInvisibles; } + + Verbosity Config::verbosity() const { return m_data.verbosity; } + + bool Config::benchmarkNoAnalysis() const { return m_data.benchmarkNoAnalysis; } + + int Config::benchmarkSamples() const { return m_data.benchmarkSamples; } + + double Config::benchmarkConfidenceInterval() const { return m_data.benchmarkConfidenceInterval; } + + unsigned int Config::benchmarkResamples() const { return m_data.benchmarkResamples; } + + std::chrono::milliseconds Config::benchmarkWarmupTime() const { + return std::chrono::milliseconds(m_data.benchmarkWarmupTime); + } + + IStream const *Config::openStream() { + return Catch::makeStream(m_data.outputFilename); + } } // end namespace Catch // end catch_config.cpp @@ -10038,13 +10654,15 @@ IStream const *Config::openStream() { namespace Catch { -class ErrnoGuard { - public: - ErrnoGuard(); - ~ErrnoGuard(); - private: - int m_oldErrno; -}; + class ErrnoGuard { + public: + ErrnoGuard(); + + ~ErrnoGuard(); + + private: + int m_oldErrno; + }; } @@ -10082,23 +10700,24 @@ class ErrnoGuard { #include namespace Catch { -namespace { + namespace { -struct IColourImpl { - virtual ~IColourImpl() = default; - virtual void use(Colour::Code _colourCode) = 0; -}; + struct IColourImpl { + virtual ~IColourImpl() = default; + + virtual void use(Colour::Code _colourCode) = 0; + }; -struct NoColourImpl : IColourImpl { - void use(Colour::Code) override {} + struct NoColourImpl : IColourImpl { + void use(Colour::Code) override {} - static IColourImpl *instance() { - static NoColourImpl s_instance; - return &s_instance; - } -}; + static IColourImpl *instance() { + static NoColourImpl s_instance; + return &s_instance; + } + }; -} // anon namespace + } // anon namespace } // namespace Catch #if !defined( CATCH_CONFIG_COLOUR_NONE ) && !defined( CATCH_CONFIG_COLOUR_WINDOWS ) && !defined( CATCH_CONFIG_COLOUR_ANSI ) @@ -10179,75 +10798,91 @@ namespace { #include namespace Catch { -namespace { + namespace { // use POSIX/ ANSI console terminal codes // Thanks to Adam Strzelecki for original contribution // (http://github.com/nanoant) // https://github.com/philsquared/Catch/pull/131 -class PosixColourImpl : public IColourImpl { - public: - void use(Colour::Code _colourCode) override { - switch (_colourCode) { - case Colour::None: - case Colour::White: return setColour("[0m"); - case Colour::Red: return setColour("[0;31m"); - case Colour::Green: return setColour("[0;32m"); - case Colour::Blue: return setColour("[0;34m"); - case Colour::Cyan: return setColour("[0;36m"); - case Colour::Yellow: return setColour("[0;33m"); - case Colour::Grey: return setColour("[1;30m"); - - case Colour::LightGrey: return setColour("[0;37m"); - case Colour::BrightRed: return setColour("[1;31m"); - case Colour::BrightGreen: return setColour("[1;32m"); - case Colour::BrightWhite: return setColour("[1;37m"); - case Colour::BrightYellow: return setColour("[1;33m"); - - case Colour::Bright: CATCH_INTERNAL_ERROR("not a colour"); - default: CATCH_INTERNAL_ERROR("Unknown colour requested"); - } - } - static IColourImpl *instance() { - static PosixColourImpl s_instance; - return &s_instance; - } - - private: - void setColour(const char *_escapeCode) { - getCurrentContext().getConfig()->stream() - << '\033' << _escapeCode; - } -}; + class PosixColourImpl : public IColourImpl { + public: + void use(Colour::Code _colourCode) override { + switch (_colourCode) { + case Colour::None: + case Colour::White: + return setColour("[0m"); + case Colour::Red: + return setColour("[0;31m"); + case Colour::Green: + return setColour("[0;32m"); + case Colour::Blue: + return setColour("[0;34m"); + case Colour::Cyan: + return setColour("[0;36m"); + case Colour::Yellow: + return setColour("[0;33m"); + case Colour::Grey: + return setColour("[1;30m"); + + case Colour::LightGrey: + return setColour("[0;37m"); + case Colour::BrightRed: + return setColour("[1;31m"); + case Colour::BrightGreen: + return setColour("[1;32m"); + case Colour::BrightWhite: + return setColour("[1;37m"); + case Colour::BrightYellow: + return setColour("[1;33m"); + + case Colour::Bright: + CATCH_INTERNAL_ERROR("not a colour"); + default: + CATCH_INTERNAL_ERROR("Unknown colour requested"); + } + } + + static IColourImpl *instance() { + static PosixColourImpl s_instance; + return &s_instance; + } + + private: + void setColour(const char *_escapeCode) { + getCurrentContext().getConfig()->stream() + << '\033' << _escapeCode; + } + }; -bool useColourOnPlatform() { - return + bool useColourOnPlatform() { + return #if defined(CATCH_PLATFORM_MAC) || defined(CATCH_PLATFORM_IPHONE) - !isDebuggerActive() && + !isDebuggerActive() && #endif #if !(defined(__DJGPP__) && defined(__STRICT_ANSI__)) - isatty(STDOUT_FILENO) + isatty(STDOUT_FILENO) #else - false + false #endif - ; -} -IColourImpl *platformColourInstance() { - ErrnoGuard guard; - IConfigPtr config = getCurrentContext().getConfig(); - UseColour::YesOrNo colourMode = config - ? config->useColour() - : UseColour::Auto; - if (colourMode == UseColour::Auto) - colourMode = useColourOnPlatform() - ? UseColour::Yes - : UseColour::No; - return colourMode == UseColour::Yes - ? PosixColourImpl::instance() - : NoColourImpl::instance(); -} + ; + } -} // end anon namespace + IColourImpl *platformColourInstance() { + ErrnoGuard guard; + IConfigPtr config = getCurrentContext().getConfig(); + UseColour::YesOrNo colourMode = config + ? config->useColour() + : UseColour::Auto; + if (colourMode == UseColour::Auto) + colourMode = useColourOnPlatform() + ? UseColour::Yes + : UseColour::No; + return colourMode == UseColour::Yes + ? PosixColourImpl::instance() + : NoColourImpl::instance(); + } + + } // end anon namespace } // end namespace Catch #else // not Windows or ANSI /////////////////////////////////////////////// @@ -10262,33 +10897,35 @@ IColourImpl *platformColourInstance() { namespace Catch { -Colour::Colour(Code _colourCode) { use(_colourCode); } -Colour::Colour(Colour &&other) noexcept { - m_moved = other.m_moved; - other.m_moved = true; -} -Colour &Colour::operator=(Colour &&other) noexcept { - m_moved = other.m_moved; - other.m_moved = true; - return *this; -} + Colour::Colour(Code _colourCode) { use(_colourCode); } -Colour::~Colour() { if (!m_moved) use(None); } - -void Colour::use(Code _colourCode) { - static IColourImpl *impl = platformColourInstance(); - // Strictly speaking, this cannot possibly happen. - // However, under some conditions it does happen (see #1626), - // and this change is small enough that we can let practicality - // triumph over purity in this case. - if (impl != nullptr) { - impl->use(_colourCode); - } -} + Colour::Colour(Colour &&other) noexcept { + m_moved = other.m_moved; + other.m_moved = true; + } -std::ostream &operator<<(std::ostream &os, Colour const &) { - return os; -} + Colour &Colour::operator=(Colour &&other) noexcept { + m_moved = other.m_moved; + other.m_moved = true; + return *this; + } + + Colour::~Colour() { if (!m_moved) use(None); } + + void Colour::use(Code _colourCode) { + static IColourImpl *impl = platformColourInstance(); + // Strictly speaking, this cannot possibly happen. + // However, under some conditions it does happen (see #1626), + // and this change is small enough that we can let practicality + // triumph over purity in this case. + if (impl != nullptr) { + impl->use(_colourCode); + } + } + + std::ostream &operator<<(std::ostream &os, Colour const &) { + return os; + } } // end namespace Catch @@ -10301,59 +10938,65 @@ std::ostream &operator<<(std::ostream &os, Colour const &) { namespace Catch { -class Context : public IMutableContext, NonCopyable { - - public: // IContext - IResultCapture *getResultCapture() override { - return m_resultCapture; - } - IRunner *getRunner() override { - return m_runner; - } - - IConfigPtr const &getConfig() const override { - return m_config; - } - - ~Context() override; - - public: // IMutableContext - void setResultCapture(IResultCapture *resultCapture) override { - m_resultCapture = resultCapture; - } - void setRunner(IRunner *runner) override { - m_runner = runner; - } - void setConfig(IConfigPtr const &config) override { - m_config = config; - } - - friend IMutableContext &getCurrentMutableContext(); - - private: - IConfigPtr m_config; - IRunner *m_runner = nullptr; - IResultCapture *m_resultCapture = nullptr; -}; + class Context : public IMutableContext, NonCopyable { -IMutableContext *IMutableContext::currentContext = nullptr; + public: // IContext + IResultCapture *getResultCapture() override { + return m_resultCapture; + } -void IMutableContext::createContext() { - currentContext = new Context(); -} + IRunner *getRunner() override { + return m_runner; + } -void cleanUpContext() { - delete IMutableContext::currentContext; - IMutableContext::currentContext = nullptr; -} -IContext::~IContext() = default; -IMutableContext::~IMutableContext() = default; -Context::~Context() = default; + IConfigPtr const &getConfig() const override { + return m_config; + } -SimplePcg32 &rng() { - static SimplePcg32 s_rng; - return s_rng; -} + ~Context() override; + + public: // IMutableContext + void setResultCapture(IResultCapture *resultCapture) override { + m_resultCapture = resultCapture; + } + + void setRunner(IRunner *runner) override { + m_runner = runner; + } + + void setConfig(IConfigPtr const &config) override { + m_config = config; + } + + friend IMutableContext &getCurrentMutableContext(); + + private: + IConfigPtr m_config; + IRunner *m_runner = nullptr; + IResultCapture *m_resultCapture = nullptr; + }; + + IMutableContext *IMutableContext::currentContext = nullptr; + + void IMutableContext::createContext() { + currentContext = new Context(); + } + + void cleanUpContext() { + delete IMutableContext::currentContext; + IMutableContext::currentContext = nullptr; + } + + IContext::~IContext() = default; + + IMutableContext::~IMutableContext() = default; + + Context::~Context() = default; + + SimplePcg32 &rng() { + static SimplePcg32 s_rng; + return s_rng; + } } // end catch_context.cpp @@ -10364,7 +11007,7 @@ SimplePcg32 &rng() { #include namespace Catch { -void writeToDebugConsole(std::string const &text); + void writeToDebugConsole(std::string const &text); } // end catch_debug_console.h @@ -10388,10 +11031,10 @@ void writeToDebugConsole(std::string const &text); #else namespace Catch { -void writeToDebugConsole(std::string const &text) { - // !TBD: Need a version for Mac/ XCode and other IDEs - Catch::cout() << text; -} + void writeToDebugConsole(std::string const &text) { + // !TBD: Need a version for Mac/ XCode and other IDEs + Catch::cout() << text; + } } #endif // Platform @@ -10458,6 +11101,7 @@ void writeToDebugConsole(std::string const &text) { } // namespace Catch #elif defined(CATCH_PLATFORM_LINUX) + #include #include @@ -10469,23 +11113,23 @@ namespace Catch { // "debugger" (which doesn't need to be gdb, of course, it could also // be strace, for example) in /proc/$PID/status, so just get it from // there instead. -bool isDebuggerActive() { - // Libstdc++ has a bug, where std::ifstream sets errno to 0 - // This way our users can properly assert over errno values - ErrnoGuard guard; - std::ifstream in("/proc/self/status"); - for (std::string line; std::getline(in, line);) { - static const int PREFIX_LEN = 11; - if (line.compare(0, PREFIX_LEN, "TracerPid:\t") == 0) { - // We're traced if the PID is not 0 and no other PID starts - // with 0 digit, so it's enough to check for just a single - // character. - return line.length() > PREFIX_LEN && line[PREFIX_LEN] != '0'; - } - } - - return false; -} + bool isDebuggerActive() { + // Libstdc++ has a bug, where std::ifstream sets errno to 0 + // This way our users can properly assert over errno values + ErrnoGuard guard; + std::ifstream in("/proc/self/status"); + for (std::string line; std::getline(in, line);) { + static const int PREFIX_LEN = 11; + if (line.compare(0, PREFIX_LEN, "TracerPid:\t") == 0) { + // We're traced if the PID is not 0 and no other PID starts + // with 0 digit, so it's enough to check for just a single + // character. + return line.length() > PREFIX_LEN && line[PREFIX_LEN] != '0'; + } + } + + return false; + } } // namespace Catch #elif defined(_MSC_VER) extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); @@ -10511,16 +11155,16 @@ bool isDebuggerActive() { namespace Catch { -ITransientExpression::~ITransientExpression() = default; + ITransientExpression::~ITransientExpression() = default; -void formatReconstructedExpression(std::ostream &os, std::string const &lhs, StringRef op, std::string const &rhs) { - if (lhs.size() + rhs.size() < 40 && - lhs.find('\n') == std::string::npos && - rhs.find('\n') == std::string::npos) - os << lhs << " " << op << " " << rhs; - else - os << lhs << "\n" << op << "\n" << rhs; -} + void formatReconstructedExpression(std::ostream &os, std::string const &lhs, StringRef op, std::string const &rhs) { + if (lhs.size() + rhs.size() < 40 && + lhs.find('\n') == std::string::npos && + rhs.find('\n') == std::string::npos) + os << lhs << " " << op << " " << rhs; + else + os << lhs << "\n" << op << "\n" << rhs; + } } // end catch_decomposer.cpp // start catch_enforce.cpp @@ -10529,7 +11173,7 @@ void formatReconstructedExpression(std::ostream &os, std::string const &lhs, Str namespace Catch { #if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS_CUSTOM_HANDLER) - [[noreturn]] + [[noreturn]] void throw_exception(std::exception const& e) { Catch::cerr() << "Catch will terminate because it needed to throw an exception.\n" << "The message was: " << e.what() << '\n'; @@ -10537,20 +11181,20 @@ namespace Catch { } #endif -[[noreturn]] -void throw_logic_error(std::string const &msg) { - throw_exception(std::logic_error(msg)); -} + [[noreturn]] + void throw_logic_error(std::string const &msg) { + throw_exception(std::logic_error(msg)); + } -[[noreturn]] -void throw_domain_error(std::string const &msg) { - throw_exception(std::domain_error(msg)); -} + [[noreturn]] + void throw_domain_error(std::string const &msg) { + throw_exception(std::domain_error(msg)); + } -[[noreturn]] -void throw_runtime_error(std::string const &msg) { - throw_exception(std::runtime_error(msg)); -} + [[noreturn]] + void throw_runtime_error(std::string const &msg) { + throw_exception(std::runtime_error(msg)); + } } // namespace Catch; // end catch_enforce.cpp @@ -10562,20 +11206,22 @@ void throw_runtime_error(std::string const &msg) { namespace Catch { -namespace Detail { + namespace Detail { -std::unique_ptr makeEnumInfo(StringRef enumName, StringRef allValueNames, std::vector const &values); + std::unique_ptr + makeEnumInfo(StringRef enumName, StringRef allValueNames, std::vector const &values); -class EnumValuesRegistry : public IMutableEnumValuesRegistry { + class EnumValuesRegistry : public IMutableEnumValuesRegistry { - std::vector> m_enumInfos; + std::vector> m_enumInfos; - EnumInfo const ®isterEnum(StringRef enumName, StringRef allEnums, std::vector const &values) override; -}; + EnumInfo const & + registerEnum(StringRef enumName, StringRef allEnums, std::vector const &values) override; + }; -std::vector parseEnums(StringRef enums); + std::vector parseEnums(StringRef enums); -} // Detail + } // Detail } // Catch @@ -10586,65 +11232,66 @@ std::vector parseEnums(StringRef enums); namespace Catch { -IMutableEnumValuesRegistry::~IMutableEnumValuesRegistry() {} + IMutableEnumValuesRegistry::~IMutableEnumValuesRegistry() {} -namespace Detail { + namespace Detail { -namespace { + namespace { // Extracts the actual name part of an enum instance // In other words, it returns the Blue part of Bikeshed::Colour::Blue -StringRef extractInstanceName(StringRef enumInstance) { - // Find last occurrence of ":" - size_t name_start = enumInstance.size(); - while (name_start > 0 && enumInstance[name_start - 1] != ':') { - --name_start; - } - return enumInstance.substr(name_start, enumInstance.size() - name_start); -} -} + StringRef extractInstanceName(StringRef enumInstance) { + // Find last occurrence of ":" + size_t name_start = enumInstance.size(); + while (name_start > 0 && enumInstance[name_start - 1] != ':') { + --name_start; + } + return enumInstance.substr(name_start, enumInstance.size() - name_start); + } + } -std::vector parseEnums(StringRef enums) { - auto enumValues = splitStringRef(enums, ','); - std::vector parsed; - parsed.reserve(enumValues.size()); - for (auto const &enumValue : enumValues) { - parsed.push_back(trim(extractInstanceName(enumValue))); - } - return parsed; -} + std::vector parseEnums(StringRef enums) { + auto enumValues = splitStringRef(enums, ','); + std::vector parsed; + parsed.reserve(enumValues.size()); + for (auto const &enumValue: enumValues) { + parsed.push_back(trim(extractInstanceName(enumValue))); + } + return parsed; + } -EnumInfo::~EnumInfo() {} + EnumInfo::~EnumInfo() {} -StringRef EnumInfo::lookup(int value) const { - for (auto const &valueToName : m_values) { - if (valueToName.first == value) - return valueToName.second; - } - return "{** unexpected enum value **}"_sr; -} + StringRef EnumInfo::lookup(int value) const { + for (auto const &valueToName: m_values) { + if (valueToName.first == value) + return valueToName.second; + } + return "{** unexpected enum value **}"_sr; + } -std::unique_ptr makeEnumInfo(StringRef enumName, StringRef allValueNames, std::vector const &values) { - std::unique_ptr enumInfo(new EnumInfo); - enumInfo->m_name = enumName; - enumInfo->m_values.reserve(values.size()); + std::unique_ptr + makeEnumInfo(StringRef enumName, StringRef allValueNames, std::vector const &values) { + std::unique_ptr enumInfo(new EnumInfo); + enumInfo->m_name = enumName; + enumInfo->m_values.reserve(values.size()); - const auto valueNames = Catch::Detail::parseEnums(allValueNames); - assert(valueNames.size() == values.size()); - std::size_t i = 0; - for (auto value : values) - enumInfo->m_values.emplace_back(value, valueNames[i++]); + const auto valueNames = Catch::Detail::parseEnums(allValueNames); + assert(valueNames.size() == values.size()); + std::size_t i = 0; + for (auto value: values) + enumInfo->m_values.emplace_back(value, valueNames[i++]); - return enumInfo; -} + return enumInfo; + } -EnumInfo const &EnumValuesRegistry::registerEnum(StringRef enumName, - StringRef allValueNames, - std::vector const &values) { - m_enumInfos.push_back(makeEnumInfo(enumName, allValueNames, values)); - return *m_enumInfos.back(); -} + EnumInfo const &EnumValuesRegistry::registerEnum(StringRef enumName, + StringRef allValueNames, + std::vector const &values) { + m_enumInfos.push_back(makeEnumInfo(enumName, allValueNames, values)); + return *m_enumInfos.back(); + } -} // Detail + } // Detail } // Catch // end catch_enum_values_registry.cpp @@ -10653,8 +11300,9 @@ EnumInfo const &EnumValuesRegistry::registerEnum(StringRef enumName, #include namespace Catch { -ErrnoGuard::ErrnoGuard() : m_oldErrno(errno) {} -ErrnoGuard::~ErrnoGuard() { errno = m_oldErrno; } + ErrnoGuard::ErrnoGuard() : m_oldErrno(errno) {} + + ErrnoGuard::~ErrnoGuard() { errno = m_oldErrno; } } // end catch_errno_guard.cpp // start catch_exception_translator_registry.cpp @@ -10667,16 +11315,19 @@ ErrnoGuard::~ErrnoGuard() { errno = m_oldErrno; } namespace Catch { -class ExceptionTranslatorRegistry : public IExceptionTranslatorRegistry { - public: - ~ExceptionTranslatorRegistry(); - virtual void registerTranslator(const IExceptionTranslator *translator); - std::string translateActiveException() const override; - std::string tryTranslators() const; + class ExceptionTranslatorRegistry : public IExceptionTranslatorRegistry { + public: + ~ExceptionTranslatorRegistry(); - private: - std::vector> m_translators; -}; + virtual void registerTranslator(const IExceptionTranslator *translator); + + std::string translateActiveException() const override; + + std::string tryTranslators() const; + + private: + std::vector> m_translators; + }; } // end catch_exception_translator_registry.h @@ -10686,18 +11337,19 @@ class ExceptionTranslatorRegistry : public IExceptionTranslatorRegistry { namespace Catch { -ExceptionTranslatorRegistry::~ExceptionTranslatorRegistry() { -} + ExceptionTranslatorRegistry::~ExceptionTranslatorRegistry() { + } -void ExceptionTranslatorRegistry::registerTranslator(const IExceptionTranslator *translator) { - m_translators.push_back(std::unique_ptr(translator)); -} + void ExceptionTranslatorRegistry::registerTranslator(const IExceptionTranslator *translator) { + m_translators.push_back(std::unique_ptr(translator)); + } #if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) -std::string ExceptionTranslatorRegistry::translateActiveException() const { - try { + + std::string ExceptionTranslatorRegistry::translateActiveException() const { + try { #ifdef __OBJC__ - // In Objective-C try objective-c exceptions first + // In Objective-C try objective-c exceptions first @try { return tryTranslators(); } @@ -10705,47 +11357,47 @@ std::string ExceptionTranslatorRegistry::translateActiveException() const { return Catch::Detail::stringify( [exception description] ); } #else - // Compiling a mixed mode project with MSVC means that CLR - // exceptions will be caught in (...) as well. However, these - // do not fill-in std::current_exception and thus lead to crash - // when attempting rethrow. - // /EHa switch also causes structured exceptions to be caught - // here, but they fill-in current_exception properly, so - // at worst the output should be a little weird, instead of - // causing a crash. - if (std::current_exception() == nullptr) { - return "Non C++ exception. Possibly a CLR exception."; - } - return tryTranslators(); + // Compiling a mixed mode project with MSVC means that CLR + // exceptions will be caught in (...) as well. However, these + // do not fill-in std::current_exception and thus lead to crash + // when attempting rethrow. + // /EHa switch also causes structured exceptions to be caught + // here, but they fill-in current_exception properly, so + // at worst the output should be a little weird, instead of + // causing a crash. + if (std::current_exception() == nullptr) { + return "Non C++ exception. Possibly a CLR exception."; + } + return tryTranslators(); #endif - } - catch (TestFailureException &) { - std::rethrow_exception(std::current_exception()); - } - catch (std::exception &ex) { - return ex.what(); - } - catch (std::string &msg) { - return msg; - } - catch (const char *msg) { - return msg; - } - catch (...) { - return "Unknown exception"; - } -} + } + catch (TestFailureException &) { + std::rethrow_exception(std::current_exception()); + } + catch (std::exception &ex) { + return ex.what(); + } + catch (std::string &msg) { + return msg; + } + catch (const char *msg) { + return msg; + } + catch (...) { + return "Unknown exception"; + } + } -std::string ExceptionTranslatorRegistry::tryTranslators() const { - if (m_translators.empty()) { - std::rethrow_exception(std::current_exception()); - } else { - return m_translators[0]->translate(m_translators.begin() + 1, m_translators.end()); - } -} + std::string ExceptionTranslatorRegistry::tryTranslators() const { + if (m_translators.empty()) { + std::rethrow_exception(std::current_exception()); + } else { + return m_translators[0]->translate(m_translators.begin() + 1, m_translators.end()); + } + } #else // ^^ Exceptions are enabled // Exceptions are disabled vv - std::string ExceptionTranslatorRegistry::translateActiveException() const { + std::string ExceptionTranslatorRegistry::translateActiveException() const { CATCH_INTERNAL_ERROR("Attempted to translate active exception under CATCH_CONFIG_DISABLE_EXCEPTIONS!"); } @@ -10783,14 +11435,14 @@ std::string ExceptionTranslatorRegistry::tryTranslators() const { namespace { //! Signals fatal error message to the run context -void reportFatal(char const *const message) { - Catch::getCurrentContext().getResultCapture()->handleFatalErrorCondition(message); -} + void reportFatal(char const *const message) { + Catch::getCurrentContext().getResultCapture()->handleFatalErrorCondition(message); + } //! Minimal size Catch2 needs for its own fatal error handling. //! Picked anecdotally, so it might not be sufficient on all //! platforms, and for all configurations. -constexpr std::size_t minStackSizeForErrors = 32 * 1024; + constexpr std::size_t minStackSizeForErrors = 32 * 1024; } // end unnamed namespace #endif // CATCH_CONFIG_WINDOWS_SEH || CATCH_CONFIG_POSIX_SIGNALS @@ -10869,19 +11521,19 @@ constexpr std::size_t minStackSizeForErrors = 32 * 1024; namespace Catch { -struct SignalDefs { - int id; - const char *name; -}; + struct SignalDefs { + int id; + const char *name; + }; -static SignalDefs signalDefs[] = { - {SIGINT, "SIGINT - Terminal interrupt signal"}, - {SIGILL, "SIGILL - Illegal instruction signal"}, - {SIGFPE, "SIGFPE - Floating point error signal"}, - {SIGSEGV, "SIGSEGV - Segmentation violation signal"}, - {SIGTERM, "SIGTERM - Termination request signal"}, - {SIGABRT, "SIGABRT - Abort (abnormal termination) signal"} -}; + static SignalDefs signalDefs[] = { + {SIGINT, "SIGINT - Terminal interrupt signal"}, + {SIGILL, "SIGILL - Illegal instruction signal"}, + {SIGFPE, "SIGFPE - Floating point error signal"}, + {SIGSEGV, "SIGSEGV - Segmentation violation signal"}, + {SIGTERM, "SIGTERM - Termination request signal"}, + {SIGABRT, "SIGABRT - Abort (abnormal termination) signal"} + }; // Older GCCs trigger -Wmissing-field-initializers for T foo = {} // which is zero initialization, but not explicit. We want to avoid @@ -10891,76 +11543,76 @@ static SignalDefs signalDefs[] = { # pragma GCC diagnostic ignored "-Wmissing-field-initializers" #endif -static char *altStackMem = nullptr; -static std::size_t altStackSize = 0; -static stack_t oldSigStack{}; -static struct sigaction oldSigActions[sizeof(signalDefs) / sizeof(SignalDefs)]{}; - -static void restorePreviousSignalHandlers() { - // We set signal handlers back to the previous ones. Hopefully - // nobody overwrote them in the meantime, and doesn't expect - // their signal handlers to live past ours given that they - // installed them after ours.. - for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) { - sigaction(signalDefs[i].id, &oldSigActions[i], nullptr); - } - // Return the old stack - sigaltstack(&oldSigStack, nullptr); -} + static char *altStackMem = nullptr; + static std::size_t altStackSize = 0; + static stack_t oldSigStack{}; + static struct sigaction oldSigActions[sizeof(signalDefs) / sizeof(SignalDefs)]{}; + + static void restorePreviousSignalHandlers() { + // We set signal handlers back to the previous ones. Hopefully + // nobody overwrote them in the meantime, and doesn't expect + // their signal handlers to live past ours given that they + // installed them after ours.. + for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) { + sigaction(signalDefs[i].id, &oldSigActions[i], nullptr); + } + // Return the old stack + sigaltstack(&oldSigStack, nullptr); + } -static void handleSignal(int sig) { - char const *name = ""; - for (auto const &def : signalDefs) { - if (sig == def.id) { - name = def.name; - break; - } - } - // We need to restore previous signal handlers and let them do - // their thing, so that the users can have the debugger break - // when a signal is raised, and so on. - restorePreviousSignalHandlers(); - reportFatal(name); - raise(sig); -} + static void handleSignal(int sig) { + char const *name = ""; + for (auto const &def: signalDefs) { + if (sig == def.id) { + name = def.name; + break; + } + } + // We need to restore previous signal handlers and let them do + // their thing, so that the users can have the debugger break + // when a signal is raised, and so on. + restorePreviousSignalHandlers(); + reportFatal(name); + raise(sig); + } -FatalConditionHandler::FatalConditionHandler() { - assert(!altStackMem && "Cannot initialize POSIX signal handler when one already exists"); - if (altStackSize == 0) { - altStackSize = std::max(static_cast(SIGSTKSZ), minStackSizeForErrors); - } - altStackMem = new char[altStackSize](); -} + FatalConditionHandler::FatalConditionHandler() { + assert(!altStackMem && "Cannot initialize POSIX signal handler when one already exists"); + if (altStackSize == 0) { + altStackSize = std::max(static_cast(SIGSTKSZ), minStackSizeForErrors); + } + altStackMem = new char[altStackSize](); + } -FatalConditionHandler::~FatalConditionHandler() { - delete[] altStackMem; - // We signal that another instance can be constructed by zeroing - // out the pointer. - altStackMem = nullptr; -} + FatalConditionHandler::~FatalConditionHandler() { + delete[] altStackMem; + // We signal that another instance can be constructed by zeroing + // out the pointer. + altStackMem = nullptr; + } -void FatalConditionHandler::engage_platform() { - stack_t sigStack; - sigStack.ss_sp = altStackMem; - sigStack.ss_size = altStackSize; - sigStack.ss_flags = 0; - sigaltstack(&sigStack, &oldSigStack); - struct sigaction sa = {}; - - sa.sa_handler = handleSignal; - sa.sa_flags = SA_ONSTACK; - for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) { - sigaction(signalDefs[i].id, &sa, &oldSigActions[i]); - } -} + void FatalConditionHandler::engage_platform() { + stack_t sigStack; + sigStack.ss_sp = altStackMem; + sigStack.ss_size = altStackSize; + sigStack.ss_flags = 0; + sigaltstack(&sigStack, &oldSigStack); + struct sigaction sa = {}; + + sa.sa_handler = handleSignal; + sa.sa_flags = SA_ONSTACK; + for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) { + sigaction(signalDefs[i].id, &sa, &oldSigActions[i]); + } + } #if defined(__GNUC__) # pragma GCC diagnostic pop #endif -void FatalConditionHandler::disengage_platform() { - restorePreviousSignalHandlers(); -} + void FatalConditionHandler::disengage_platform() { + restorePreviousSignalHandlers(); + } } // end namespace Catch @@ -10973,47 +11625,49 @@ void FatalConditionHandler::disengage_platform() { namespace Catch { -IGeneratorTracker::~IGeneratorTracker() {} + IGeneratorTracker::~IGeneratorTracker() {} -const char *GeneratorException::what() const noexcept { - return m_msg; -} + const char *GeneratorException::what() const noexcept { + return m_msg; + } -namespace Generators { + namespace Generators { -GeneratorUntypedBase::~GeneratorUntypedBase() {} + GeneratorUntypedBase::~GeneratorUntypedBase() {} -auto acquireGeneratorTracker(StringRef generatorName, SourceLineInfo const &lineInfo) -> IGeneratorTracker & { - return getResultCapture().acquireGeneratorTracker(generatorName, lineInfo); -} + auto acquireGeneratorTracker(StringRef generatorName, SourceLineInfo const &lineInfo) -> IGeneratorTracker & { + return getResultCapture().acquireGeneratorTracker(generatorName, lineInfo); + } -} // namespace Generators + } // namespace Generators } // namespace Catch // end catch_generators.cpp // start catch_interfaces_capture.cpp namespace Catch { -IResultCapture::~IResultCapture() = default; + IResultCapture::~IResultCapture() = default; } // end catch_interfaces_capture.cpp // start catch_interfaces_config.cpp namespace Catch { -IConfig::~IConfig() = default; + IConfig::~IConfig() = default; } // end catch_interfaces_config.cpp // start catch_interfaces_exception.cpp namespace Catch { -IExceptionTranslator::~IExceptionTranslator() = default; -IExceptionTranslatorRegistry::~IExceptionTranslatorRegistry() = default; + IExceptionTranslator::~IExceptionTranslator() = default; + + IExceptionTranslatorRegistry::~IExceptionTranslatorRegistry() = default; } // end catch_interfaces_exception.cpp // start catch_interfaces_registry_hub.cpp namespace Catch { -IRegistryHub::~IRegistryHub() = default; -IMutableRegistryHub::~IMutableRegistryHub() = default; + IRegistryHub::~IRegistryHub() = default; + + IMutableRegistryHub::~IMutableRegistryHub() = default; } // end catch_interfaces_registry_hub.cpp // start catch_interfaces_reporter.cpp @@ -11022,164 +11676,179 @@ IMutableRegistryHub::~IMutableRegistryHub() = default; namespace Catch { -class ListeningReporter : public IStreamingReporter { - using Reporters = std::vector; - Reporters m_listeners; - IStreamingReporterPtr m_reporter = nullptr; - ReporterPreferences m_preferences; + class ListeningReporter : public IStreamingReporter { + using Reporters = std::vector; + Reporters m_listeners; + IStreamingReporterPtr m_reporter = nullptr; + ReporterPreferences m_preferences; + + public: + ListeningReporter(); - public: - ListeningReporter(); + void addListener(IStreamingReporterPtr &&listener); - void addListener(IStreamingReporterPtr &&listener); - void addReporter(IStreamingReporterPtr &&reporter); + void addReporter(IStreamingReporterPtr &&reporter); - public: // IStreamingReporter + public: // IStreamingReporter - ReporterPreferences getPreferences() const override; + ReporterPreferences getPreferences() const override; - void noMatchingTestCases(std::string const &spec) override; + void noMatchingTestCases(std::string const &spec) override; - void reportInvalidArguments(std::string const &arg) override; + void reportInvalidArguments(std::string const &arg) override; - static std::set getSupportedVerbosities(); + static std::set getSupportedVerbosities(); #if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) - void benchmarkPreparing(std::string const& name) override; + void benchmarkPreparing(std::string const& name) override; void benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) override; void benchmarkEnded( BenchmarkStats<> const& benchmarkStats ) override; void benchmarkFailed(std::string const&) override; #endif // CATCH_CONFIG_ENABLE_BENCHMARKING - void testRunStarting(TestRunInfo const &testRunInfo) override; - void testGroupStarting(GroupInfo const &groupInfo) override; - void testCaseStarting(TestCaseInfo const &testInfo) override; - void sectionStarting(SectionInfo const §ionInfo) override; - void assertionStarting(AssertionInfo const &assertionInfo) override; + void testRunStarting(TestRunInfo const &testRunInfo) override; - // The return value indicates if the messages buffer should be cleared: - bool assertionEnded(AssertionStats const &assertionStats) override; - void sectionEnded(SectionStats const §ionStats) override; - void testCaseEnded(TestCaseStats const &testCaseStats) override; - void testGroupEnded(TestGroupStats const &testGroupStats) override; - void testRunEnded(TestRunStats const &testRunStats) override; + void testGroupStarting(GroupInfo const &groupInfo) override; - void skipTest(TestCaseInfo const &testInfo) override; - bool isMulti() const override; + void testCaseStarting(TestCaseInfo const &testInfo) override; -}; + void sectionStarting(SectionInfo const §ionInfo) override; + + void assertionStarting(AssertionInfo const &assertionInfo) override; + + // The return value indicates if the messages buffer should be cleared: + bool assertionEnded(AssertionStats const &assertionStats) override; + + void sectionEnded(SectionStats const §ionStats) override; + + void testCaseEnded(TestCaseStats const &testCaseStats) override; + + void testGroupEnded(TestGroupStats const &testGroupStats) override; + + void testRunEnded(TestRunStats const &testRunStats) override; + + void skipTest(TestCaseInfo const &testInfo) override; + + bool isMulti() const override; + + }; } // end namespace Catch // end catch_reporter_listening.h namespace Catch { -ReporterConfig::ReporterConfig(IConfigPtr const &_fullConfig) - : m_stream(&_fullConfig->stream()), m_fullConfig(_fullConfig) {} - -ReporterConfig::ReporterConfig(IConfigPtr const &_fullConfig, std::ostream &_stream) - : m_stream(&_stream), m_fullConfig(_fullConfig) {} - -std::ostream &ReporterConfig::stream() const { return *m_stream; } -IConfigPtr ReporterConfig::fullConfig() const { return m_fullConfig; } - -TestRunInfo::TestRunInfo(std::string const &_name) : name(_name) {} - -GroupInfo::GroupInfo(std::string const &_name, - std::size_t _groupIndex, - std::size_t _groupsCount) - : name(_name), - groupIndex(_groupIndex), - groupsCounts(_groupsCount) {} - -AssertionStats::AssertionStats(AssertionResult const &_assertionResult, - std::vector const &_infoMessages, - Totals const &_totals) - : assertionResult(_assertionResult), - infoMessages(_infoMessages), - totals(_totals) { - assertionResult.m_resultData.lazyExpression.m_transientExpression = - _assertionResult.m_resultData.lazyExpression.m_transientExpression; - - if (assertionResult.hasMessage()) { - // Copy message into messages list. - // !TBD This should have been done earlier, somewhere - MessageBuilder - builder(assertionResult.getTestMacroName(), assertionResult.getSourceInfo(), assertionResult.getResultType()); - builder << assertionResult.getMessage(); - builder.m_info.message = builder.m_stream.str(); - - infoMessages.push_back(builder.m_info); - } -} + ReporterConfig::ReporterConfig(IConfigPtr const &_fullConfig) + : m_stream(&_fullConfig->stream()), m_fullConfig(_fullConfig) {} + + ReporterConfig::ReporterConfig(IConfigPtr const &_fullConfig, std::ostream &_stream) + : m_stream(&_stream), m_fullConfig(_fullConfig) {} + + std::ostream &ReporterConfig::stream() const { return *m_stream; } + + IConfigPtr ReporterConfig::fullConfig() const { return m_fullConfig; } -AssertionStats::~AssertionStats() = default; - -SectionStats::SectionStats(SectionInfo const &_sectionInfo, - Counts const &_assertions, - double _durationInSeconds, - bool _missingAssertions) - : sectionInfo(_sectionInfo), - assertions(_assertions), - durationInSeconds(_durationInSeconds), - missingAssertions(_missingAssertions) {} - -SectionStats::~SectionStats() = default; - -TestCaseStats::TestCaseStats(TestCaseInfo const &_testInfo, - Totals const &_totals, - std::string const &_stdOut, - std::string const &_stdErr, - bool _aborting) - : testInfo(_testInfo), - totals(_totals), - stdOut(_stdOut), - stdErr(_stdErr), - aborting(_aborting) {} - -TestCaseStats::~TestCaseStats() = default; - -TestGroupStats::TestGroupStats(GroupInfo const &_groupInfo, + TestRunInfo::TestRunInfo(std::string const &_name) : name(_name) {} + + GroupInfo::GroupInfo(std::string const &_name, + std::size_t _groupIndex, + std::size_t _groupsCount) + : name(_name), + groupIndex(_groupIndex), + groupsCounts(_groupsCount) {} + + AssertionStats::AssertionStats(AssertionResult const &_assertionResult, + std::vector const &_infoMessages, + Totals const &_totals) + : assertionResult(_assertionResult), + infoMessages(_infoMessages), + totals(_totals) { + assertionResult.m_resultData.lazyExpression.m_transientExpression = + _assertionResult.m_resultData.lazyExpression.m_transientExpression; + + if (assertionResult.hasMessage()) { + // Copy message into messages list. + // !TBD This should have been done earlier, somewhere + MessageBuilder + builder(assertionResult.getTestMacroName(), assertionResult.getSourceInfo(), + assertionResult.getResultType()); + builder << assertionResult.getMessage(); + builder.m_info.message = builder.m_stream.str(); + + infoMessages.push_back(builder.m_info); + } + } + + AssertionStats::~AssertionStats() = default; + + SectionStats::SectionStats(SectionInfo const &_sectionInfo, + Counts const &_assertions, + double _durationInSeconds, + bool _missingAssertions) + : sectionInfo(_sectionInfo), + assertions(_assertions), + durationInSeconds(_durationInSeconds), + missingAssertions(_missingAssertions) {} + + SectionStats::~SectionStats() = default; + + TestCaseStats::TestCaseStats(TestCaseInfo const &_testInfo, + Totals const &_totals, + std::string const &_stdOut, + std::string const &_stdErr, + bool _aborting) + : testInfo(_testInfo), + totals(_totals), + stdOut(_stdOut), + stdErr(_stdErr), + aborting(_aborting) {} + + TestCaseStats::~TestCaseStats() = default; + + TestGroupStats::TestGroupStats(GroupInfo const &_groupInfo, + Totals const &_totals, + bool _aborting) + : groupInfo(_groupInfo), + totals(_totals), + aborting(_aborting) {} + + TestGroupStats::TestGroupStats(GroupInfo const &_groupInfo) + : groupInfo(_groupInfo), + aborting(false) {} + + TestGroupStats::~TestGroupStats() = default; + + TestRunStats::TestRunStats(TestRunInfo const &_runInfo, Totals const &_totals, bool _aborting) - : groupInfo(_groupInfo), - totals(_totals), - aborting(_aborting) {} - -TestGroupStats::TestGroupStats(GroupInfo const &_groupInfo) - : groupInfo(_groupInfo), - aborting(false) {} + : runInfo(_runInfo), + totals(_totals), + aborting(_aborting) {} -TestGroupStats::~TestGroupStats() = default; + TestRunStats::~TestRunStats() = default; -TestRunStats::TestRunStats(TestRunInfo const &_runInfo, - Totals const &_totals, - bool _aborting) - : runInfo(_runInfo), - totals(_totals), - aborting(_aborting) {} + void IStreamingReporter::fatalErrorEncountered(StringRef) {} -TestRunStats::~TestRunStats() = default; + bool IStreamingReporter::isMulti() const { return false; } -void IStreamingReporter::fatalErrorEncountered(StringRef) {} -bool IStreamingReporter::isMulti() const { return false; } + IReporterFactory::~IReporterFactory() = default; -IReporterFactory::~IReporterFactory() = default; -IReporterRegistry::~IReporterRegistry() = default; + IReporterRegistry::~IReporterRegistry() = default; } // end namespace Catch // end catch_interfaces_reporter.cpp // start catch_interfaces_runner.cpp namespace Catch { -IRunner::~IRunner() = default; + IRunner::~IRunner() = default; } // end catch_interfaces_runner.cpp // start catch_interfaces_testcase.cpp namespace Catch { -ITestInvoker::~ITestInvoker() = default; -ITestCaseRegistry::~ITestCaseRegistry() = default; + ITestInvoker::~ITestInvoker() = default; + + ITestCaseRegistry::~ITestCaseRegistry() = default; } // end catch_interfaces_testcase.cpp // start catch_leak_detector.cpp @@ -11208,7 +11877,7 @@ Catch::LeakDetector::LeakDetector() {} #endif Catch::LeakDetector::~LeakDetector() { - Catch::cleanUp(); + Catch::cleanUp(); } // end catch_leak_detector.cpp // start catch_list.cpp @@ -11219,23 +11888,24 @@ Catch::LeakDetector::~LeakDetector() { namespace Catch { -std::size_t listTests(Config const &config); + std::size_t listTests(Config const &config); -std::size_t listTestsNamesOnly(Config const &config); + std::size_t listTestsNamesOnly(Config const &config); -struct TagInfo { - void add(std::string const &spelling); - std::string all() const; + struct TagInfo { + void add(std::string const &spelling); - std::set spellings; - std::size_t count = 0; -}; + std::string all() const; + + std::set spellings; + std::size_t count = 0; + }; -std::size_t listTags(Config const &config); + std::size_t listTags(Config const &config); -std::size_t listReporters(); + std::size_t listReporters(); -Option list(std::shared_ptr const &config); + Option list(std::shared_ptr const &config); } // end namespace Catch @@ -11243,7 +11913,7 @@ Option list(std::shared_ptr const &config); // start catch_text.h namespace Catch { -using namespace clara::TextFlow; + using namespace clara::TextFlow; } // end catch_text.h @@ -11253,195 +11923,196 @@ using namespace clara::TextFlow; namespace Catch { -std::size_t listTests(Config const &config) { - TestSpec const &testSpec = config.testSpec(); - if (config.hasTestFilters()) - Catch::cout() << "Matching test cases:\n"; - else { - Catch::cout() << "All available test cases:\n"; - } - - auto matchedTestCases = filterTests(getAllTestCasesSorted(config), testSpec, config); - for (auto const &testCaseInfo : matchedTestCases) { - Colour::Code colour = testCaseInfo.isHidden() - ? Colour::SecondaryText - : Colour::None; - Colour colourGuard(colour); - - Catch::cout() << Column(testCaseInfo.name).initialIndent(2).indent(4) << "\n"; - if (config.verbosity() >= Verbosity::High) { - Catch::cout() << Column(Catch::Detail::stringify(testCaseInfo.lineInfo)).indent(4) << std::endl; - std::string description = testCaseInfo.description; - if (description.empty()) - description = "(NO DESCRIPTION)"; - Catch::cout() << Column(description).indent(4) << std::endl; - } - if (!testCaseInfo.tags.empty()) - Catch::cout() << Column(testCaseInfo.tagsAsString()).indent(6) << "\n"; - } - - if (!config.hasTestFilters()) - Catch::cout() << pluralise(matchedTestCases.size(), "test case") << '\n' << std::endl; - else - Catch::cout() << pluralise(matchedTestCases.size(), "matching test case") << '\n' << std::endl; - return matchedTestCases.size(); -} + std::size_t listTests(Config const &config) { + TestSpec const &testSpec = config.testSpec(); + if (config.hasTestFilters()) + Catch::cout() << "Matching test cases:\n"; + else { + Catch::cout() << "All available test cases:\n"; + } -std::size_t listTestsNamesOnly(Config const &config) { - TestSpec const &testSpec = config.testSpec(); - std::size_t matchedTests = 0; - std::vector matchedTestCases = filterTests(getAllTestCasesSorted(config), testSpec, config); - for (auto const &testCaseInfo : matchedTestCases) { - matchedTests++; - if (startsWith(testCaseInfo.name, '#')) - Catch::cout() << '"' << testCaseInfo.name << '"'; - else - Catch::cout() << testCaseInfo.name; - if (config.verbosity() >= Verbosity::High) - Catch::cout() << "\t@" << testCaseInfo.lineInfo; - Catch::cout() << std::endl; - } - return matchedTests; -} + auto matchedTestCases = filterTests(getAllTestCasesSorted(config), testSpec, config); + for (auto const &testCaseInfo: matchedTestCases) { + Colour::Code colour = testCaseInfo.isHidden() + ? Colour::SecondaryText + : Colour::None; + Colour colourGuard(colour); + + Catch::cout() << Column(testCaseInfo.name).initialIndent(2).indent(4) << "\n"; + if (config.verbosity() >= Verbosity::High) { + Catch::cout() << Column(Catch::Detail::stringify(testCaseInfo.lineInfo)).indent(4) << std::endl; + std::string description = testCaseInfo.description; + if (description.empty()) + description = "(NO DESCRIPTION)"; + Catch::cout() << Column(description).indent(4) << std::endl; + } + if (!testCaseInfo.tags.empty()) + Catch::cout() << Column(testCaseInfo.tagsAsString()).indent(6) << "\n"; + } -void TagInfo::add(std::string const &spelling) { - ++count; - spellings.insert(spelling); -} + if (!config.hasTestFilters()) + Catch::cout() << pluralise(matchedTestCases.size(), "test case") << '\n' << std::endl; + else + Catch::cout() << pluralise(matchedTestCases.size(), "matching test case") << '\n' << std::endl; + return matchedTestCases.size(); + } -std::string TagInfo::all() const { - size_t size = 0; - for (auto const &spelling : spellings) { - // Add 2 for the brackes - size += spelling.size() + 2; - } - - std::string out; - out.reserve(size); - for (auto const &spelling : spellings) { - out += '['; - out += spelling; - out += ']'; - } - return out; -} + std::size_t listTestsNamesOnly(Config const &config) { + TestSpec const &testSpec = config.testSpec(); + std::size_t matchedTests = 0; + std::vector matchedTestCases = filterTests(getAllTestCasesSorted(config), testSpec, config); + for (auto const &testCaseInfo: matchedTestCases) { + matchedTests++; + if (startsWith(testCaseInfo.name, '#')) + Catch::cout() << '"' << testCaseInfo.name << '"'; + else + Catch::cout() << testCaseInfo.name; + if (config.verbosity() >= Verbosity::High) + Catch::cout() << "\t@" << testCaseInfo.lineInfo; + Catch::cout() << std::endl; + } + return matchedTests; + } -std::size_t listTags(Config const &config) { - TestSpec const &testSpec = config.testSpec(); - if (config.hasTestFilters()) - Catch::cout() << "Tags for matching test cases:\n"; - else { - Catch::cout() << "All available tags:\n"; - } - - std::map tagCounts; - - std::vector matchedTestCases = filterTests(getAllTestCasesSorted(config), testSpec, config); - for (auto const &testCase : matchedTestCases) { - for (auto const &tagName : testCase.getTestCaseInfo().tags) { - std::string lcaseTagName = toLower(tagName); - auto countIt = tagCounts.find(lcaseTagName); - if (countIt == tagCounts.end()) - countIt = tagCounts.insert(std::make_pair(lcaseTagName, TagInfo())).first; - countIt->second.add(tagName); - } - } - - for (auto const &tagCount : tagCounts) { - ReusableStringStream rss; - rss << " " << std::setw(2) << tagCount.second.count << " "; - auto str = rss.str(); - auto wrapper = Column(tagCount.second.all()) - .initialIndent(0) - .indent(str.size()) - .width(CATCH_CONFIG_CONSOLE_WIDTH - 10); - Catch::cout() << str << wrapper << '\n'; - } - Catch::cout() << pluralise(tagCounts.size(), "tag") << '\n' << std::endl; - return tagCounts.size(); -} + void TagInfo::add(std::string const &spelling) { + ++count; + spellings.insert(spelling); + } -std::size_t listReporters() { - Catch::cout() << "Available reporters:\n"; - IReporterRegistry::FactoryMap const &factories = getRegistryHub().getReporterRegistry().getFactories(); - std::size_t maxNameLen = 0; - for (auto const &factoryKvp : factories) - maxNameLen = (std::max) (maxNameLen, factoryKvp.first.size()); - - for (auto const &factoryKvp : factories) { - Catch::cout() - << Column(factoryKvp.first + ":") - .indent(2) - .width(5 + maxNameLen) - + Column(factoryKvp.second->getDescription()) - .initialIndent(0) - .indent(2) - .width(CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen - 8) - << "\n"; - } - Catch::cout() << std::endl; - return factories.size(); -} + std::string TagInfo::all() const { + size_t size = 0; + for (auto const &spelling: spellings) { + // Add 2 for the brackes + size += spelling.size() + 2; + } -Option list(std::shared_ptr const &config) { - Option listedCount; - getCurrentMutableContext().setConfig(config); - if (config->listTests()) - listedCount = listedCount.valueOr(0) + listTests(*config); - if (config->listTestNamesOnly()) - listedCount = listedCount.valueOr(0) + listTestsNamesOnly(*config); - if (config->listTags()) - listedCount = listedCount.valueOr(0) + listTags(*config); - if (config->listReporters()) - listedCount = listedCount.valueOr(0) + listReporters(); - return listedCount; -} + std::string out; + out.reserve(size); + for (auto const &spelling: spellings) { + out += '['; + out += spelling; + out += ']'; + } + return out; + } + + std::size_t listTags(Config const &config) { + TestSpec const &testSpec = config.testSpec(); + if (config.hasTestFilters()) + Catch::cout() << "Tags for matching test cases:\n"; + else { + Catch::cout() << "All available tags:\n"; + } + + std::map tagCounts; + + std::vector matchedTestCases = filterTests(getAllTestCasesSorted(config), testSpec, config); + for (auto const &testCase: matchedTestCases) { + for (auto const &tagName: testCase.getTestCaseInfo().tags) { + std::string lcaseTagName = toLower(tagName); + auto countIt = tagCounts.find(lcaseTagName); + if (countIt == tagCounts.end()) + countIt = tagCounts.insert(std::make_pair(lcaseTagName, TagInfo())).first; + countIt->second.add(tagName); + } + } + + for (auto const &tagCount: tagCounts) { + ReusableStringStream rss; + rss << " " << std::setw(2) << tagCount.second.count << " "; + auto str = rss.str(); + auto wrapper = Column(tagCount.second.all()) + .initialIndent(0) + .indent(str.size()) + .width(CATCH_CONFIG_CONSOLE_WIDTH - 10); + Catch::cout() << str << wrapper << '\n'; + } + Catch::cout() << pluralise(tagCounts.size(), "tag") << '\n' << std::endl; + return tagCounts.size(); + } + + std::size_t listReporters() { + Catch::cout() << "Available reporters:\n"; + IReporterRegistry::FactoryMap const &factories = getRegistryHub().getReporterRegistry().getFactories(); + std::size_t maxNameLen = 0; + for (auto const &factoryKvp: factories) + maxNameLen = (std::max) (maxNameLen, factoryKvp.first.size()); + + for (auto const &factoryKvp: factories) { + Catch::cout() + << Column(factoryKvp.first + ":") + .indent(2) + .width(5 + maxNameLen) + + Column(factoryKvp.second->getDescription()) + .initialIndent(0) + .indent(2) + .width(CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen - 8) + << "\n"; + } + Catch::cout() << std::endl; + return factories.size(); + } + + Option list(std::shared_ptr const &config) { + Option listedCount; + getCurrentMutableContext().setConfig(config); + if (config->listTests()) + listedCount = listedCount.valueOr(0) + listTests(*config); + if (config->listTestNamesOnly()) + listedCount = listedCount.valueOr(0) + listTestsNamesOnly(*config); + if (config->listTags()) + listedCount = listedCount.valueOr(0) + listTags(*config); + if (config->listReporters()) + listedCount = listedCount.valueOr(0) + listReporters(); + return listedCount; + } } // end namespace Catch // end catch_list.cpp // start catch_matchers.cpp namespace Catch { -namespace Matchers { -namespace Impl { + namespace Matchers { + namespace Impl { -std::string MatcherUntypedBase::toString() const { - if (m_cachedToString.empty()) - m_cachedToString = describe(); - return m_cachedToString; -} + std::string MatcherUntypedBase::toString() const { + if (m_cachedToString.empty()) + m_cachedToString = describe(); + return m_cachedToString; + } -MatcherUntypedBase::~MatcherUntypedBase() = default; + MatcherUntypedBase::~MatcherUntypedBase() = default; -} // namespace Impl -} // namespace Matchers + } // namespace Impl + } // namespace Matchers -using namespace Matchers; -using Matchers::Impl::MatcherBase; + using namespace Matchers; + using Matchers::Impl::MatcherBase; } // namespace Catch // end catch_matchers.cpp // start catch_matchers_exception.cpp namespace Catch { -namespace Matchers { -namespace Exception { + namespace Matchers { + namespace Exception { -bool ExceptionMessageMatcher::match(std::exception const &ex) const { - return ex.what() == m_message; -} + bool ExceptionMessageMatcher::match(std::exception const &ex) const { + return ex.what() == m_message; + } -std::string ExceptionMessageMatcher::describe() const { - return "exception message matches \"" + m_message + "\""; -} + std::string ExceptionMessageMatcher::describe() const { + return "exception message matches \"" + m_message + "\""; + } -} -Exception::ExceptionMessageMatcher Message(std::string const &message) { - return Exception::ExceptionMessageMatcher(message); -} + } + + Exception::ExceptionMessageMatcher Message(std::string const &message) { + return Exception::ExceptionMessageMatcher(message); + } // namespace Exception -} // namespace Matchers + } // namespace Matchers } // namespace Catch // end catch_matchers_exception.cpp // start catch_matchers_floating.cpp @@ -11449,8 +12120,9 @@ Exception::ExceptionMessageMatcher Message(std::string const &message) { // start catch_polyfills.hpp namespace Catch { -bool isnan(float f); -bool isnan(double d); + bool isnan(float f); + + bool isnan(double d); } // end catch_polyfills.hpp @@ -11459,16 +12131,16 @@ bool isnan(double d); #include namespace Catch { -template -std::string to_string(T const &t) { + template + std::string to_string(T const &t) { #if defined(CATCH_CONFIG_CPP11_TO_STRING) - return std::to_string(t); + return std::to_string(t); #else - ReusableStringStream rss; + ReusableStringStream rss; rss << t; return rss.str(); #endif -} + } } // end namespace Catch // end catch_to_string.hpp @@ -11483,46 +12155,46 @@ std::string to_string(T const &t) { #include namespace Catch { -namespace { + namespace { -int32_t convert(float f) { - static_assert(sizeof(float) == sizeof(int32_t), "Important ULP matcher assumption violated"); - int32_t i; - std::memcpy(&i, &f, sizeof(f)); - return i; -} + int32_t convert(float f) { + static_assert(sizeof(float) == sizeof(int32_t), "Important ULP matcher assumption violated"); + int32_t i; + std::memcpy(&i, &f, sizeof(f)); + return i; + } -int64_t convert(double d) { - static_assert(sizeof(double) == sizeof(int64_t), "Important ULP matcher assumption violated"); - int64_t i; - std::memcpy(&i, &d, sizeof(d)); - return i; -} + int64_t convert(double d) { + static_assert(sizeof(double) == sizeof(int64_t), "Important ULP matcher assumption violated"); + int64_t i; + std::memcpy(&i, &d, sizeof(d)); + return i; + } -template -bool almostEqualUlps(FP lhs, FP rhs, uint64_t maxUlpDiff) { - // Comparison with NaN should always be false. - // This way we can rule it out before getting into the ugly details - if (Catch::isnan(lhs) || Catch::isnan(rhs)) { - return false; - } - - auto lc = convert(lhs); - auto rc = convert(rhs); - - if ((lc < 0) != (rc < 0)) { - // Potentially we can have +0 and -0 - return lhs == rhs; - } - - // static cast as a workaround for IBM XLC - auto ulpDiff = std::abs(static_cast(lc - rc)); - return static_cast(ulpDiff) <= maxUlpDiff; -} + template + bool almostEqualUlps(FP lhs, FP rhs, uint64_t maxUlpDiff) { + // Comparison with NaN should always be false. + // This way we can rule it out before getting into the ugly details + if (Catch::isnan(lhs) || Catch::isnan(rhs)) { + return false; + } + + auto lc = convert(lhs); + auto rc = convert(rhs); + + if ((lc < 0) != (rc < 0)) { + // Potentially we can have +0 and -0 + return lhs == rhs; + } + + // static cast as a workaround for IBM XLC + auto ulpDiff = std::abs(static_cast(lc - rc)); + return static_cast(ulpDiff) <= maxUlpDiff; + } #if defined(CATCH_CONFIG_GLOBAL_NEXTAFTER) - float nextafter(float x, float y) { + float nextafter(float x, float y) { return ::nextafterf(x, y); } @@ -11532,173 +12204,176 @@ bool almostEqualUlps(FP lhs, FP rhs, uint64_t maxUlpDiff) { #endif // ^^^ CATCH_CONFIG_GLOBAL_NEXTAFTER ^^^ -template -FP step(FP start, FP direction, uint64_t steps) { - for (uint64_t i = 0; i < steps; ++i) { + template + FP step(FP start, FP direction, uint64_t steps) { + for (uint64_t i = 0; i < steps; ++i) { #if defined(CATCH_CONFIG_GLOBAL_NEXTAFTER) - start = Catch::nextafter(start, direction); + start = Catch::nextafter(start, direction); #else - start = std::nextafter(start, direction); + start = std::nextafter(start, direction); #endif - } - return start; -} + } + return start; + } // Performs equivalent check of std::fabs(lhs - rhs) <= margin // But without the subtraction to allow for INFINITY in comparison -bool marginComparison(double lhs, double rhs, double margin) { - return (lhs + margin >= rhs) && (rhs + margin >= lhs); -} + bool marginComparison(double lhs, double rhs, double margin) { + return (lhs + margin >= rhs) && (rhs + margin >= lhs); + } -template -void write(std::ostream &out, FloatingPoint num) { - out << std::scientific - << std::setprecision(std::numeric_limits::max_digits10 - 1) - << num; -} + template + void write(std::ostream &out, FloatingPoint num) { + out << std::scientific + << std::setprecision(std::numeric_limits::max_digits10 - 1) + << num; + } -} // end anonymous namespace + } // end anonymous namespace -namespace Matchers { -namespace Floating { + namespace Matchers { + namespace Floating { -enum class FloatingPointKind : uint8_t { - Float, - Double -}; + enum class FloatingPointKind : uint8_t { + Float, + Double + }; -WithinAbsMatcher::WithinAbsMatcher(double target, double margin) - : m_target{target}, m_margin{margin} { - CATCH_ENFORCE(margin >= 0, "Invalid margin: " << margin << '.' - << " Margin has to be non-negative."); -} + WithinAbsMatcher::WithinAbsMatcher(double target, double margin) + : m_target{target}, m_margin{margin} { + CATCH_ENFORCE(margin >= 0, "Invalid margin: " << margin << '.' + << " Margin has to be non-negative."); + } // Performs equivalent check of std::fabs(lhs - rhs) <= margin // But without the subtraction to allow for INFINITY in comparison -bool WithinAbsMatcher::match(double const &matchee) const { - return (matchee + m_margin >= m_target) && (m_target + m_margin >= matchee); -} + bool WithinAbsMatcher::match(double const &matchee) const { + return (matchee + m_margin >= m_target) && (m_target + m_margin >= matchee); + } -std::string WithinAbsMatcher::describe() const { - return "is within " + ::Catch::Detail::stringify(m_margin) + " of " + ::Catch::Detail::stringify(m_target); -} + std::string WithinAbsMatcher::describe() const { + return "is within " + ::Catch::Detail::stringify(m_margin) + " of " + + ::Catch::Detail::stringify(m_target); + } -WithinUlpsMatcher::WithinUlpsMatcher(double target, uint64_t ulps, FloatingPointKind baseType) - : m_target{target}, m_ulps{ulps}, m_type{baseType} { - CATCH_ENFORCE(m_type == FloatingPointKind::Double - || m_ulps < (std::numeric_limits::max) (), - "Provided ULP is impossibly large for a float comparison."); -} + WithinUlpsMatcher::WithinUlpsMatcher(double target, uint64_t ulps, FloatingPointKind baseType) + : m_target{target}, m_ulps{ulps}, m_type{baseType} { + CATCH_ENFORCE(m_type == FloatingPointKind::Double + || m_ulps < (std::numeric_limits::max) (), + "Provided ULP is impossibly large for a float comparison."); + } #if defined(__clang__) - #pragma clang diagnostic push + #pragma clang diagnostic push // Clang <3.5 reports on the default branch in the switch below #pragma clang diagnostic ignored "-Wunreachable-code" #endif -bool WithinUlpsMatcher::match(double const &matchee) const { - switch (m_type) { - case FloatingPointKind::Float: - return almostEqualUlps(static_cast(matchee), - static_cast(m_target), - m_ulps); - case FloatingPointKind::Double:return almostEqualUlps(matchee, m_target, m_ulps); - default:CATCH_INTERNAL_ERROR("Unknown FloatingPointKind value"); - } -} + bool WithinUlpsMatcher::match(double const &matchee) const { + switch (m_type) { + case FloatingPointKind::Float: + return almostEqualUlps(static_cast(matchee), + static_cast(m_target), + m_ulps); + case FloatingPointKind::Double: + return almostEqualUlps(matchee, m_target, m_ulps); + default: + CATCH_INTERNAL_ERROR("Unknown FloatingPointKind value"); + } + } #if defined(__clang__) #pragma clang diagnostic pop #endif -std::string WithinUlpsMatcher::describe() const { - std::stringstream ret; - - ret << "is within " << m_ulps << " ULPs of "; - - if (m_type == FloatingPointKind::Float) { - write(ret, static_cast(m_target)); - ret << 'f'; - } else { - write(ret, m_target); - } - - ret << " (["; - if (m_type == FloatingPointKind::Double) { - write(ret, step(m_target, static_cast(-INFINITY), m_ulps)); - ret << ", "; - write(ret, step(m_target, static_cast(INFINITY), m_ulps)); - } else { - // We have to cast INFINITY to float because of MinGW, see #1782 - write(ret, step(static_cast(m_target), static_cast(-INFINITY), m_ulps)); - ret << ", "; - write(ret, step(static_cast(m_target), static_cast(INFINITY), m_ulps)); - } - ret << "])"; - - return ret.str(); -} + std::string WithinUlpsMatcher::describe() const { + std::stringstream ret; -WithinRelMatcher::WithinRelMatcher(double target, double epsilon) : - m_target(target), - m_epsilon(epsilon) { - CATCH_ENFORCE(m_epsilon >= 0., "Relative comparison with epsilon < 0 does not make sense."); - CATCH_ENFORCE(m_epsilon < 1., "Relative comparison with epsilon >= 1 does not make sense."); -} + ret << "is within " << m_ulps << " ULPs of "; -bool WithinRelMatcher::match(double const &matchee) const { - const auto relMargin = m_epsilon * (std::max) (std::fabs(matchee), std::fabs(m_target)); - return marginComparison(matchee, m_target, - std::isinf(relMargin) ? 0 : relMargin); -} + if (m_type == FloatingPointKind::Float) { + write(ret, static_cast(m_target)); + ret << 'f'; + } else { + write(ret, m_target); + } -std::string WithinRelMatcher::describe() const { - Catch::ReusableStringStream sstr; - sstr << "and " << m_target << " are within " << m_epsilon * 100. << "% of each other"; - return sstr.str(); -} + ret << " (["; + if (m_type == FloatingPointKind::Double) { + write(ret, step(m_target, static_cast(-INFINITY), m_ulps)); + ret << ", "; + write(ret, step(m_target, static_cast(INFINITY), m_ulps)); + } else { + // We have to cast INFINITY to float because of MinGW, see #1782 + write(ret, step(static_cast(m_target), static_cast(-INFINITY), m_ulps)); + ret << ", "; + write(ret, step(static_cast(m_target), static_cast(INFINITY), m_ulps)); + } + ret << "])"; + + return ret.str(); + } -}// namespace Floating + WithinRelMatcher::WithinRelMatcher(double target, double epsilon) : + m_target(target), + m_epsilon(epsilon) { + CATCH_ENFORCE(m_epsilon >= 0., "Relative comparison with epsilon < 0 does not make sense."); + CATCH_ENFORCE(m_epsilon < 1., "Relative comparison with epsilon >= 1 does not make sense."); + } -Floating::WithinUlpsMatcher WithinULP(double target, uint64_t maxUlpDiff) { - return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Double); -} + bool WithinRelMatcher::match(double const &matchee) const { + const auto relMargin = m_epsilon * (std::max) (std::fabs(matchee), std::fabs(m_target)); + return marginComparison(matchee, m_target, + std::isinf(relMargin) ? 0 : relMargin); + } -Floating::WithinUlpsMatcher WithinULP(float target, uint64_t maxUlpDiff) { - return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Float); -} + std::string WithinRelMatcher::describe() const { + Catch::ReusableStringStream sstr; + sstr << "and " << m_target << " are within " << m_epsilon * 100. << "% of each other"; + return sstr.str(); + } -Floating::WithinAbsMatcher WithinAbs(double target, double margin) { - return Floating::WithinAbsMatcher(target, margin); -} + }// namespace Floating -Floating::WithinRelMatcher WithinRel(double target, double eps) { - return Floating::WithinRelMatcher(target, eps); -} + Floating::WithinUlpsMatcher WithinULP(double target, uint64_t maxUlpDiff) { + return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Double); + } -Floating::WithinRelMatcher WithinRel(double target) { - return Floating::WithinRelMatcher(target, std::numeric_limits::epsilon() * 100); -} + Floating::WithinUlpsMatcher WithinULP(float target, uint64_t maxUlpDiff) { + return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Float); + } -Floating::WithinRelMatcher WithinRel(float target, float eps) { - return Floating::WithinRelMatcher(target, eps); -} + Floating::WithinAbsMatcher WithinAbs(double target, double margin) { + return Floating::WithinAbsMatcher(target, margin); + } -Floating::WithinRelMatcher WithinRel(float target) { - return Floating::WithinRelMatcher(target, std::numeric_limits::epsilon() * 100); -} + Floating::WithinRelMatcher WithinRel(double target, double eps) { + return Floating::WithinRelMatcher(target, eps); + } + + Floating::WithinRelMatcher WithinRel(double target) { + return Floating::WithinRelMatcher(target, std::numeric_limits::epsilon() * 100); + } + + Floating::WithinRelMatcher WithinRel(float target, float eps) { + return Floating::WithinRelMatcher(target, eps); + } + + Floating::WithinRelMatcher WithinRel(float target) { + return Floating::WithinRelMatcher(target, std::numeric_limits::epsilon() * 100); + } -} // namespace Matchers + } // namespace Matchers } // namespace Catch // end catch_matchers_floating.cpp // start catch_matchers_generic.cpp std::string Catch::Matchers::Generic::Detail::finalizeDescription(const std::string &desc) { - if (desc.empty()) { - return "matches undescribed predicate"; - } else { - return "matches predicate: \"" + desc + '"'; - } + if (desc.empty()) { + return "matches undescribed predicate"; + } else { + return "matches predicate: \"" + desc + '"'; + } } // end catch_matchers_generic.cpp // start catch_matchers_string.cpp @@ -11706,102 +12381,111 @@ std::string Catch::Matchers::Generic::Detail::finalizeDescription(const std::str #include namespace Catch { -namespace Matchers { + namespace Matchers { -namespace StdString { + namespace StdString { -CasedString::CasedString(std::string const &str, CaseSensitive::Choice caseSensitivity) - : m_caseSensitivity(caseSensitivity), - m_str(adjustString(str)) {} -std::string CasedString::adjustString(std::string const &str) const { - return m_caseSensitivity == CaseSensitive::No - ? toLower(str) - : str; -} -std::string CasedString::caseSensitivitySuffix() const { - return m_caseSensitivity == CaseSensitive::No - ? " (case insensitive)" - : std::string(); -} + CasedString::CasedString(std::string const &str, CaseSensitive::Choice caseSensitivity) + : m_caseSensitivity(caseSensitivity), + m_str(adjustString(str)) {} -StringMatcherBase::StringMatcherBase(std::string const &operation, CasedString const &comparator) - : m_comparator(comparator), - m_operation(operation) { -} + std::string CasedString::adjustString(std::string const &str) const { + return m_caseSensitivity == CaseSensitive::No + ? toLower(str) + : str; + } -std::string StringMatcherBase::describe() const { - std::string description; - description.reserve(5 + m_operation.size() + m_comparator.m_str.size() + - m_comparator.caseSensitivitySuffix().size()); - description += m_operation; - description += ": \""; - description += m_comparator.m_str; - description += "\""; - description += m_comparator.caseSensitivitySuffix(); - return description; -} + std::string CasedString::caseSensitivitySuffix() const { + return m_caseSensitivity == CaseSensitive::No + ? " (case insensitive)" + : std::string(); + } + + StringMatcherBase::StringMatcherBase(std::string const &operation, CasedString const &comparator) + : m_comparator(comparator), + m_operation(operation) { + } -EqualsMatcher::EqualsMatcher(CasedString const &comparator) : StringMatcherBase("equals", comparator) {} + std::string StringMatcherBase::describe() const { + std::string description; + description.reserve(5 + m_operation.size() + m_comparator.m_str.size() + + m_comparator.caseSensitivitySuffix().size()); + description += m_operation; + description += ": \""; + description += m_comparator.m_str; + description += "\""; + description += m_comparator.caseSensitivitySuffix(); + return description; + } -bool EqualsMatcher::match(std::string const &source) const { - return m_comparator.adjustString(source) == m_comparator.m_str; -} + EqualsMatcher::EqualsMatcher(CasedString const &comparator) : StringMatcherBase("equals", comparator) {} -ContainsMatcher::ContainsMatcher(CasedString const &comparator) : StringMatcherBase("contains", comparator) {} + bool EqualsMatcher::match(std::string const &source) const { + return m_comparator.adjustString(source) == m_comparator.m_str; + } -bool ContainsMatcher::match(std::string const &source) const { - return contains(m_comparator.adjustString(source), m_comparator.m_str); -} + ContainsMatcher::ContainsMatcher(CasedString const &comparator) : StringMatcherBase("contains", + comparator) {} -StartsWithMatcher::StartsWithMatcher(CasedString const &comparator) : StringMatcherBase("starts with", comparator) {} + bool ContainsMatcher::match(std::string const &source) const { + return contains(m_comparator.adjustString(source), m_comparator.m_str); + } -bool StartsWithMatcher::match(std::string const &source) const { - return startsWith(m_comparator.adjustString(source), m_comparator.m_str); -} + StartsWithMatcher::StartsWithMatcher(CasedString const &comparator) : StringMatcherBase("starts with", + comparator) {} -EndsWithMatcher::EndsWithMatcher(CasedString const &comparator) : StringMatcherBase("ends with", comparator) {} + bool StartsWithMatcher::match(std::string const &source) const { + return startsWith(m_comparator.adjustString(source), m_comparator.m_str); + } -bool EndsWithMatcher::match(std::string const &source) const { - return endsWith(m_comparator.adjustString(source), m_comparator.m_str); -} + EndsWithMatcher::EndsWithMatcher(CasedString const &comparator) : StringMatcherBase("ends with", + comparator) {} -RegexMatcher::RegexMatcher(std::string regex, CaseSensitive::Choice caseSensitivity) - : m_regex(std::move(regex)), m_caseSensitivity(caseSensitivity) {} + bool EndsWithMatcher::match(std::string const &source) const { + return endsWith(m_comparator.adjustString(source), m_comparator.m_str); + } -bool RegexMatcher::match(std::string const &matchee) const { - auto flags = std::regex::ECMAScript; // ECMAScript is the default syntax option anyway - if (m_caseSensitivity == CaseSensitive::Choice::No) { - flags |= std::regex::icase; - } - auto reg = std::regex(m_regex, flags); - return std::regex_match(matchee, reg); -} + RegexMatcher::RegexMatcher(std::string regex, CaseSensitive::Choice caseSensitivity) + : m_regex(std::move(regex)), m_caseSensitivity(caseSensitivity) {} -std::string RegexMatcher::describe() const { - return "matches " + ::Catch::Detail::stringify(m_regex) - + ((m_caseSensitivity == CaseSensitive::Choice::Yes) ? " case sensitively" : " case insensitively"); -} + bool RegexMatcher::match(std::string const &matchee) const { + auto flags = std::regex::ECMAScript; // ECMAScript is the default syntax option anyway + if (m_caseSensitivity == CaseSensitive::Choice::No) { + flags |= std::regex::icase; + } + auto reg = std::regex(m_regex, flags); + return std::regex_match(matchee, reg); + } -} // namespace StdString + std::string RegexMatcher::describe() const { + return "matches " + ::Catch::Detail::stringify(m_regex) + + ((m_caseSensitivity == CaseSensitive::Choice::Yes) ? " case sensitively" + : " case insensitively"); + } -StdString::EqualsMatcher Equals(std::string const &str, CaseSensitive::Choice caseSensitivity) { - return StdString::EqualsMatcher(StdString::CasedString(str, caseSensitivity)); -} -StdString::ContainsMatcher Contains(std::string const &str, CaseSensitive::Choice caseSensitivity) { - return StdString::ContainsMatcher(StdString::CasedString(str, caseSensitivity)); -} -StdString::EndsWithMatcher EndsWith(std::string const &str, CaseSensitive::Choice caseSensitivity) { - return StdString::EndsWithMatcher(StdString::CasedString(str, caseSensitivity)); -} -StdString::StartsWithMatcher StartsWith(std::string const &str, CaseSensitive::Choice caseSensitivity) { - return StdString::StartsWithMatcher(StdString::CasedString(str, caseSensitivity)); -} + } // namespace StdString -StdString::RegexMatcher Matches(std::string const ®ex, CaseSensitive::Choice caseSensitivity) { - return StdString::RegexMatcher(regex, caseSensitivity); -} + StdString::EqualsMatcher Equals(std::string const &str, CaseSensitive::Choice caseSensitivity) { + return StdString::EqualsMatcher(StdString::CasedString(str, caseSensitivity)); + } + + StdString::ContainsMatcher Contains(std::string const &str, CaseSensitive::Choice caseSensitivity) { + return StdString::ContainsMatcher(StdString::CasedString(str, caseSensitivity)); + } + + StdString::EndsWithMatcher EndsWith(std::string const &str, CaseSensitive::Choice caseSensitivity) { + return StdString::EndsWithMatcher(StdString::CasedString(str, caseSensitivity)); + } + + StdString::StartsWithMatcher StartsWith(std::string const &str, CaseSensitive::Choice caseSensitivity) { + return StdString::StartsWithMatcher(StdString::CasedString(str, caseSensitivity)); + } + + StdString::RegexMatcher Matches(std::string const ®ex, CaseSensitive::Choice caseSensitivity) { + return StdString::RegexMatcher(regex, caseSensitivity); + } -} // namespace Matchers + } // namespace Matchers } // namespace Catch // end catch_matchers_string.cpp // start catch_message.cpp @@ -11809,7 +12493,7 @@ StdString::RegexMatcher Matches(std::string const ®ex, CaseSensitive::Choice // start catch_uncaught_exceptions.h namespace Catch { -bool uncaught_exceptions(); + bool uncaught_exceptions(); } // end namespace Catch // end catch_uncaught_exceptions.h @@ -11818,121 +12502,124 @@ bool uncaught_exceptions(); namespace Catch { -MessageInfo::MessageInfo(StringRef const &_macroName, - SourceLineInfo const &_lineInfo, - ResultWas::OfType _type) - : macroName(_macroName), - lineInfo(_lineInfo), - type(_type), - sequence(++globalCount) {} + MessageInfo::MessageInfo(StringRef const &_macroName, + SourceLineInfo const &_lineInfo, + ResultWas::OfType _type) + : macroName(_macroName), + lineInfo(_lineInfo), + type(_type), + sequence(++globalCount) {} -bool MessageInfo::operator==(MessageInfo const &other) const { - return sequence == other.sequence; -} + bool MessageInfo::operator==(MessageInfo const &other) const { + return sequence == other.sequence; + } -bool MessageInfo::operator<(MessageInfo const &other) const { - return sequence < other.sequence; -} + bool MessageInfo::operator<(MessageInfo const &other) const { + return sequence < other.sequence; + } // This may need protecting if threading support is added -unsigned int MessageInfo::globalCount = 0; + unsigned int MessageInfo::globalCount = 0; //////////////////////////////////////////////////////////////////////////// -Catch::MessageBuilder::MessageBuilder(StringRef const ¯oName, - SourceLineInfo const &lineInfo, - ResultWas::OfType type) - : m_info(macroName, lineInfo, type) {} + Catch::MessageBuilder::MessageBuilder(StringRef const ¯oName, + SourceLineInfo const &lineInfo, + ResultWas::OfType type) + : m_info(macroName, lineInfo, type) {} //////////////////////////////////////////////////////////////////////////// -ScopedMessage::ScopedMessage(MessageBuilder const &builder) - : m_info(builder.m_info), m_moved() { - m_info.message = builder.m_stream.str(); - getResultCapture().pushScopedMessage(m_info); -} + ScopedMessage::ScopedMessage(MessageBuilder const &builder) + : m_info(builder.m_info), m_moved() { + m_info.message = builder.m_stream.str(); + getResultCapture().pushScopedMessage(m_info); + } -ScopedMessage::ScopedMessage(ScopedMessage &&old) - : m_info(old.m_info), m_moved() { - old.m_moved = true; -} + ScopedMessage::ScopedMessage(ScopedMessage &&old) + : m_info(old.m_info), m_moved() { + old.m_moved = true; + } -ScopedMessage::~ScopedMessage() { - if (!uncaught_exceptions() && !m_moved) { - getResultCapture().popScopedMessage(m_info); - } -} + ScopedMessage::~ScopedMessage() { + if (!uncaught_exceptions() && !m_moved) { + getResultCapture().popScopedMessage(m_info); + } + } + + Capturer::Capturer(StringRef macroName, SourceLineInfo const &lineInfo, ResultWas::OfType resultType, + StringRef names) { + auto trimmed = [&](size_t start, size_t end) { + while (names[start] == ',' || isspace(static_cast(names[start]))) { + ++start; + } + while (names[end] == ',' || isspace(static_cast(names[end]))) { + --end; + } + return names.substr(start, end - start + 1); + }; + auto skipq = [&](size_t start, char quote) { + for (auto i = start + 1; i < names.size(); ++i) { + if (names[i] == quote) + return i; + if (names[i] == '\\') + ++i; + } + CATCH_INTERNAL_ERROR("CAPTURE parsing encountered unmatched quote"); + }; -Capturer::Capturer(StringRef macroName, SourceLineInfo const &lineInfo, ResultWas::OfType resultType, StringRef names) { - auto trimmed = [&](size_t start, size_t end) { - while (names[start] == ',' || isspace(static_cast(names[start]))) { - ++start; - } - while (names[end] == ',' || isspace(static_cast(names[end]))) { - --end; - } - return names.substr(start, end - start + 1); - }; - auto skipq = [&](size_t start, char quote) { - for (auto i = start + 1; i < names.size(); ++i) { - if (names[i] == quote) - return i; - if (names[i] == '\\') - ++i; - } - CATCH_INTERNAL_ERROR("CAPTURE parsing encountered unmatched quote"); - }; - - size_t start = 0; - std::stack openings; - for (size_t pos = 0; pos < names.size(); ++pos) { - char c = names[pos]; - switch (c) { - case '[': - case '{': - case '(': - // It is basically impossible to disambiguate between - // comparison and start of template args in this context + size_t start = 0; + std::stack openings; + for (size_t pos = 0; pos < names.size(); ++pos) { + char c = names[pos]; + switch (c) { + case '[': + case '{': + case '(': + // It is basically impossible to disambiguate between + // comparison and start of template args in this context // case '<': - openings.push(c); - break; - case ']': - case '}': - case ')': + openings.push(c); + break; + case ']': + case '}': + case ')': // case '>': - openings.pop(); - break; - case '"': - case '\'':pos = skipq(pos, c); - break; - case ',': - if (start != pos && openings.empty()) { - m_messages.emplace_back(macroName, lineInfo, resultType); - m_messages.back().message = static_cast(trimmed(start, pos)); - m_messages.back().message += " := "; - start = pos; - } - } - } - assert(openings.empty() && "Mismatched openings"); - m_messages.emplace_back(macroName, lineInfo, resultType); - m_messages.back().message = static_cast(trimmed(start, names.size() - 1)); - m_messages.back().message += " := "; -} -Capturer::~Capturer() { - if (!uncaught_exceptions()) { - assert(m_captured == m_messages.size()); - for (size_t i = 0; i < m_captured; ++i) - m_resultCapture.popScopedMessage(m_messages[i]); - } -} + openings.pop(); + break; + case '"': + case '\'': + pos = skipq(pos, c); + break; + case ',': + if (start != pos && openings.empty()) { + m_messages.emplace_back(macroName, lineInfo, resultType); + m_messages.back().message = static_cast(trimmed(start, pos)); + m_messages.back().message += " := "; + start = pos; + } + } + } + assert(openings.empty() && "Mismatched openings"); + m_messages.emplace_back(macroName, lineInfo, resultType); + m_messages.back().message = static_cast(trimmed(start, names.size() - 1)); + m_messages.back().message += " := "; + } -void Capturer::captureValue(size_t index, std::string const &value) { - assert(index < m_messages.size()); - m_messages[index].message += value; - m_resultCapture.pushScopedMessage(m_messages[index]); - m_captured++; -} + Capturer::~Capturer() { + if (!uncaught_exceptions()) { + assert(m_captured == m_messages.size()); + for (size_t i = 0; i < m_captured; ++i) + m_resultCapture.popScopedMessage(m_messages[i]); + } + } + + void Capturer::captureValue(size_t index, std::string const &value) { + assert(index < m_messages.size()); + m_messages[index].message += value; + m_resultCapture.pushScopedMessage(m_messages[index]); + m_captured++; + } } // end namespace Catch // end catch_message.cpp @@ -11948,55 +12635,63 @@ void Capturer::captureValue(size_t index, std::string const &value) { namespace Catch { -class RedirectedStream { - std::ostream &m_originalStream; - std::ostream &m_redirectionStream; - std::streambuf *m_prevBuf; + class RedirectedStream { + std::ostream &m_originalStream; + std::ostream &m_redirectionStream; + std::streambuf *m_prevBuf; - public: - RedirectedStream(std::ostream &originalStream, std::ostream &redirectionStream); - ~RedirectedStream(); -}; + public: + RedirectedStream(std::ostream &originalStream, std::ostream &redirectionStream); -class RedirectedStdOut { - ReusableStringStream m_rss; - RedirectedStream m_cout; - public: - RedirectedStdOut(); - auto str() const -> std::string; -}; + ~RedirectedStream(); + }; + + class RedirectedStdOut { + ReusableStringStream m_rss; + RedirectedStream m_cout; + public: + RedirectedStdOut(); + + auto str() const -> std::string; + }; // StdErr has two constituent streams in C++, std::cerr and std::clog // This means that we need to redirect 2 streams into 1 to keep proper // order of writes -class RedirectedStdErr { - ReusableStringStream m_rss; - RedirectedStream m_cerr; - RedirectedStream m_clog; - public: - RedirectedStdErr(); - auto str() const -> std::string; -}; + class RedirectedStdErr { + ReusableStringStream m_rss; + RedirectedStream m_cerr; + RedirectedStream m_clog; + public: + RedirectedStdErr(); -class RedirectedStreams { - public: - RedirectedStreams(RedirectedStreams const &) = delete; - RedirectedStreams &operator=(RedirectedStreams const &) = delete; - RedirectedStreams(RedirectedStreams &&) = delete; - RedirectedStreams &operator=(RedirectedStreams &&) = delete; - - RedirectedStreams(std::string &redirectedCout, std::string &redirectedCerr); - ~RedirectedStreams(); - private: - std::string &m_redirectedCout; - std::string &m_redirectedCerr; - RedirectedStdOut m_redirectedStdOut; - RedirectedStdErr m_redirectedStdErr; -}; + auto str() const -> std::string; + }; + + class RedirectedStreams { + public: + RedirectedStreams(RedirectedStreams const &) = delete; + + RedirectedStreams &operator=(RedirectedStreams const &) = delete; + + RedirectedStreams(RedirectedStreams &&) = delete; + + RedirectedStreams &operator=(RedirectedStreams &&) = delete; + + RedirectedStreams(std::string &redirectedCout, std::string &redirectedCerr); + + ~RedirectedStreams(); + + private: + std::string &m_redirectedCout; + std::string &m_redirectedCerr; + RedirectedStdOut m_redirectedStdOut; + RedirectedStdErr m_redirectedStdErr; + }; #if defined(CATCH_CONFIG_NEW_CAPTURE) - // Windows's implementation of std::tmpfile is terrible (it tries + // Windows's implementation of std::tmpfile is terrible (it tries // to create a file inside system folder, thus requiring elevated // privileges for the binary), so we have to use tmpnam(_s) and // create the file ourselves there. @@ -12064,37 +12759,39 @@ class RedirectedStreams { namespace Catch { -RedirectedStream::RedirectedStream(std::ostream &originalStream, std::ostream &redirectionStream) - : m_originalStream(originalStream), - m_redirectionStream(redirectionStream), - m_prevBuf(m_originalStream.rdbuf()) { - m_originalStream.rdbuf(m_redirectionStream.rdbuf()); -} + RedirectedStream::RedirectedStream(std::ostream &originalStream, std::ostream &redirectionStream) + : m_originalStream(originalStream), + m_redirectionStream(redirectionStream), + m_prevBuf(m_originalStream.rdbuf()) { + m_originalStream.rdbuf(m_redirectionStream.rdbuf()); + } -RedirectedStream::~RedirectedStream() { - m_originalStream.rdbuf(m_prevBuf); -} + RedirectedStream::~RedirectedStream() { + m_originalStream.rdbuf(m_prevBuf); + } -RedirectedStdOut::RedirectedStdOut() : m_cout(Catch::cout(), m_rss.get()) {} -auto RedirectedStdOut::str() const -> std::string { return m_rss.str(); } + RedirectedStdOut::RedirectedStdOut() : m_cout(Catch::cout(), m_rss.get()) {} -RedirectedStdErr::RedirectedStdErr() - : m_cerr(Catch::cerr(), m_rss.get()), - m_clog(Catch::clog(), m_rss.get()) {} -auto RedirectedStdErr::str() const -> std::string { return m_rss.str(); } + auto RedirectedStdOut::str() const -> std::string { return m_rss.str(); } -RedirectedStreams::RedirectedStreams(std::string &redirectedCout, std::string &redirectedCerr) - : m_redirectedCout(redirectedCout), - m_redirectedCerr(redirectedCerr) {} + RedirectedStdErr::RedirectedStdErr() + : m_cerr(Catch::cerr(), m_rss.get()), + m_clog(Catch::clog(), m_rss.get()) {} -RedirectedStreams::~RedirectedStreams() { - m_redirectedCout += m_redirectedStdOut.str(); - m_redirectedCerr += m_redirectedStdErr.str(); -} + auto RedirectedStdErr::str() const -> std::string { return m_rss.str(); } + + RedirectedStreams::RedirectedStreams(std::string &redirectedCout, std::string &redirectedCerr) + : m_redirectedCout(redirectedCout), + m_redirectedCerr(redirectedCerr) {} + + RedirectedStreams::~RedirectedStreams() { + m_redirectedCout += m_redirectedStdOut.str(); + m_redirectedCerr += m_redirectedStdErr.str(); + } #if defined(CATCH_CONFIG_NEW_CAPTURE) - #if defined(_MSC_VER) + #if defined(_MSC_VER) TempFile::TempFile() { if (tmpnam_s(m_buffer)) { CATCH_RUNTIME_ERROR("Could not get a temp filename"); @@ -12184,15 +12881,18 @@ RedirectedStreams::~RedirectedStreams() { namespace Catch { -#if !defined(CATCH_CONFIG_POLYFILL_ISNAN) -bool isnan(float f) { - return std::isnan(f); -} -bool isnan(double d) { - return std::isnan(d); -} +#if !defined(CATCH_CONFIG_POLYFILL_ISNAN) + + bool isnan(float f) { + return std::isnan(f); + } + + bool isnan(double d) { + return std::isnan(d); + } + #else - // For now we only use this for embarcadero + // For now we only use this for embarcadero bool isnan(float f) { return std::_isnan(f); } @@ -12207,62 +12907,63 @@ bool isnan(double d) { namespace Catch { -namespace { + namespace { #if defined(_MSC_VER) - #pragma warning(push) + #pragma warning(push) #pragma warning(disable:4146) // we negate uint32 during the rotate #endif + // Safe rotr implementation thanks to John Regehr -uint32_t rotate_right(uint32_t val, uint32_t count) { - const uint32_t mask = 31; - count &= mask; - return (val >> count) | (val << (-count & mask)); -} + uint32_t rotate_right(uint32_t val, uint32_t count) { + const uint32_t mask = 31; + count &= mask; + return (val >> count) | (val << (-count & mask)); + } #if defined(_MSC_VER) #pragma warning(pop) #endif -} + } -SimplePcg32::SimplePcg32(result_type seed_) { - seed(seed_); -} + SimplePcg32::SimplePcg32(result_type seed_) { + seed(seed_); + } -void SimplePcg32::seed(result_type seed_) { - m_state = 0; - (*this)(); - m_state += seed_; - (*this)(); -} + void SimplePcg32::seed(result_type seed_) { + m_state = 0; + (*this)(); + m_state += seed_; + (*this)(); + } -void SimplePcg32::discard(uint64_t skip) { - // We could implement this to run in O(log n) steps, but this - // should suffice for our use case. - for (uint64_t s = 0; s < skip; ++s) { - static_cast((*this)()); - } -} + void SimplePcg32::discard(uint64_t skip) { + // We could implement this to run in O(log n) steps, but this + // should suffice for our use case. + for (uint64_t s = 0; s < skip; ++s) { + static_cast((*this)()); + } + } -SimplePcg32::result_type SimplePcg32::operator()() { - // prepare the output value - const uint32_t xorshifted = static_cast(((m_state >> 18u) ^ m_state) >> 27u); - const auto output = rotate_right(xorshifted, m_state >> 59u); + SimplePcg32::result_type SimplePcg32::operator()() { + // prepare the output value + const uint32_t xorshifted = static_cast(((m_state >> 18u) ^ m_state) >> 27u); + const auto output = rotate_right(xorshifted, m_state >> 59u); - // advance state - m_state = m_state * 6364136223846793005ULL + s_inc; + // advance state + m_state = m_state * 6364136223846793005ULL + s_inc; - return output; -} + return output; + } -bool operator==(SimplePcg32 const &lhs, SimplePcg32 const &rhs) { - return lhs.m_state == rhs.m_state; -} + bool operator==(SimplePcg32 const &lhs, SimplePcg32 const &rhs) { + return lhs.m_state == rhs.m_state; + } -bool operator!=(SimplePcg32 const &lhs, SimplePcg32 const &rhs) { - return lhs.m_state != rhs.m_state; -} + bool operator!=(SimplePcg32 const &lhs, SimplePcg32 const &rhs) { + return lhs.m_state != rhs.m_state; + } } // end catch_random_number_generator.cpp // start catch_registry_hub.cpp @@ -12276,49 +12977,54 @@ bool operator!=(SimplePcg32 const &lhs, SimplePcg32 const &rhs) { namespace Catch { -class TestCase; -struct IConfig; + class TestCase; -std::vector sortTests(IConfig const &config, std::vector const &unsortedTestCases); + struct IConfig; -bool isThrowSafe(TestCase const &testCase, IConfig const &config); -bool matchTest(TestCase const &testCase, TestSpec const &testSpec, IConfig const &config); + std::vector sortTests(IConfig const &config, std::vector const &unsortedTestCases); -void enforceNoDuplicateTestCases(std::vector const &functions); + bool isThrowSafe(TestCase const &testCase, IConfig const &config); -std::vector filterTests(std::vector const &testCases, - TestSpec const &testSpec, - IConfig const &config); -std::vector const &getAllTestCasesSorted(IConfig const &config); + bool matchTest(TestCase const &testCase, TestSpec const &testSpec, IConfig const &config); -class TestRegistry : public ITestCaseRegistry { - public: - virtual ~TestRegistry() = default; + void enforceNoDuplicateTestCases(std::vector const &functions); - virtual void registerTest(TestCase const &testCase); + std::vector filterTests(std::vector const &testCases, + TestSpec const &testSpec, + IConfig const &config); - std::vector const &getAllTests() const override; - std::vector const &getAllTestsSorted(IConfig const &config) const override; + std::vector const &getAllTestCasesSorted(IConfig const &config); - private: - std::vector m_functions; - mutable RunTests::InWhatOrder m_currentSortOrder = RunTests::InDeclarationOrder; - mutable std::vector m_sortedFunctions; - std::size_t m_unnamedCount = 0; - std::ios_base::Init m_ostreamInit; // Forces cout/ cerr to be initialised -}; + class TestRegistry : public ITestCaseRegistry { + public: + virtual ~TestRegistry() = default; + + virtual void registerTest(TestCase const &testCase); + + std::vector const &getAllTests() const override; + + std::vector const &getAllTestsSorted(IConfig const &config) const override; + + private: + std::vector m_functions; + mutable RunTests::InWhatOrder m_currentSortOrder = RunTests::InDeclarationOrder; + mutable std::vector m_sortedFunctions; + std::size_t m_unnamedCount = 0; + std::ios_base::Init m_ostreamInit; // Forces cout/ cerr to be initialised + }; /////////////////////////////////////////////////////////////////////////// -class TestInvokerAsFunction : public ITestInvoker { - void (*m_testAsFunction)(); - public: - TestInvokerAsFunction(void(*testAsFunction)()) noexcept; + class TestInvokerAsFunction : public ITestInvoker { + void (*m_testAsFunction)(); - void invoke() const override; -}; + public: + TestInvokerAsFunction(void(*testAsFunction)()) noexcept; + + void invoke() const override; + }; -std::string extractClassName(StringRef const &classOrQualifiedMethodName); + std::string extractClassName(StringRef const &classOrQualifiedMethodName); /////////////////////////////////////////////////////////////////////////// @@ -12331,24 +13037,26 @@ std::string extractClassName(StringRef const &classOrQualifiedMethodName); namespace Catch { -class ReporterRegistry : public IReporterRegistry { + class ReporterRegistry : public IReporterRegistry { - public: + public: - ~ReporterRegistry() override; + ~ReporterRegistry() override; - IStreamingReporterPtr create(std::string const &name, IConfigPtr const &config) const override; + IStreamingReporterPtr create(std::string const &name, IConfigPtr const &config) const override; - void registerReporter(std::string const &name, IReporterFactoryPtr const &factory); - void registerListener(IReporterFactoryPtr const &factory); + void registerReporter(std::string const &name, IReporterFactoryPtr const &factory); - FactoryMap const &getFactories() const override; - Listeners const &getListeners() const override; + void registerListener(IReporterFactoryPtr const &factory); - private: - FactoryMap m_factories; - Listeners m_listeners; -}; + FactoryMap const &getFactories() const override; + + Listeners const &getListeners() const override; + + private: + FactoryMap m_factories; + Listeners m_listeners; + }; } // end catch_reporter_registry.h @@ -12360,12 +13068,12 @@ class ReporterRegistry : public IReporterRegistry { namespace Catch { -struct TagAlias { - TagAlias(std::string const &_tag, SourceLineInfo _lineInfo); + struct TagAlias { + TagAlias(std::string const &_tag, SourceLineInfo _lineInfo); - std::string tag; - SourceLineInfo lineInfo; -}; + std::string tag; + SourceLineInfo lineInfo; + }; } // end namespace Catch @@ -12374,16 +13082,19 @@ struct TagAlias { namespace Catch { -class TagAliasRegistry : public ITagAliasRegistry { - public: - ~TagAliasRegistry() override; - TagAlias const *find(std::string const &alias) const override; - std::string expandAliases(std::string const &unexpandedTestSpec) const override; - void add(std::string const &alias, std::string const &tag, SourceLineInfo const &lineInfo); + class TagAliasRegistry : public ITagAliasRegistry { + public: + ~TagAliasRegistry() override; - private: - std::map m_registry; -}; + TagAlias const *find(std::string const &alias) const override; + + std::string expandAliases(std::string const &unexpandedTestSpec) const override; + + void add(std::string const &alias, std::string const &tag, SourceLineInfo const &lineInfo); + + private: + std::map m_registry; + }; } // end namespace Catch @@ -12395,15 +13106,17 @@ class TagAliasRegistry : public ITagAliasRegistry { namespace Catch { -class StartupExceptionRegistry { + class StartupExceptionRegistry { #if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) - public: - void add(std::exception_ptr const &exception) noexcept; - std::vector const &getExceptions() const noexcept; - private: - std::vector m_exceptions; + public: + void add(std::exception_ptr const &exception) noexcept; + + std::vector const &getExceptions() const noexcept; + + private: + std::vector m_exceptions; #endif -}; + }; } // end namespace Catch @@ -12412,114 +13125,131 @@ class StartupExceptionRegistry { namespace Catch { -struct ISingleton { - virtual ~ISingleton(); -}; + struct ISingleton { + virtual ~ISingleton(); + }; -void addSingleton(ISingleton *singleton); -void cleanupSingletons(); + void addSingleton(ISingleton *singleton); -template -class Singleton : SingletonImplT, public ISingleton { + void cleanupSingletons(); - static auto getInternal() -> Singleton * { - static Singleton *s_instance = nullptr; - if (!s_instance) { - s_instance = new Singleton; - addSingleton(s_instance); - } - return s_instance; - } + template + class Singleton : SingletonImplT, public ISingleton { - public: - static auto get() -> InterfaceT const & { - return *getInternal(); - } - static auto getMutable() -> MutableInterfaceT & { - return *getInternal(); - } -}; + static auto getInternal() -> Singleton * { + static Singleton *s_instance = nullptr; + if (!s_instance) { + s_instance = new Singleton; + addSingleton(s_instance); + } + return s_instance; + } + + public: + static auto get() -> InterfaceT const & { + return *getInternal(); + } + + static auto getMutable() -> MutableInterfaceT & { + return *getInternal(); + } + }; } // namespace Catch // end catch_singletons.hpp namespace Catch { -namespace { + namespace { + + class RegistryHub : public IRegistryHub, public IMutableRegistryHub, + private NonCopyable { + + public: // IRegistryHub + RegistryHub() = default; + + IReporterRegistry const &getReporterRegistry() const override { + return m_reporterRegistry; + } + + ITestCaseRegistry const &getTestCaseRegistry() const override { + return m_testCaseRegistry; + } + + IExceptionTranslatorRegistry const &getExceptionTranslatorRegistry() const override { + return m_exceptionTranslatorRegistry; + } + + ITagAliasRegistry const &getTagAliasRegistry() const override { + return m_tagAliasRegistry; + } -class RegistryHub : public IRegistryHub, public IMutableRegistryHub, - private NonCopyable { - - public: // IRegistryHub - RegistryHub() = default; - IReporterRegistry const &getReporterRegistry() const override { - return m_reporterRegistry; - } - ITestCaseRegistry const &getTestCaseRegistry() const override { - return m_testCaseRegistry; - } - IExceptionTranslatorRegistry const &getExceptionTranslatorRegistry() const override { - return m_exceptionTranslatorRegistry; - } - ITagAliasRegistry const &getTagAliasRegistry() const override { - return m_tagAliasRegistry; - } - StartupExceptionRegistry const &getStartupExceptionRegistry() const override { - return m_exceptionRegistry; - } - - public: // IMutableRegistryHub - void registerReporter(std::string const &name, IReporterFactoryPtr const &factory) override { - m_reporterRegistry.registerReporter(name, factory); - } - void registerListener(IReporterFactoryPtr const &factory) override { - m_reporterRegistry.registerListener(factory); - } - void registerTest(TestCase const &testInfo) override { - m_testCaseRegistry.registerTest(testInfo); - } - void registerTranslator(const IExceptionTranslator *translator) override { - m_exceptionTranslatorRegistry.registerTranslator(translator); - } - void registerTagAlias(std::string const &alias, std::string const &tag, SourceLineInfo const &lineInfo) override { - m_tagAliasRegistry.add(alias, tag, lineInfo); - } - void registerStartupException() noexcept override { + StartupExceptionRegistry const &getStartupExceptionRegistry() const override { + return m_exceptionRegistry; + } + + public: // IMutableRegistryHub + void registerReporter(std::string const &name, IReporterFactoryPtr const &factory) override { + m_reporterRegistry.registerReporter(name, factory); + } + + void registerListener(IReporterFactoryPtr const &factory) override { + m_reporterRegistry.registerListener(factory); + } + + void registerTest(TestCase const &testInfo) override { + m_testCaseRegistry.registerTest(testInfo); + } + + void registerTranslator(const IExceptionTranslator *translator) override { + m_exceptionTranslatorRegistry.registerTranslator(translator); + } + + void registerTagAlias(std::string const &alias, std::string const &tag, + SourceLineInfo const &lineInfo) override { + m_tagAliasRegistry.add(alias, tag, lineInfo); + } + + void registerStartupException() noexcept override { #if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) - m_exceptionRegistry.add(std::current_exception()); + m_exceptionRegistry.add(std::current_exception()); #else - CATCH_INTERNAL_ERROR("Attempted to register active exception under CATCH_CONFIG_DISABLE_EXCEPTIONS!"); + CATCH_INTERNAL_ERROR("Attempted to register active exception under CATCH_CONFIG_DISABLE_EXCEPTIONS!"); #endif - } - IMutableEnumValuesRegistry &getMutableEnumValuesRegistry() override { - return m_enumValuesRegistry; - } - - private: - TestRegistry m_testCaseRegistry; - ReporterRegistry m_reporterRegistry; - ExceptionTranslatorRegistry m_exceptionTranslatorRegistry; - TagAliasRegistry m_tagAliasRegistry; - StartupExceptionRegistry m_exceptionRegistry; - Detail::EnumValuesRegistry m_enumValuesRegistry; -}; -} + } + + IMutableEnumValuesRegistry &getMutableEnumValuesRegistry() override { + return m_enumValuesRegistry; + } -using RegistryHubSingleton = Singleton; + private: + TestRegistry m_testCaseRegistry; + ReporterRegistry m_reporterRegistry; + ExceptionTranslatorRegistry m_exceptionTranslatorRegistry; + TagAliasRegistry m_tagAliasRegistry; + StartupExceptionRegistry m_exceptionRegistry; + Detail::EnumValuesRegistry m_enumValuesRegistry; + }; + } -IRegistryHub const &getRegistryHub() { - return RegistryHubSingleton::get(); -} -IMutableRegistryHub &getMutableRegistryHub() { - return RegistryHubSingleton::getMutable(); -} -void cleanUp() { - cleanupSingletons(); - cleanUpContext(); -} -std::string translateActiveException() { - return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException(); -} + using RegistryHubSingleton = Singleton; + + IRegistryHub const &getRegistryHub() { + return RegistryHubSingleton::get(); + } + + IMutableRegistryHub &getMutableRegistryHub() { + return RegistryHubSingleton::getMutable(); + } + + void cleanUp() { + cleanupSingletons(); + cleanUpContext(); + } + + std::string translateActiveException() { + return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException(); + } } // end namespace Catch // end catch_registry_hub.cpp @@ -12527,28 +13257,30 @@ std::string translateActiveException() { namespace Catch { -ReporterRegistry::~ReporterRegistry() = default; + ReporterRegistry::~ReporterRegistry() = default; -IStreamingReporterPtr ReporterRegistry::create(std::string const &name, IConfigPtr const &config) const { - auto it = m_factories.find(name); - if (it == m_factories.end()) - return nullptr; - return it->second->create(ReporterConfig(config)); -} + IStreamingReporterPtr ReporterRegistry::create(std::string const &name, IConfigPtr const &config) const { + auto it = m_factories.find(name); + if (it == m_factories.end()) + return nullptr; + return it->second->create(ReporterConfig(config)); + } -void ReporterRegistry::registerReporter(std::string const &name, IReporterFactoryPtr const &factory) { - m_factories.emplace(name, factory); -} -void ReporterRegistry::registerListener(IReporterFactoryPtr const &factory) { - m_listeners.push_back(factory); -} + void ReporterRegistry::registerReporter(std::string const &name, IReporterFactoryPtr const &factory) { + m_factories.emplace(name, factory); + } -IReporterRegistry::FactoryMap const &ReporterRegistry::getFactories() const { - return m_factories; -} -IReporterRegistry::Listeners const &ReporterRegistry::getListeners() const { - return m_listeners; -} + void ReporterRegistry::registerListener(IReporterFactoryPtr const &factory) { + m_listeners.push_back(factory); + } + + IReporterRegistry::FactoryMap const &ReporterRegistry::getFactories() const { + return m_factories; + } + + IReporterRegistry::Listeners const &ReporterRegistry::getListeners() const { + return m_listeners; + } } // end catch_reporter_registry.cpp @@ -12556,19 +13288,21 @@ IReporterRegistry::Listeners const &ReporterRegistry::getListeners() const { namespace Catch { -bool isOk(ResultWas::OfType resultType) { - return (resultType & ResultWas::FailureBit) == 0; -} -bool isJustInfo(int flags) { - return flags == ResultWas::Info; -} + bool isOk(ResultWas::OfType resultType) { + return (resultType & ResultWas::FailureBit) == 0; + } -ResultDisposition::Flags operator|(ResultDisposition::Flags lhs, ResultDisposition::Flags rhs) { - return static_cast( static_cast( lhs ) | static_cast( rhs )); -} + bool isJustInfo(int flags) { + return flags == ResultWas::Info; + } + + ResultDisposition::Flags operator|(ResultDisposition::Flags lhs, ResultDisposition::Flags rhs) { + return static_cast( static_cast( lhs ) | static_cast( rhs )); + } + + bool shouldContinueOnFailure(int flags) { return (flags & ResultDisposition::ContinueOnFailure) != 0; } -bool shouldContinueOnFailure(int flags) { return (flags & ResultDisposition::ContinueOnFailure) != 0; } -bool shouldSuppressFailure(int flags) { return (flags & ResultDisposition::SuppressFail) != 0; } + bool shouldSuppressFailure(int flags) { return (flags & ResultDisposition::SuppressFail) != 0; } } // end namespace Catch // end catch_result_type.cpp @@ -12580,307 +13314,319 @@ bool shouldSuppressFailure(int flags) { return (flags & ResultDisposition::Suppr namespace Catch { -namespace Generators { -struct GeneratorTracker : TestCaseTracking::TrackerBase, IGeneratorTracker { - GeneratorBasePtr m_generator; - - GeneratorTracker(TestCaseTracking::NameAndLocation const &nameAndLocation, TrackerContext &ctx, ITracker *parent) - : TrackerBase(nameAndLocation, ctx, parent) {} - ~GeneratorTracker(); - - static GeneratorTracker &acquire(TrackerContext &ctx, TestCaseTracking::NameAndLocation const &nameAndLocation) { - std::shared_ptr tracker; - - ITracker ¤tTracker = ctx.currentTracker(); - // Under specific circumstances, the generator we want - // to acquire is also the current tracker. If this is - // the case, we have to avoid looking through current - // tracker's children, and instead return the current - // tracker. - // A case where this check is important is e.g. - // for (int i = 0; i < 5; ++i) { - // int n = GENERATE(1, 2); - // } - // - // without it, the code above creates 5 nested generators. - if (currentTracker.nameAndLocation() == nameAndLocation) { - auto thisTracker = currentTracker.parent().findChild(nameAndLocation); - assert(thisTracker); - assert(thisTracker->isGeneratorTracker()); - tracker = std::static_pointer_cast(thisTracker); - } else if (TestCaseTracking::ITrackerPtr childTracker = currentTracker.findChild(nameAndLocation)) { - assert(childTracker); - assert(childTracker->isGeneratorTracker()); - tracker = std::static_pointer_cast(childTracker); - } else { - tracker = std::make_shared(nameAndLocation, ctx, ¤tTracker); - currentTracker.addChild(tracker); - } - - if (!tracker->isComplete()) { - tracker->open(); - } - - return *tracker; - } - - // TrackerBase interface - bool isGeneratorTracker() const override { return true; } - auto hasGenerator() const -> bool override { - return !!m_generator; - } - void close() override { - TrackerBase::close(); - // If a generator has a child (it is followed by a section) - // and none of its children have started, then we must wait - // until later to start consuming its values. - // This catches cases where `GENERATE` is placed between two - // `SECTION`s. - // **The check for m_children.empty cannot be removed**. - // doing so would break `GENERATE` _not_ followed by `SECTION`s. - const bool should_wait_for_child = [&]() { - // No children -> nobody to wait for - if (m_children.empty()) { - return false; - } - // If at least one child started executing, don't wait - if (std::find_if( - m_children.begin(), - m_children.end(), - [](TestCaseTracking::ITrackerPtr tracker) { - return tracker->hasStarted(); - }) != m_children.end()) { - return false; - } - - // No children have started. We need to check if they _can_ - // start, and thus we should wait for them, or they cannot - // start (due to filters), and we shouldn't wait for them - auto *parent = m_parent; - // This is safe: there is always at least one section - // tracker in a test case tracking tree - while (!parent->isSectionTracker()) { - parent = &(parent->parent()); - } - assert(parent && - "Missing root (test case) level section"); - - auto const &parentSection = - static_cast( *parent ); - auto const &filters = parentSection.getFilters(); - // No filters -> no restrictions on running sections - if (filters.empty()) { - return true; - } - - for (auto const &child : m_children) { - if (child->isSectionTracker() && - std::find(filters.begin(), - filters.end(), - static_cast( *child ) - .trimmedName()) != - filters.end()) { - return true; - } - } - return false; - }(); - - // This check is a bit tricky, because m_generator->next() - // has a side-effect, where it consumes generator's current - // value, but we do not want to invoke the side-effect if - // this generator is still waiting for any child to start. - if (should_wait_for_child || - (m_runState == CompletedSuccessfully && - m_generator->next())) { - m_children.clear(); - m_runState = Executing; - } - } - - // IGeneratorTracker interface - auto getGenerator() const -> GeneratorBasePtr const & override { - return m_generator; - } - void setGenerator(GeneratorBasePtr &&generator) override { - m_generator = std::move(generator); - } -}; -GeneratorTracker::~GeneratorTracker() {} -} + namespace Generators { + struct GeneratorTracker : TestCaseTracking::TrackerBase, IGeneratorTracker { + GeneratorBasePtr m_generator; + + GeneratorTracker(TestCaseTracking::NameAndLocation const &nameAndLocation, TrackerContext &ctx, + ITracker *parent) + : TrackerBase(nameAndLocation, ctx, parent) {} + + ~GeneratorTracker(); + + static GeneratorTracker & + acquire(TrackerContext &ctx, TestCaseTracking::NameAndLocation const &nameAndLocation) { + std::shared_ptr tracker; + + ITracker ¤tTracker = ctx.currentTracker(); + // Under specific circumstances, the generator we want + // to acquire is also the current tracker. If this is + // the case, we have to avoid looking through current + // tracker's children, and instead return the current + // tracker. + // A case where this check is important is e.g. + // for (int i = 0; i < 5; ++i) { + // int n = GENERATE(1, 2); + // } + // + // without it, the code above creates 5 nested generators. + if (currentTracker.nameAndLocation() == nameAndLocation) { + auto thisTracker = currentTracker.parent().findChild(nameAndLocation); + assert(thisTracker); + assert(thisTracker->isGeneratorTracker()); + tracker = std::static_pointer_cast(thisTracker); + } else if (TestCaseTracking::ITrackerPtr childTracker = currentTracker.findChild(nameAndLocation)) { + assert(childTracker); + assert(childTracker->isGeneratorTracker()); + tracker = std::static_pointer_cast(childTracker); + } else { + tracker = std::make_shared(nameAndLocation, ctx, ¤tTracker); + currentTracker.addChild(tracker); + } -RunContext::RunContext(IConfigPtr const &_config, IStreamingReporterPtr &&reporter) - : m_runInfo(_config->name()), - m_context(getCurrentMutableContext()), - m_config(_config), - m_reporter(std::move(reporter)), - m_lastAssertionInfo{StringRef(), SourceLineInfo("", 0), StringRef(), ResultDisposition::Normal}, - m_includeSuccessfulResults( - m_config->includeSuccessfulResults() || m_reporter->getPreferences().shouldReportAllAssertions) { - m_context.setRunner(this); - m_context.setConfig(m_config); - m_context.setResultCapture(this); - m_reporter->testRunStarting(m_runInfo); -} + if (!tracker->isComplete()) { + tracker->open(); + } -RunContext::~RunContext() { - m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, aborting())); -} + return *tracker; + } -void RunContext::testGroupStarting(std::string const &testSpec, std::size_t groupIndex, std::size_t groupsCount) { - m_reporter->testGroupStarting(GroupInfo(testSpec, groupIndex, groupsCount)); -} + // TrackerBase interface + bool isGeneratorTracker() const override { return true; } -void RunContext::testGroupEnded(std::string const &testSpec, - Totals const &totals, - std::size_t groupIndex, - std::size_t groupsCount) { - m_reporter->testGroupEnded(TestGroupStats(GroupInfo(testSpec, groupIndex, groupsCount), totals, aborting())); -} + auto hasGenerator() const -> bool override { + return !!m_generator; + } -Totals RunContext::runTest(TestCase const &testCase) { - Totals prevTotals = m_totals; - - std::string redirectedCout; - std::string redirectedCerr; - - auto const &testInfo = testCase.getTestCaseInfo(); - - m_reporter->testCaseStarting(testInfo); - - m_activeTestCase = &testCase; - - ITracker &rootTracker = m_trackerContext.startRun(); - assert(rootTracker.isSectionTracker()); - static_cast(rootTracker).addInitialFilters(m_config->getSectionsToRun()); - do { - m_trackerContext.startCycle(); - m_testCaseTracker = - &SectionTracker::acquire(m_trackerContext, TestCaseTracking::NameAndLocation(testInfo.name, testInfo.lineInfo)); - runCurrentTest(redirectedCout, redirectedCerr); - } while (!m_testCaseTracker->isSuccessfullyCompleted() && !aborting()); - - Totals deltaTotals = m_totals.delta(prevTotals); - if (testInfo.expectedToFail() && deltaTotals.testCases.passed > 0) { - deltaTotals.assertions.failed++; - deltaTotals.testCases.passed--; - deltaTotals.testCases.failed++; - } - m_totals.testCases += deltaTotals.testCases; - m_reporter->testCaseEnded(TestCaseStats(testInfo, - deltaTotals, - redirectedCout, - redirectedCerr, - aborting())); - - m_activeTestCase = nullptr; - m_testCaseTracker = nullptr; - - return deltaTotals; -} + void close() override { + TrackerBase::close(); + // If a generator has a child (it is followed by a section) + // and none of its children have started, then we must wait + // until later to start consuming its values. + // This catches cases where `GENERATE` is placed between two + // `SECTION`s. + // **The check for m_children.empty cannot be removed**. + // doing so would break `GENERATE` _not_ followed by `SECTION`s. + const bool should_wait_for_child = [&]() { + // No children -> nobody to wait for + if (m_children.empty()) { + return false; + } + // If at least one child started executing, don't wait + if (std::find_if( + m_children.begin(), + m_children.end(), + [](TestCaseTracking::ITrackerPtr tracker) { + return tracker->hasStarted(); + }) != m_children.end()) { + return false; + } -IConfigPtr RunContext::config() const { - return m_config; -} + // No children have started. We need to check if they _can_ + // start, and thus we should wait for them, or they cannot + // start (due to filters), and we shouldn't wait for them + auto *parent = m_parent; + // This is safe: there is always at least one section + // tracker in a test case tracking tree + while (!parent->isSectionTracker()) { + parent = &(parent->parent()); + } + assert(parent && + "Missing root (test case) level section"); + + auto const &parentSection = + static_cast( *parent ); + auto const &filters = parentSection.getFilters(); + // No filters -> no restrictions on running sections + if (filters.empty()) { + return true; + } -IStreamingReporter &RunContext::reporter() const { - return *m_reporter; -} + for (auto const &child: m_children) { + if (child->isSectionTracker() && + std::find(filters.begin(), + filters.end(), + static_cast( *child ) + .trimmedName()) != + filters.end()) { + return true; + } + } + return false; + }(); + + // This check is a bit tricky, because m_generator->next() + // has a side-effect, where it consumes generator's current + // value, but we do not want to invoke the side-effect if + // this generator is still waiting for any child to start. + if (should_wait_for_child || + (m_runState == CompletedSuccessfully && + m_generator->next())) { + m_children.clear(); + m_runState = Executing; + } + } -void RunContext::assertionEnded(AssertionResult const &result) { - if (result.getResultType() == ResultWas::Ok) { - m_totals.assertions.passed++; - m_lastAssertionPassed = true; - } else if (!result.isOk()) { - m_lastAssertionPassed = false; - if (m_activeTestCase->getTestCaseInfo().okToFail()) - m_totals.assertions.failedButOk++; - else - m_totals.assertions.failed++; - } else { - m_lastAssertionPassed = true; - } + // IGeneratorTracker interface + auto getGenerator() const -> GeneratorBasePtr const & override { + return m_generator; + } - // We have no use for the return value (whether messages should be cleared), because messages were made scoped - // and should be let to clear themselves out. - static_cast(m_reporter->assertionEnded(AssertionStats(result, m_messages, m_totals))); + void setGenerator(GeneratorBasePtr &&generator) override { + m_generator = std::move(generator); + } + }; - if (result.getResultType() != ResultWas::Warning) - m_messageScopes.clear(); + GeneratorTracker::~GeneratorTracker() {} + } - // Reset working state - resetAssertionInfo(); - m_lastResult = result; -} -void RunContext::resetAssertionInfo() { - m_lastAssertionInfo.macroName = StringRef(); - m_lastAssertionInfo.capturedExpression = "{Unknown expression after the reported line}"_sr; -} + RunContext::RunContext(IConfigPtr const &_config, IStreamingReporterPtr &&reporter) + : m_runInfo(_config->name()), + m_context(getCurrentMutableContext()), + m_config(_config), + m_reporter(std::move(reporter)), + m_lastAssertionInfo{StringRef(), SourceLineInfo("", 0), StringRef(), ResultDisposition::Normal}, + m_includeSuccessfulResults( + m_config->includeSuccessfulResults() || m_reporter->getPreferences().shouldReportAllAssertions) { + m_context.setRunner(this); + m_context.setConfig(m_config); + m_context.setResultCapture(this); + m_reporter->testRunStarting(m_runInfo); + } -bool RunContext::sectionStarted(SectionInfo const §ionInfo, Counts &assertions) { - ITracker §ionTracker = SectionTracker::acquire(m_trackerContext, - TestCaseTracking::NameAndLocation(sectionInfo.name, - sectionInfo.lineInfo)); - if (!sectionTracker.isOpen()) - return false; - m_activeSections.push_back(§ionTracker); + RunContext::~RunContext() { + m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, aborting())); + } - m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo; + void RunContext::testGroupStarting(std::string const &testSpec, std::size_t groupIndex, std::size_t groupsCount) { + m_reporter->testGroupStarting(GroupInfo(testSpec, groupIndex, groupsCount)); + } - m_reporter->sectionStarting(sectionInfo); + void RunContext::testGroupEnded(std::string const &testSpec, + Totals const &totals, + std::size_t groupIndex, + std::size_t groupsCount) { + m_reporter->testGroupEnded(TestGroupStats(GroupInfo(testSpec, groupIndex, groupsCount), totals, aborting())); + } - assertions = m_totals.assertions; + Totals RunContext::runTest(TestCase const &testCase) { + Totals prevTotals = m_totals; - return true; -} -auto RunContext::acquireGeneratorTracker(StringRef generatorName, - SourceLineInfo const &lineInfo) -> IGeneratorTracker & { - using namespace Generators; - GeneratorTracker &tracker = GeneratorTracker::acquire(m_trackerContext, - TestCaseTracking::NameAndLocation(static_cast(generatorName), - lineInfo)); - m_lastAssertionInfo.lineInfo = lineInfo; - return tracker; -} + std::string redirectedCout; + std::string redirectedCerr; -bool RunContext::testForMissingAssertions(Counts &assertions) { - if (assertions.total() != 0) - return false; - if (!m_config->warnAboutMissingAssertions()) - return false; - if (m_trackerContext.currentTracker().hasChildren()) - return false; - m_totals.assertions.failed++; - assertions.failed++; - return true; -} + auto const &testInfo = testCase.getTestCaseInfo(); -void RunContext::sectionEnded(SectionEndInfo const &endInfo) { - Counts assertions = m_totals.assertions - endInfo.prevAssertions; - bool missingAssertions = testForMissingAssertions(assertions); + m_reporter->testCaseStarting(testInfo); - if (!m_activeSections.empty()) { - m_activeSections.back()->close(); - m_activeSections.pop_back(); - } + m_activeTestCase = &testCase; - m_reporter->sectionEnded(SectionStats(endInfo.sectionInfo, assertions, endInfo.durationInSeconds, missingAssertions)); - m_messages.clear(); - m_messageScopes.clear(); -} + ITracker &rootTracker = m_trackerContext.startRun(); + assert(rootTracker.isSectionTracker()); + static_cast(rootTracker).addInitialFilters(m_config->getSectionsToRun()); + do { + m_trackerContext.startCycle(); + m_testCaseTracker = + &SectionTracker::acquire(m_trackerContext, + TestCaseTracking::NameAndLocation(testInfo.name, testInfo.lineInfo)); + runCurrentTest(redirectedCout, redirectedCerr); + } while (!m_testCaseTracker->isSuccessfullyCompleted() && !aborting()); -void RunContext::sectionEndedEarly(SectionEndInfo const &endInfo) { - if (m_unfinishedSections.empty()) - m_activeSections.back()->fail(); - else - m_activeSections.back()->close(); - m_activeSections.pop_back(); + Totals deltaTotals = m_totals.delta(prevTotals); + if (testInfo.expectedToFail() && deltaTotals.testCases.passed > 0) { + deltaTotals.assertions.failed++; + deltaTotals.testCases.passed--; + deltaTotals.testCases.failed++; + } + m_totals.testCases += deltaTotals.testCases; + m_reporter->testCaseEnded(TestCaseStats(testInfo, + deltaTotals, + redirectedCout, + redirectedCerr, + aborting())); - m_unfinishedSections.push_back(endInfo); -} + m_activeTestCase = nullptr; + m_testCaseTracker = nullptr; + + return deltaTotals; + } + + IConfigPtr RunContext::config() const { + return m_config; + } + + IStreamingReporter &RunContext::reporter() const { + return *m_reporter; + } + + void RunContext::assertionEnded(AssertionResult const &result) { + if (result.getResultType() == ResultWas::Ok) { + m_totals.assertions.passed++; + m_lastAssertionPassed = true; + } else if (!result.isOk()) { + m_lastAssertionPassed = false; + if (m_activeTestCase->getTestCaseInfo().okToFail()) + m_totals.assertions.failedButOk++; + else + m_totals.assertions.failed++; + } else { + m_lastAssertionPassed = true; + } + + // We have no use for the return value (whether messages should be cleared), because messages were made scoped + // and should be let to clear themselves out. + static_cast(m_reporter->assertionEnded(AssertionStats(result, m_messages, m_totals))); + + if (result.getResultType() != ResultWas::Warning) + m_messageScopes.clear(); + + // Reset working state + resetAssertionInfo(); + m_lastResult = result; + } + + void RunContext::resetAssertionInfo() { + m_lastAssertionInfo.macroName = StringRef(); + m_lastAssertionInfo.capturedExpression = "{Unknown expression after the reported line}"_sr; + } + + bool RunContext::sectionStarted(SectionInfo const §ionInfo, Counts &assertions) { + ITracker §ionTracker = SectionTracker::acquire(m_trackerContext, + TestCaseTracking::NameAndLocation(sectionInfo.name, + sectionInfo.lineInfo)); + if (!sectionTracker.isOpen()) + return false; + m_activeSections.push_back(§ionTracker); + + m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo; + + m_reporter->sectionStarting(sectionInfo); + + assertions = m_totals.assertions; + + return true; + } + + auto RunContext::acquireGeneratorTracker(StringRef generatorName, + SourceLineInfo const &lineInfo) -> IGeneratorTracker & { + using namespace Generators; + GeneratorTracker &tracker = GeneratorTracker::acquire(m_trackerContext, + TestCaseTracking::NameAndLocation( + static_cast(generatorName), + lineInfo)); + m_lastAssertionInfo.lineInfo = lineInfo; + return tracker; + } + + bool RunContext::testForMissingAssertions(Counts &assertions) { + if (assertions.total() != 0) + return false; + if (!m_config->warnAboutMissingAssertions()) + return false; + if (m_trackerContext.currentTracker().hasChildren()) + return false; + m_totals.assertions.failed++; + assertions.failed++; + return true; + } + + void RunContext::sectionEnded(SectionEndInfo const &endInfo) { + Counts assertions = m_totals.assertions - endInfo.prevAssertions; + bool missingAssertions = testForMissingAssertions(assertions); + + if (!m_activeSections.empty()) { + m_activeSections.back()->close(); + m_activeSections.pop_back(); + } + + m_reporter->sectionEnded( + SectionStats(endInfo.sectionInfo, assertions, endInfo.durationInSeconds, missingAssertions)); + m_messages.clear(); + m_messageScopes.clear(); + } + + void RunContext::sectionEndedEarly(SectionEndInfo const &endInfo) { + if (m_unfinishedSections.empty()) + m_activeSections.back()->fail(); + else + m_activeSections.back()->close(); + m_activeSections.pop_back(); + + m_unfinishedSections.push_back(endInfo); + } #if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) - void RunContext::benchmarkPreparing(std::string const& name) { + void RunContext::benchmarkPreparing(std::string const& name) { m_reporter->benchmarkPreparing(name); } void RunContext::benchmarkStarting( BenchmarkInfo const& info ) { @@ -12894,273 +13640,276 @@ void RunContext::sectionEndedEarly(SectionEndInfo const &endInfo) { } #endif // CATCH_CONFIG_ENABLE_BENCHMARKING -void RunContext::pushScopedMessage(MessageInfo const &message) { - m_messages.push_back(message); -} + void RunContext::pushScopedMessage(MessageInfo const &message) { + m_messages.push_back(message); + } -void RunContext::popScopedMessage(MessageInfo const &message) { - m_messages.erase(std::remove(m_messages.begin(), m_messages.end(), message), m_messages.end()); -} + void RunContext::popScopedMessage(MessageInfo const &message) { + m_messages.erase(std::remove(m_messages.begin(), m_messages.end(), message), m_messages.end()); + } -void RunContext::emplaceUnscopedMessage(MessageBuilder const &builder) { - m_messageScopes.emplace_back(builder); -} + void RunContext::emplaceUnscopedMessage(MessageBuilder const &builder) { + m_messageScopes.emplace_back(builder); + } -std::string RunContext::getCurrentTestName() const { - return m_activeTestCase - ? m_activeTestCase->getTestCaseInfo().name - : std::string(); -} + std::string RunContext::getCurrentTestName() const { + return m_activeTestCase + ? m_activeTestCase->getTestCaseInfo().name + : std::string(); + } -const AssertionResult *RunContext::getLastResult() const { - return &(*m_lastResult); -} + const AssertionResult *RunContext::getLastResult() const { + return &(*m_lastResult); + } -void RunContext::exceptionEarlyReported() { - m_shouldReportUnexpected = false; -} + void RunContext::exceptionEarlyReported() { + m_shouldReportUnexpected = false; + } -void RunContext::handleFatalErrorCondition(StringRef message) { - // First notify reporter that bad things happened - m_reporter->fatalErrorEncountered(message); - - // Don't rebuild the result -- the stringification itself can cause more fatal errors - // Instead, fake a result data. - AssertionResultData tempResult(ResultWas::FatalErrorCondition, {false}); - tempResult.message = static_cast(message); - AssertionResult result(m_lastAssertionInfo, tempResult); - - assertionEnded(result); - - handleUnfinishedSections(); - - // Recreate section for test case (as we will lose the one that was in scope) - auto const &testCaseInfo = m_activeTestCase->getTestCaseInfo(); - SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name); - - Counts assertions; - assertions.failed = 1; - SectionStats testCaseSectionStats(testCaseSection, assertions, 0, false); - m_reporter->sectionEnded(testCaseSectionStats); - - auto const &testInfo = m_activeTestCase->getTestCaseInfo(); - - Totals deltaTotals; - deltaTotals.testCases.failed = 1; - deltaTotals.assertions.failed = 1; - m_reporter->testCaseEnded(TestCaseStats(testInfo, - deltaTotals, - std::string(), - std::string(), - false)); - m_totals.testCases.failed++; - testGroupEnded(std::string(), m_totals, 1, 1); - m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, false)); -} + void RunContext::handleFatalErrorCondition(StringRef message) { + // First notify reporter that bad things happened + m_reporter->fatalErrorEncountered(message); + + // Don't rebuild the result -- the stringification itself can cause more fatal errors + // Instead, fake a result data. + AssertionResultData tempResult(ResultWas::FatalErrorCondition, {false}); + tempResult.message = static_cast(message); + AssertionResult result(m_lastAssertionInfo, tempResult); + + assertionEnded(result); + + handleUnfinishedSections(); + + // Recreate section for test case (as we will lose the one that was in scope) + auto const &testCaseInfo = m_activeTestCase->getTestCaseInfo(); + SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name); + + Counts assertions; + assertions.failed = 1; + SectionStats testCaseSectionStats(testCaseSection, assertions, 0, false); + m_reporter->sectionEnded(testCaseSectionStats); + + auto const &testInfo = m_activeTestCase->getTestCaseInfo(); + + Totals deltaTotals; + deltaTotals.testCases.failed = 1; + deltaTotals.assertions.failed = 1; + m_reporter->testCaseEnded(TestCaseStats(testInfo, + deltaTotals, + std::string(), + std::string(), + false)); + m_totals.testCases.failed++; + testGroupEnded(std::string(), m_totals, 1, 1); + m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, false)); + } -bool RunContext::lastAssertionPassed() { - return m_lastAssertionPassed; -} + bool RunContext::lastAssertionPassed() { + return m_lastAssertionPassed; + } -void RunContext::assertionPassed() { - m_lastAssertionPassed = true; - ++m_totals.assertions.passed; - resetAssertionInfo(); - m_messageScopes.clear(); -} + void RunContext::assertionPassed() { + m_lastAssertionPassed = true; + ++m_totals.assertions.passed; + resetAssertionInfo(); + m_messageScopes.clear(); + } + + bool RunContext::aborting() const { + return m_totals.assertions.failed >= static_cast(m_config->abortAfter()); + } + + void RunContext::runCurrentTest(std::string &redirectedCout, std::string &redirectedCerr) { + auto const &testCaseInfo = m_activeTestCase->getTestCaseInfo(); + SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name); + m_reporter->sectionStarting(testCaseSection); + Counts prevAssertions = m_totals.assertions; + double duration = 0; + m_shouldReportUnexpected = true; + m_lastAssertionInfo = {"TEST_CASE"_sr, testCaseInfo.lineInfo, StringRef(), ResultDisposition::Normal}; + + seedRng(*m_config); + + Timer timer; + CATCH_TRY { + if (m_reporter->getPreferences().shouldRedirectStdOut) { +#if !defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT) + RedirectedStreams redirectedStreams(redirectedCout, redirectedCerr); + + timer.start(); + invokeActiveTestCase(); +#else + OutputRedirect r(redirectedCout, redirectedCerr); + timer.start(); + invokeActiveTestCase(); +#endif + } else { + timer.start(); + invokeActiveTestCase(); + } + duration = timer.getElapsedSeconds(); + } CATCH_CATCH_ANON (TestFailureException &) { + // This just means the test was aborted due to failure + } CATCH_CATCH_ALL { + // Under CATCH_CONFIG_FAST_COMPILE, unexpected exceptions under REQUIRE assertions + // are reported without translation at the point of origin. + if (m_shouldReportUnexpected) { + AssertionReaction dummyReaction; + handleUnexpectedInflightException(m_lastAssertionInfo, translateActiveException(), dummyReaction); + } + } + Counts assertions = m_totals.assertions - prevAssertions; + bool missingAssertions = testForMissingAssertions(assertions); + + m_testCaseTracker->close(); + handleUnfinishedSections(); + m_messages.clear(); + m_messageScopes.clear(); + + SectionStats testCaseSectionStats(testCaseSection, assertions, duration, missingAssertions); + m_reporter->sectionEnded(testCaseSectionStats); + } -bool RunContext::aborting() const { - return m_totals.assertions.failed >= static_cast(m_config->abortAfter()); -} + void RunContext::invokeActiveTestCase() { + FatalConditionHandlerGuard _(&m_fatalConditionhandler); + m_activeTestCase->invoke(); + } -void RunContext::runCurrentTest(std::string &redirectedCout, std::string &redirectedCerr) { - auto const &testCaseInfo = m_activeTestCase->getTestCaseInfo(); - SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name); - m_reporter->sectionStarting(testCaseSection); - Counts prevAssertions = m_totals.assertions; - double duration = 0; - m_shouldReportUnexpected = true; - m_lastAssertionInfo = {"TEST_CASE"_sr, testCaseInfo.lineInfo, StringRef(), ResultDisposition::Normal}; + void RunContext::handleUnfinishedSections() { + // If sections ended prematurely due to an exception we stored their + // infos here so we can tear them down outside the unwind process. + for (auto it = m_unfinishedSections.rbegin(), + itEnd = m_unfinishedSections.rend(); + it != itEnd; + ++it) + sectionEnded(*it); + m_unfinishedSections.clear(); + } - seedRng(*m_config); + void RunContext::handleExpr( + AssertionInfo const &info, + ITransientExpression const &expr, + AssertionReaction &reaction + ) { + m_reporter->assertionStarting(info); - Timer timer; - CATCH_TRY { - if (m_reporter->getPreferences().shouldRedirectStdOut) { -#if !defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT) - RedirectedStreams redirectedStreams(redirectedCout, redirectedCerr); + bool negated = isFalseTest(info.resultDisposition); + bool result = expr.getResult() != negated; - timer.start(); - invokeActiveTestCase(); -#else - OutputRedirect r(redirectedCout, redirectedCerr); - timer.start(); - invokeActiveTestCase(); -#endif - } else { - timer.start(); - invokeActiveTestCase(); - } - duration = timer.getElapsedSeconds(); - } CATCH_CATCH_ANON (TestFailureException &) { - // This just means the test was aborted due to failure - } CATCH_CATCH_ALL { - // Under CATCH_CONFIG_FAST_COMPILE, unexpected exceptions under REQUIRE assertions - // are reported without translation at the point of origin. - if (m_shouldReportUnexpected) { - AssertionReaction dummyReaction; - handleUnexpectedInflightException(m_lastAssertionInfo, translateActiveException(), dummyReaction); - } - } - Counts assertions = m_totals.assertions - prevAssertions; - bool missingAssertions = testForMissingAssertions(assertions); - - m_testCaseTracker->close(); - handleUnfinishedSections(); - m_messages.clear(); - m_messageScopes.clear(); - - SectionStats testCaseSectionStats(testCaseSection, assertions, duration, missingAssertions); - m_reporter->sectionEnded(testCaseSectionStats); -} + if (result) { + if (!m_includeSuccessfulResults) { + assertionPassed(); + } else { + reportExpr(info, ResultWas::Ok, &expr, negated); + } + } else { + reportExpr(info, ResultWas::ExpressionFailed, &expr, negated); + populateReaction(reaction); + } + } -void RunContext::invokeActiveTestCase() { - FatalConditionHandlerGuard _(&m_fatalConditionhandler); - m_activeTestCase->invoke(); -} + void RunContext::reportExpr( + AssertionInfo const &info, + ResultWas::OfType resultType, + ITransientExpression const *expr, + bool negated) { -void RunContext::handleUnfinishedSections() { - // If sections ended prematurely due to an exception we stored their - // infos here so we can tear them down outside the unwind process. - for (auto it = m_unfinishedSections.rbegin(), - itEnd = m_unfinishedSections.rend(); - it != itEnd; - ++it) - sectionEnded(*it); - m_unfinishedSections.clear(); -} + m_lastAssertionInfo = info; + AssertionResultData data(resultType, LazyExpression(negated)); -void RunContext::handleExpr( - AssertionInfo const &info, - ITransientExpression const &expr, - AssertionReaction &reaction -) { - m_reporter->assertionStarting(info); + AssertionResult assertionResult{info, data}; + assertionResult.m_resultData.lazyExpression.m_transientExpression = expr; - bool negated = isFalseTest(info.resultDisposition); - bool result = expr.getResult() != negated; + assertionEnded(assertionResult); + } - if (result) { - if (!m_includeSuccessfulResults) { - assertionPassed(); - } else { - reportExpr(info, ResultWas::Ok, &expr, negated); + void RunContext::handleMessage( + AssertionInfo const &info, + ResultWas::OfType resultType, + StringRef const &message, + AssertionReaction &reaction + ) { + m_reporter->assertionStarting(info); + + m_lastAssertionInfo = info; + + AssertionResultData data(resultType, LazyExpression(false)); + data.message = static_cast(message); + AssertionResult assertionResult{m_lastAssertionInfo, data}; + assertionEnded(assertionResult); + if (!assertionResult.isOk()) + populateReaction(reaction); } - } else { - reportExpr(info, ResultWas::ExpressionFailed, &expr, negated); - populateReaction(reaction); - } -} -void RunContext::reportExpr( - AssertionInfo const &info, - ResultWas::OfType resultType, - ITransientExpression const *expr, - bool negated) { - m_lastAssertionInfo = info; - AssertionResultData data(resultType, LazyExpression(negated)); + void RunContext::handleUnexpectedExceptionNotThrown( + AssertionInfo const &info, + AssertionReaction &reaction + ) { + handleNonExpr(info, Catch::ResultWas::DidntThrowException, reaction); + } - AssertionResult assertionResult{info, data}; - assertionResult.m_resultData.lazyExpression.m_transientExpression = expr; + void RunContext::handleUnexpectedInflightException( + AssertionInfo const &info, + std::string const &message, + AssertionReaction &reaction + ) { + m_lastAssertionInfo = info; + + AssertionResultData data(ResultWas::ThrewException, LazyExpression(false)); + data.message = message; + AssertionResult assertionResult{info, data}; + assertionEnded(assertionResult); + populateReaction(reaction); + } - assertionEnded(assertionResult); -} + void RunContext::populateReaction(AssertionReaction &reaction) { + reaction.shouldDebugBreak = m_config->shouldDebugBreak(); + reaction.shouldThrow = aborting() || (m_lastAssertionInfo.resultDisposition & ResultDisposition::Normal); + } -void RunContext::handleMessage( - AssertionInfo const &info, - ResultWas::OfType resultType, - StringRef const &message, - AssertionReaction &reaction -) { - m_reporter->assertionStarting(info); - - m_lastAssertionInfo = info; - - AssertionResultData data(resultType, LazyExpression(false)); - data.message = static_cast(message); - AssertionResult assertionResult{m_lastAssertionInfo, data}; - assertionEnded(assertionResult); - if (!assertionResult.isOk()) - populateReaction(reaction); -} -void RunContext::handleUnexpectedExceptionNotThrown( - AssertionInfo const &info, - AssertionReaction &reaction -) { - handleNonExpr(info, Catch::ResultWas::DidntThrowException, reaction); -} + void RunContext::handleIncomplete( + AssertionInfo const &info + ) { + m_lastAssertionInfo = info; -void RunContext::handleUnexpectedInflightException( - AssertionInfo const &info, - std::string const &message, - AssertionReaction &reaction -) { - m_lastAssertionInfo = info; - - AssertionResultData data(ResultWas::ThrewException, LazyExpression(false)); - data.message = message; - AssertionResult assertionResult{info, data}; - assertionEnded(assertionResult); - populateReaction(reaction); -} + AssertionResultData data(ResultWas::ThrewException, LazyExpression(false)); + data.message = "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE"; + AssertionResult assertionResult{info, data}; + assertionEnded(assertionResult); + } -void RunContext::populateReaction(AssertionReaction &reaction) { - reaction.shouldDebugBreak = m_config->shouldDebugBreak(); - reaction.shouldThrow = aborting() || (m_lastAssertionInfo.resultDisposition & ResultDisposition::Normal); -} + void RunContext::handleNonExpr( + AssertionInfo const &info, + ResultWas::OfType resultType, + AssertionReaction &reaction + ) { + m_lastAssertionInfo = info; -void RunContext::handleIncomplete( - AssertionInfo const &info -) { - m_lastAssertionInfo = info; + AssertionResultData data(resultType, LazyExpression(false)); + AssertionResult assertionResult{info, data}; + assertionEnded(assertionResult); - AssertionResultData data(ResultWas::ThrewException, LazyExpression(false)); - data.message = "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE"; - AssertionResult assertionResult{info, data}; - assertionEnded(assertionResult); -} -void RunContext::handleNonExpr( - AssertionInfo const &info, - ResultWas::OfType resultType, - AssertionReaction &reaction -) { - m_lastAssertionInfo = info; - - AssertionResultData data(resultType, LazyExpression(false)); - AssertionResult assertionResult{info, data}; - assertionEnded(assertionResult); - - if (!assertionResult.isOk()) - populateReaction(reaction); -} + if (!assertionResult.isOk()) + populateReaction(reaction); + } -IResultCapture &getResultCapture() { - if (auto *capture = getCurrentContext().getResultCapture()) - return *capture; - else - CATCH_INTERNAL_ERROR("No result capture instance"); -} + IResultCapture &getResultCapture() { + if (auto *capture = getCurrentContext().getResultCapture()) + return *capture; + else + CATCH_INTERNAL_ERROR("No result capture instance"); + } -void seedRng(IConfig const &config) { - if (config.rngSeed() != 0) { - std::srand(config.rngSeed()); - rng().seed(config.rngSeed()); - } -} + void seedRng(IConfig const &config) { + if (config.rngSeed() != 0) { + std::srand(config.rngSeed()); + rng().seed(config.rngSeed()); + } + } -unsigned int rngSeed() { - return getCurrentContext().getConfig()->rngSeed(); -} + unsigned int rngSeed() { + return getCurrentContext().getConfig()->rngSeed(); + } } // end catch_run_context.cpp @@ -13168,26 +13917,26 @@ unsigned int rngSeed() { namespace Catch { -Section::Section(SectionInfo const &info) - : m_info(info), - m_sectionIncluded(getResultCapture().sectionStarted(m_info, m_assertions)) { - m_timer.start(); -} + Section::Section(SectionInfo const &info) + : m_info(info), + m_sectionIncluded(getResultCapture().sectionStarted(m_info, m_assertions)) { + m_timer.start(); + } -Section::~Section() { - if (m_sectionIncluded) { - SectionEndInfo endInfo{m_info, m_assertions, m_timer.getElapsedSeconds()}; - if (uncaught_exceptions()) - getResultCapture().sectionEndedEarly(endInfo); - else - getResultCapture().sectionEnded(endInfo); - } -} + Section::~Section() { + if (m_sectionIncluded) { + SectionEndInfo endInfo{m_info, m_assertions, m_timer.getElapsedSeconds()}; + if (uncaught_exceptions()) + getResultCapture().sectionEndedEarly(endInfo); + else + getResultCapture().sectionEnded(endInfo); + } + } // This indicates whether the section should be executed or not -Section::operator bool() const { - return m_sectionIncluded; -} + Section::operator bool() const { + return m_sectionIncluded; + } } // end namespace Catch // end catch_section.cpp @@ -13195,11 +13944,11 @@ Section::operator bool() const { namespace Catch { -SectionInfo::SectionInfo - (SourceLineInfo const &_lineInfo, - std::string const &_name) - : name(_name), - lineInfo(_lineInfo) {} + SectionInfo::SectionInfo + (SourceLineInfo const &_lineInfo, + std::string const &_name) + : name(_name), + lineInfo(_lineInfo) {} } // end namespace Catch // end catch_section_info.cpp @@ -13211,46 +13960,53 @@ SectionInfo::SectionInfo namespace Catch { -class Session : NonCopyable { - public: + class Session : NonCopyable { + public: + + Session(); + + ~Session() override; + + void showHelp() const; - Session(); - ~Session() override; + void libIdentify(); - void showHelp() const; - void libIdentify(); + int applyCommandLine(int argc, char const *const *argv); - int applyCommandLine(int argc, char const *const *argv); #if defined(CATCH_CONFIG_WCHAR) && defined(_WIN32) && defined(UNICODE) - int applyCommandLine( int argc, wchar_t const * const * argv ); + int applyCommandLine( int argc, wchar_t const * const * argv ); #endif - void useConfigData(ConfigData const &configData); - - template - int run(int argc, CharT const *const argv[]) { - if (m_startupExceptions) - return 1; - int returnCode = applyCommandLine(argc, argv); - if (returnCode == 0) - returnCode = run(); - return returnCode; - } - - int run(); - - clara::Parser const &cli() const; - void cli(clara::Parser const &newParser); - ConfigData &configData(); - Config &config(); - private: - int runInternal(); - - clara::Parser m_cli; - ConfigData m_configData; - std::shared_ptr m_config; - bool m_startupExceptions = false; -}; + void useConfigData(ConfigData const &configData); + + template + int run(int argc, CharT const *const argv[]) { + if (m_startupExceptions) + return 1; + int returnCode = applyCommandLine(argc, argv); + if (returnCode == 0) + returnCode = run(); + return returnCode; + } + + int run(); + + clara::Parser const &cli() const; + + void cli(clara::Parser const &newParser); + + ConfigData &configData(); + + Config &config(); + + private: + int runInternal(); + + clara::Parser m_cli; + ConfigData m_configData; + std::shared_ptr m_config; + bool m_startupExceptions = false; + }; } // end namespace Catch @@ -13262,27 +14018,29 @@ class Session : NonCopyable { namespace Catch { // Versioning information -struct Version { - Version(Version const &) = delete; - Version &operator=(Version const &) = delete; - Version(unsigned int _majorVersion, - unsigned int _minorVersion, - unsigned int _patchNumber, - char const *const _branchName, - unsigned int _buildNumber); - - unsigned int const majorVersion; - unsigned int const minorVersion; - unsigned int const patchNumber; - - // buildNumber is only used if branchName is not null - char const *const branchName; - unsigned int const buildNumber; - - friend std::ostream &operator<<(std::ostream &os, Version const &version); -}; + struct Version { + Version(Version const &) = delete; + + Version &operator=(Version const &) = delete; + + Version(unsigned int _majorVersion, + unsigned int _minorVersion, + unsigned int _patchNumber, + char const *const _branchName, + unsigned int _buildNumber); + + unsigned int const majorVersion; + unsigned int const minorVersion; + unsigned int const patchNumber; -Version const &libraryVersion(); + // buildNumber is only used if branchName is not null + char const *const branchName; + unsigned int const buildNumber; + + friend std::ostream &operator<<(std::ostream &os, Version const &version); + }; + + Version const &libraryVersion(); } // end catch_version.h @@ -13293,192 +14051,194 @@ Version const &libraryVersion(); namespace Catch { -namespace { -const int MaxExitCode = 255; + namespace { + const int MaxExitCode = 255; -IStreamingReporterPtr createReporter(std::string const &reporterName, IConfigPtr const &config) { - auto reporter = Catch::getRegistryHub().getReporterRegistry().create(reporterName, config); - CATCH_ENFORCE(reporter, "No reporter registered with name: '" << reporterName << "'"); + IStreamingReporterPtr createReporter(std::string const &reporterName, IConfigPtr const &config) { + auto reporter = Catch::getRegistryHub().getReporterRegistry().create(reporterName, config); + CATCH_ENFORCE(reporter, "No reporter registered with name: '" << reporterName << "'"); - return reporter; -} + return reporter; + } -IStreamingReporterPtr makeReporter(std::shared_ptr const &config) { - if (Catch::getRegistryHub().getReporterRegistry().getListeners().empty()) { - return createReporter(config->getReporterName(), config); - } - - // On older platforms, returning std::unique_ptr - // when the return type is std::unique_ptr - // doesn't compile without a std::move call. However, this causes - // a warning on newer platforms. Thus, we have to work around - // it a bit and downcast the pointer manually. - auto ret = std::unique_ptr(new ListeningReporter); - auto &multi = static_cast(*ret); - auto const &listeners = Catch::getRegistryHub().getReporterRegistry().getListeners(); - for (auto const &listener : listeners) { - multi.addListener(listener->create(Catch::ReporterConfig(config))); - } - multi.addReporter(createReporter(config->getReporterName(), config)); - return ret; -} + IStreamingReporterPtr makeReporter(std::shared_ptr const &config) { + if (Catch::getRegistryHub().getReporterRegistry().getListeners().empty()) { + return createReporter(config->getReporterName(), config); + } -class TestGroup { - public: - explicit TestGroup(std::shared_ptr const &config) - : m_config{config}, m_context{config, makeReporter(config)} { - auto const &allTestCases = getAllTestCasesSorted(*m_config); - m_matches = m_config->testSpec().matchesByFilter(allTestCases, *m_config); - auto const &invalidArgs = m_config->testSpec().getInvalidArgs(); - - if (m_matches.empty() && invalidArgs.empty()) { - for (auto const &test : allTestCases) - if (!test.isHidden()) - m_tests.emplace(&test); - } else { - for (auto const &match : m_matches) - m_tests.insert(match.tests.begin(), match.tests.end()); - } - } + // On older platforms, returning std::unique_ptr + // when the return type is std::unique_ptr + // doesn't compile without a std::move call. However, this causes + // a warning on newer platforms. Thus, we have to work around + // it a bit and downcast the pointer manually. + auto ret = std::unique_ptr(new ListeningReporter); + auto &multi = static_cast(*ret); + auto const &listeners = Catch::getRegistryHub().getReporterRegistry().getListeners(); + for (auto const &listener: listeners) { + multi.addListener(listener->create(Catch::ReporterConfig(config))); + } + multi.addReporter(createReporter(config->getReporterName(), config)); + return ret; + } - Totals execute() { - auto const &invalidArgs = m_config->testSpec().getInvalidArgs(); - Totals totals; - m_context.testGroupStarting(m_config->name(), 1, 1); - for (auto const &testCase : m_tests) { - if (!m_context.aborting()) - totals += m_context.runTest(*testCase); - else - m_context.reporter().skipTest(*testCase); - } + class TestGroup { + public: + explicit TestGroup(std::shared_ptr const &config) + : m_config{config}, m_context{config, makeReporter(config)} { + auto const &allTestCases = getAllTestCasesSorted(*m_config); + m_matches = m_config->testSpec().matchesByFilter(allTestCases, *m_config); + auto const &invalidArgs = m_config->testSpec().getInvalidArgs(); + + if (m_matches.empty() && invalidArgs.empty()) { + for (auto const &test: allTestCases) + if (!test.isHidden()) + m_tests.emplace(&test); + } else { + for (auto const &match: m_matches) + m_tests.insert(match.tests.begin(), match.tests.end()); + } + } - for (auto const &match : m_matches) { - if (match.tests.empty()) { - m_context.reporter().noMatchingTestCases(match.name); - totals.error = -1; - } - } + Totals execute() { + auto const &invalidArgs = m_config->testSpec().getInvalidArgs(); + Totals totals; + m_context.testGroupStarting(m_config->name(), 1, 1); + for (auto const &testCase: m_tests) { + if (!m_context.aborting()) + totals += m_context.runTest(*testCase); + else + m_context.reporter().skipTest(*testCase); + } - if (!invalidArgs.empty()) { - for (auto const &invalidArg : invalidArgs) - m_context.reporter().reportInvalidArguments(invalidArg); - } + for (auto const &match: m_matches) { + if (match.tests.empty()) { + m_context.reporter().noMatchingTestCases(match.name); + totals.error = -1; + } + } - m_context.testGroupEnded(m_config->name(), totals, 1, 1); - return totals; - } + if (!invalidArgs.empty()) { + for (auto const &invalidArg: invalidArgs) + m_context.reporter().reportInvalidArguments(invalidArg); + } - private: - using Tests = std::set; + m_context.testGroupEnded(m_config->name(), totals, 1, 1); + return totals; + } - std::shared_ptr m_config; - RunContext m_context; - Tests m_tests; - TestSpec::Matches m_matches; -}; + private: + using Tests = std::set; -void applyFilenamesAsTags(Catch::IConfig const &config) { - auto &tests = const_cast &>(getAllTestCasesSorted(config)); - for (auto &testCase : tests) { - auto tags = testCase.tags; + std::shared_ptr m_config; + RunContext m_context; + Tests m_tests; + TestSpec::Matches m_matches; + }; - std::string filename = testCase.lineInfo.file; - auto lastSlash = filename.find_last_of("\\/"); - if (lastSlash != std::string::npos) { - filename.erase(0, lastSlash); - filename[0] = '#'; - } else { - filename.insert(0, "#"); - } + void applyFilenamesAsTags(Catch::IConfig const &config) { + auto &tests = const_cast &>(getAllTestCasesSorted(config)); + for (auto &testCase: tests) { + auto tags = testCase.tags; - auto lastDot = filename.find_last_of('.'); - if (lastDot != std::string::npos) { - filename.erase(lastDot); - } + std::string filename = testCase.lineInfo.file; + auto lastSlash = filename.find_last_of("\\/"); + if (lastSlash != std::string::npos) { + filename.erase(0, lastSlash); + filename[0] = '#'; + } else { + filename.insert(0, "#"); + } - tags.push_back(std::move(filename)); - setTags(testCase, tags); - } -} + auto lastDot = filename.find_last_of('.'); + if (lastDot != std::string::npos) { + filename.erase(lastDot); + } -} // anon namespace + tags.push_back(std::move(filename)); + setTags(testCase, tags); + } + } + + } // anon namespace -Session::Session() { - static bool alreadyInstantiated = false; - if (alreadyInstantiated) { - CATCH_TRY { CATCH_INTERNAL_ERROR("Only one instance of Catch::Session can ever be used"); } - CATCH_CATCH_ALL { getMutableRegistryHub().registerStartupException(); } - } + Session::Session() { + static bool alreadyInstantiated = false; + if (alreadyInstantiated) { + CATCH_TRY { CATCH_INTERNAL_ERROR("Only one instance of Catch::Session can ever be used"); } + CATCH_CATCH_ALL { getMutableRegistryHub().registerStartupException(); } + } - // There cannot be exceptions at startup in no-exception mode. + // There cannot be exceptions at startup in no-exception mode. #if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) - const auto &exceptions = getRegistryHub().getStartupExceptionRegistry().getExceptions(); - if (!exceptions.empty()) { - config(); - getCurrentMutableContext().setConfig(m_config); - - m_startupExceptions = true; - Colour colourGuard(Colour::Red); - Catch::cerr() << "Errors occurred during startup!" << '\n'; - // iterate over all exceptions and notify user - for (const auto &ex_ptr : exceptions) { - try { - std::rethrow_exception(ex_ptr); - } catch (std::exception const &ex) { - Catch::cerr() << Column(ex.what()).indent(2) << '\n'; - } - } - } + const auto &exceptions = getRegistryHub().getStartupExceptionRegistry().getExceptions(); + if (!exceptions.empty()) { + config(); + getCurrentMutableContext().setConfig(m_config); + + m_startupExceptions = true; + Colour colourGuard(Colour::Red); + Catch::cerr() << "Errors occurred during startup!" << '\n'; + // iterate over all exceptions and notify user + for (const auto &ex_ptr: exceptions) { + try { + std::rethrow_exception(ex_ptr); + } catch (std::exception const &ex) { + Catch::cerr() << Column(ex.what()).indent(2) << '\n'; + } + } + } #endif - alreadyInstantiated = true; - m_cli = makeCommandLineParser(m_configData); -} -Session::~Session() { - Catch::cleanUp(); -} + alreadyInstantiated = true; + m_cli = makeCommandLineParser(m_configData); + } -void Session::showHelp() const { - Catch::cout() - << "\nCatch v" << libraryVersion() << "\n" - << m_cli << std::endl - << "For more detailed usage please see the project docs\n" << std::endl; -} -void Session::libIdentify() { - Catch::cout() - << std::left << std::setw(16) << "description: " << "A Catch2 test executable\n" - << std::left << std::setw(16) << "category: " << "testframework\n" - << std::left << std::setw(16) << "framework: " << "Catch Test\n" - << std::left << std::setw(16) << "version: " << libraryVersion() << std::endl; -} + Session::~Session() { + Catch::cleanUp(); + } -int Session::applyCommandLine(int argc, char const *const *argv) { - if (m_startupExceptions) - return 1; - - auto result = m_cli.parse(clara::Args(argc, argv)); - if (!result) { - config(); - getCurrentMutableContext().setConfig(m_config); - Catch::cerr() - << Colour(Colour::Red) - << "\nError(s) in input:\n" - << Column(result.errorMessage()).indent(2) - << "\n\n"; - Catch::cerr() << "Run with -? for usage\n" << std::endl; - return MaxExitCode; - } - - if (m_configData.showHelp) - showHelp(); - if (m_configData.libIdentify) - libIdentify(); - m_config.reset(); - return 0; -} + void Session::showHelp() const { + Catch::cout() + << "\nCatch v" << libraryVersion() << "\n" + << m_cli << std::endl + << "For more detailed usage please see the project docs\n" << std::endl; + } + + void Session::libIdentify() { + Catch::cout() + << std::left << std::setw(16) << "description: " << "A Catch2 test executable\n" + << std::left << std::setw(16) << "category: " << "testframework\n" + << std::left << std::setw(16) << "framework: " << "Catch Test\n" + << std::left << std::setw(16) << "version: " << libraryVersion() << std::endl; + } + + int Session::applyCommandLine(int argc, char const *const *argv) { + if (m_startupExceptions) + return 1; + + auto result = m_cli.parse(clara::Args(argc, argv)); + if (!result) { + config(); + getCurrentMutableContext().setConfig(m_config); + Catch::cerr() + << Colour(Colour::Red) + << "\nError(s) in input:\n" + << Column(result.errorMessage()).indent(2) + << "\n\n"; + Catch::cerr() << "Run with -? for usage\n" << std::endl; + return MaxExitCode; + } + + if (m_configData.showHelp) + showHelp(); + if (m_configData.libIdentify) + libIdentify(); + m_config.reset(); + return 0; + } #if defined(CATCH_CONFIG_WCHAR) && defined(_WIN32) && defined(UNICODE) - int Session::applyCommandLine( int argc, wchar_t const * const * argv ) { + int Session::applyCommandLine( int argc, wchar_t const * const * argv ) { char **utf8Argv = new char *[ argc ]; @@ -13501,77 +14261,80 @@ int Session::applyCommandLine(int argc, char const *const *argv) { } #endif -void Session::useConfigData(ConfigData const &configData) { - m_configData = configData; - m_config.reset(); -} + void Session::useConfigData(ConfigData const &configData) { + m_configData = configData; + m_config.reset(); + } -int Session::run() { - if ((m_configData.waitForKeypress & WaitForKeypress::BeforeStart) != 0) { - Catch::cout() << "...waiting for enter/ return before starting" << std::endl; - static_cast(std::getchar()); - } - int exitCode = runInternal(); - if ((m_configData.waitForKeypress & WaitForKeypress::BeforeExit) != 0) { - Catch::cout() << "...waiting for enter/ return before exiting, with code: " << exitCode << std::endl; - static_cast(std::getchar()); - } - return exitCode; -} + int Session::run() { + if ((m_configData.waitForKeypress & WaitForKeypress::BeforeStart) != 0) { + Catch::cout() << "...waiting for enter/ return before starting" << std::endl; + static_cast(std::getchar()); + } + int exitCode = runInternal(); + if ((m_configData.waitForKeypress & WaitForKeypress::BeforeExit) != 0) { + Catch::cout() << "...waiting for enter/ return before exiting, with code: " << exitCode << std::endl; + static_cast(std::getchar()); + } + return exitCode; + } -clara::Parser const &Session::cli() const { - return m_cli; -} -void Session::cli(clara::Parser const &newParser) { - m_cli = newParser; -} -ConfigData &Session::configData() { - return m_configData; -} -Config &Session::config() { - if (!m_config) - m_config = std::make_shared(m_configData); - return *m_config; -} + clara::Parser const &Session::cli() const { + return m_cli; + } + + void Session::cli(clara::Parser const &newParser) { + m_cli = newParser; + } + + ConfigData &Session::configData() { + return m_configData; + } + + Config &Session::config() { + if (!m_config) + m_config = std::make_shared(m_configData); + return *m_config; + } -int Session::runInternal() { - if (m_startupExceptions) - return 1; + int Session::runInternal() { + if (m_startupExceptions) + return 1; - if (m_configData.showHelp || m_configData.libIdentify) { - return 0; - } + if (m_configData.showHelp || m_configData.libIdentify) { + return 0; + } - CATCH_TRY { - config(); // Force config to be constructed + CATCH_TRY { + config(); // Force config to be constructed - seedRng(*m_config); + seedRng(*m_config); - if (m_configData.filenamesAsTags) - applyFilenamesAsTags(*m_config); + if (m_configData.filenamesAsTags) + applyFilenamesAsTags(*m_config); - // Handle list request - if (Option listed = list(m_config)) - return (std::min) (MaxExitCode, static_cast(*listed)); + // Handle list request + if (Option listed = list(m_config)) + return (std::min) (MaxExitCode, static_cast(*listed)); - TestGroup tests{m_config}; - auto const totals = tests.execute(); + TestGroup tests{m_config}; + auto const totals = tests.execute(); - if (m_config->warnAboutNoTests() && totals.error == -1) - return 2; + if (m_config->warnAboutNoTests() && totals.error == -1) + return 2; - // Note that on unices only the lower 8 bits are usually used, clamping - // the return value to 255 prevents false negative when some multiple - // of 256 tests has failed - return (std::min) (MaxExitCode, (std::max) (totals.error, static_cast(totals.assertions.failed))); - } + // Note that on unices only the lower 8 bits are usually used, clamping + // the return value to 255 prevents false negative when some multiple + // of 256 tests has failed + return (std::min) (MaxExitCode, (std::max) (totals.error, static_cast(totals.assertions.failed))); + } #if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) - catch (std::exception &ex) { - Catch::cerr() << ex.what() << std::endl; - return MaxExitCode; - } + catch (std::exception &ex) { + Catch::cerr() << ex.what() << std::endl; + return MaxExitCode; + } #endif -} + } } // end namespace Catch // end catch_session.cpp @@ -13581,27 +14344,28 @@ int Session::runInternal() { namespace Catch { -namespace { -static auto getSingletons() -> std::vector *& { - static std::vector *g_singletons = nullptr; - if (!g_singletons) - g_singletons = new std::vector(); - return g_singletons; -} -} + namespace { + static auto getSingletons() -> std::vector *& { + static std::vector *g_singletons = nullptr; + if (!g_singletons) + g_singletons = new std::vector(); + return g_singletons; + } + } -ISingleton::~ISingleton() {} + ISingleton::~ISingleton() {} -void addSingleton(ISingleton *singleton) { - getSingletons()->push_back(singleton); -} -void cleanupSingletons() { - auto &singletons = getSingletons(); - for (auto singleton : *singletons) - delete singleton; - delete singletons; - singletons = nullptr; -} + void addSingleton(ISingleton *singleton) { + getSingletons()->push_back(singleton); + } + + void cleanupSingletons() { + auto &singletons = getSingletons(); + for (auto singleton: *singletons) + delete singleton; + delete singletons; + singletons = nullptr; + } } // namespace Catch // end catch_singletons.cpp @@ -13609,18 +14373,18 @@ void cleanupSingletons() { #if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) namespace Catch { -void StartupExceptionRegistry::add(std::exception_ptr const &exception) noexcept { - CATCH_TRY { - m_exceptions.push_back(exception); - } CATCH_CATCH_ALL { - // If we run out of memory during start-up there's really not a lot more we can do about it - std::terminate(); - } -} + void StartupExceptionRegistry::add(std::exception_ptr const &exception) noexcept { + CATCH_TRY { + m_exceptions.push_back(exception); + } CATCH_CATCH_ALL { + // If we run out of memory during start-up there's really not a lot more we can do about it + std::terminate(); + } + } -std::vector const &StartupExceptionRegistry::getExceptions() const noexcept { - return m_exceptions; -} + std::vector const &StartupExceptionRegistry::getExceptions() const noexcept { + return m_exceptions; + } } // end namespace Catch #endif @@ -13636,161 +14400,168 @@ std::vector const &StartupExceptionRegistry::getExceptions() namespace Catch { -Catch::IStream::~IStream() = default; + Catch::IStream::~IStream() = default; -namespace Detail { -namespace { -template -class StreamBufImpl : public std::streambuf { - char data[bufferSize]; - WriterF m_writer; - - public: - StreamBufImpl() { - setp(data, data + sizeof(data)); - } - - ~StreamBufImpl() noexcept { - StreamBufImpl::sync(); - } - - private: - int overflow(int c) override { - sync(); - - if (c != EOF) { - if (pbase() == epptr()) - m_writer(std::string(1, static_cast( c ))); - else - sputc(static_cast( c )); - } - return 0; - } - - int sync() override { - if (pbase() != pptr()) { - m_writer(std::string(pbase(), static_cast( pptr() - pbase()))); - setp(pbase(), epptr()); - } - return 0; - } -}; + namespace Detail { + namespace { + template + class StreamBufImpl : public std::streambuf { + char data[bufferSize]; + WriterF m_writer; + + public: + StreamBufImpl() { + setp(data, data + sizeof(data)); + } + + ~StreamBufImpl() noexcept { + StreamBufImpl::sync(); + } + + private: + int overflow(int c) override { + sync(); + + if (c != EOF) { + if (pbase() == epptr()) + m_writer(std::string(1, static_cast( c ))); + else + sputc(static_cast( c )); + } + return 0; + } + + int sync() override { + if (pbase() != pptr()) { + m_writer(std::string(pbase(), static_cast( pptr() - pbase()))); + setp(pbase(), epptr()); + } + return 0; + } + }; /////////////////////////////////////////////////////////////////////////// -struct OutputDebugWriter { + struct OutputDebugWriter { - void operator()(std::string const &str) { - writeToDebugConsole(str); - } -}; + void operator()(std::string const &str) { + writeToDebugConsole(str); + } + }; /////////////////////////////////////////////////////////////////////////// -class FileStream : public IStream { - mutable std::ofstream m_ofs; - public: - FileStream(StringRef filename) { - m_ofs.open(filename.c_str()); - CATCH_ENFORCE(!m_ofs.fail(), "Unable to open file: '" << filename << "'"); - } - ~FileStream() override = default; - public: // IStream - std::ostream &stream() const override { - return m_ofs; - } -}; + class FileStream : public IStream { + mutable std::ofstream m_ofs; + public: + FileStream(StringRef filename) { + m_ofs.open(filename.c_str()); + CATCH_ENFORCE(!m_ofs.fail(), "Unable to open file: '" << filename << "'"); + } + + ~FileStream() override = default; + + public: // IStream + std::ostream &stream() const override { + return m_ofs; + } + }; /////////////////////////////////////////////////////////////////////////// -class CoutStream : public IStream { - mutable std::ostream m_os; - public: - // Store the streambuf from cout up-front because - // cout may get redirected when running tests - CoutStream() : m_os(Catch::cout().rdbuf()) {} - ~CoutStream() override = default; + class CoutStream : public IStream { + mutable std::ostream m_os; + public: + // Store the streambuf from cout up-front because + // cout may get redirected when running tests + CoutStream() : m_os(Catch::cout().rdbuf()) {} + + ~CoutStream() override = default; - public: // IStream - std::ostream &stream() const override { return m_os; } -}; + public: // IStream + std::ostream &stream() const override { return m_os; } + }; /////////////////////////////////////////////////////////////////////////// -class DebugOutStream : public IStream { - std::unique_ptr> m_streamBuf; - mutable std::ostream m_os; - public: - DebugOutStream() - : m_streamBuf(new StreamBufImpl()), - m_os(m_streamBuf.get()) {} + class DebugOutStream : public IStream { + std::unique_ptr> m_streamBuf; + mutable std::ostream m_os; + public: + DebugOutStream() + : m_streamBuf(new StreamBufImpl()), + m_os(m_streamBuf.get()) {} - ~DebugOutStream() override = default; + ~DebugOutStream() override = default; - public: // IStream - std::ostream &stream() const override { return m_os; } -}; + public: // IStream + std::ostream &stream() const override { return m_os; } + }; -} -} // namespace anon::detail + } + } // namespace anon::detail /////////////////////////////////////////////////////////////////////////// -auto makeStream(StringRef const &filename) -> IStream const * { - if (filename.empty()) - return new Detail::CoutStream(); - else if (filename[0] == '%') { - if (filename == "%debug") - return new Detail::DebugOutStream(); - else - CATCH_ERROR("Unrecognised stream: '" << filename << "'"); - } else - return new Detail::FileStream(filename); -} + auto makeStream(StringRef const &filename) -> IStream const * { + if (filename.empty()) + return new Detail::CoutStream(); + else if (filename[0] == '%') { + if (filename == "%debug") + return new Detail::DebugOutStream(); + else + CATCH_ERROR("Unrecognised stream: '" << filename << "'"); + } else + return new Detail::FileStream(filename); + } // This class encapsulates the idea of a pool of ostringstreams that can be reused. -struct StringStreams { - std::vector> m_streams; - std::vector m_unused; - std::ostringstream m_referenceStream; // Used for copy state/ flags from - - auto add() -> std::size_t { - if (m_unused.empty()) { - m_streams.push_back(std::unique_ptr(new std::ostringstream)); - return m_streams.size() - 1; - } else { - auto index = m_unused.back(); - m_unused.pop_back(); - return index; - } - } + struct StringStreams { + std::vector> m_streams; + std::vector m_unused; + std::ostringstream m_referenceStream; // Used for copy state/ flags from + + auto add() -> std::size_t { + if (m_unused.empty()) { + m_streams.push_back(std::unique_ptr(new std::ostringstream)); + return m_streams.size() - 1; + } else { + auto index = m_unused.back(); + m_unused.pop_back(); + return index; + } + } - void release(std::size_t index) { - m_streams[index]->copyfmt(m_referenceStream); // Restore initial flags and other state - m_unused.push_back(index); - } -}; + void release(std::size_t index) { + m_streams[index]->copyfmt(m_referenceStream); // Restore initial flags and other state + m_unused.push_back(index); + } + }; -ReusableStringStream::ReusableStringStream() - : m_index(Singleton::getMutable().add()), - m_oss(Singleton::getMutable().m_streams[m_index].get()) {} + ReusableStringStream::ReusableStringStream() + : m_index(Singleton::getMutable().add()), + m_oss(Singleton::getMutable().m_streams[m_index].get()) {} -ReusableStringStream::~ReusableStringStream() { - static_cast( m_oss )->str(""); - m_oss->clear(); - Singleton::getMutable().release(m_index); -} + ReusableStringStream::~ReusableStringStream() { + static_cast( m_oss )->str(""); + m_oss->clear(); + Singleton::getMutable().release(m_index); + } -auto ReusableStringStream::str() const -> std::string { - return static_cast( m_oss )->str(); -} + auto ReusableStringStream::str() const -> std::string { + return static_cast( m_oss )->str(); + } /////////////////////////////////////////////////////////////////////////// #ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement these functions -std::ostream &cout() { return std::cout; } -std::ostream &cerr() { return std::cerr; } -std::ostream &clog() { return std::clog; } + + std::ostream &cout() { return std::cout; } + + std::ostream &cerr() { return std::cerr; } + + std::ostream &clog() { return std::clog; } + #endif } // end catch_stream.cpp @@ -13804,94 +14575,101 @@ std::ostream &clog() { return std::clog; } namespace Catch { -namespace { -char toLowerCh(char c) { - return static_cast( std::tolower(static_cast(c))); -} -} + namespace { + char toLowerCh(char c) { + return static_cast( std::tolower(static_cast(c))); + } + } -bool startsWith(std::string const &s, std::string const &prefix) { - return s.size() >= prefix.size() && std::equal(prefix.begin(), prefix.end(), s.begin()); -} -bool startsWith(std::string const &s, char prefix) { - return !s.empty() && s[0] == prefix; -} -bool endsWith(std::string const &s, std::string const &suffix) { - return s.size() >= suffix.size() && std::equal(suffix.rbegin(), suffix.rend(), s.rbegin()); -} -bool endsWith(std::string const &s, char suffix) { - return !s.empty() && s[s.size() - 1] == suffix; -} -bool contains(std::string const &s, std::string const &infix) { - return s.find(infix) != std::string::npos; -} -void toLowerInPlace(std::string &s) { - std::transform(s.begin(), s.end(), s.begin(), toLowerCh); -} -std::string toLower(std::string const &s) { - std::string lc = s; - toLowerInPlace(lc); - return lc; -} -std::string trim(std::string const &str) { - static char const *whitespaceChars = "\n\r\t "; - std::string::size_type start = str.find_first_not_of(whitespaceChars); - std::string::size_type end = str.find_last_not_of(whitespaceChars); + bool startsWith(std::string const &s, std::string const &prefix) { + return s.size() >= prefix.size() && std::equal(prefix.begin(), prefix.end(), s.begin()); + } - return start != std::string::npos ? str.substr(start, 1 + end - start) : std::string(); -} + bool startsWith(std::string const &s, char prefix) { + return !s.empty() && s[0] == prefix; + } -StringRef trim(StringRef ref) { - const auto is_ws = [](char c) { - return c == ' ' || c == '\t' || c == '\n' || c == '\r'; - }; - size_t real_begin = 0; - while (real_begin < ref.size() && is_ws(ref[real_begin])) { ++real_begin; } - size_t real_end = ref.size(); - while (real_end > real_begin && is_ws(ref[real_end - 1])) { --real_end; } + bool endsWith(std::string const &s, std::string const &suffix) { + return s.size() >= suffix.size() && std::equal(suffix.rbegin(), suffix.rend(), s.rbegin()); + } - return ref.substr(real_begin, real_end - real_begin); -} + bool endsWith(std::string const &s, char suffix) { + return !s.empty() && s[s.size() - 1] == suffix; + } -bool replaceInPlace(std::string &str, std::string const &replaceThis, std::string const &withThis) { - bool replaced = false; - std::size_t i = str.find(replaceThis); - while (i != std::string::npos) { - replaced = true; - str = str.substr(0, i) + withThis + str.substr(i + replaceThis.size()); - if (i < str.size() - withThis.size()) - i = str.find(replaceThis, i + withThis.size()); - else - i = std::string::npos; - } - return replaced; -} + bool contains(std::string const &s, std::string const &infix) { + return s.find(infix) != std::string::npos; + } -std::vector splitStringRef(StringRef str, char delimiter) { - std::vector subStrings; - std::size_t start = 0; - for (std::size_t pos = 0; pos < str.size(); ++pos) { - if (str[pos] == delimiter) { - if (pos - start > 1) - subStrings.push_back(str.substr(start, pos - start)); - start = pos + 1; - } - } - if (start < str.size()) - subStrings.push_back(str.substr(start, str.size() - start)); - return subStrings; -} + void toLowerInPlace(std::string &s) { + std::transform(s.begin(), s.end(), s.begin(), toLowerCh); + } + + std::string toLower(std::string const &s) { + std::string lc = s; + toLowerInPlace(lc); + return lc; + } + + std::string trim(std::string const &str) { + static char const *whitespaceChars = "\n\r\t "; + std::string::size_type start = str.find_first_not_of(whitespaceChars); + std::string::size_type end = str.find_last_not_of(whitespaceChars); + + return start != std::string::npos ? str.substr(start, 1 + end - start) : std::string(); + } + + StringRef trim(StringRef ref) { + const auto is_ws = [](char c) { + return c == ' ' || c == '\t' || c == '\n' || c == '\r'; + }; + size_t real_begin = 0; + while (real_begin < ref.size() && is_ws(ref[real_begin])) { ++real_begin; } + size_t real_end = ref.size(); + while (real_end > real_begin && is_ws(ref[real_end - 1])) { --real_end; } + + return ref.substr(real_begin, real_end - real_begin); + } + + bool replaceInPlace(std::string &str, std::string const &replaceThis, std::string const &withThis) { + bool replaced = false; + std::size_t i = str.find(replaceThis); + while (i != std::string::npos) { + replaced = true; + str = str.substr(0, i) + withThis + str.substr(i + replaceThis.size()); + if (i < str.size() - withThis.size()) + i = str.find(replaceThis, i + withThis.size()); + else + i = std::string::npos; + } + return replaced; + } + + std::vector splitStringRef(StringRef str, char delimiter) { + std::vector subStrings; + std::size_t start = 0; + for (std::size_t pos = 0; pos < str.size(); ++pos) { + if (str[pos] == delimiter) { + if (pos - start > 1) + subStrings.push_back(str.substr(start, pos - start)); + start = pos + 1; + } + } + if (start < str.size()) + subStrings.push_back(str.substr(start, str.size() - start)); + return subStrings; + } -pluralise::pluralise(std::size_t count, std::string const &label) - : m_count(count), - m_label(label) {} + pluralise::pluralise(std::size_t count, std::string const &label) + : m_count(count), + m_label(label) {} -std::ostream &operator<<(std::ostream &os, pluralise const &pluraliser) { - os << pluraliser.m_count << ' ' << pluraliser.m_label; - if (pluraliser.m_count != 1) - os << 's'; - return os; -} + std::ostream &operator<<(std::ostream &os, pluralise const &pluraliser) { + os << pluraliser.m_count << ' ' << pluraliser.m_label; + if (pluraliser.m_count != 1) + os << 's'; + return os; + } } // end catch_string_manip.cpp @@ -13903,58 +14681,60 @@ std::ostream &operator<<(std::ostream &os, pluralise const &pluraliser) { #include namespace Catch { -StringRef::StringRef(char const *rawChars) noexcept - : StringRef(rawChars, static_cast(std::strlen(rawChars))) {} + StringRef::StringRef(char const *rawChars) noexcept + : StringRef(rawChars, static_cast(std::strlen(rawChars))) {} -auto StringRef::c_str() const -> char const * { - CATCH_ENFORCE(isNullTerminated(), "Called StringRef::c_str() on a non-null-terminated instance"); - return m_start; -} -auto StringRef::data() const noexcept -> char const * { - return m_start; -} + auto StringRef::c_str() const -> char const * { + CATCH_ENFORCE(isNullTerminated(), "Called StringRef::c_str() on a non-null-terminated instance"); + return m_start; + } -auto StringRef::substr(size_type start, size_type size) const noexcept -> StringRef { - if (start < m_size) { - return StringRef(m_start + start, (std::min) (m_size - start, size)); - } else { - return StringRef(); - } -} -auto StringRef::operator==(StringRef const &other) const noexcept -> bool { - return m_size == other.m_size - && (std::memcmp(m_start, other.m_start, m_size) == 0); -} + auto StringRef::data() const noexcept -> char const * { + return m_start; + } -auto operator<<(std::ostream &os, StringRef const &str) -> std::ostream & { - return os.write(str.data(), str.size()); -} + auto StringRef::substr(size_type start, size_type size) const noexcept -> StringRef { + if (start < m_size) { + return StringRef(m_start + start, (std::min) (m_size - start, size)); + } else { + return StringRef(); + } + } -auto operator+=(std::string &lhs, StringRef const &rhs) -> std::string & { - lhs.append(rhs.data(), rhs.size()); - return lhs; -} + auto StringRef::operator==(StringRef const &other) const noexcept -> bool { + return m_size == other.m_size + && (std::memcmp(m_start, other.m_start, m_size) == 0); + } + + auto operator<<(std::ostream &os, StringRef const &str) -> std::ostream & { + return os.write(str.data(), str.size()); + } + + auto operator+=(std::string &lhs, StringRef const &rhs) -> std::string & { + lhs.append(rhs.data(), rhs.size()); + return lhs; + } } // namespace Catch // end catch_stringref.cpp // start catch_tag_alias.cpp namespace Catch { -TagAlias::TagAlias(std::string const &_tag, SourceLineInfo _lineInfo) : tag(_tag), lineInfo(_lineInfo) {} + TagAlias::TagAlias(std::string const &_tag, SourceLineInfo _lineInfo) : tag(_tag), lineInfo(_lineInfo) {} } // end catch_tag_alias.cpp // start catch_tag_alias_autoregistrar.cpp namespace Catch { -RegistrarForTagAliases::RegistrarForTagAliases(char const *alias, char const *tag, SourceLineInfo const &lineInfo) { - CATCH_TRY { - getMutableRegistryHub().registerTagAlias(alias, tag, lineInfo); - } CATCH_CATCH_ALL { - // Do not throw when constructing global objects, instead register the exception to be processed later - getMutableRegistryHub().registerStartupException(); - } -} + RegistrarForTagAliases::RegistrarForTagAliases(char const *alias, char const *tag, SourceLineInfo const &lineInfo) { + CATCH_TRY { + getMutableRegistryHub().registerTagAlias(alias, tag, lineInfo); + } CATCH_CATCH_ALL { + // Do not throw when constructing global objects, instead register the exception to be processed later + getMutableRegistryHub().registerStartupException(); + } + } } // end catch_tag_alias_autoregistrar.cpp @@ -13964,44 +14744,44 @@ RegistrarForTagAliases::RegistrarForTagAliases(char const *alias, char const *ta namespace Catch { -TagAliasRegistry::~TagAliasRegistry() {} + TagAliasRegistry::~TagAliasRegistry() {} -TagAlias const *TagAliasRegistry::find(std::string const &alias) const { - auto it = m_registry.find(alias); - if (it != m_registry.end()) - return &(it->second); - else - return nullptr; -} + TagAlias const *TagAliasRegistry::find(std::string const &alias) const { + auto it = m_registry.find(alias); + if (it != m_registry.end()) + return &(it->second); + else + return nullptr; + } -std::string TagAliasRegistry::expandAliases(std::string const &unexpandedTestSpec) const { - std::string expandedTestSpec = unexpandedTestSpec; - for (auto const ®istryKvp : m_registry) { - std::size_t pos = expandedTestSpec.find(registryKvp.first); - if (pos != std::string::npos) { - expandedTestSpec = expandedTestSpec.substr(0, pos) + - registryKvp.second.tag + - expandedTestSpec.substr(pos + registryKvp.first.size()); - } - } - return expandedTestSpec; -} + std::string TagAliasRegistry::expandAliases(std::string const &unexpandedTestSpec) const { + std::string expandedTestSpec = unexpandedTestSpec; + for (auto const ®istryKvp: m_registry) { + std::size_t pos = expandedTestSpec.find(registryKvp.first); + if (pos != std::string::npos) { + expandedTestSpec = expandedTestSpec.substr(0, pos) + + registryKvp.second.tag + + expandedTestSpec.substr(pos + registryKvp.first.size()); + } + } + return expandedTestSpec; + } -void TagAliasRegistry::add(std::string const &alias, std::string const &tag, SourceLineInfo const &lineInfo) { - CATCH_ENFORCE(startsWith(alias, "[@") && endsWith(alias, ']'), - "error: tag alias, '" << alias << "' is not of the form [@alias name].\n" << lineInfo); + void TagAliasRegistry::add(std::string const &alias, std::string const &tag, SourceLineInfo const &lineInfo) { + CATCH_ENFORCE(startsWith(alias, "[@") && endsWith(alias, ']'), + "error: tag alias, '" << alias << "' is not of the form [@alias name].\n" << lineInfo); - CATCH_ENFORCE(m_registry.insert(std::make_pair(alias, TagAlias(tag, lineInfo))).second, - "error: tag alias, '" << alias << "' already registered.\n" - << "\tFirst seen at: " << find(alias)->lineInfo << "\n" - << "\tRedefined at: " << lineInfo); -} + CATCH_ENFORCE(m_registry.insert(std::make_pair(alias, TagAlias(tag, lineInfo))).second, + "error: tag alias, '" << alias << "' already registered.\n" + << "\tFirst seen at: " << find(alias)->lineInfo << "\n" + << "\tRedefined at: " << lineInfo); + } -ITagAliasRegistry::~ITagAliasRegistry() {} + ITagAliasRegistry::~ITagAliasRegistry() {} -ITagAliasRegistry const &ITagAliasRegistry::get() { - return getRegistryHub().getTagAliasRegistry(); -} + ITagAliasRegistry const &ITagAliasRegistry::get() { + return getRegistryHub().getTagAliasRegistry(); + } } // end namespace Catch // end catch_tag_alias_registry.cpp @@ -14014,164 +14794,169 @@ ITagAliasRegistry const &ITagAliasRegistry::get() { namespace Catch { -namespace { -TestCaseInfo::SpecialProperties parseSpecialTag(std::string const &tag) { - if (startsWith(tag, '.') || - tag == "!hide") - return TestCaseInfo::IsHidden; - else if (tag == "!throws") - return TestCaseInfo::Throws; - else if (tag == "!shouldfail") - return TestCaseInfo::ShouldFail; - else if (tag == "!mayfail") - return TestCaseInfo::MayFail; - else if (tag == "!nonportable") - return TestCaseInfo::NonPortable; - else if (tag == "!benchmark") - return static_cast( TestCaseInfo::Benchmark | TestCaseInfo::IsHidden ); - else - return TestCaseInfo::None; -} -bool isReservedTag(std::string const &tag) { - return parseSpecialTag(tag) == TestCaseInfo::None && tag.size() > 0 - && !std::isalnum(static_cast(tag[0])); -} -void enforceNotReservedTag(std::string const &tag, SourceLineInfo const &_lineInfo) { - CATCH_ENFORCE(!isReservedTag(tag), - "Tag name: [" << tag << "] is not allowed.\n" - << "Tag names starting with non alphanumeric characters are reserved\n" - << _lineInfo); -} -} + namespace { + TestCaseInfo::SpecialProperties parseSpecialTag(std::string const &tag) { + if (startsWith(tag, '.') || + tag == "!hide") + return TestCaseInfo::IsHidden; + else if (tag == "!throws") + return TestCaseInfo::Throws; + else if (tag == "!shouldfail") + return TestCaseInfo::ShouldFail; + else if (tag == "!mayfail") + return TestCaseInfo::MayFail; + else if (tag == "!nonportable") + return TestCaseInfo::NonPortable; + else if (tag == "!benchmark") + return static_cast( TestCaseInfo::Benchmark | TestCaseInfo::IsHidden ); + else + return TestCaseInfo::None; + } -TestCase makeTestCase(ITestInvoker *_testCase, - std::string const &_className, - NameAndTags const &nameAndTags, - SourceLineInfo const &_lineInfo) { - bool isHidden = false; - - // Parse out tags - std::vector tags; - std::string desc, tag; - bool inTag = false; - for (char c : nameAndTags.tags) { - if (!inTag) { - if (c == '[') - inTag = true; - else - desc += c; - } else { - if (c == ']') { - TestCaseInfo::SpecialProperties prop = parseSpecialTag(tag); - if ((prop & TestCaseInfo::IsHidden) != 0) - isHidden = true; - else if (prop == TestCaseInfo::None) - enforceNotReservedTag(tag, _lineInfo); - - // Merged hide tags like `[.approvals]` should be added as - // `[.][approvals]`. The `[.]` is added at later point, so - // we only strip the prefix - if (startsWith(tag, '.') && tag.size() > 1) { - tag.erase(0, 1); - } - tags.push_back(tag); - tag.clear(); - inTag = false; - } else - tag += c; - } - } - if (isHidden) { - // Add all "hidden" tags to make them behave identically - tags.insert(tags.end(), {".", "!hide"}); - } - - TestCaseInfo info(static_cast(nameAndTags.name), _className, desc, tags, _lineInfo); - return TestCase(_testCase, std::move(info)); -} + bool isReservedTag(std::string const &tag) { + return parseSpecialTag(tag) == TestCaseInfo::None && tag.size() > 0 + && !std::isalnum(static_cast(tag[0])); + } -void setTags(TestCaseInfo &testCaseInfo, std::vector tags) { - std::sort(begin(tags), end(tags)); - tags.erase(std::unique(begin(tags), end(tags)), end(tags)); - testCaseInfo.lcaseTags.clear(); - - for (auto const &tag : tags) { - std::string lcaseTag = toLower(tag); - testCaseInfo.properties = - static_cast( testCaseInfo.properties | parseSpecialTag(lcaseTag)); - testCaseInfo.lcaseTags.push_back(lcaseTag); - } - testCaseInfo.tags = std::move(tags); -} + void enforceNotReservedTag(std::string const &tag, SourceLineInfo const &_lineInfo) { + CATCH_ENFORCE(!isReservedTag(tag), + "Tag name: [" << tag << "] is not allowed.\n" + << "Tag names starting with non alphanumeric characters are reserved\n" + << _lineInfo); + } + } -TestCaseInfo::TestCaseInfo(std::string const &_name, - std::string const &_className, - std::string const &_description, - std::vector const &_tags, - SourceLineInfo const &_lineInfo) - : name(_name), - className(_className), - description(_description), - lineInfo(_lineInfo), - properties(None) { - setTags(*this, _tags); -} + TestCase makeTestCase(ITestInvoker *_testCase, + std::string const &_className, + NameAndTags const &nameAndTags, + SourceLineInfo const &_lineInfo) { + bool isHidden = false; + + // Parse out tags + std::vector tags; + std::string desc, tag; + bool inTag = false; + for (char c: nameAndTags.tags) { + if (!inTag) { + if (c == '[') + inTag = true; + else + desc += c; + } else { + if (c == ']') { + TestCaseInfo::SpecialProperties prop = parseSpecialTag(tag); + if ((prop & TestCaseInfo::IsHidden) != 0) + isHidden = true; + else if (prop == TestCaseInfo::None) + enforceNotReservedTag(tag, _lineInfo); + + // Merged hide tags like `[.approvals]` should be added as + // `[.][approvals]`. The `[.]` is added at later point, so + // we only strip the prefix + if (startsWith(tag, '.') && tag.size() > 1) { + tag.erase(0, 1); + } + tags.push_back(tag); + tag.clear(); + inTag = false; + } else + tag += c; + } + } + if (isHidden) { + // Add all "hidden" tags to make them behave identically + tags.insert(tags.end(), {".", "!hide"}); + } -bool TestCaseInfo::isHidden() const { - return (properties & IsHidden) != 0; -} -bool TestCaseInfo::throws() const { - return (properties & Throws) != 0; -} -bool TestCaseInfo::okToFail() const { - return (properties & (ShouldFail | MayFail)) != 0; -} -bool TestCaseInfo::expectedToFail() const { - return (properties & (ShouldFail)) != 0; -} + TestCaseInfo info(static_cast(nameAndTags.name), _className, desc, tags, _lineInfo); + return TestCase(_testCase, std::move(info)); + } -std::string TestCaseInfo::tagsAsString() const { - std::string ret; - // '[' and ']' per tag - std::size_t full_size = 2 * tags.size(); - for (const auto &tag : tags) { - full_size += tag.size(); - } - ret.reserve(full_size); - for (const auto &tag : tags) { - ret.push_back('['); - ret.append(tag); - ret.push_back(']'); - } - - return ret; -} + void setTags(TestCaseInfo &testCaseInfo, std::vector tags) { + std::sort(begin(tags), end(tags)); + tags.erase(std::unique(begin(tags), end(tags)), end(tags)); + testCaseInfo.lcaseTags.clear(); -TestCase::TestCase(ITestInvoker *testCase, TestCaseInfo &&info) : TestCaseInfo(std::move(info)), test(testCase) {} + for (auto const &tag: tags) { + std::string lcaseTag = toLower(tag); + testCaseInfo.properties = + static_cast( testCaseInfo.properties | parseSpecialTag(lcaseTag)); + testCaseInfo.lcaseTags.push_back(lcaseTag); + } + testCaseInfo.tags = std::move(tags); + } -TestCase TestCase::withName(std::string const &_newName) const { - TestCase other(*this); - other.name = _newName; - return other; -} + TestCaseInfo::TestCaseInfo(std::string const &_name, + std::string const &_className, + std::string const &_description, + std::vector const &_tags, + SourceLineInfo const &_lineInfo) + : name(_name), + className(_className), + description(_description), + lineInfo(_lineInfo), + properties(None) { + setTags(*this, _tags); + } -void TestCase::invoke() const { - test->invoke(); -} + bool TestCaseInfo::isHidden() const { + return (properties & IsHidden) != 0; + } -bool TestCase::operator==(TestCase const &other) const { - return test.get() == other.test.get() && - name == other.name && - className == other.className; -} + bool TestCaseInfo::throws() const { + return (properties & Throws) != 0; + } -bool TestCase::operator<(TestCase const &other) const { - return name < other.name; -} + bool TestCaseInfo::okToFail() const { + return (properties & (ShouldFail | MayFail)) != 0; + } -TestCaseInfo const &TestCase::getTestCaseInfo() const { - return *this; -} + bool TestCaseInfo::expectedToFail() const { + return (properties & (ShouldFail)) != 0; + } + + std::string TestCaseInfo::tagsAsString() const { + std::string ret; + // '[' and ']' per tag + std::size_t full_size = 2 * tags.size(); + for (const auto &tag: tags) { + full_size += tag.size(); + } + ret.reserve(full_size); + for (const auto &tag: tags) { + ret.push_back('['); + ret.append(tag); + ret.push_back(']'); + } + + return ret; + } + + TestCase::TestCase(ITestInvoker *testCase, TestCaseInfo &&info) : TestCaseInfo(std::move(info)), test(testCase) {} + + TestCase TestCase::withName(std::string const &_newName) const { + TestCase other(*this); + other.name = _newName; + return other; + } + + void TestCase::invoke() const { + test->invoke(); + } + + bool TestCase::operator==(TestCase const &other) const { + return test.get() == other.test.get() && + name == other.name && + className == other.className; + } + + bool TestCase::operator<(TestCase const &other) const { + return name < other.name; + } + + TestCaseInfo const &TestCase::getTestCaseInfo() const { + return *this; + } } // end namespace Catch // end catch_test_case_info.cpp @@ -14182,156 +14967,158 @@ TestCaseInfo const &TestCase::getTestCaseInfo() const { namespace Catch { -namespace { -struct TestHasher { - using hash_t = uint64_t; - - explicit TestHasher(hash_t hashSuffix) : - m_hashSuffix{hashSuffix} {} - - uint32_t operator()(TestCase const &t) const { - // FNV-1a hash with multiplication fold. - const hash_t prime = 1099511628211u; - hash_t hash = 14695981039346656037u; - for (const char c : t.name) { - hash ^= c; - hash *= prime; - } - hash ^= m_hashSuffix; - hash *= prime; - const uint32_t low{static_cast( hash )}; - const uint32_t high{static_cast( hash >> 32 )}; - return low * high; - } - - private: - hash_t m_hashSuffix; -}; -} // end unnamed namespace + namespace { + struct TestHasher { + using hash_t = uint64_t; -std::vector sortTests(IConfig const &config, std::vector const &unsortedTestCases) { - switch (config.runOrder()) { - case RunTests::InDeclarationOrder: - // already in declaration order - break; + explicit TestHasher(hash_t hashSuffix) : + m_hashSuffix{hashSuffix} {} - case RunTests::InLexicographicalOrder: { - std::vector sorted = unsortedTestCases; - std::sort(sorted.begin(), sorted.end()); - return sorted; - } + uint32_t operator()(TestCase const &t) const { + // FNV-1a hash with multiplication fold. + const hash_t prime = 1099511628211u; + hash_t hash = 14695981039346656037u; + for (const char c: t.name) { + hash ^= c; + hash *= prime; + } + hash ^= m_hashSuffix; + hash *= prime; + const uint32_t low{static_cast( hash )}; + const uint32_t high{static_cast( hash >> 32 )}; + return low * high; + } + + private: + hash_t m_hashSuffix; + }; + } // end unnamed namespace + + std::vector sortTests(IConfig const &config, std::vector const &unsortedTestCases) { + switch (config.runOrder()) { + case RunTests::InDeclarationOrder: + // already in declaration order + break; + + case RunTests::InLexicographicalOrder: { + std::vector sorted = unsortedTestCases; + std::sort(sorted.begin(), sorted.end()); + return sorted; + } - case RunTests::InRandomOrder: { - seedRng(config); - TestHasher h{config.rngSeed()}; + case RunTests::InRandomOrder: { + seedRng(config); + TestHasher h{config.rngSeed()}; - using hashedTest = std::pair; - std::vector indexed_tests; - indexed_tests.reserve(unsortedTestCases.size()); + using hashedTest = std::pair; + std::vector indexed_tests; + indexed_tests.reserve(unsortedTestCases.size()); - for (auto const &testCase : unsortedTestCases) { - indexed_tests.emplace_back(h(testCase), &testCase); - } + for (auto const &testCase: unsortedTestCases) { + indexed_tests.emplace_back(h(testCase), &testCase); + } - std::sort(indexed_tests.begin(), indexed_tests.end(), - [](hashedTest const &lhs, hashedTest const &rhs) { - if (lhs.first == rhs.first) { - return lhs.second->name < rhs.second->name; - } - return lhs.first < rhs.first; - }); + std::sort(indexed_tests.begin(), indexed_tests.end(), + [](hashedTest const &lhs, hashedTest const &rhs) { + if (lhs.first == rhs.first) { + return lhs.second->name < rhs.second->name; + } + return lhs.first < rhs.first; + }); - std::vector sorted; - sorted.reserve(indexed_tests.size()); + std::vector sorted; + sorted.reserve(indexed_tests.size()); - for (auto const &hashed : indexed_tests) { - sorted.emplace_back(*hashed.second); - } + for (auto const &hashed: indexed_tests) { + sorted.emplace_back(*hashed.second); + } - return sorted; + return sorted; + } + } + return unsortedTestCases; } - } - return unsortedTestCases; -} -bool isThrowSafe(TestCase const &testCase, IConfig const &config) { - return !testCase.throws() || config.allowThrows(); -} + bool isThrowSafe(TestCase const &testCase, IConfig const &config) { + return !testCase.throws() || config.allowThrows(); + } -bool matchTest(TestCase const &testCase, TestSpec const &testSpec, IConfig const &config) { - return testSpec.matches(testCase) && isThrowSafe(testCase, config); -} + bool matchTest(TestCase const &testCase, TestSpec const &testSpec, IConfig const &config) { + return testSpec.matches(testCase) && isThrowSafe(testCase, config); + } -void enforceNoDuplicateTestCases(std::vector const &functions) { - std::set seenFunctions; - for (auto const &function : functions) { - auto prev = seenFunctions.insert(function); - CATCH_ENFORCE(prev.second, - "error: TEST_CASE( \"" << function.name << "\" ) already defined.\n" - << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << "\n" - << "\tRedefined at " << function.getTestCaseInfo().lineInfo); - } -} + void enforceNoDuplicateTestCases(std::vector const &functions) { + std::set seenFunctions; + for (auto const &function: functions) { + auto prev = seenFunctions.insert(function); + CATCH_ENFORCE(prev.second, + "error: TEST_CASE( \"" << function.name << "\" ) already defined.\n" + << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << "\n" + << "\tRedefined at " << function.getTestCaseInfo().lineInfo); + } + } -std::vector filterTests(std::vector const &testCases, - TestSpec const &testSpec, - IConfig const &config) { - std::vector filtered; - filtered.reserve(testCases.size()); - for (auto const &testCase : testCases) { - if ((!testSpec.hasFilters() && !testCase.isHidden()) || - (testSpec.hasFilters() && matchTest(testCase, testSpec, config))) { - filtered.push_back(testCase); - } - } - return filtered; -} -std::vector const &getAllTestCasesSorted(IConfig const &config) { - return getRegistryHub().getTestCaseRegistry().getAllTestsSorted(config); -} + std::vector filterTests(std::vector const &testCases, + TestSpec const &testSpec, + IConfig const &config) { + std::vector filtered; + filtered.reserve(testCases.size()); + for (auto const &testCase: testCases) { + if ((!testSpec.hasFilters() && !testCase.isHidden()) || + (testSpec.hasFilters() && matchTest(testCase, testSpec, config))) { + filtered.push_back(testCase); + } + } + return filtered; + } -void TestRegistry::registerTest(TestCase const &testCase) { - std::string name = testCase.getTestCaseInfo().name; - if (name.empty()) { - ReusableStringStream rss; - rss << "Anonymous test case " << ++m_unnamedCount; - return registerTest(testCase.withName(rss.str())); - } - m_functions.push_back(testCase); -} + std::vector const &getAllTestCasesSorted(IConfig const &config) { + return getRegistryHub().getTestCaseRegistry().getAllTestsSorted(config); + } -std::vector const &TestRegistry::getAllTests() const { - return m_functions; -} -std::vector const &TestRegistry::getAllTestsSorted(IConfig const &config) const { - if (m_sortedFunctions.empty()) - enforceNoDuplicateTestCases(m_functions); - - if (m_currentSortOrder != config.runOrder() || m_sortedFunctions.empty()) { - m_sortedFunctions = sortTests(config, m_functions); - m_currentSortOrder = config.runOrder(); - } - return m_sortedFunctions; -} + void TestRegistry::registerTest(TestCase const &testCase) { + std::string name = testCase.getTestCaseInfo().name; + if (name.empty()) { + ReusableStringStream rss; + rss << "Anonymous test case " << ++m_unnamedCount; + return registerTest(testCase.withName(rss.str())); + } + m_functions.push_back(testCase); + } + + std::vector const &TestRegistry::getAllTests() const { + return m_functions; + } + + std::vector const &TestRegistry::getAllTestsSorted(IConfig const &config) const { + if (m_sortedFunctions.empty()) + enforceNoDuplicateTestCases(m_functions); + + if (m_currentSortOrder != config.runOrder() || m_sortedFunctions.empty()) { + m_sortedFunctions = sortTests(config, m_functions); + m_currentSortOrder = config.runOrder(); + } + return m_sortedFunctions; + } /////////////////////////////////////////////////////////////////////////// -TestInvokerAsFunction::TestInvokerAsFunction(void(*testAsFunction)()) noexcept: m_testAsFunction(testAsFunction) {} + TestInvokerAsFunction::TestInvokerAsFunction(void(*testAsFunction)()) noexcept: m_testAsFunction(testAsFunction) {} -void TestInvokerAsFunction::invoke() const { - m_testAsFunction(); -} + void TestInvokerAsFunction::invoke() const { + m_testAsFunction(); + } -std::string extractClassName(StringRef const &classOrQualifiedMethodName) { - std::string className(classOrQualifiedMethodName); - if (startsWith(className, '&')) { - std::size_t lastColons = className.rfind("::"); - std::size_t penultimateColons = className.rfind("::", lastColons - 1); - if (penultimateColons == std::string::npos) - penultimateColons = 1; - className = className.substr(penultimateColons, lastColons - penultimateColons); - } - return className; -} + std::string extractClassName(StringRef const &classOrQualifiedMethodName) { + std::string className(classOrQualifiedMethodName); + if (startsWith(className, '&')) { + std::size_t lastColons = className.rfind("::"); + std::size_t penultimateColons = className.rfind("::", lastColons - 1); + if (penultimateColons == std::string::npos) + penultimateColons = 1; + className = className.substr(penultimateColons, lastColons - penultimateColons); + } + return className; + } } // end namespace Catch // end catch_test_case_registry_impl.cpp @@ -14349,218 +15136,236 @@ std::string extractClassName(StringRef const &classOrQualifiedMethodName) { #endif namespace Catch { -namespace TestCaseTracking { + namespace TestCaseTracking { -NameAndLocation::NameAndLocation(std::string const &_name, SourceLineInfo const &_location) - : name(_name), - location(_location) {} + NameAndLocation::NameAndLocation(std::string const &_name, SourceLineInfo const &_location) + : name(_name), + location(_location) {} -ITracker::~ITracker() = default; + ITracker::~ITracker() = default; -ITracker &TrackerContext::startRun() { - m_rootTracker = std::make_shared(NameAndLocation("{root}", CATCH_INTERNAL_LINEINFO), *this, nullptr); - m_currentTracker = nullptr; - m_runState = Executing; - return *m_rootTracker; -} + ITracker &TrackerContext::startRun() { + m_rootTracker = std::make_shared(NameAndLocation("{root}", CATCH_INTERNAL_LINEINFO), *this, + nullptr); + m_currentTracker = nullptr; + m_runState = Executing; + return *m_rootTracker; + } -void TrackerContext::endRun() { - m_rootTracker.reset(); - m_currentTracker = nullptr; - m_runState = NotStarted; -} + void TrackerContext::endRun() { + m_rootTracker.reset(); + m_currentTracker = nullptr; + m_runState = NotStarted; + } -void TrackerContext::startCycle() { - m_currentTracker = m_rootTracker.get(); - m_runState = Executing; -} -void TrackerContext::completeCycle() { - m_runState = CompletedCycle; -} + void TrackerContext::startCycle() { + m_currentTracker = m_rootTracker.get(); + m_runState = Executing; + } -bool TrackerContext::completedCycle() const { - return m_runState == CompletedCycle; -} -ITracker &TrackerContext::currentTracker() { - return *m_currentTracker; -} -void TrackerContext::setCurrentTracker(ITracker *tracker) { - m_currentTracker = tracker; -} + void TrackerContext::completeCycle() { + m_runState = CompletedCycle; + } -TrackerBase::TrackerBase(NameAndLocation const &nameAndLocation, TrackerContext &ctx, ITracker *parent) : - ITracker(nameAndLocation), - m_ctx(ctx), - m_parent(parent) {} + bool TrackerContext::completedCycle() const { + return m_runState == CompletedCycle; + } -bool TrackerBase::isComplete() const { - return m_runState == CompletedSuccessfully || m_runState == Failed; -} -bool TrackerBase::isSuccessfullyCompleted() const { - return m_runState == CompletedSuccessfully; -} -bool TrackerBase::isOpen() const { - return m_runState != NotStarted && !isComplete(); -} -bool TrackerBase::hasChildren() const { - return !m_children.empty(); -} + ITracker &TrackerContext::currentTracker() { + return *m_currentTracker; + } -void TrackerBase::addChild(ITrackerPtr const &child) { - m_children.push_back(child); -} + void TrackerContext::setCurrentTracker(ITracker *tracker) { + m_currentTracker = tracker; + } -ITrackerPtr TrackerBase::findChild(NameAndLocation const &nameAndLocation) { - auto it = std::find_if(m_children.begin(), m_children.end(), - [&nameAndLocation](ITrackerPtr const &tracker) { - return - tracker->nameAndLocation().location == nameAndLocation.location && - tracker->nameAndLocation().name == nameAndLocation.name; - }); - return (it != m_children.end()) - ? *it - : nullptr; -} -ITracker &TrackerBase::parent() { - assert(m_parent); // Should always be non-null except for root - return *m_parent; -} + TrackerBase::TrackerBase(NameAndLocation const &nameAndLocation, TrackerContext &ctx, ITracker *parent) : + ITracker(nameAndLocation), + m_ctx(ctx), + m_parent(parent) {} -void TrackerBase::openChild() { - if (m_runState != ExecutingChildren) { - m_runState = ExecutingChildren; - if (m_parent) - m_parent->openChild(); - } -} + bool TrackerBase::isComplete() const { + return m_runState == CompletedSuccessfully || m_runState == Failed; + } -bool TrackerBase::isSectionTracker() const { return false; } -bool TrackerBase::isGeneratorTracker() const { return false; } + bool TrackerBase::isSuccessfullyCompleted() const { + return m_runState == CompletedSuccessfully; + } -void TrackerBase::open() { - m_runState = Executing; - moveToThis(); - if (m_parent) - m_parent->openChild(); -} + bool TrackerBase::isOpen() const { + return m_runState != NotStarted && !isComplete(); + } + + bool TrackerBase::hasChildren() const { + return !m_children.empty(); + } + + void TrackerBase::addChild(ITrackerPtr const &child) { + m_children.push_back(child); + } -void TrackerBase::close() { + ITrackerPtr TrackerBase::findChild(NameAndLocation const &nameAndLocation) { + auto it = std::find_if(m_children.begin(), m_children.end(), + [&nameAndLocation](ITrackerPtr const &tracker) { + return + tracker->nameAndLocation().location == nameAndLocation.location && + tracker->nameAndLocation().name == nameAndLocation.name; + }); + return (it != m_children.end()) + ? *it + : nullptr; + } - // Close any still open children (e.g. generators) - while (&m_ctx.currentTracker() != this) - m_ctx.currentTracker().close(); + ITracker &TrackerBase::parent() { + assert(m_parent); // Should always be non-null except for root + return *m_parent; + } - switch (m_runState) { - case NeedsAnotherRun:break; + void TrackerBase::openChild() { + if (m_runState != ExecutingChildren) { + m_runState = ExecutingChildren; + if (m_parent) + m_parent->openChild(); + } + } - case Executing:m_runState = CompletedSuccessfully; - break; - case ExecutingChildren: - if (std::all_of(m_children.begin(), m_children.end(), [](ITrackerPtr const &t) { return t->isComplete(); })) - m_runState = CompletedSuccessfully; - break; + bool TrackerBase::isSectionTracker() const { return false; } - case NotStarted: - case CompletedSuccessfully: - case Failed:CATCH_INTERNAL_ERROR("Illogical state: " << m_runState); + bool TrackerBase::isGeneratorTracker() const { return false; } - default:CATCH_INTERNAL_ERROR("Unknown state: " << m_runState); - } - moveToParent(); - m_ctx.completeCycle(); -} -void TrackerBase::fail() { - m_runState = Failed; - if (m_parent) - m_parent->markAsNeedingAnotherRun(); - moveToParent(); - m_ctx.completeCycle(); -} -void TrackerBase::markAsNeedingAnotherRun() { - m_runState = NeedsAnotherRun; -} + void TrackerBase::open() { + m_runState = Executing; + moveToThis(); + if (m_parent) + m_parent->openChild(); + } -void TrackerBase::moveToParent() { - assert(m_parent); - m_ctx.setCurrentTracker(m_parent); -} -void TrackerBase::moveToThis() { - m_ctx.setCurrentTracker(this); -} + void TrackerBase::close() { -SectionTracker::SectionTracker(NameAndLocation const &nameAndLocation, TrackerContext &ctx, ITracker *parent) - : TrackerBase(nameAndLocation, ctx, parent), - m_trimmed_name(trim(nameAndLocation.name)) { - if (parent) { - while (!parent->isSectionTracker()) - parent = &parent->parent(); + // Close any still open children (e.g. generators) + while (&m_ctx.currentTracker() != this) + m_ctx.currentTracker().close(); - SectionTracker & parentSection = static_cast( *parent ); - addNextFilters(parentSection.m_filters); - } -} + switch (m_runState) { + case NeedsAnotherRun: + break; -bool SectionTracker::isComplete() const { - bool complete = true; + case Executing: + m_runState = CompletedSuccessfully; + break; + case ExecutingChildren: + if (std::all_of(m_children.begin(), m_children.end(), + [](ITrackerPtr const &t) { return t->isComplete(); })) + m_runState = CompletedSuccessfully; + break; - if (m_filters.empty() - || m_filters[0] == "" - || std::find(m_filters.begin(), m_filters.end(), m_trimmed_name) != m_filters.end()) { - complete = TrackerBase::isComplete(); - } - return complete; -} + case NotStarted: + case CompletedSuccessfully: + case Failed: + CATCH_INTERNAL_ERROR("Illogical state: " << m_runState); -bool SectionTracker::isSectionTracker() const { return true; } - -SectionTracker &SectionTracker::acquire(TrackerContext &ctx, NameAndLocation const &nameAndLocation) { - std::shared_ptr section; - - ITracker ¤tTracker = ctx.currentTracker(); - if (ITrackerPtr childTracker = currentTracker.findChild(nameAndLocation)) { - assert(childTracker); - assert(childTracker->isSectionTracker()); - section = std::static_pointer_cast(childTracker); - } else { - section = std::make_shared(nameAndLocation, ctx, ¤tTracker); - currentTracker.addChild(section); - } - if (!ctx.completedCycle()) - section->tryOpen(); - return *section; -} + default: + CATCH_INTERNAL_ERROR("Unknown state: " << m_runState); + } + moveToParent(); + m_ctx.completeCycle(); + } -void SectionTracker::tryOpen() { - if (!isComplete()) - open(); -} + void TrackerBase::fail() { + m_runState = Failed; + if (m_parent) + m_parent->markAsNeedingAnotherRun(); + moveToParent(); + m_ctx.completeCycle(); + } -void SectionTracker::addInitialFilters(std::vector const &filters) { - if (!filters.empty()) { - m_filters.reserve(m_filters.size() + filters.size() + 2); - m_filters.emplace_back(""); // Root - should never be consulted - m_filters.emplace_back(""); // Test Case - not a section filter - m_filters.insert(m_filters.end(), filters.begin(), filters.end()); - } -} -void SectionTracker::addNextFilters(std::vector const &filters) { - if (filters.size() > 1) - m_filters.insert(m_filters.end(), filters.begin() + 1, filters.end()); -} + void TrackerBase::markAsNeedingAnotherRun() { + m_runState = NeedsAnotherRun; + } -std::vector const &SectionTracker::getFilters() const { - return m_filters; -} + void TrackerBase::moveToParent() { + assert(m_parent); + m_ctx.setCurrentTracker(m_parent); + } -std::string const &SectionTracker::trimmedName() const { - return m_trimmed_name; -} + void TrackerBase::moveToThis() { + m_ctx.setCurrentTracker(this); + } + + SectionTracker::SectionTracker(NameAndLocation const &nameAndLocation, TrackerContext &ctx, ITracker *parent) + : TrackerBase(nameAndLocation, ctx, parent), + m_trimmed_name(trim(nameAndLocation.name)) { + if (parent) { + while (!parent->isSectionTracker()) + parent = &parent->parent(); + + SectionTracker & parentSection = static_cast( *parent ); + addNextFilters(parentSection.m_filters); + } + } + + bool SectionTracker::isComplete() const { + bool complete = true; + + if (m_filters.empty() + || m_filters[0] == "" + || std::find(m_filters.begin(), m_filters.end(), m_trimmed_name) != m_filters.end()) { + complete = TrackerBase::isComplete(); + } + return complete; + } + + bool SectionTracker::isSectionTracker() const { return true; } + + SectionTracker &SectionTracker::acquire(TrackerContext &ctx, NameAndLocation const &nameAndLocation) { + std::shared_ptr section; + + ITracker ¤tTracker = ctx.currentTracker(); + if (ITrackerPtr childTracker = currentTracker.findChild(nameAndLocation)) { + assert(childTracker); + assert(childTracker->isSectionTracker()); + section = std::static_pointer_cast(childTracker); + } else { + section = std::make_shared(nameAndLocation, ctx, ¤tTracker); + currentTracker.addChild(section); + } + if (!ctx.completedCycle()) + section->tryOpen(); + return *section; + } + + void SectionTracker::tryOpen() { + if (!isComplete()) + open(); + } + + void SectionTracker::addInitialFilters(std::vector const &filters) { + if (!filters.empty()) { + m_filters.reserve(m_filters.size() + filters.size() + 2); + m_filters.emplace_back(""); // Root - should never be consulted + m_filters.emplace_back(""); // Test Case - not a section filter + m_filters.insert(m_filters.end(), filters.begin(), filters.end()); + } + } + + void SectionTracker::addNextFilters(std::vector const &filters) { + if (filters.size() > 1) + m_filters.insert(m_filters.end(), filters.begin() + 1, filters.end()); + } + + std::vector const &SectionTracker::getFilters() const { + return m_filters; + } -} // namespace TestCaseTracking + std::string const &SectionTracker::trimmedName() const { + return m_trimmed_name; + } + + } // namespace TestCaseTracking -using TestCaseTracking::ITracker; -using TestCaseTracking::TrackerContext; -using TestCaseTracking::SectionTracker; + using TestCaseTracking::ITracker; + using TestCaseTracking::TrackerContext; + using TestCaseTracking::SectionTracker; } // namespace Catch @@ -14572,32 +15377,32 @@ using TestCaseTracking::SectionTracker; namespace Catch { -auto makeTestInvoker(void(*testAsFunction)()) noexcept -> ITestInvoker * { - return new(std::nothrow) - TestInvokerAsFunction( testAsFunction ); -} - -NameAndTags::NameAndTags(StringRef const &name_, StringRef const &tags_) noexcept: name(name_), tags(tags_) {} + auto makeTestInvoker(void(*testAsFunction)()) noexcept -> ITestInvoker * { + return new(std::nothrow) + TestInvokerAsFunction( testAsFunction ); + } -AutoReg::AutoReg(ITestInvoker *invoker, - SourceLineInfo const &lineInfo, - StringRef const &classOrMethod, - NameAndTags const &nameAndTags) noexcept { - CATCH_TRY { - getMutableRegistryHub() - .registerTest( - makeTestCase( - invoker, - extractClassName(classOrMethod), - nameAndTags, - lineInfo)); - } CATCH_CATCH_ALL { - // Do not throw when constructing global objects, instead register the exception to be processed later - getMutableRegistryHub().registerStartupException(); - } -} + NameAndTags::NameAndTags(StringRef const &name_, StringRef const &tags_) noexcept: name(name_), tags(tags_) {} + + AutoReg::AutoReg(ITestInvoker *invoker, + SourceLineInfo const &lineInfo, + StringRef const &classOrMethod, + NameAndTags const &nameAndTags) noexcept { + CATCH_TRY { + getMutableRegistryHub() + .registerTest( + makeTestCase( + invoker, + extractClassName(classOrMethod), + nameAndTags, + lineInfo)); + } CATCH_CATCH_ALL { + // Do not throw when constructing global objects, instead register the exception to be processed later + getMutableRegistryHub().registerStartupException(); + } + } -AutoReg::~AutoReg() = default; + AutoReg::~AutoReg() = default; } // end catch_test_registry.cpp // start catch_test_spec.cpp @@ -14609,288 +15414,315 @@ AutoReg::~AutoReg() = default; namespace Catch { -TestSpec::Pattern::Pattern(std::string const &name) - : m_name(name) {} + TestSpec::Pattern::Pattern(std::string const &name) + : m_name(name) {} -TestSpec::Pattern::~Pattern() = default; + TestSpec::Pattern::~Pattern() = default; -std::string const &TestSpec::Pattern::name() const { - return m_name; -} + std::string const &TestSpec::Pattern::name() const { + return m_name; + } -TestSpec::NamePattern::NamePattern(std::string const &name, std::string const &filterString) - : Pattern(filterString), m_wildcardPattern(toLower(name), CaseSensitive::No) {} + TestSpec::NamePattern::NamePattern(std::string const &name, std::string const &filterString) + : Pattern(filterString), m_wildcardPattern(toLower(name), CaseSensitive::No) {} -bool TestSpec::NamePattern::matches(TestCaseInfo const &testCase) const { - return m_wildcardPattern.matches(testCase.name); -} + bool TestSpec::NamePattern::matches(TestCaseInfo const &testCase) const { + return m_wildcardPattern.matches(testCase.name); + } -TestSpec::TagPattern::TagPattern(std::string const &tag, std::string const &filterString) - : Pattern(filterString), m_tag(toLower(tag)) {} + TestSpec::TagPattern::TagPattern(std::string const &tag, std::string const &filterString) + : Pattern(filterString), m_tag(toLower(tag)) {} -bool TestSpec::TagPattern::matches(TestCaseInfo const &testCase) const { - return std::find(begin(testCase.lcaseTags), - end(testCase.lcaseTags), - m_tag) != end(testCase.lcaseTags); -} + bool TestSpec::TagPattern::matches(TestCaseInfo const &testCase) const { + return std::find(begin(testCase.lcaseTags), + end(testCase.lcaseTags), + m_tag) != end(testCase.lcaseTags); + } -TestSpec::ExcludedPattern::ExcludedPattern(PatternPtr const &underlyingPattern) - : Pattern(underlyingPattern->name()), m_underlyingPattern(underlyingPattern) {} + TestSpec::ExcludedPattern::ExcludedPattern(PatternPtr const &underlyingPattern) + : Pattern(underlyingPattern->name()), m_underlyingPattern(underlyingPattern) {} -bool TestSpec::ExcludedPattern::matches(TestCaseInfo const &testCase) const { - return !m_underlyingPattern->matches(testCase); -} + bool TestSpec::ExcludedPattern::matches(TestCaseInfo const &testCase) const { + return !m_underlyingPattern->matches(testCase); + } -bool TestSpec::Filter::matches(TestCaseInfo const &testCase) const { - return std::all_of(m_patterns.begin(), m_patterns.end(), [&](PatternPtr const &p) { return p->matches(testCase); }); -} + bool TestSpec::Filter::matches(TestCaseInfo const &testCase) const { + return std::all_of(m_patterns.begin(), m_patterns.end(), + [&](PatternPtr const &p) { return p->matches(testCase); }); + } -std::string TestSpec::Filter::name() const { - std::string name; - for (auto const &p : m_patterns) - name += p->name(); - return name; -} + std::string TestSpec::Filter::name() const { + std::string name; + for (auto const &p: m_patterns) + name += p->name(); + return name; + } + + bool TestSpec::hasFilters() const { + return !m_filters.empty(); + } + + bool TestSpec::matches(TestCaseInfo const &testCase) const { + return std::any_of(m_filters.begin(), m_filters.end(), [&](Filter const &f) { return f.matches(testCase); }); + } + + TestSpec::Matches TestSpec::matchesByFilter(std::vector const &testCases, IConfig const &config) const { + Matches matches(m_filters.size()); + std::transform(m_filters.begin(), m_filters.end(), matches.begin(), [&](Filter const &filter) { + std::vector currentMatches; + for (auto const &test: testCases) + if (isThrowSafe(test, config) && filter.matches(test)) + currentMatches.emplace_back(&test); + return FilterMatch{filter.name(), currentMatches}; + }); + return matches; + } + + const TestSpec::vectorStrings &TestSpec::getInvalidArgs() const { + return (m_invalidArgs); + } -bool TestSpec::hasFilters() const { - return !m_filters.empty(); } +// end catch_test_spec.cpp +// start catch_test_spec_parser.cpp + +namespace Catch { + + TestSpecParser::TestSpecParser(ITagAliasRegistry const &tagAliases) : m_tagAliases(&tagAliases) {} + + TestSpecParser &TestSpecParser::parse(std::string const &arg) { + m_mode = None; + m_exclusion = false; + m_arg = m_tagAliases->expandAliases(arg); + m_escapeChars.clear(); + m_substring.reserve(m_arg.size()); + m_patternName.reserve(m_arg.size()); + m_realPatternPos = 0; + + for (m_pos = 0; m_pos < m_arg.size(); ++m_pos) + //if visitChar fails + if (!visitChar(m_arg[m_pos])) { + m_testSpec.m_invalidArgs.push_back(arg); + break; + } + endMode(); + return *this; + } + + TestSpec TestSpecParser::testSpec() { + addFilter(); + return m_testSpec; + } + + bool TestSpecParser::visitChar(char c) { + if ((m_mode != EscapedName) && (c == '\\')) { + escape(); + addCharToPattern(c); + return true; + } else if ((m_mode != EscapedName) && (c == ',')) { + return separate(); + } + + switch (m_mode) { + case None: + if (processNoneChar(c)) + return true; + break; + case Name: + processNameChar(c); + break; + case EscapedName: + endMode(); + addCharToPattern(c); + return true; + default: + case Tag: + case QuotedName: + if (processOtherChar(c)) + return true; + break; + } + + m_substring += c; + if (!isControlChar(c)) { + m_patternName += c; + m_realPatternPos++; + } + return true; + } -bool TestSpec::matches(TestCaseInfo const &testCase) const { - return std::any_of(m_filters.begin(), m_filters.end(), [&](Filter const &f) { return f.matches(testCase); }); -} +// Two of the processing methods return true to signal the caller to return +// without adding the given character to the current pattern strings + bool TestSpecParser::processNoneChar(char c) { + switch (c) { + case ' ': + return true; + case '~': + m_exclusion = true; + return false; + case '[': + startNewMode(Tag); + return false; + case '"': + startNewMode(QuotedName); + return false; + default: + startNewMode(Name); + return false; + } + } -TestSpec::Matches TestSpec::matchesByFilter(std::vector const &testCases, IConfig const &config) const { - Matches matches(m_filters.size()); - std::transform(m_filters.begin(), m_filters.end(), matches.begin(), [&](Filter const &filter) { - std::vector currentMatches; - for (auto const &test : testCases) - if (isThrowSafe(test, config) && filter.matches(test)) - currentMatches.emplace_back(&test); - return FilterMatch{filter.name(), currentMatches}; - }); - return matches; -} + void TestSpecParser::processNameChar(char c) { + if (c == '[') { + if (m_substring == "exclude:") + m_exclusion = true; + else + endMode(); + startNewMode(Tag); + } + } -const TestSpec::vectorStrings &TestSpec::getInvalidArgs() const { - return (m_invalidArgs); -} + bool TestSpecParser::processOtherChar(char c) { + if (!isControlChar(c)) + return false; + m_substring += c; + endMode(); + return true; + } -} -// end catch_test_spec.cpp -// start catch_test_spec_parser.cpp + void TestSpecParser::startNewMode(Mode mode) { + m_mode = mode; + } -namespace Catch { + void TestSpecParser::endMode() { + switch (m_mode) { + case Name: + case QuotedName: + return addNamePattern(); + case Tag: + return addTagPattern(); + case EscapedName: + revertBackToLastMode(); + return; + case None: + default: + return startNewMode(None); + } + } -TestSpecParser::TestSpecParser(ITagAliasRegistry const &tagAliases) : m_tagAliases(&tagAliases) {} - -TestSpecParser &TestSpecParser::parse(std::string const &arg) { - m_mode = None; - m_exclusion = false; - m_arg = m_tagAliases->expandAliases(arg); - m_escapeChars.clear(); - m_substring.reserve(m_arg.size()); - m_patternName.reserve(m_arg.size()); - m_realPatternPos = 0; - - for (m_pos = 0; m_pos < m_arg.size(); ++m_pos) - //if visitChar fails - if (!visitChar(m_arg[m_pos])) { - m_testSpec.m_invalidArgs.push_back(arg); - break; - } - endMode(); - return *this; -} -TestSpec TestSpecParser::testSpec() { - addFilter(); - return m_testSpec; -} -bool TestSpecParser::visitChar(char c) { - if ((m_mode != EscapedName) && (c == '\\')) { - escape(); - addCharToPattern(c); - return true; - } else if ((m_mode != EscapedName) && (c == ',')) { - return separate(); - } - - switch (m_mode) { - case None: - if (processNoneChar(c)) - return true; - break; - case Name:processNameChar(c); - break; - case EscapedName:endMode(); - addCharToPattern(c); - return true; - default: - case Tag: - case QuotedName: - if (processOtherChar(c)) - return true; - break; - } - - m_substring += c; - if (!isControlChar(c)) { - m_patternName += c; - m_realPatternPos++; - } - return true; -} -// Two of the processing methods return true to signal the caller to return -// without adding the given character to the current pattern strings -bool TestSpecParser::processNoneChar(char c) { - switch (c) { - case ' ':return true; - case '~':m_exclusion = true; - return false; - case '[':startNewMode(Tag); - return false; - case '"':startNewMode(QuotedName); - return false; - default:startNewMode(Name); - return false; - } -} -void TestSpecParser::processNameChar(char c) { - if (c == '[') { - if (m_substring == "exclude:") - m_exclusion = true; - else - endMode(); - startNewMode(Tag); - } -} -bool TestSpecParser::processOtherChar(char c) { - if (!isControlChar(c)) - return false; - m_substring += c; - endMode(); - return true; -} -void TestSpecParser::startNewMode(Mode mode) { - m_mode = mode; -} -void TestSpecParser::endMode() { - switch (m_mode) { - case Name: - case QuotedName:return addNamePattern(); - case Tag:return addTagPattern(); - case EscapedName:revertBackToLastMode(); - return; - case None: - default:return startNewMode(None); - } -} -void TestSpecParser::escape() { - saveLastMode(); - m_mode = EscapedName; - m_escapeChars.push_back(m_realPatternPos); -} -bool TestSpecParser::isControlChar(char c) const { - switch (m_mode) { - default:return false; - case None:return c == '~'; - case Name:return c == '['; - case EscapedName:return true; - case QuotedName:return c == '"'; - case Tag:return c == '[' || c == ']'; - } -} + void TestSpecParser::escape() { + saveLastMode(); + m_mode = EscapedName; + m_escapeChars.push_back(m_realPatternPos); + } -void TestSpecParser::addFilter() { - if (!m_currentFilter.m_patterns.empty()) { - m_testSpec.m_filters.push_back(m_currentFilter); - m_currentFilter = TestSpec::Filter(); - } -} + bool TestSpecParser::isControlChar(char c) const { + switch (m_mode) { + default: + return false; + case None: + return c == '~'; + case Name: + return c == '['; + case EscapedName: + return true; + case QuotedName: + return c == '"'; + case Tag: + return c == '[' || c == ']'; + } + } -void TestSpecParser::saveLastMode() { - lastMode = m_mode; -} + void TestSpecParser::addFilter() { + if (!m_currentFilter.m_patterns.empty()) { + m_testSpec.m_filters.push_back(m_currentFilter); + m_currentFilter = TestSpec::Filter(); + } + } -void TestSpecParser::revertBackToLastMode() { - m_mode = lastMode; -} + void TestSpecParser::saveLastMode() { + lastMode = m_mode; + } -bool TestSpecParser::separate() { - if ((m_mode == QuotedName) || (m_mode == Tag)) { - //invalid argument, signal failure to previous scope. - m_mode = None; - m_pos = m_arg.size(); - m_substring.clear(); - m_patternName.clear(); - m_realPatternPos = 0; - return false; - } - endMode(); - addFilter(); - return true; //success -} + void TestSpecParser::revertBackToLastMode() { + m_mode = lastMode; + } -std::string TestSpecParser::preprocessPattern() { - std::string token = m_patternName; - for (std::size_t i = 0; i < m_escapeChars.size(); ++i) - token = token.substr(0, m_escapeChars[i] - i) + token.substr(m_escapeChars[i] - i + 1); - m_escapeChars.clear(); - if (startsWith(token, "exclude:")) { - m_exclusion = true; - token = token.substr(8); - } + bool TestSpecParser::separate() { + if ((m_mode == QuotedName) || (m_mode == Tag)) { + //invalid argument, signal failure to previous scope. + m_mode = None; + m_pos = m_arg.size(); + m_substring.clear(); + m_patternName.clear(); + m_realPatternPos = 0; + return false; + } + endMode(); + addFilter(); + return true; //success + } - m_patternName.clear(); - m_realPatternPos = 0; + std::string TestSpecParser::preprocessPattern() { + std::string token = m_patternName; + for (std::size_t i = 0; i < m_escapeChars.size(); ++i) + token = token.substr(0, m_escapeChars[i] - i) + token.substr(m_escapeChars[i] - i + 1); + m_escapeChars.clear(); + if (startsWith(token, "exclude:")) { + m_exclusion = true; + token = token.substr(8); + } - return token; -} + m_patternName.clear(); + m_realPatternPos = 0; -void TestSpecParser::addNamePattern() { - auto token = preprocessPattern(); - - if (!token.empty()) { - TestSpec::PatternPtr pattern = std::make_shared(token, m_substring); - if (m_exclusion) - pattern = std::make_shared(pattern); - m_currentFilter.m_patterns.push_back(pattern); - } - m_substring.clear(); - m_exclusion = false; - m_mode = None; -} + return token; + } -void TestSpecParser::addTagPattern() { - auto token = preprocessPattern(); + void TestSpecParser::addNamePattern() { + auto token = preprocessPattern(); - if (!token.empty()) { - // If the tag pattern is the "hide and tag" shorthand (e.g. [.foo]) - // we have to create a separate hide tag and shorten the real one - if (token.size() > 1 && token[0] == '.') { - token.erase(token.begin()); - TestSpec::PatternPtr pattern = std::make_shared(".", m_substring); - if (m_exclusion) { - pattern = std::make_shared(pattern); - } - m_currentFilter.m_patterns.push_back(pattern); + if (!token.empty()) { + TestSpec::PatternPtr pattern = std::make_shared(token, m_substring); + if (m_exclusion) + pattern = std::make_shared(pattern); + m_currentFilter.m_patterns.push_back(pattern); + } + m_substring.clear(); + m_exclusion = false; + m_mode = None; } - TestSpec::PatternPtr pattern = std::make_shared(token, m_substring); + void TestSpecParser::addTagPattern() { + auto token = preprocessPattern(); + + if (!token.empty()) { + // If the tag pattern is the "hide and tag" shorthand (e.g. [.foo]) + // we have to create a separate hide tag and shorten the real one + if (token.size() > 1 && token[0] == '.') { + token.erase(token.begin()); + TestSpec::PatternPtr pattern = std::make_shared(".", m_substring); + if (m_exclusion) { + pattern = std::make_shared(pattern); + } + m_currentFilter.m_patterns.push_back(pattern); + } - if (m_exclusion) { - pattern = std::make_shared(pattern); + TestSpec::PatternPtr pattern = std::make_shared(token, m_substring); + + if (m_exclusion) { + pattern = std::make_shared(pattern); + } + m_currentFilter.m_patterns.push_back(pattern); + } + m_substring.clear(); + m_exclusion = false; + m_mode = None; } - m_currentFilter.m_patterns.push_back(pattern); - } - m_substring.clear(); - m_exclusion = false; - m_mode = None; -} -TestSpec parseTestSpec(std::string const &arg) { - return TestSpecParser(ITagAliasRegistry::get()).parse(arg).testSpec(); -} + TestSpec parseTestSpec(std::string const &arg) { + return TestSpecParser(ITagAliasRegistry::get()).parse(arg).testSpec(); + } } // namespace Catch // end catch_test_spec_parser.cpp @@ -14902,61 +15734,67 @@ static const uint64_t nanosecondsInSecond = 1000000000; namespace Catch { -auto getCurrentNanosecondsSinceEpoch() -> uint64_t { - return std::chrono::duration_cast(std::chrono::high_resolution_clock::now().time_since_epoch()).count(); -} + auto getCurrentNanosecondsSinceEpoch() -> uint64_t { + return std::chrono::duration_cast( + std::chrono::high_resolution_clock::now().time_since_epoch()).count(); + } -namespace { -auto estimateClockResolution() -> uint64_t { - uint64_t sum = 0; - static const uint64_t iterations = 1000000; + namespace { + auto estimateClockResolution() -> uint64_t { + uint64_t sum = 0; + static const uint64_t iterations = 1000000; + + auto startTime = getCurrentNanosecondsSinceEpoch(); - auto startTime = getCurrentNanosecondsSinceEpoch(); + for (std::size_t i = 0; i < iterations; ++i) { - for (std::size_t i = 0; i < iterations; ++i) { + uint64_t ticks; + uint64_t baseTicks = getCurrentNanosecondsSinceEpoch(); + do { + ticks = getCurrentNanosecondsSinceEpoch(); + } while (ticks == baseTicks); - uint64_t ticks; - uint64_t baseTicks = getCurrentNanosecondsSinceEpoch(); - do { - ticks = getCurrentNanosecondsSinceEpoch(); - } while (ticks == baseTicks); + auto delta = ticks - baseTicks; + sum += delta; - auto delta = ticks - baseTicks; - sum += delta; + // If we have been calibrating for over 3 seconds -- the clock + // is terrible and we should move on. + // TBD: How to signal that the measured resolution is probably wrong? + if (ticks > startTime + 3 * nanosecondsInSecond) { + return sum / (i + 1u); + } + } - // If we have been calibrating for over 3 seconds -- the clock - // is terrible and we should move on. - // TBD: How to signal that the measured resolution is probably wrong? - if (ticks > startTime + 3 * nanosecondsInSecond) { - return sum / (i + 1u); + // We're just taking the mean, here. To do better we could take the std. dev and exclude outliers + // - and potentially do more iterations if there's a high variance. + return sum / iterations; + } } - } - // We're just taking the mean, here. To do better we could take the std. dev and exclude outliers - // - and potentially do more iterations if there's a high variance. - return sum / iterations; -} -} -auto getEstimatedClockResolution() -> uint64_t { - static auto s_resolution = estimateClockResolution(); - return s_resolution; -} + auto getEstimatedClockResolution() -> uint64_t { + static auto s_resolution = estimateClockResolution(); + return s_resolution; + } -void Timer::start() { - m_nanoseconds = getCurrentNanosecondsSinceEpoch(); -} -auto Timer::getElapsedNanoseconds() const -> uint64_t { - return getCurrentNanosecondsSinceEpoch() - m_nanoseconds; -} -auto Timer::getElapsedMicroseconds() const -> uint64_t { - return getElapsedNanoseconds() / 1000; -} -auto Timer::getElapsedMilliseconds() const -> unsigned int { - return static_cast(getElapsedMicroseconds() / 1000); -} -auto Timer::getElapsedSeconds() const -> double { - return getElapsedMicroseconds() / 1000000.0; -} + void Timer::start() { + m_nanoseconds = getCurrentNanosecondsSinceEpoch(); + } + + auto Timer::getElapsedNanoseconds() const -> uint64_t { + return getCurrentNanosecondsSinceEpoch() - m_nanoseconds; + } + + auto Timer::getElapsedMicroseconds() const -> uint64_t { + return getElapsedNanoseconds() / 1000; + } + + auto Timer::getElapsedMilliseconds() const -> unsigned int { + return static_cast(getElapsedMicroseconds() / 1000); + } + + auto Timer::getElapsedSeconds() const -> double { + return getElapsedMicroseconds() / 1000000.0; + } } // namespace Catch // end catch_timer.cpp @@ -14978,62 +15816,64 @@ auto Timer::getElapsedSeconds() const -> double { namespace Catch { -namespace Detail { + namespace Detail { -const std::string unprintableString = "{?}"; + const std::string unprintableString = "{?}"; -namespace { -const int hexThreshold = 255; - -struct Endianness { - enum Arch { Big, Little }; - - static Arch which() { - int one = 1; - // If the lowest byte we read is non-zero, we can assume - // that little endian format is used. - auto value = *reinterpret_cast(&one); - return value ? Little : Big; - } -}; -} + namespace { + const int hexThreshold = 255; -std::string rawMemoryToString(const void *object, std::size_t size) { - // Reverse order for little endian architectures - int i = 0, end = static_cast( size ), inc = 1; - if (Endianness::which() == Endianness::Little) { - i = end - 1; - end = inc = -1; - } - - unsigned char const *bytes = static_cast(object); - ReusableStringStream rss; - rss << "0x" << std::setfill('0') << std::hex; - for (; i != end; i += inc) - rss << std::setw(2) << static_cast(bytes[i]); - return rss.str(); -} -} + struct Endianness { + enum Arch { + Big, Little + }; -template -std::string fpToString(T value, int precision) { - if (Catch::isnan(value)) { - return "nan"; - } - - ReusableStringStream rss; - rss << std::setprecision(precision) - << std::fixed - << value; - std::string d = rss.str(); - std::size_t i = d.find_last_not_of('0'); - if (i != std::string::npos && i != d.size() - 1) { - if (d[i] == '.') - i++; - d = d.substr(0, i + 1); - } - return d; -} + static Arch which() { + int one = 1; + // If the lowest byte we read is non-zero, we can assume + // that little endian format is used. + auto value = *reinterpret_cast(&one); + return value ? Little : Big; + } + }; + } + + std::string rawMemoryToString(const void *object, std::size_t size) { + // Reverse order for little endian architectures + int i = 0, end = static_cast( size ), inc = 1; + if (Endianness::which() == Endianness::Little) { + i = end - 1; + end = inc = -1; + } + + unsigned char const *bytes = static_cast(object); + ReusableStringStream rss; + rss << "0x" << std::setfill('0') << std::hex; + for (; i != end; i += inc) + rss << std::setw(2) << static_cast(bytes[i]); + return rss.str(); + } + } + + template + std::string fpToString(T value, int precision) { + if (Catch::isnan(value)) { + return "nan"; + } + + ReusableStringStream rss; + rss << std::setprecision(precision) + << std::fixed + << value; + std::string d = rss.str(); + std::size_t i = d.find_last_not_of('0'); + if (i != std::string::npos && i != d.size() - 1) { + if (d[i] == '.') + i++; + d = d.substr(0, i + 1); + } + return d; + } //// ======================================================= //// // @@ -15041,166 +15881,191 @@ std::string fpToString(T value, int precision) { // //// ======================================================= //// -std::string StringMaker::convert(const std::string &str) { - if (!getCurrentContext().getConfig()->showInvisibles()) { - return '"' + str + '"'; - } - - std::string s("\""); - for (char c : str) { - switch (c) { - case '\n':s.append("\\n"); - break; - case '\t':s.append("\\t"); - break; - default:s.push_back(c); - break; - } - } - s.append("\""); - return s; -} + std::string StringMaker::convert(const std::string &str) { + if (!getCurrentContext().getConfig()->showInvisibles()) { + return '"' + str + '"'; + } + + std::string s("\""); + for (char c: str) { + switch (c) { + case '\n': + s.append("\\n"); + break; + case '\t': + s.append("\\t"); + break; + default: + s.push_back(c); + break; + } + } + s.append("\""); + return s; + } #ifdef CATCH_CONFIG_CPP17_STRING_VIEW -std::string StringMaker::convert(std::string_view str) { - return ::Catch::Detail::stringify(std::string{str}); -} + + std::string StringMaker::convert(std::string_view str) { + return ::Catch::Detail::stringify(std::string{str}); + } + #endif -std::string StringMaker::convert(char const *str) { - if (str) { - return ::Catch::Detail::stringify(std::string{str}); - } else { - return {"{null string}"}; - } -} -std::string StringMaker::convert(char *str) { - if (str) { - return ::Catch::Detail::stringify(std::string{str}); - } else { - return {"{null string}"}; - } -} + std::string StringMaker::convert(char const *str) { + if (str) { + return ::Catch::Detail::stringify(std::string{str}); + } else { + return {"{null string}"}; + } + } + + std::string StringMaker::convert(char *str) { + if (str) { + return ::Catch::Detail::stringify(std::string{str}); + } else { + return {"{null string}"}; + } + } #ifdef CATCH_CONFIG_WCHAR -std::string StringMaker::convert(const std::wstring &wstr) { - std::string s; - s.reserve(wstr.size()); - for (auto c : wstr) { - s += (c <= 0xff) ? static_cast(c) : '?'; - } - return ::Catch::Detail::stringify(s); -} + + std::string StringMaker::convert(const std::wstring &wstr) { + std::string s; + s.reserve(wstr.size()); + for (auto c: wstr) { + s += (c <= 0xff) ? static_cast(c) : '?'; + } + return ::Catch::Detail::stringify(s); + } # ifdef CATCH_CONFIG_CPP17_STRING_VIEW -std::string StringMaker::convert(std::wstring_view str) { - return StringMaker::convert(std::wstring(str)); -} + + std::string StringMaker::convert(std::wstring_view str) { + return StringMaker::convert(std::wstring(str)); + } + # endif -std::string StringMaker::convert(wchar_t const *str) { - if (str) { - return ::Catch::Detail::stringify(std::wstring{str}); - } else { - return {"{null string}"}; - } -} -std::string StringMaker::convert(wchar_t *str) { - if (str) { - return ::Catch::Detail::stringify(std::wstring{str}); - } else { - return {"{null string}"}; - } -} + std::string StringMaker::convert(wchar_t const *str) { + if (str) { + return ::Catch::Detail::stringify(std::wstring{str}); + } else { + return {"{null string}"}; + } + } + + std::string StringMaker::convert(wchar_t *str) { + if (str) { + return ::Catch::Detail::stringify(std::wstring{str}); + } else { + return {"{null string}"}; + } + } + #endif #if defined(CATCH_CONFIG_CPP17_BYTE) + #include -std::string StringMaker::convert(std::byte value) { - return ::Catch::Detail::stringify(std::to_integer(value)); -} + + std::string StringMaker::convert(std::byte value) { + return ::Catch::Detail::stringify(std::to_integer(value)); + } + #endif // defined(CATCH_CONFIG_CPP17_BYTE) -std::string StringMaker::convert(int value) { - return ::Catch::Detail::stringify(static_cast(value)); -} -std::string StringMaker::convert(long value) { - return ::Catch::Detail::stringify(static_cast(value)); -} -std::string StringMaker::convert(long long value) { - ReusableStringStream rss; - rss << value; - if (value > Detail::hexThreshold) { - rss << " (0x" << std::hex << value << ')'; - } - return rss.str(); -} + std::string StringMaker::convert(int value) { + return ::Catch::Detail::stringify(static_cast(value)); + } -std::string StringMaker::convert(unsigned int value) { - return ::Catch::Detail::stringify(static_cast(value)); -} -std::string StringMaker::convert(unsigned long value) { - return ::Catch::Detail::stringify(static_cast(value)); -} -std::string StringMaker::convert(unsigned long long value) { - ReusableStringStream rss; - rss << value; - if (value > Detail::hexThreshold) { - rss << " (0x" << std::hex << value << ')'; - } - return rss.str(); -} + std::string StringMaker::convert(long value) { + return ::Catch::Detail::stringify(static_cast(value)); + } -std::string StringMaker::convert(bool b) { - return b ? "true" : "false"; -} + std::string StringMaker::convert(long long value) { + ReusableStringStream rss; + rss << value; + if (value > Detail::hexThreshold) { + rss << " (0x" << std::hex << value << ')'; + } + return rss.str(); + } -std::string StringMaker::convert(signed char value) { - if (value == '\r') { - return "'\\r'"; - } else if (value == '\f') { - return "'\\f'"; - } else if (value == '\n') { - return "'\\n'"; - } else if (value == '\t') { - return "'\\t'"; - } else if ('\0' <= value && value < ' ') { - return ::Catch::Detail::stringify(static_cast(value)); - } else { - char chstr[] = "' '"; - chstr[1] = value; - return chstr; - } -} -std::string StringMaker::convert(char c) { - return ::Catch::Detail::stringify(static_cast(c)); -} -std::string StringMaker::convert(unsigned char c) { - return ::Catch::Detail::stringify(static_cast(c)); -} + std::string StringMaker::convert(unsigned int value) { + return ::Catch::Detail::stringify(static_cast(value)); + } -std::string StringMaker::convert(std::nullptr_t) { - return "nullptr"; -} + std::string StringMaker::convert(unsigned long value) { + return ::Catch::Detail::stringify(static_cast(value)); + } + + std::string StringMaker::convert(unsigned long long value) { + ReusableStringStream rss; + rss << value; + if (value > Detail::hexThreshold) { + rss << " (0x" << std::hex << value << ')'; + } + return rss.str(); + } -int StringMaker::precision = 5; + std::string StringMaker::convert(bool b) { + return b ? "true" : "false"; + } -std::string StringMaker::convert(float value) { - return fpToString(value, precision) + 'f'; -} + std::string StringMaker::convert(signed char value) { + if (value == '\r') { + return "'\\r'"; + } else if (value == '\f') { + return "'\\f'"; + } else if (value == '\n') { + return "'\\n'"; + } else if (value == '\t') { + return "'\\t'"; + } else if ('\0' <= value && value < ' ') { + return ::Catch::Detail::stringify(static_cast(value)); + } else { + char chstr[] = "' '"; + chstr[1] = value; + return chstr; + } + } + + std::string StringMaker::convert(char c) { + return ::Catch::Detail::stringify(static_cast(c)); + } -int StringMaker::precision = 10; + std::string StringMaker::convert(unsigned char c) { + return ::Catch::Detail::stringify(static_cast(c)); + } -std::string StringMaker::convert(double value) { - return fpToString(value, precision); -} + std::string StringMaker::convert(std::nullptr_t) { + return "nullptr"; + } -std::string ratio_string::symbol() { return "a"; } -std::string ratio_string::symbol() { return "f"; } -std::string ratio_string::symbol() { return "p"; } -std::string ratio_string::symbol() { return "n"; } -std::string ratio_string::symbol() { return "u"; } -std::string ratio_string::symbol() { return "m"; } + int StringMaker::precision = 5; + + std::string StringMaker::convert(float value) { + return fpToString(value, precision) + 'f'; + } + + int StringMaker::precision = 10; + + std::string StringMaker::convert(double value) { + return fpToString(value, precision); + } + + std::string ratio_string::symbol() { return "a"; } + + std::string ratio_string::symbol() { return "f"; } + + std::string ratio_string::symbol() { return "p"; } + + std::string ratio_string::symbol() { return "n"; } + + std::string ratio_string::symbol() { return "u"; } + + std::string ratio_string::symbol() { return "m"; } } // end namespace Catch @@ -15213,54 +16078,56 @@ std::string ratio_string::symbol() { return "m"; } namespace Catch { -Counts Counts::operator-(Counts const &other) const { - Counts diff; - diff.passed = passed - other.passed; - diff.failed = failed - other.failed; - diff.failedButOk = failedButOk - other.failedButOk; - return diff; -} + Counts Counts::operator-(Counts const &other) const { + Counts diff; + diff.passed = passed - other.passed; + diff.failed = failed - other.failed; + diff.failedButOk = failedButOk - other.failedButOk; + return diff; + } -Counts &Counts::operator+=(Counts const &other) { - passed += other.passed; - failed += other.failed; - failedButOk += other.failedButOk; - return *this; -} + Counts &Counts::operator+=(Counts const &other) { + passed += other.passed; + failed += other.failed; + failedButOk += other.failedButOk; + return *this; + } -std::size_t Counts::total() const { - return passed + failed + failedButOk; -} -bool Counts::allPassed() const { - return failed == 0 && failedButOk == 0; -} -bool Counts::allOk() const { - return failed == 0; -} + std::size_t Counts::total() const { + return passed + failed + failedButOk; + } -Totals Totals::operator-(Totals const &other) const { - Totals diff; - diff.assertions = assertions - other.assertions; - diff.testCases = testCases - other.testCases; - return diff; -} + bool Counts::allPassed() const { + return failed == 0 && failedButOk == 0; + } -Totals &Totals::operator+=(Totals const &other) { - assertions += other.assertions; - testCases += other.testCases; - return *this; -} + bool Counts::allOk() const { + return failed == 0; + } -Totals Totals::delta(Totals const &prevTotals) const { - Totals diff = *this - prevTotals; - if (diff.assertions.failed > 0) - ++diff.testCases.failed; - else if (diff.assertions.failedButOk > 0) - ++diff.testCases.failedButOk; - else - ++diff.testCases.passed; - return diff; -} + Totals Totals::operator-(Totals const &other) const { + Totals diff; + diff.assertions = assertions - other.assertions; + diff.testCases = testCases - other.testCases; + return diff; + } + + Totals &Totals::operator+=(Totals const &other) { + assertions += other.assertions; + testCases += other.testCases; + return *this; + } + + Totals Totals::delta(Totals const &prevTotals) const { + Totals diff = *this - prevTotals; + if (diff.assertions.failed > 0) + ++diff.testCases.failed; + else if (diff.assertions.failedButOk > 0) + ++diff.testCases.failedButOk; + else + ++diff.testCases.passed; + return diff; + } } // end catch_totals.cpp @@ -15304,15 +16171,15 @@ Totals Totals::delta(Totals const &prevTotals) const { #include namespace Catch { -bool uncaught_exceptions() { + bool uncaught_exceptions() { #if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) - return false; + return false; #elif defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) - return std::uncaught_exceptions() > 0; + return std::uncaught_exceptions() > 0; #else - return std::uncaught_exception(); + return std::uncaught_exception(); #endif -} + } } // end namespace Catch // end catch_uncaught_exceptions.cpp // start catch_version.cpp @@ -15321,34 +16188,34 @@ bool uncaught_exceptions() { namespace Catch { -Version::Version - (unsigned int _majorVersion, - unsigned int _minorVersion, - unsigned int _patchNumber, - char const *const _branchName, - unsigned int _buildNumber) - : majorVersion(_majorVersion), - minorVersion(_minorVersion), - patchNumber(_patchNumber), - branchName(_branchName), - buildNumber(_buildNumber) {} - -std::ostream &operator<<(std::ostream &os, Version const &version) { - os << version.majorVersion << '.' - << version.minorVersion << '.' - << version.patchNumber; - // branchName is never null -> 0th char is \0 if it is empty - if (version.branchName[0]) { - os << '-' << version.branchName - << '.' << version.buildNumber; - } - return os; -} + Version::Version + (unsigned int _majorVersion, + unsigned int _minorVersion, + unsigned int _patchNumber, + char const *const _branchName, + unsigned int _buildNumber) + : majorVersion(_majorVersion), + minorVersion(_minorVersion), + patchNumber(_patchNumber), + branchName(_branchName), + buildNumber(_buildNumber) {} + + std::ostream &operator<<(std::ostream &os, Version const &version) { + os << version.majorVersion << '.' + << version.minorVersion << '.' + << version.patchNumber; + // branchName is never null -> 0th char is \0 if it is empty + if (version.branchName[0]) { + os << '-' << version.branchName + << '.' << version.buildNumber; + } + return os; + } -Version const &libraryVersion() { - static Version version(2, 13, 10, "", 0); - return version; -} + Version const &libraryVersion() { + static Version version(2, 13, 10, "", 0); + return version; + } } // end catch_version.cpp @@ -15356,355 +16223,363 @@ Version const &libraryVersion() { namespace Catch { -WildcardPattern::WildcardPattern(std::string const &pattern, - CaseSensitive::Choice caseSensitivity) - : m_caseSensitivity(caseSensitivity), - m_pattern(normaliseString(pattern)) { - if (startsWith(m_pattern, '*')) { - m_pattern = m_pattern.substr(1); - m_wildcard = WildcardAtStart; - } - if (endsWith(m_pattern, '*')) { - m_pattern = m_pattern.substr(0, m_pattern.size() - 1); - m_wildcard = static_cast( m_wildcard | WildcardAtEnd ); - } -} + WildcardPattern::WildcardPattern(std::string const &pattern, + CaseSensitive::Choice caseSensitivity) + : m_caseSensitivity(caseSensitivity), + m_pattern(normaliseString(pattern)) { + if (startsWith(m_pattern, '*')) { + m_pattern = m_pattern.substr(1); + m_wildcard = WildcardAtStart; + } + if (endsWith(m_pattern, '*')) { + m_pattern = m_pattern.substr(0, m_pattern.size() - 1); + m_wildcard = static_cast( m_wildcard | WildcardAtEnd ); + } + } + + bool WildcardPattern::matches(std::string const &str) const { + switch (m_wildcard) { + case NoWildcard: + return m_pattern == normaliseString(str); + case WildcardAtStart: + return endsWith(normaliseString(str), m_pattern); + case WildcardAtEnd: + return startsWith(normaliseString(str), m_pattern); + case WildcardAtBothEnds: + return contains(normaliseString(str), m_pattern); + default: + CATCH_INTERNAL_ERROR("Unknown enum"); + } + } + + std::string WildcardPattern::normaliseString(std::string const &str) const { + return trim(m_caseSensitivity == CaseSensitive::No ? toLower(str) : str); + } +} +// end catch_wildcard_pattern.cpp +// start catch_xmlwriter.cpp + +#include +#include + +namespace Catch { + + namespace { + + size_t trailingBytes(unsigned char c) { + if ((c & 0xE0) == 0xC0) { + return 2; + } + if ((c & 0xF0) == 0xE0) { + return 3; + } + if ((c & 0xF8) == 0xF0) { + return 4; + } + CATCH_INTERNAL_ERROR("Invalid multibyte utf-8 start byte encountered"); + } + + uint32_t headerValue(unsigned char c) { + if ((c & 0xE0) == 0xC0) { + return c & 0x1F; + } + if ((c & 0xF0) == 0xE0) { + return c & 0x0F; + } + if ((c & 0xF8) == 0xF0) { + return c & 0x07; + } + CATCH_INTERNAL_ERROR("Invalid multibyte utf-8 start byte encountered"); + } + + void hexEscapeChar(std::ostream &os, unsigned char c) { + std::ios_base::fmtflags f(os.flags()); + os << "\\x" + << std::uppercase << std::hex << std::setfill('0') << std::setw(2) + << static_cast(c); + os.flags(f); + } -bool WildcardPattern::matches(std::string const &str) const { - switch (m_wildcard) { - case NoWildcard:return m_pattern == normaliseString(str); - case WildcardAtStart:return endsWith(normaliseString(str), m_pattern); - case WildcardAtEnd:return startsWith(normaliseString(str), m_pattern); - case WildcardAtBothEnds:return contains(normaliseString(str), m_pattern); - default:CATCH_INTERNAL_ERROR("Unknown enum"); - } -} + bool shouldNewline(XmlFormatting fmt) { + return !!(static_cast::type>(fmt & XmlFormatting::Newline)); + } -std::string WildcardPattern::normaliseString(std::string const &str) const { - return trim(m_caseSensitivity == CaseSensitive::No ? toLower(str) : str); -} -} -// end catch_wildcard_pattern.cpp -// start catch_xmlwriter.cpp + bool shouldIndent(XmlFormatting fmt) { + return !!(static_cast::type>(fmt & XmlFormatting::Indent)); + } -#include -#include + } // anonymous namespace -namespace Catch { + XmlFormatting operator|(XmlFormatting lhs, XmlFormatting rhs) { + return static_cast( + static_cast::type>(lhs) | + static_cast::type>(rhs) + ); + } -namespace { + XmlFormatting operator&(XmlFormatting lhs, XmlFormatting rhs) { + return static_cast( + static_cast::type>(lhs) & + static_cast::type>(rhs) + ); + } -size_t trailingBytes(unsigned char c) { - if ((c & 0xE0) == 0xC0) { - return 2; - } - if ((c & 0xF0) == 0xE0) { - return 3; - } - if ((c & 0xF8) == 0xF0) { - return 4; - } - CATCH_INTERNAL_ERROR("Invalid multibyte utf-8 start byte encountered"); -} + XmlEncode::XmlEncode(std::string const &str, ForWhat forWhat) + : m_str(str), + m_forWhat(forWhat) {} + + void XmlEncode::encodeTo(std::ostream &os) const { + // Apostrophe escaping not necessary if we always use " to write attributes + // (see: http://www.w3.org/TR/xml/#syntax) + + for (std::size_t idx = 0; idx < m_str.size(); ++idx) { + unsigned char c = m_str[idx]; + switch (c) { + case '<': + os << "<"; + break; + case '&': + os << "&"; + break; + + case '>': + // See: http://www.w3.org/TR/xml/#syntax + if (idx > 2 && m_str[idx - 1] == ']' && m_str[idx - 2] == ']') + os << ">"; + else + os << c; + break; + + case '\"': + if (m_forWhat == ForAttributes) + os << """; + else + os << c; + break; -uint32_t headerValue(unsigned char c) { - if ((c & 0xE0) == 0xC0) { - return c & 0x1F; - } - if ((c & 0xF0) == 0xE0) { - return c & 0x0F; - } - if ((c & 0xF8) == 0xF0) { - return c & 0x07; - } - CATCH_INTERNAL_ERROR("Invalid multibyte utf-8 start byte encountered"); -} + default: + // Check for control characters and invalid utf-8 -void hexEscapeChar(std::ostream &os, unsigned char c) { - std::ios_base::fmtflags f(os.flags()); - os << "\\x" - << std::uppercase << std::hex << std::setfill('0') << std::setw(2) - << static_cast(c); - os.flags(f); -} + // Escape control characters in standard ascii + // see http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0 + if (c < 0x09 || (c > 0x0D && c < 0x20) || c == 0x7F) { + hexEscapeChar(os, c); + break; + } -bool shouldNewline(XmlFormatting fmt) { - return !!(static_cast::type>(fmt & XmlFormatting::Newline)); -} + // Plain ASCII: Write it to stream + if (c < 0x7F) { + os << c; + break; + } -bool shouldIndent(XmlFormatting fmt) { - return !!(static_cast::type>(fmt & XmlFormatting::Indent)); -} + // UTF-8 territory + // Check if the encoding is valid and if it is not, hex escape bytes. + // Important: We do not check the exact decoded values for validity, only the encoding format + // First check that this bytes is a valid lead byte: + // This means that it is not encoded as 1111 1XXX + // Or as 10XX XXXX + if (c < 0xC0 || + c >= 0xF8) { + hexEscapeChar(os, c); + break; + } -} // anonymous namespace + auto encBytes = trailingBytes(c); + // Are there enough bytes left to avoid accessing out-of-bounds memory? + if (idx + encBytes - 1 >= m_str.size()) { + hexEscapeChar(os, c); + break; + } + // The header is valid, check data + // The next encBytes bytes must together be a valid utf-8 + // This means: bitpattern 10XX XXXX and the extracted value is sane (ish) + bool valid = true; + uint32_t value = headerValue(c); + for (std::size_t n = 1; n < encBytes; ++n) { + unsigned char nc = m_str[idx + n]; + valid &= ((nc & 0xC0) == 0x80); + value = (value << 6) | (nc & 0x3F); + } -XmlFormatting operator|(XmlFormatting lhs, XmlFormatting rhs) { - return static_cast( - static_cast::type>(lhs) | - static_cast::type>(rhs) - ); -} + if ( + // Wrong bit pattern of following bytes + (!valid) || + // Overlong encodings + (value < 0x80) || + (0x80 <= value && value < 0x800 && encBytes > 2) || + (0x800 < value && value < 0x10000 && encBytes > 3) || + // Encoded value out of range + (value >= 0x110000) + ) { + hexEscapeChar(os, c); + break; + } -XmlFormatting operator&(XmlFormatting lhs, XmlFormatting rhs) { - return static_cast( - static_cast::type>(lhs) & - static_cast::type>(rhs) - ); -} + // If we got here, this is in fact a valid(ish) utf-8 sequence + for (std::size_t n = 0; n < encBytes; ++n) { + os << m_str[idx + n]; + } + idx += encBytes - 1; + break; + } + } + } -XmlEncode::XmlEncode(std::string const &str, ForWhat forWhat) - : m_str(str), - m_forWhat(forWhat) {} - -void XmlEncode::encodeTo(std::ostream &os) const { - // Apostrophe escaping not necessary if we always use " to write attributes - // (see: http://www.w3.org/TR/xml/#syntax) - - for (std::size_t idx = 0; idx < m_str.size(); ++idx) { - unsigned char c = m_str[idx]; - switch (c) { - case '<': os << "<"; - break; - case '&': os << "&"; - break; - - case '>': - // See: http://www.w3.org/TR/xml/#syntax - if (idx > 2 && m_str[idx - 1] == ']' && m_str[idx - 2] == ']') - os << ">"; - else - os << c; - break; + std::ostream &operator<<(std::ostream &os, XmlEncode const &xmlEncode) { + xmlEncode.encodeTo(os); + return os; + } - case '\"': - if (m_forWhat == ForAttributes) - os << """; - else - os << c; - break; - - default: - // Check for control characters and invalid utf-8 - - // Escape control characters in standard ascii - // see http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0 - if (c < 0x09 || (c > 0x0D && c < 0x20) || c == 0x7F) { - hexEscapeChar(os, c); - break; - } - - // Plain ASCII: Write it to stream - if (c < 0x7F) { - os << c; - break; - } - - // UTF-8 territory - // Check if the encoding is valid and if it is not, hex escape bytes. - // Important: We do not check the exact decoded values for validity, only the encoding format - // First check that this bytes is a valid lead byte: - // This means that it is not encoded as 1111 1XXX - // Or as 10XX XXXX - if (c < 0xC0 || - c >= 0xF8) { - hexEscapeChar(os, c); - break; - } - - auto encBytes = trailingBytes(c); - // Are there enough bytes left to avoid accessing out-of-bounds memory? - if (idx + encBytes - 1 >= m_str.size()) { - hexEscapeChar(os, c); - break; - } - // The header is valid, check data - // The next encBytes bytes must together be a valid utf-8 - // This means: bitpattern 10XX XXXX and the extracted value is sane (ish) - bool valid = true; - uint32_t value = headerValue(c); - for (std::size_t n = 1; n < encBytes; ++n) { - unsigned char nc = m_str[idx + n]; - valid &= ((nc & 0xC0) == 0x80); - value = (value << 6) | (nc & 0x3F); - } - - if ( - // Wrong bit pattern of following bytes - (!valid) || - // Overlong encodings - (value < 0x80) || - (0x80 <= value && value < 0x800 && encBytes > 2) || - (0x800 < value && value < 0x10000 && encBytes > 3) || - // Encoded value out of range - (value >= 0x110000) - ) { - hexEscapeChar(os, c); - break; - } - - // If we got here, this is in fact a valid(ish) utf-8 sequence - for (std::size_t n = 0; n < encBytes; ++n) { - os << m_str[idx + n]; - } - idx += encBytes - 1; - break; - } - } -} + XmlWriter::ScopedElement::ScopedElement(XmlWriter *writer, XmlFormatting fmt) + : m_writer(writer), + m_fmt(fmt) {} -std::ostream &operator<<(std::ostream &os, XmlEncode const &xmlEncode) { - xmlEncode.encodeTo(os); - return os; -} + XmlWriter::ScopedElement::ScopedElement(ScopedElement &&other) noexcept + : m_writer(other.m_writer), + m_fmt(other.m_fmt) { + other.m_writer = nullptr; + other.m_fmt = XmlFormatting::None; + } -XmlWriter::ScopedElement::ScopedElement(XmlWriter *writer, XmlFormatting fmt) - : m_writer(writer), - m_fmt(fmt) {} + XmlWriter::ScopedElement &XmlWriter::ScopedElement::operator=(ScopedElement &&other) noexcept { + if (m_writer) { + m_writer->endElement(); + } + m_writer = other.m_writer; + other.m_writer = nullptr; + m_fmt = other.m_fmt; + other.m_fmt = XmlFormatting::None; + return *this; + } -XmlWriter::ScopedElement::ScopedElement(ScopedElement &&other) noexcept - : m_writer(other.m_writer), - m_fmt(other.m_fmt) { - other.m_writer = nullptr; - other.m_fmt = XmlFormatting::None; -} -XmlWriter::ScopedElement &XmlWriter::ScopedElement::operator=(ScopedElement &&other) noexcept { - if (m_writer) { - m_writer->endElement(); - } - m_writer = other.m_writer; - other.m_writer = nullptr; - m_fmt = other.m_fmt; - other.m_fmt = XmlFormatting::None; - return *this; -} + XmlWriter::ScopedElement::~ScopedElement() { + if (m_writer) { + m_writer->endElement(m_fmt); + } + } -XmlWriter::ScopedElement::~ScopedElement() { - if (m_writer) { - m_writer->endElement(m_fmt); - } -} + XmlWriter::ScopedElement &XmlWriter::ScopedElement::writeText(std::string const &text, XmlFormatting fmt) { + m_writer->writeText(text, fmt); + return *this; + } -XmlWriter::ScopedElement &XmlWriter::ScopedElement::writeText(std::string const &text, XmlFormatting fmt) { - m_writer->writeText(text, fmt); - return *this; -} + XmlWriter::XmlWriter(std::ostream &os) : m_os(os) { + writeDeclaration(); + } -XmlWriter::XmlWriter(std::ostream &os) : m_os(os) { - writeDeclaration(); -} + XmlWriter::~XmlWriter() { + while (!m_tags.empty()) { + endElement(); + } + newlineIfNecessary(); + } -XmlWriter::~XmlWriter() { - while (!m_tags.empty()) { - endElement(); - } - newlineIfNecessary(); -} + XmlWriter &XmlWriter::startElement(std::string const &name, XmlFormatting fmt) { + ensureTagClosed(); + newlineIfNecessary(); + if (shouldIndent(fmt)) { + m_os << m_indent; + m_indent += " "; + } + m_os << '<' << name; + m_tags.push_back(name); + m_tagIsOpen = true; + applyFormatting(fmt); + return *this; + } -XmlWriter &XmlWriter::startElement(std::string const &name, XmlFormatting fmt) { - ensureTagClosed(); - newlineIfNecessary(); - if (shouldIndent(fmt)) { - m_os << m_indent; - m_indent += " "; - } - m_os << '<' << name; - m_tags.push_back(name); - m_tagIsOpen = true; - applyFormatting(fmt); - return *this; -} + XmlWriter::ScopedElement XmlWriter::scopedElement(std::string const &name, XmlFormatting fmt) { + ScopedElement scoped(this, fmt); + startElement(name, fmt); + return scoped; + } -XmlWriter::ScopedElement XmlWriter::scopedElement(std::string const &name, XmlFormatting fmt) { - ScopedElement scoped(this, fmt); - startElement(name, fmt); - return scoped; -} + XmlWriter &XmlWriter::endElement(XmlFormatting fmt) { + m_indent = m_indent.substr(0, m_indent.size() - 2); -XmlWriter &XmlWriter::endElement(XmlFormatting fmt) { - m_indent = m_indent.substr(0, m_indent.size() - 2); - - if (m_tagIsOpen) { - m_os << "/>"; - m_tagIsOpen = false; - } else { - newlineIfNecessary(); - if (shouldIndent(fmt)) { - m_os << m_indent; - } - m_os << ""; - } - m_os << std::flush; - applyFormatting(fmt); - m_tags.pop_back(); - return *this; -} + if (m_tagIsOpen) { + m_os << "/>"; + m_tagIsOpen = false; + } else { + newlineIfNecessary(); + if (shouldIndent(fmt)) { + m_os << m_indent; + } + m_os << ""; + } + m_os << std::flush; + applyFormatting(fmt); + m_tags.pop_back(); + return *this; + } -XmlWriter &XmlWriter::writeAttribute(std::string const &name, std::string const &attribute) { - if (!name.empty() && !attribute.empty()) - m_os << ' ' << name << "=\"" << XmlEncode(attribute, XmlEncode::ForAttributes) << '"'; - return *this; -} + XmlWriter &XmlWriter::writeAttribute(std::string const &name, std::string const &attribute) { + if (!name.empty() && !attribute.empty()) + m_os << ' ' << name << "=\"" << XmlEncode(attribute, XmlEncode::ForAttributes) << '"'; + return *this; + } -XmlWriter &XmlWriter::writeAttribute(std::string const &name, bool attribute) { - m_os << ' ' << name << "=\"" << (attribute ? "true" : "false") << '"'; - return *this; -} + XmlWriter &XmlWriter::writeAttribute(std::string const &name, bool attribute) { + m_os << ' ' << name << "=\"" << (attribute ? "true" : "false") << '"'; + return *this; + } -XmlWriter &XmlWriter::writeText(std::string const &text, XmlFormatting fmt) { - if (!text.empty()) { - bool tagWasOpen = m_tagIsOpen; - ensureTagClosed(); - if (tagWasOpen && shouldIndent(fmt)) { - m_os << m_indent; - } - m_os << XmlEncode(text); - applyFormatting(fmt); - } - return *this; -} + XmlWriter &XmlWriter::writeText(std::string const &text, XmlFormatting fmt) { + if (!text.empty()) { + bool tagWasOpen = m_tagIsOpen; + ensureTagClosed(); + if (tagWasOpen && shouldIndent(fmt)) { + m_os << m_indent; + } + m_os << XmlEncode(text); + applyFormatting(fmt); + } + return *this; + } -XmlWriter &XmlWriter::writeComment(std::string const &text, XmlFormatting fmt) { - ensureTagClosed(); - if (shouldIndent(fmt)) { - m_os << m_indent; - } - m_os << ""; - applyFormatting(fmt); - return *this; -} + XmlWriter &XmlWriter::writeComment(std::string const &text, XmlFormatting fmt) { + ensureTagClosed(); + if (shouldIndent(fmt)) { + m_os << m_indent; + } + m_os << ""; + applyFormatting(fmt); + return *this; + } -void XmlWriter::writeStylesheetRef(std::string const &url) { - m_os << "\n"; -} + void XmlWriter::writeStylesheetRef(std::string const &url) { + m_os << "\n"; + } -XmlWriter &XmlWriter::writeBlankLine() { - ensureTagClosed(); - m_os << '\n'; - return *this; -} + XmlWriter &XmlWriter::writeBlankLine() { + ensureTagClosed(); + m_os << '\n'; + return *this; + } -void XmlWriter::ensureTagClosed() { - if (m_tagIsOpen) { - m_os << '>' << std::flush; - newlineIfNecessary(); - m_tagIsOpen = false; - } -} + void XmlWriter::ensureTagClosed() { + if (m_tagIsOpen) { + m_os << '>' << std::flush; + newlineIfNecessary(); + m_tagIsOpen = false; + } + } -void XmlWriter::applyFormatting(XmlFormatting fmt) { - m_needsNewline = shouldNewline(fmt); -} + void XmlWriter::applyFormatting(XmlFormatting fmt) { + m_needsNewline = shouldNewline(fmt); + } -void XmlWriter::writeDeclaration() { - m_os << "\n"; -} + void XmlWriter::writeDeclaration() { + m_os << "\n"; + } -void XmlWriter::newlineIfNecessary() { - if (m_needsNewline) { - m_os << std::endl; - m_needsNewline = false; - } -} + void XmlWriter::newlineIfNecessary() { + if (m_needsNewline) { + m_os << std::endl; + m_needsNewline = false; + } + } } // end catch_xmlwriter.cpp // start catch_reporter_bases.cpp @@ -15716,67 +16591,67 @@ void XmlWriter::newlineIfNecessary() { #include namespace Catch { -void prepareExpandedExpression(AssertionResult &result) { - result.getExpandedExpression(); -} + void prepareExpandedExpression(AssertionResult &result) { + result.getExpandedExpression(); + } // Because formatting using c++ streams is stateful, drop down to C is required // Alternatively we could use stringstream, but its performance is... not good. -std::string getFormattedDuration(double duration) { - // Max exponent + 1 is required to represent the whole part - // + 1 for decimal point - // + 3 for the 3 decimal places - // + 1 for null terminator - const std::size_t maxDoubleSize = DBL_MAX_10_EXP + 1 + 1 + 3 + 1; - char buffer[maxDoubleSize]; - - // Save previous errno, to prevent sprintf from overwriting it - ErrnoGuard guard; + std::string getFormattedDuration(double duration) { + // Max exponent + 1 is required to represent the whole part + // + 1 for decimal point + // + 3 for the 3 decimal places + // + 1 for null terminator + const std::size_t maxDoubleSize = DBL_MAX_10_EXP + 1 + 1 + 3 + 1; + char buffer[maxDoubleSize]; + + // Save previous errno, to prevent sprintf from overwriting it + ErrnoGuard guard; #ifdef _MSC_VER - sprintf_s(buffer, "%.3f", duration); + sprintf_s(buffer, "%.3f", duration); #else - std::sprintf(buffer, "%.3f", duration); + std::sprintf(buffer, "%.3f", duration); #endif - return std::string(buffer); -} + return std::string(buffer); + } -bool shouldShowDuration(IConfig const &config, double duration) { - if (config.showDurations() == ShowDurations::Always) { - return true; - } - if (config.showDurations() == ShowDurations::Never) { - return false; - } - const double min = config.minDuration(); - return min >= 0 && duration >= min; -} + bool shouldShowDuration(IConfig const &config, double duration) { + if (config.showDurations() == ShowDurations::Always) { + return true; + } + if (config.showDurations() == ShowDurations::Never) { + return false; + } + const double min = config.minDuration(); + return min >= 0 && duration >= min; + } -std::string serializeFilters(std::vector const &container) { - ReusableStringStream oss; - bool first = true; - for (auto &&filter : container) { - if (!first) - oss << ' '; - else - first = false; + std::string serializeFilters(std::vector const &container) { + ReusableStringStream oss; + bool first = true; + for (auto &&filter: container) { + if (!first) + oss << ' '; + else + first = false; - oss << filter; - } - return oss.str(); -} + oss << filter; + } + return oss.str(); + } -TestEventListenerBase::TestEventListenerBase(ReporterConfig const &_config) - : StreamingReporterBase(_config) {} + TestEventListenerBase::TestEventListenerBase(ReporterConfig const &_config) + : StreamingReporterBase(_config) {} -std::set TestEventListenerBase::getSupportedVerbosities() { - return {Verbosity::Quiet, Verbosity::Normal, Verbosity::High}; -} + std::set TestEventListenerBase::getSupportedVerbosities() { + return {Verbosity::Quiet, Verbosity::Normal, Verbosity::High}; + } -void TestEventListenerBase::assertionStarting(AssertionInfo const &) {} + void TestEventListenerBase::assertionStarting(AssertionInfo const &) {} -bool TestEventListenerBase::assertionEnded(AssertionStats const &) { - return false; -} + bool TestEventListenerBase::assertionEnded(AssertionStats const &) { + return false; + } } // end namespace Catch // end catch_reporter_bases.cpp @@ -15785,271 +16660,284 @@ bool TestEventListenerBase::assertionEnded(AssertionStats const &) { namespace { #ifdef CATCH_PLATFORM_MAC - const char* failedString() { return "FAILED"; } + const char* failedString() { return "FAILED"; } const char* passedString() { return "PASSED"; } #else -const char *failedString() { return "failed"; } -const char *passedString() { return "passed"; } + + const char *failedString() { return "failed"; } + + const char *passedString() { return "passed"; } + #endif // Colour::LightGrey -Catch::Colour::Code dimColour() { return Catch::Colour::FileName; } + Catch::Colour::Code dimColour() { return Catch::Colour::FileName; } -std::string bothOrAll(std::size_t count) { - return count == 1 ? std::string() : - count == 2 ? "both " : "all "; -} + std::string bothOrAll(std::size_t count) { + return count == 1 ? std::string() : + count == 2 ? "both " : "all "; + } } // anon namespace namespace Catch { -namespace { + namespace { // Colour, message variants: // - white: No tests ran. // - red: Failed [both/all] N test cases, failed [both/all] M assertions. // - white: Passed [both/all] N test cases (no assertions). // - red: Failed N tests cases, failed M assertions. // - green: Passed [both/all] N tests cases with M assertions. -void printTotals(std::ostream &out, const Totals &totals) { - if (totals.testCases.total() == 0) { - out << "No tests ran."; - } else if (totals.testCases.failed == totals.testCases.total()) { - Colour colour(Colour::ResultError); - const std::string qualify_assertions_failed = - totals.assertions.failed == totals.assertions.total() ? - bothOrAll(totals.assertions.failed) : std::string(); - out << - "Failed " << bothOrAll(totals.testCases.failed) - << pluralise(totals.testCases.failed, "test case") << ", " - "failed " << qualify_assertions_failed << - pluralise(totals.assertions.failed, "assertion") << '.'; - } else if (totals.assertions.total() == 0) { - out << - "Passed " << bothOrAll(totals.testCases.total()) - << pluralise(totals.testCases.total(), "test case") - << " (no assertions)."; - } else if (totals.assertions.failed) { - Colour colour(Colour::ResultError); - out << - "Failed " << pluralise(totals.testCases.failed, "test case") << ", " - "failed " - << pluralise(totals.assertions.failed, "assertion") << '.'; - } else { - Colour colour(Colour::ResultSuccess); - out << - "Passed " << bothOrAll(totals.testCases.passed) - << pluralise(totals.testCases.passed, "test case") << - " with " << pluralise(totals.assertions.passed, "assertion") << '.'; - } -} + void printTotals(std::ostream &out, const Totals &totals) { + if (totals.testCases.total() == 0) { + out << "No tests ran."; + } else if (totals.testCases.failed == totals.testCases.total()) { + Colour colour(Colour::ResultError); + const std::string qualify_assertions_failed = + totals.assertions.failed == totals.assertions.total() ? + bothOrAll(totals.assertions.failed) : std::string(); + out << + "Failed " << bothOrAll(totals.testCases.failed) + << pluralise(totals.testCases.failed, "test case") << ", " + "failed " << qualify_assertions_failed << + pluralise(totals.assertions.failed, "assertion") << '.'; + } else if (totals.assertions.total() == 0) { + out << + "Passed " << bothOrAll(totals.testCases.total()) + << pluralise(totals.testCases.total(), "test case") + << " (no assertions)."; + } else if (totals.assertions.failed) { + Colour colour(Colour::ResultError); + out << + "Failed " << pluralise(totals.testCases.failed, "test case") << ", " + "failed " + << pluralise(totals.assertions.failed, "assertion") << '.'; + } else { + Colour colour(Colour::ResultSuccess); + out << + "Passed " << bothOrAll(totals.testCases.passed) + << pluralise(totals.testCases.passed, "test case") << + " with " << pluralise(totals.assertions.passed, "assertion") << '.'; + } + } // Implementation of CompactReporter formatting -class AssertionPrinter { - public: - AssertionPrinter &operator=(AssertionPrinter const &) = delete; - AssertionPrinter(AssertionPrinter const &) = delete; - AssertionPrinter(std::ostream &_stream, AssertionStats const &_stats, bool _printInfoMessages) - : stream(_stream), - result(_stats.assertionResult), - messages(_stats.infoMessages), - itMessage(_stats.infoMessages.begin()), - printInfoMessages(_printInfoMessages) {} - - void print() { - printSourceInfo(); - - itMessage = messages.begin(); - - switch (result.getResultType()) { - case ResultWas::Ok:printResultType(Colour::ResultSuccess, passedString()); - printOriginalExpression(); - printReconstructedExpression(); - if (!result.hasExpression()) - printRemainingMessages(Colour::None); - else - printRemainingMessages(); - break; - case ResultWas::ExpressionFailed: - if (result.isOk()) - printResultType(Colour::ResultSuccess, failedString() + std::string(" - but was ok")); - else - printResultType(Colour::Error, failedString()); - printOriginalExpression(); - printReconstructedExpression(); - printRemainingMessages(); - break; - case ResultWas::ThrewException:printResultType(Colour::Error, failedString()); - printIssue("unexpected exception with message:"); - printMessage(); - printExpressionWas(); - printRemainingMessages(); - break; - case ResultWas::FatalErrorCondition:printResultType(Colour::Error, failedString()); - printIssue("fatal error condition with message:"); - printMessage(); - printExpressionWas(); - printRemainingMessages(); - break; - case ResultWas::DidntThrowException:printResultType(Colour::Error, failedString()); - printIssue("expected exception, got none"); - printExpressionWas(); - printRemainingMessages(); - break; - case ResultWas::Info:printResultType(Colour::None, "info"); - printMessage(); - printRemainingMessages(); - break; - case ResultWas::Warning:printResultType(Colour::None, "warning"); - printMessage(); - printRemainingMessages(); - break; - case ResultWas::ExplicitFailure:printResultType(Colour::Error, failedString()); - printIssue("explicitly"); - printRemainingMessages(Colour::None); - break; - // These cases are here to prevent compiler warnings - case ResultWas::Unknown: - case ResultWas::FailureBit: - case ResultWas::Exception:printResultType(Colour::Error, "** internal error **"); - break; - } - } - - private: - void printSourceInfo() const { - Colour colourGuard(Colour::FileName); - stream << result.getSourceInfo() << ':'; - } - - void printResultType(Colour::Code colour, std::string const &passOrFail) const { - if (!passOrFail.empty()) { - { - Colour colourGuard(colour); - stream << ' ' << passOrFail; - } - stream << ':'; - } - } - - void printIssue(std::string const &issue) const { - stream << ' ' << issue; - } - - void printExpressionWas() { - if (result.hasExpression()) { - stream << ';'; - { - Colour colour(dimColour()); - stream << " expression was:"; - } - printOriginalExpression(); - } - } - - void printOriginalExpression() const { - if (result.hasExpression()) { - stream << ' ' << result.getExpression(); - } - } - - void printReconstructedExpression() const { - if (result.hasExpandedExpression()) { - { - Colour colour(dimColour()); - stream << " for: "; - } - stream << result.getExpandedExpression(); - } - } - - void printMessage() { - if (itMessage != messages.end()) { - stream << " '" << itMessage->message << '\''; - ++itMessage; - } - } - - void printRemainingMessages(Colour::Code colour = dimColour()) { - if (itMessage == messages.end()) - return; - - const auto itEnd = messages.cend(); - const auto N = static_cast(std::distance(itMessage, itEnd)); + class AssertionPrinter { + public: + AssertionPrinter &operator=(AssertionPrinter const &) = delete; + + AssertionPrinter(AssertionPrinter const &) = delete; + + AssertionPrinter(std::ostream &_stream, AssertionStats const &_stats, bool _printInfoMessages) + : stream(_stream), + result(_stats.assertionResult), + messages(_stats.infoMessages), + itMessage(_stats.infoMessages.begin()), + printInfoMessages(_printInfoMessages) {} + + void print() { + printSourceInfo(); + + itMessage = messages.begin(); + + switch (result.getResultType()) { + case ResultWas::Ok: + printResultType(Colour::ResultSuccess, passedString()); + printOriginalExpression(); + printReconstructedExpression(); + if (!result.hasExpression()) + printRemainingMessages(Colour::None); + else + printRemainingMessages(); + break; + case ResultWas::ExpressionFailed: + if (result.isOk()) + printResultType(Colour::ResultSuccess, failedString() + std::string(" - but was ok")); + else + printResultType(Colour::Error, failedString()); + printOriginalExpression(); + printReconstructedExpression(); + printRemainingMessages(); + break; + case ResultWas::ThrewException: + printResultType(Colour::Error, failedString()); + printIssue("unexpected exception with message:"); + printMessage(); + printExpressionWas(); + printRemainingMessages(); + break; + case ResultWas::FatalErrorCondition: + printResultType(Colour::Error, failedString()); + printIssue("fatal error condition with message:"); + printMessage(); + printExpressionWas(); + printRemainingMessages(); + break; + case ResultWas::DidntThrowException: + printResultType(Colour::Error, failedString()); + printIssue("expected exception, got none"); + printExpressionWas(); + printRemainingMessages(); + break; + case ResultWas::Info: + printResultType(Colour::None, "info"); + printMessage(); + printRemainingMessages(); + break; + case ResultWas::Warning: + printResultType(Colour::None, "warning"); + printMessage(); + printRemainingMessages(); + break; + case ResultWas::ExplicitFailure: + printResultType(Colour::Error, failedString()); + printIssue("explicitly"); + printRemainingMessages(Colour::None); + break; + // These cases are here to prevent compiler warnings + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: + printResultType(Colour::Error, "** internal error **"); + break; + } + } - { - Colour colourGuard(colour); - stream << " with " << pluralise(N, "message") << ':'; - } - - while (itMessage != itEnd) { - // If this assertion is a warning ignore any INFO messages - if (printInfoMessages || itMessage->type != ResultWas::Info) { - printMessage(); - if (itMessage != itEnd) { - Colour colourGuard(dimColour()); - stream << " and"; - } - continue; - } - ++itMessage; - } - } - - private: - std::ostream &stream; - AssertionResult const &result; - std::vector messages; - std::vector::const_iterator itMessage; - bool printInfoMessages; -}; + private: + void printSourceInfo() const { + Colour colourGuard(Colour::FileName); + stream << result.getSourceInfo() << ':'; + } -} // anon namespace + void printResultType(Colour::Code colour, std::string const &passOrFail) const { + if (!passOrFail.empty()) { + { + Colour colourGuard(colour); + stream << ' ' << passOrFail; + } + stream << ':'; + } + } -std::string CompactReporter::getDescription() { - return "Reports test results on a single line, suitable for IDEs"; -} + void printIssue(std::string const &issue) const { + stream << ' ' << issue; + } -void CompactReporter::noMatchingTestCases(std::string const &spec) { - stream << "No test cases matched '" << spec << '\'' << std::endl; -} + void printExpressionWas() { + if (result.hasExpression()) { + stream << ';'; + { + Colour colour(dimColour()); + stream << " expression was:"; + } + printOriginalExpression(); + } + } + + void printOriginalExpression() const { + if (result.hasExpression()) { + stream << ' ' << result.getExpression(); + } + } + + void printReconstructedExpression() const { + if (result.hasExpandedExpression()) { + { + Colour colour(dimColour()); + stream << " for: "; + } + stream << result.getExpandedExpression(); + } + } -void CompactReporter::assertionStarting(AssertionInfo const &) {} + void printMessage() { + if (itMessage != messages.end()) { + stream << " '" << itMessage->message << '\''; + ++itMessage; + } + } -bool CompactReporter::assertionEnded(AssertionStats const &_assertionStats) { - AssertionResult const &result = _assertionStats.assertionResult; + void printRemainingMessages(Colour::Code colour = dimColour()) { + if (itMessage == messages.end()) + return; - bool printInfoMessages = true; + const auto itEnd = messages.cend(); + const auto N = static_cast(std::distance(itMessage, itEnd)); + + { + Colour colourGuard(colour); + stream << " with " << pluralise(N, "message") << ':'; + } - // Drop out if result was successful and we're not printing those - if (!m_config->includeSuccessfulResults() && result.isOk()) { - if (result.getResultType() != ResultWas::Warning) - return false; - printInfoMessages = false; - } + while (itMessage != itEnd) { + // If this assertion is a warning ignore any INFO messages + if (printInfoMessages || itMessage->type != ResultWas::Info) { + printMessage(); + if (itMessage != itEnd) { + Colour colourGuard(dimColour()); + stream << " and"; + } + continue; + } + ++itMessage; + } + } - AssertionPrinter printer(stream, _assertionStats, printInfoMessages); - printer.print(); + private: + std::ostream &stream; + AssertionResult const &result; + std::vector messages; + std::vector::const_iterator itMessage; + bool printInfoMessages; + }; - stream << std::endl; - return true; -} + } // anon namespace -void CompactReporter::sectionEnded(SectionStats const &_sectionStats) { - double dur = _sectionStats.durationInSeconds; - if (shouldShowDuration(*m_config, dur)) { - stream << getFormattedDuration(dur) << " s: " << _sectionStats.sectionInfo.name << std::endl; - } -} + std::string CompactReporter::getDescription() { + return "Reports test results on a single line, suitable for IDEs"; + } -void CompactReporter::testRunEnded(TestRunStats const &_testRunStats) { - printTotals(stream, _testRunStats.totals); - stream << '\n' << std::endl; - StreamingReporterBase::testRunEnded(_testRunStats); -} + void CompactReporter::noMatchingTestCases(std::string const &spec) { + stream << "No test cases matched '" << spec << '\'' << std::endl; + } -CompactReporter::~CompactReporter() {} + void CompactReporter::assertionStarting(AssertionInfo const &) {} -CATCH_REGISTER_REPORTER("compact", CompactReporter) + bool CompactReporter::assertionEnded(AssertionStats const &_assertionStats) { + AssertionResult const &result = _assertionStats.assertionResult; + + bool printInfoMessages = true; + + // Drop out if result was successful and we're not printing those + if (!m_config->includeSuccessfulResults() && result.isOk()) { + if (result.getResultType() != ResultWas::Warning) + return false; + printInfoMessages = false; + } + + AssertionPrinter printer(stream, _assertionStats, printInfoMessages); + printer.print(); + + stream << std::endl; + return true; + } + + void CompactReporter::sectionEnded(SectionStats const &_sectionStats) { + double dur = _sectionStats.durationInSeconds; + if (shouldShowDuration(*m_config, dur)) { + stream << getFormattedDuration(dur) << " s: " << _sectionStats.sectionInfo.name << std::endl; + } + } + + void CompactReporter::testRunEnded(TestRunStats const &_testRunStats) { + printTotals(stream, _testRunStats.totals); + stream << '\n' << std::endl; + StreamingReporterBase::testRunEnded(_testRunStats); + } + + CompactReporter::~CompactReporter() {} + + CATCH_REGISTER_REPORTER("compact", CompactReporter) } // end namespace Catch // end catch_reporter_compact.cpp @@ -16072,381 +16960,417 @@ CATCH_REGISTER_REPORTER("compact", CompactReporter) namespace Catch { -namespace { + namespace { // Formatter impl for ConsoleReporter -class ConsoleAssertionPrinter { - public: - ConsoleAssertionPrinter &operator=(ConsoleAssertionPrinter const &) = delete; - ConsoleAssertionPrinter(ConsoleAssertionPrinter const &) = delete; - ConsoleAssertionPrinter(std::ostream &_stream, AssertionStats const &_stats, bool _printInfoMessages) - : stream(_stream), - stats(_stats), - result(_stats.assertionResult), - colour(Colour::None), - message(result.getMessage()), - messages(_stats.infoMessages), - printInfoMessages(_printInfoMessages) { - switch (result.getResultType()) { - case ResultWas::Ok:colour = Colour::Success; - passOrFail = "PASSED"; - //if( result.hasMessage() ) - if (_stats.infoMessages.size() == 1) - messageLabel = "with message"; - if (_stats.infoMessages.size() > 1) - messageLabel = "with messages"; - break; - case ResultWas::ExpressionFailed: - if (result.isOk()) { - colour = Colour::Success; - passOrFail = "FAILED - but was ok"; - } else { - colour = Colour::Error; - passOrFail = "FAILED"; - } - if (_stats.infoMessages.size() == 1) - messageLabel = "with message"; - if (_stats.infoMessages.size() > 1) - messageLabel = "with messages"; - break; - case ResultWas::ThrewException:colour = Colour::Error; - passOrFail = "FAILED"; - messageLabel = "due to unexpected exception with "; - if (_stats.infoMessages.size() == 1) - messageLabel += "message"; - if (_stats.infoMessages.size() > 1) - messageLabel += "messages"; - break; - case ResultWas::FatalErrorCondition:colour = Colour::Error; - passOrFail = "FAILED"; - messageLabel = "due to a fatal error condition"; - break; - case ResultWas::DidntThrowException:colour = Colour::Error; - passOrFail = "FAILED"; - messageLabel = "because no exception was thrown where one was expected"; - break; - case ResultWas::Info:messageLabel = "info"; - break; - case ResultWas::Warning:messageLabel = "warning"; - break; - case ResultWas::ExplicitFailure:passOrFail = "FAILED"; - colour = Colour::Error; - if (_stats.infoMessages.size() == 1) - messageLabel = "explicitly with message"; - if (_stats.infoMessages.size() > 1) - messageLabel = "explicitly with messages"; - break; - // These cases are here to prevent compiler warnings - case ResultWas::Unknown: - case ResultWas::FailureBit: - case ResultWas::Exception:passOrFail = "** internal error **"; - colour = Colour::Error; - break; - } - } - - void print() const { - printSourceInfo(); - if (stats.totals.assertions.total() > 0) { - printResultType(); - printOriginalExpression(); - printReconstructedExpression(); - } else { - stream << '\n'; - } - printMessage(); - } - - private: - void printResultType() const { - if (!passOrFail.empty()) { - Colour colourGuard(colour); - stream << passOrFail << ":\n"; - } - } - void printOriginalExpression() const { - if (result.hasExpression()) { - Colour colourGuard(Colour::OriginalExpression); - stream << " "; - stream << result.getExpressionInMacro(); - stream << '\n'; - } - } - void printReconstructedExpression() const { - if (result.hasExpandedExpression()) { - stream << "with expansion:\n"; - Colour colourGuard(Colour::ReconstructedExpression); - stream << Column(result.getExpandedExpression()).indent(2) << '\n'; - } - } - void printMessage() const { - if (!messageLabel.empty()) - stream << messageLabel << ':' << '\n'; - for (auto const &msg : messages) { - // If this assertion is a warning ignore any INFO messages - if (printInfoMessages || msg.type != ResultWas::Info) - stream << Column(msg.message).indent(2) << '\n'; - } - } - void printSourceInfo() const { - Colour colourGuard(Colour::FileName); - stream << result.getSourceInfo() << ": "; - } - - std::ostream &stream; - AssertionStats const &stats; - AssertionResult const &result; - Colour::Code colour; - std::string passOrFail; - std::string messageLabel; - std::string message; - std::vector messages; - bool printInfoMessages; -}; + class ConsoleAssertionPrinter { + public: + ConsoleAssertionPrinter &operator=(ConsoleAssertionPrinter const &) = delete; + + ConsoleAssertionPrinter(ConsoleAssertionPrinter const &) = delete; + + ConsoleAssertionPrinter(std::ostream &_stream, AssertionStats const &_stats, bool _printInfoMessages) + : stream(_stream), + stats(_stats), + result(_stats.assertionResult), + colour(Colour::None), + message(result.getMessage()), + messages(_stats.infoMessages), + printInfoMessages(_printInfoMessages) { + switch (result.getResultType()) { + case ResultWas::Ok: + colour = Colour::Success; + passOrFail = "PASSED"; + //if( result.hasMessage() ) + if (_stats.infoMessages.size() == 1) + messageLabel = "with message"; + if (_stats.infoMessages.size() > 1) + messageLabel = "with messages"; + break; + case ResultWas::ExpressionFailed: + if (result.isOk()) { + colour = Colour::Success; + passOrFail = "FAILED - but was ok"; + } else { + colour = Colour::Error; + passOrFail = "FAILED"; + } + if (_stats.infoMessages.size() == 1) + messageLabel = "with message"; + if (_stats.infoMessages.size() > 1) + messageLabel = "with messages"; + break; + case ResultWas::ThrewException: + colour = Colour::Error; + passOrFail = "FAILED"; + messageLabel = "due to unexpected exception with "; + if (_stats.infoMessages.size() == 1) + messageLabel += "message"; + if (_stats.infoMessages.size() > 1) + messageLabel += "messages"; + break; + case ResultWas::FatalErrorCondition: + colour = Colour::Error; + passOrFail = "FAILED"; + messageLabel = "due to a fatal error condition"; + break; + case ResultWas::DidntThrowException: + colour = Colour::Error; + passOrFail = "FAILED"; + messageLabel = "because no exception was thrown where one was expected"; + break; + case ResultWas::Info: + messageLabel = "info"; + break; + case ResultWas::Warning: + messageLabel = "warning"; + break; + case ResultWas::ExplicitFailure: + passOrFail = "FAILED"; + colour = Colour::Error; + if (_stats.infoMessages.size() == 1) + messageLabel = "explicitly with message"; + if (_stats.infoMessages.size() > 1) + messageLabel = "explicitly with messages"; + break; + // These cases are here to prevent compiler warnings + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: + passOrFail = "** internal error **"; + colour = Colour::Error; + break; + } + } -std::size_t makeRatio(std::size_t number, std::size_t total) { - std::size_t ratio = total > 0 ? CATCH_CONFIG_CONSOLE_WIDTH * number / total : 0; - return (ratio == 0 && number > 0) ? 1 : ratio; -} + void print() const { + printSourceInfo(); + if (stats.totals.assertions.total() > 0) { + printResultType(); + printOriginalExpression(); + printReconstructedExpression(); + } else { + stream << '\n'; + } + printMessage(); + } -std::size_t &findMax(std::size_t &i, std::size_t &j, std::size_t &k) { - if (i > j && i > k) - return i; - else if (j > k) - return j; - else - return k; -} + private: + void printResultType() const { + if (!passOrFail.empty()) { + Colour colourGuard(colour); + stream << passOrFail << ":\n"; + } + } -struct ColumnInfo { - enum Justification { Left, Right }; - std::string name; - int width; - Justification justification; -}; -struct ColumnBreak {}; -struct RowBreak {}; - -class Duration { - enum class Unit { - Auto, - Nanoseconds, - Microseconds, - Milliseconds, - Seconds, - Minutes - }; - static const uint64_t s_nanosecondsInAMicrosecond = 1000; - static const uint64_t s_nanosecondsInAMillisecond = 1000 * s_nanosecondsInAMicrosecond; - static const uint64_t s_nanosecondsInASecond = 1000 * s_nanosecondsInAMillisecond; - static const uint64_t s_nanosecondsInAMinute = 60 * s_nanosecondsInASecond; - - double m_inNanoseconds; - Unit m_units; - - public: - explicit Duration(double inNanoseconds, Unit units = Unit::Auto) - : m_inNanoseconds(inNanoseconds), - m_units(units) { - if (m_units == Unit::Auto) { - if (m_inNanoseconds < s_nanosecondsInAMicrosecond) - m_units = Unit::Nanoseconds; - else if (m_inNanoseconds < s_nanosecondsInAMillisecond) - m_units = Unit::Microseconds; - else if (m_inNanoseconds < s_nanosecondsInASecond) - m_units = Unit::Milliseconds; - else if (m_inNanoseconds < s_nanosecondsInAMinute) - m_units = Unit::Seconds; - else - m_units = Unit::Minutes; - } - - } - - auto value() const -> double { - switch (m_units) { - case Unit::Microseconds:return m_inNanoseconds / static_cast(s_nanosecondsInAMicrosecond); - case Unit::Milliseconds:return m_inNanoseconds / static_cast(s_nanosecondsInAMillisecond); - case Unit::Seconds:return m_inNanoseconds / static_cast(s_nanosecondsInASecond); - case Unit::Minutes:return m_inNanoseconds / static_cast(s_nanosecondsInAMinute); - default:return m_inNanoseconds; - } - } - auto unitsAsString() const -> std::string { - switch (m_units) { - case Unit::Nanoseconds:return "ns"; - case Unit::Microseconds:return "us"; - case Unit::Milliseconds:return "ms"; - case Unit::Seconds:return "s"; - case Unit::Minutes:return "m"; - default:return "** internal error **"; - } - - } - friend auto operator<<(std::ostream &os, Duration const &duration) -> std::ostream & { - return os << duration.value() << ' ' << duration.unitsAsString(); - } -}; -} // end anon namespace + void printOriginalExpression() const { + if (result.hasExpression()) { + Colour colourGuard(Colour::OriginalExpression); + stream << " "; + stream << result.getExpressionInMacro(); + stream << '\n'; + } + } -class TablePrinter { - std::ostream &m_os; - std::vector m_columnInfos; - std::ostringstream m_oss; - int m_currentColumn = -1; - bool m_isOpen = false; - - public: - TablePrinter(std::ostream &os, std::vector columnInfos) - : m_os(os), - m_columnInfos(std::move(columnInfos)) {} - - auto columnInfos() const -> std::vector const & { - return m_columnInfos; - } - - void open() { - if (!m_isOpen) { - m_isOpen = true; - *this << RowBreak(); - - Columns headerCols; - Spacer spacer(2); - for (auto const &info : m_columnInfos) { - headerCols += Column(info.name).width(static_cast(info.width - 2)); - headerCols += spacer; - } - m_os << headerCols << '\n'; - - m_os << Catch::getLineOfChars<'-'>() << '\n'; - } - } - void close() { - if (m_isOpen) { - *this << RowBreak(); - m_os << std::endl; - m_isOpen = false; - } - } - - template - friend TablePrinter &operator<<(TablePrinter &tp, T const &value) { - tp.m_oss << value; - return tp; - } - - friend TablePrinter &operator<<(TablePrinter &tp, ColumnBreak) { - auto colStr = tp.m_oss.str(); - const auto strSize = colStr.size(); - tp.m_oss.str(""); - tp.open(); - if (tp.m_currentColumn == static_cast(tp.m_columnInfos.size() - 1)) { - tp.m_currentColumn = -1; - tp.m_os << '\n'; - } - tp.m_currentColumn++; - - auto colInfo = tp.m_columnInfos[tp.m_currentColumn]; - auto padding = (strSize + 1 < static_cast(colInfo.width)) - ? std::string(colInfo.width - (strSize + 1), ' ') - : std::string(); - if (colInfo.justification == ColumnInfo::Left) - tp.m_os << colStr << padding << ' '; - else - tp.m_os << padding << colStr << ' '; - return tp; - } + void printReconstructedExpression() const { + if (result.hasExpandedExpression()) { + stream << "with expansion:\n"; + Colour colourGuard(Colour::ReconstructedExpression); + stream << Column(result.getExpandedExpression()).indent(2) << '\n'; + } + } + + void printMessage() const { + if (!messageLabel.empty()) + stream << messageLabel << ':' << '\n'; + for (auto const &msg: messages) { + // If this assertion is a warning ignore any INFO messages + if (printInfoMessages || msg.type != ResultWas::Info) + stream << Column(msg.message).indent(2) << '\n'; + } + } + + void printSourceInfo() const { + Colour colourGuard(Colour::FileName); + stream << result.getSourceInfo() << ": "; + } + + std::ostream &stream; + AssertionStats const &stats; + AssertionResult const &result; + Colour::Code colour; + std::string passOrFail; + std::string messageLabel; + std::string message; + std::vector messages; + bool printInfoMessages; + }; + + std::size_t makeRatio(std::size_t number, std::size_t total) { + std::size_t ratio = total > 0 ? CATCH_CONFIG_CONSOLE_WIDTH * number / total : 0; + return (ratio == 0 && number > 0) ? 1 : ratio; + } + + std::size_t &findMax(std::size_t &i, std::size_t &j, std::size_t &k) { + if (i > j && i > k) + return i; + else if (j > k) + return j; + else + return k; + } + + struct ColumnInfo { + enum Justification { + Left, Right + }; + std::string name; + int width; + Justification justification; + }; + struct ColumnBreak { + }; + struct RowBreak { + }; + + class Duration { + enum class Unit { + Auto, + Nanoseconds, + Microseconds, + Milliseconds, + Seconds, + Minutes + }; + static const uint64_t s_nanosecondsInAMicrosecond = 1000; + static const uint64_t s_nanosecondsInAMillisecond = 1000 * s_nanosecondsInAMicrosecond; + static const uint64_t s_nanosecondsInASecond = 1000 * s_nanosecondsInAMillisecond; + static const uint64_t s_nanosecondsInAMinute = 60 * s_nanosecondsInASecond; + + double m_inNanoseconds; + Unit m_units; + + public: + explicit Duration(double inNanoseconds, Unit units = Unit::Auto) + : m_inNanoseconds(inNanoseconds), + m_units(units) { + if (m_units == Unit::Auto) { + if (m_inNanoseconds < s_nanosecondsInAMicrosecond) + m_units = Unit::Nanoseconds; + else if (m_inNanoseconds < s_nanosecondsInAMillisecond) + m_units = Unit::Microseconds; + else if (m_inNanoseconds < s_nanosecondsInASecond) + m_units = Unit::Milliseconds; + else if (m_inNanoseconds < s_nanosecondsInAMinute) + m_units = Unit::Seconds; + else + m_units = Unit::Minutes; + } + + } + + auto value() const -> double { + switch (m_units) { + case Unit::Microseconds: + return m_inNanoseconds / static_cast(s_nanosecondsInAMicrosecond); + case Unit::Milliseconds: + return m_inNanoseconds / static_cast(s_nanosecondsInAMillisecond); + case Unit::Seconds: + return m_inNanoseconds / static_cast(s_nanosecondsInASecond); + case Unit::Minutes: + return m_inNanoseconds / static_cast(s_nanosecondsInAMinute); + default: + return m_inNanoseconds; + } + } + + auto unitsAsString() const -> std::string { + switch (m_units) { + case Unit::Nanoseconds: + return "ns"; + case Unit::Microseconds: + return "us"; + case Unit::Milliseconds: + return "ms"; + case Unit::Seconds: + return "s"; + case Unit::Minutes: + return "m"; + default: + return "** internal error **"; + } + + } + + friend auto operator<<(std::ostream &os, Duration const &duration) -> std::ostream & { + return os << duration.value() << ' ' << duration.unitsAsString(); + } + }; + } // end anon namespace + + class TablePrinter { + std::ostream &m_os; + std::vector m_columnInfos; + std::ostringstream m_oss; + int m_currentColumn = -1; + bool m_isOpen = false; + + public: + TablePrinter(std::ostream &os, std::vector columnInfos) + : m_os(os), + m_columnInfos(std::move(columnInfos)) {} + + auto columnInfos() const -> std::vector const & { + return m_columnInfos; + } + + void open() { + if (!m_isOpen) { + m_isOpen = true; + *this << RowBreak(); + + Columns headerCols; + Spacer spacer(2); + for (auto const &info: m_columnInfos) { + headerCols += Column(info.name).width(static_cast(info.width - 2)); + headerCols += spacer; + } + m_os << headerCols << '\n'; + + m_os << Catch::getLineOfChars<'-'>() << '\n'; + } + } + + void close() { + if (m_isOpen) { + *this << RowBreak(); + m_os << std::endl; + m_isOpen = false; + } + } + + template + friend TablePrinter &operator<<(TablePrinter &tp, T const &value) { + tp.m_oss << value; + return tp; + } + + friend TablePrinter &operator<<(TablePrinter &tp, ColumnBreak) { + auto colStr = tp.m_oss.str(); + const auto strSize = colStr.size(); + tp.m_oss.str(""); + tp.open(); + if (tp.m_currentColumn == static_cast(tp.m_columnInfos.size() - 1)) { + tp.m_currentColumn = -1; + tp.m_os << '\n'; + } + tp.m_currentColumn++; + + auto colInfo = tp.m_columnInfos[tp.m_currentColumn]; + auto padding = (strSize + 1 < static_cast(colInfo.width)) + ? std::string(colInfo.width - (strSize + 1), ' ') + : std::string(); + if (colInfo.justification == ColumnInfo::Left) + tp.m_os << colStr << padding << ' '; + else + tp.m_os << padding << colStr << ' '; + return tp; + } + + friend TablePrinter &operator<<(TablePrinter &tp, RowBreak) { + if (tp.m_currentColumn > 0) { + tp.m_os << '\n'; + tp.m_currentColumn = -1; + } + return tp; + } + }; - friend TablePrinter &operator<<(TablePrinter &tp, RowBreak) { - if (tp.m_currentColumn > 0) { - tp.m_os << '\n'; - tp.m_currentColumn = -1; + ConsoleReporter::ConsoleReporter(ReporterConfig const &config) + : StreamingReporterBase(config), + m_tablePrinter(new TablePrinter(config.stream(), + [&config]() -> std::vector { + if (config.fullConfig()->benchmarkNoAnalysis()) { + return { + {"benchmark name", CATCH_CONFIG_CONSOLE_WIDTH - + 43, ColumnInfo::Left}, + {" samples", 14, ColumnInfo::Right}, + {" iterations", 14, ColumnInfo::Right}, + {" mean", 14, ColumnInfo::Right} + }; + } else { + return { + {"benchmark name", CATCH_CONFIG_CONSOLE_WIDTH - + 43, ColumnInfo::Left}, + {"samples mean std dev", 14, ColumnInfo::Right}, + {"iterations low mean low std dev", 14, ColumnInfo::Right}, + {"estimated high mean high std dev", 14, ColumnInfo::Right} + }; + } + }())) {} + + ConsoleReporter::~ConsoleReporter() = default; + + std::string ConsoleReporter::getDescription() { + return "Reports test results as plain lines of text"; } - return tp; - } -}; -ConsoleReporter::ConsoleReporter(ReporterConfig const &config) - : StreamingReporterBase(config), - m_tablePrinter(new TablePrinter(config.stream(), - [&config]() -> std::vector { - if (config.fullConfig()->benchmarkNoAnalysis()) { - return { - {"benchmark name", CATCH_CONFIG_CONSOLE_WIDTH - 43, ColumnInfo::Left}, - {" samples", 14, ColumnInfo::Right}, - {" iterations", 14, ColumnInfo::Right}, - {" mean", 14, ColumnInfo::Right} - }; - } else { - return { - {"benchmark name", CATCH_CONFIG_CONSOLE_WIDTH - 43, ColumnInfo::Left}, - {"samples mean std dev", 14, ColumnInfo::Right}, - {"iterations low mean low std dev", 14, ColumnInfo::Right}, - {"estimated high mean high std dev", 14, ColumnInfo::Right} - }; - } - }())) {} -ConsoleReporter::~ConsoleReporter() = default; - -std::string ConsoleReporter::getDescription() { - return "Reports test results as plain lines of text"; -} + void ConsoleReporter::noMatchingTestCases(std::string const &spec) { + stream << "No test cases matched '" << spec << '\'' << std::endl; + } -void ConsoleReporter::noMatchingTestCases(std::string const &spec) { - stream << "No test cases matched '" << spec << '\'' << std::endl; -} + void ConsoleReporter::reportInvalidArguments(std::string const &arg) { + stream << "Invalid Filter: " << arg << std::endl; + } -void ConsoleReporter::reportInvalidArguments(std::string const &arg) { - stream << "Invalid Filter: " << arg << std::endl; -} + void ConsoleReporter::assertionStarting(AssertionInfo const &) {} -void ConsoleReporter::assertionStarting(AssertionInfo const &) {} + bool ConsoleReporter::assertionEnded(AssertionStats const &_assertionStats) { + AssertionResult const &result = _assertionStats.assertionResult; -bool ConsoleReporter::assertionEnded(AssertionStats const &_assertionStats) { - AssertionResult const &result = _assertionStats.assertionResult; + bool includeResults = m_config->includeSuccessfulResults() || !result.isOk(); - bool includeResults = m_config->includeSuccessfulResults() || !result.isOk(); + // Drop out if result was successful but we're not printing them. + if (!includeResults && result.getResultType() != ResultWas::Warning) + return false; - // Drop out if result was successful but we're not printing them. - if (!includeResults && result.getResultType() != ResultWas::Warning) - return false; + lazyPrint(); - lazyPrint(); + ConsoleAssertionPrinter printer(stream, _assertionStats, includeResults); + printer.print(); + stream << std::endl; + return true; + } - ConsoleAssertionPrinter printer(stream, _assertionStats, includeResults); - printer.print(); - stream << std::endl; - return true; -} + void ConsoleReporter::sectionStarting(SectionInfo const &_sectionInfo) { + m_tablePrinter->close(); + m_headerPrinted = false; + StreamingReporterBase::sectionStarting(_sectionInfo); + } -void ConsoleReporter::sectionStarting(SectionInfo const &_sectionInfo) { - m_tablePrinter->close(); - m_headerPrinted = false; - StreamingReporterBase::sectionStarting(_sectionInfo); -} -void ConsoleReporter::sectionEnded(SectionStats const &_sectionStats) { - m_tablePrinter->close(); - if (_sectionStats.missingAssertions) { - lazyPrint(); - Colour colour(Colour::ResultError); - if (m_sectionStack.size() > 1) - stream << "\nNo assertions in section"; - else - stream << "\nNo assertions in test case"; - stream << " '" << _sectionStats.sectionInfo.name << "'\n" << std::endl; - } - double dur = _sectionStats.durationInSeconds; - if (shouldShowDuration(*m_config, dur)) { - stream << getFormattedDuration(dur) << " s: " << _sectionStats.sectionInfo.name << std::endl; - } - if (m_headerPrinted) { - m_headerPrinted = false; - } - StreamingReporterBase::sectionEnded(_sectionStats); -} + void ConsoleReporter::sectionEnded(SectionStats const &_sectionStats) { + m_tablePrinter->close(); + if (_sectionStats.missingAssertions) { + lazyPrint(); + Colour colour(Colour::ResultError); + if (m_sectionStack.size() > 1) + stream << "\nNo assertions in section"; + else + stream << "\nNo assertions in test case"; + stream << " '" << _sectionStats.sectionInfo.name << "'\n" << std::endl; + } + double dur = _sectionStats.durationInSeconds; + if (shouldShowDuration(*m_config, dur)) { + stream << getFormattedDuration(dur) << " s: " << _sectionStats.sectionInfo.name << std::endl; + } + if (m_headerPrinted) { + m_headerPrinted = false; + } + StreamingReporterBase::sectionEnded(_sectionStats); + } #if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) - void ConsoleReporter::benchmarkPreparing(std::string const& name) { + void ConsoleReporter::benchmarkPreparing(std::string const& name) { lazyPrintWithoutClosingBenchmarkTable(); auto nameCol = Column(name).width(static_cast(m_tablePrinter->columnInfos()[0].width - 2)); @@ -16493,219 +17417,229 @@ void ConsoleReporter::benchmarkFailed(std::string const& error) { } #endif // CATCH_CONFIG_ENABLE_BENCHMARKING -void ConsoleReporter::testCaseEnded(TestCaseStats const &_testCaseStats) { - m_tablePrinter->close(); - StreamingReporterBase::testCaseEnded(_testCaseStats); - m_headerPrinted = false; -} -void ConsoleReporter::testGroupEnded(TestGroupStats const &_testGroupStats) { - if (currentGroupInfo.used) { - printSummaryDivider(); - stream << "Summary for group '" << _testGroupStats.groupInfo.name << "':\n"; - printTotals(_testGroupStats.totals); - stream << '\n' << std::endl; - } - StreamingReporterBase::testGroupEnded(_testGroupStats); -} -void ConsoleReporter::testRunEnded(TestRunStats const &_testRunStats) { - printTotalsDivider(_testRunStats.totals); - printTotals(_testRunStats.totals); - stream << std::endl; - StreamingReporterBase::testRunEnded(_testRunStats); -} -void ConsoleReporter::testRunStarting(TestRunInfo const &_testInfo) { - StreamingReporterBase::testRunStarting(_testInfo); - printTestFilters(); -} + void ConsoleReporter::testCaseEnded(TestCaseStats const &_testCaseStats) { + m_tablePrinter->close(); + StreamingReporterBase::testCaseEnded(_testCaseStats); + m_headerPrinted = false; + } -void ConsoleReporter::lazyPrint() { + void ConsoleReporter::testGroupEnded(TestGroupStats const &_testGroupStats) { + if (currentGroupInfo.used) { + printSummaryDivider(); + stream << "Summary for group '" << _testGroupStats.groupInfo.name << "':\n"; + printTotals(_testGroupStats.totals); + stream << '\n' << std::endl; + } + StreamingReporterBase::testGroupEnded(_testGroupStats); + } - m_tablePrinter->close(); - lazyPrintWithoutClosingBenchmarkTable(); -} + void ConsoleReporter::testRunEnded(TestRunStats const &_testRunStats) { + printTotalsDivider(_testRunStats.totals); + printTotals(_testRunStats.totals); + stream << std::endl; + StreamingReporterBase::testRunEnded(_testRunStats); + } -void ConsoleReporter::lazyPrintWithoutClosingBenchmarkTable() { + void ConsoleReporter::testRunStarting(TestRunInfo const &_testInfo) { + StreamingReporterBase::testRunStarting(_testInfo); + printTestFilters(); + } - if (!currentTestRunInfo.used) - lazyPrintRunInfo(); - if (!currentGroupInfo.used) - lazyPrintGroupInfo(); + void ConsoleReporter::lazyPrint() { - if (!m_headerPrinted) { - printTestCaseAndSectionHeader(); - m_headerPrinted = true; - } -} -void ConsoleReporter::lazyPrintRunInfo() { - stream << '\n' << getLineOfChars<'~'>() << '\n'; - Colour colour(Colour::SecondaryText); - stream << currentTestRunInfo->name - << " is a Catch v" << libraryVersion() << " host application.\n" - << "Run with -? for options\n\n"; + m_tablePrinter->close(); + lazyPrintWithoutClosingBenchmarkTable(); + } - if (m_config->rngSeed() != 0) - stream << "Randomness seeded to: " << m_config->rngSeed() << "\n\n"; + void ConsoleReporter::lazyPrintWithoutClosingBenchmarkTable() { - currentTestRunInfo.used = true; -} -void ConsoleReporter::lazyPrintGroupInfo() { - if (!currentGroupInfo->name.empty() && currentGroupInfo->groupsCounts > 1) { - printClosedHeader("Group: " + currentGroupInfo->name); - currentGroupInfo.used = true; - } -} -void ConsoleReporter::printTestCaseAndSectionHeader() { - assert(!m_sectionStack.empty()); - printOpenHeader(currentTestCaseInfo->name); - - if (m_sectionStack.size() > 1) { - Colour colourGuard(Colour::Headers); - - auto - it = m_sectionStack.begin() + 1, // Skip first section (test case) - itEnd = m_sectionStack.end(); - for (; it != itEnd; ++it) - printHeaderString(it->name, 2); - } - - SourceLineInfo lineInfo = m_sectionStack.back().lineInfo; - - stream << getLineOfChars<'-'>() << '\n'; - Colour colourGuard(Colour::FileName); - stream << lineInfo << '\n'; - stream << getLineOfChars<'.'>() << '\n' << std::endl; -} + if (!currentTestRunInfo.used) + lazyPrintRunInfo(); + if (!currentGroupInfo.used) + lazyPrintGroupInfo(); -void ConsoleReporter::printClosedHeader(std::string const &_name) { - printOpenHeader(_name); - stream << getLineOfChars<'.'>() << '\n'; -} -void ConsoleReporter::printOpenHeader(std::string const &_name) { - stream << getLineOfChars<'-'>() << '\n'; - { - Colour colourGuard(Colour::Headers); - printHeaderString(_name); - } -} + if (!m_headerPrinted) { + printTestCaseAndSectionHeader(); + m_headerPrinted = true; + } + } + + void ConsoleReporter::lazyPrintRunInfo() { + stream << '\n' << getLineOfChars<'~'>() << '\n'; + Colour colour(Colour::SecondaryText); + stream << currentTestRunInfo->name + << " is a Catch v" << libraryVersion() << " host application.\n" + << "Run with -? for options\n\n"; + + if (m_config->rngSeed() != 0) + stream << "Randomness seeded to: " << m_config->rngSeed() << "\n\n"; + + currentTestRunInfo.used = true; + } + + void ConsoleReporter::lazyPrintGroupInfo() { + if (!currentGroupInfo->name.empty() && currentGroupInfo->groupsCounts > 1) { + printClosedHeader("Group: " + currentGroupInfo->name); + currentGroupInfo.used = true; + } + } + + void ConsoleReporter::printTestCaseAndSectionHeader() { + assert(!m_sectionStack.empty()); + printOpenHeader(currentTestCaseInfo->name); + + if (m_sectionStack.size() > 1) { + Colour colourGuard(Colour::Headers); + + auto + it = m_sectionStack.begin() + 1, // Skip first section (test case) + itEnd = m_sectionStack.end(); + for (; it != itEnd; ++it) + printHeaderString(it->name, 2); + } + + SourceLineInfo lineInfo = m_sectionStack.back().lineInfo; + + stream << getLineOfChars<'-'>() << '\n'; + Colour colourGuard(Colour::FileName); + stream << lineInfo << '\n'; + stream << getLineOfChars<'.'>() << '\n' << std::endl; + } + + void ConsoleReporter::printClosedHeader(std::string const &_name) { + printOpenHeader(_name); + stream << getLineOfChars<'.'>() << '\n'; + } + + void ConsoleReporter::printOpenHeader(std::string const &_name) { + stream << getLineOfChars<'-'>() << '\n'; + { + Colour colourGuard(Colour::Headers); + printHeaderString(_name); + } + } // if string has a : in first line will set indent to follow it on // subsequent lines -void ConsoleReporter::printHeaderString(std::string const &_string, std::size_t indent) { - std::size_t i = _string.find(": "); - if (i != std::string::npos) - i += 2; - else - i = 0; - stream << Column(_string).indent(indent + i).initialIndent(indent) << '\n'; -} + void ConsoleReporter::printHeaderString(std::string const &_string, std::size_t indent) { + std::size_t i = _string.find(": "); + if (i != std::string::npos) + i += 2; + else + i = 0; + stream << Column(_string).indent(indent + i).initialIndent(indent) << '\n'; + } -struct SummaryColumn { - - SummaryColumn(std::string _label, Colour::Code _colour) - : label(std::move(_label)), - colour(_colour) {} - SummaryColumn addRow(std::size_t count) { - ReusableStringStream rss; - rss << count; - std::string row = rss.str(); - for (auto &oldRow : rows) { - while (oldRow.size() < row.size()) - oldRow = ' ' + oldRow; - while (oldRow.size() > row.size()) - row = ' ' + row; - } - rows.push_back(row); - return *this; - } - - std::string label; - Colour::Code colour; - std::vector rows; + struct SummaryColumn { -}; + SummaryColumn(std::string _label, Colour::Code _colour) + : label(std::move(_label)), + colour(_colour) {} -void ConsoleReporter::printTotals(Totals const &totals) { - if (totals.testCases.total() == 0) { - stream << Colour(Colour::Warning) << "No tests ran\n"; - } else if (totals.assertions.total() > 0 && totals.testCases.allPassed()) { - stream << Colour(Colour::ResultSuccess) << "All tests passed"; - stream << " (" - << pluralise(totals.assertions.passed, "assertion") << " in " - << pluralise(totals.testCases.passed, "test case") << ')' - << '\n'; - } else { - - std::vector columns; - columns.push_back(SummaryColumn("", Colour::None) - .addRow(totals.testCases.total()) - .addRow(totals.assertions.total())); - columns.push_back(SummaryColumn("passed", Colour::Success) - .addRow(totals.testCases.passed) - .addRow(totals.assertions.passed)); - columns.push_back(SummaryColumn("failed", Colour::ResultError) - .addRow(totals.testCases.failed) - .addRow(totals.assertions.failed)); - columns.push_back(SummaryColumn("failed as expected", Colour::ResultExpectedFailure) - .addRow(totals.testCases.failedButOk) - .addRow(totals.assertions.failedButOk)); - - printSummaryRow("test cases", columns, 0); - printSummaryRow("assertions", columns, 1); - } -} -void ConsoleReporter::printSummaryRow(std::string const &label, - std::vector const &cols, - std::size_t row) { - for (auto col : cols) { - std::string value = col.rows[row]; - if (col.label.empty()) { - stream << label << ": "; - if (value != "0") - stream << value; - else - stream << Colour(Colour::Warning) << "- none -"; - } else if (value != "0") { - stream << Colour(Colour::LightGrey) << " | "; - stream << Colour(col.colour) - << value << ' ' << col.label; - } - } - stream << '\n'; -} + SummaryColumn addRow(std::size_t count) { + ReusableStringStream rss; + rss << count; + std::string row = rss.str(); + for (auto &oldRow: rows) { + while (oldRow.size() < row.size()) + oldRow = ' ' + oldRow; + while (oldRow.size() > row.size()) + row = ' ' + row; + } + rows.push_back(row); + return *this; + } -void ConsoleReporter::printTotalsDivider(Totals const &totals) { - if (totals.testCases.total() > 0) { - std::size_t failedRatio = makeRatio(totals.testCases.failed, totals.testCases.total()); - std::size_t failedButOkRatio = makeRatio(totals.testCases.failedButOk, totals.testCases.total()); - std::size_t passedRatio = makeRatio(totals.testCases.passed, totals.testCases.total()); - while (failedRatio + failedButOkRatio + passedRatio < CATCH_CONFIG_CONSOLE_WIDTH - 1) - findMax(failedRatio, failedButOkRatio, passedRatio)++; - while (failedRatio + failedButOkRatio + passedRatio > CATCH_CONFIG_CONSOLE_WIDTH - 1) - findMax(failedRatio, failedButOkRatio, passedRatio)--; - - stream << Colour(Colour::Error) << std::string(failedRatio, '='); - stream << Colour(Colour::ResultExpectedFailure) << std::string(failedButOkRatio, '='); - if (totals.testCases.allPassed()) - stream << Colour(Colour::ResultSuccess) << std::string(passedRatio, '='); - else - stream << Colour(Colour::Success) << std::string(passedRatio, '='); - } else { - stream << Colour(Colour::Warning) << std::string(CATCH_CONFIG_CONSOLE_WIDTH - 1, '='); - } - stream << '\n'; -} -void ConsoleReporter::printSummaryDivider() { - stream << getLineOfChars<'-'>() << '\n'; -} + std::string label; + Colour::Code colour; + std::vector rows; -void ConsoleReporter::printTestFilters() { - if (m_config->testSpec().hasFilters()) { - Colour guard(Colour::BrightYellow); - stream << "Filters: " << serializeFilters(m_config->getTestsOrTags()) << '\n'; - } -} + }; + + void ConsoleReporter::printTotals(Totals const &totals) { + if (totals.testCases.total() == 0) { + stream << Colour(Colour::Warning) << "No tests ran\n"; + } else if (totals.assertions.total() > 0 && totals.testCases.allPassed()) { + stream << Colour(Colour::ResultSuccess) << "All tests passed"; + stream << " (" + << pluralise(totals.assertions.passed, "assertion") << " in " + << pluralise(totals.testCases.passed, "test case") << ')' + << '\n'; + } else { + + std::vector columns; + columns.push_back(SummaryColumn("", Colour::None) + .addRow(totals.testCases.total()) + .addRow(totals.assertions.total())); + columns.push_back(SummaryColumn("passed", Colour::Success) + .addRow(totals.testCases.passed) + .addRow(totals.assertions.passed)); + columns.push_back(SummaryColumn("failed", Colour::ResultError) + .addRow(totals.testCases.failed) + .addRow(totals.assertions.failed)); + columns.push_back(SummaryColumn("failed as expected", Colour::ResultExpectedFailure) + .addRow(totals.testCases.failedButOk) + .addRow(totals.assertions.failedButOk)); + + printSummaryRow("test cases", columns, 0); + printSummaryRow("assertions", columns, 1); + } + } + + void ConsoleReporter::printSummaryRow(std::string const &label, + std::vector const &cols, + std::size_t row) { + for (auto col: cols) { + std::string value = col.rows[row]; + if (col.label.empty()) { + stream << label << ": "; + if (value != "0") + stream << value; + else + stream << Colour(Colour::Warning) << "- none -"; + } else if (value != "0") { + stream << Colour(Colour::LightGrey) << " | "; + stream << Colour(col.colour) + << value << ' ' << col.label; + } + } + stream << '\n'; + } + + void ConsoleReporter::printTotalsDivider(Totals const &totals) { + if (totals.testCases.total() > 0) { + std::size_t failedRatio = makeRatio(totals.testCases.failed, totals.testCases.total()); + std::size_t failedButOkRatio = makeRatio(totals.testCases.failedButOk, totals.testCases.total()); + std::size_t passedRatio = makeRatio(totals.testCases.passed, totals.testCases.total()); + while (failedRatio + failedButOkRatio + passedRatio < CATCH_CONFIG_CONSOLE_WIDTH - 1) + findMax(failedRatio, failedButOkRatio, passedRatio)++; + while (failedRatio + failedButOkRatio + passedRatio > CATCH_CONFIG_CONSOLE_WIDTH - 1) + findMax(failedRatio, failedButOkRatio, passedRatio)--; + + stream << Colour(Colour::Error) << std::string(failedRatio, '='); + stream << Colour(Colour::ResultExpectedFailure) << std::string(failedButOkRatio, '='); + if (totals.testCases.allPassed()) + stream << Colour(Colour::ResultSuccess) << std::string(passedRatio, '='); + else + stream << Colour(Colour::Success) << std::string(passedRatio, '='); + } else { + stream << Colour(Colour::Warning) << std::string(CATCH_CONFIG_CONSOLE_WIDTH - 1, '='); + } + stream << '\n'; + } + + void ConsoleReporter::printSummaryDivider() { + stream << getLineOfChars<'-'>() << '\n'; + } -CATCH_REGISTER_REPORTER("console", ConsoleReporter) + void ConsoleReporter::printTestFilters() { + if (m_config->testSpec().hasFilters()) { + Colour guard(Colour::BrightYellow); + stream << "Filters: " << serializeFilters(m_config->getTestsOrTags()) << '\n'; + } + } + + CATCH_REGISTER_REPORTER("console", ConsoleReporter) } // end namespace Catch @@ -16727,274 +17661,277 @@ CATCH_REGISTER_REPORTER("console", ConsoleReporter) namespace Catch { -namespace { -std::string getCurrentTimestamp() { - // Beware, this is not reentrant because of backward compatibility issues - // Also, UTC only, again because of backward compatibility (%z is C++11) - time_t rawtime; - std::time(&rawtime); - auto const timeStampSize = sizeof("2017-01-16T17:06:45Z"); + namespace { + std::string getCurrentTimestamp() { + // Beware, this is not reentrant because of backward compatibility issues + // Also, UTC only, again because of backward compatibility (%z is C++11) + time_t rawtime; + std::time(&rawtime); + auto const timeStampSize = sizeof("2017-01-16T17:06:45Z"); #ifdef _MSC_VER - std::tm timeInfo = {}; + std::tm timeInfo = {}; gmtime_s(&timeInfo, &rawtime); #else - std::tm *timeInfo; - timeInfo = std::gmtime(&rawtime); + std::tm *timeInfo; + timeInfo = std::gmtime(&rawtime); #endif - char timeStamp[timeStampSize]; - const char *const fmt = "%Y-%m-%dT%H:%M:%SZ"; + char timeStamp[timeStampSize]; + const char *const fmt = "%Y-%m-%dT%H:%M:%SZ"; #ifdef _MSC_VER - std::strftime(timeStamp, timeStampSize, fmt, &timeInfo); + std::strftime(timeStamp, timeStampSize, fmt, &timeInfo); #else - std::strftime(timeStamp, timeStampSize, fmt, timeInfo); + std::strftime(timeStamp, timeStampSize, fmt, timeInfo); #endif - return std::string(timeStamp, timeStampSize - 1); -} + return std::string(timeStamp, timeStampSize - 1); + } -std::string fileNameTag(const std::vector &tags) { - auto it = std::find_if(begin(tags), - end(tags), - [](std::string const &tag) { return tag.front() == '#'; }); - if (it != tags.end()) - return it->substr(1); - return std::string(); -} + std::string fileNameTag(const std::vector &tags) { + auto it = std::find_if(begin(tags), + end(tags), + [](std::string const &tag) { return tag.front() == '#'; }); + if (it != tags.end()) + return it->substr(1); + return std::string(); + } // Formats the duration in seconds to 3 decimal places. // This is done because some genius defined Maven Surefire schema // in a way that only accepts 3 decimal places, and tools like // Jenkins use that schema for validation JUnit reporter output. -std::string formatDuration(double seconds) { - ReusableStringStream rss; - rss << std::fixed << std::setprecision(3) << seconds; - return rss.str(); -} + std::string formatDuration(double seconds) { + ReusableStringStream rss; + rss << std::fixed << std::setprecision(3) << seconds; + return rss.str(); + } -} // anonymous namespace + } // anonymous namespace -JunitReporter::JunitReporter(ReporterConfig const &_config) - : CumulativeReporterBase(_config), - xml(_config.stream()) { - m_reporterPrefs.shouldRedirectStdOut = true; - m_reporterPrefs.shouldReportAllAssertions = true; -} + JunitReporter::JunitReporter(ReporterConfig const &_config) + : CumulativeReporterBase(_config), + xml(_config.stream()) { + m_reporterPrefs.shouldRedirectStdOut = true; + m_reporterPrefs.shouldReportAllAssertions = true; + } -JunitReporter::~JunitReporter() {} + JunitReporter::~JunitReporter() {} -std::string JunitReporter::getDescription() { - return "Reports test results in an XML format that looks like Ant's junitreport target"; -} + std::string JunitReporter::getDescription() { + return "Reports test results in an XML format that looks like Ant's junitreport target"; + } -void JunitReporter::noMatchingTestCases(std::string const & /*spec*/ ) {} + void JunitReporter::noMatchingTestCases(std::string const & /*spec*/ ) {} -void JunitReporter::testRunStarting(TestRunInfo const &runInfo) { - CumulativeReporterBase::testRunStarting(runInfo); - xml.startElement("testsuites"); -} + void JunitReporter::testRunStarting(TestRunInfo const &runInfo) { + CumulativeReporterBase::testRunStarting(runInfo); + xml.startElement("testsuites"); + } -void JunitReporter::testGroupStarting(GroupInfo const &groupInfo) { - suiteTimer.start(); - stdOutForSuite.clear(); - stdErrForSuite.clear(); - unexpectedExceptions = 0; - CumulativeReporterBase::testGroupStarting(groupInfo); -} + void JunitReporter::testGroupStarting(GroupInfo const &groupInfo) { + suiteTimer.start(); + stdOutForSuite.clear(); + stdErrForSuite.clear(); + unexpectedExceptions = 0; + CumulativeReporterBase::testGroupStarting(groupInfo); + } -void JunitReporter::testCaseStarting(TestCaseInfo const &testCaseInfo) { - m_okToFail = testCaseInfo.okToFail(); -} + void JunitReporter::testCaseStarting(TestCaseInfo const &testCaseInfo) { + m_okToFail = testCaseInfo.okToFail(); + } -bool JunitReporter::assertionEnded(AssertionStats const &assertionStats) { - if (assertionStats.assertionResult.getResultType() == ResultWas::ThrewException && !m_okToFail) - unexpectedExceptions++; - return CumulativeReporterBase::assertionEnded(assertionStats); -} + bool JunitReporter::assertionEnded(AssertionStats const &assertionStats) { + if (assertionStats.assertionResult.getResultType() == ResultWas::ThrewException && !m_okToFail) + unexpectedExceptions++; + return CumulativeReporterBase::assertionEnded(assertionStats); + } -void JunitReporter::testCaseEnded(TestCaseStats const &testCaseStats) { - stdOutForSuite += testCaseStats.stdOut; - stdErrForSuite += testCaseStats.stdErr; - CumulativeReporterBase::testCaseEnded(testCaseStats); -} + void JunitReporter::testCaseEnded(TestCaseStats const &testCaseStats) { + stdOutForSuite += testCaseStats.stdOut; + stdErrForSuite += testCaseStats.stdErr; + CumulativeReporterBase::testCaseEnded(testCaseStats); + } -void JunitReporter::testGroupEnded(TestGroupStats const &testGroupStats) { - double suiteTime = suiteTimer.getElapsedSeconds(); - CumulativeReporterBase::testGroupEnded(testGroupStats); - writeGroup(*m_testGroups.back(), suiteTime); -} + void JunitReporter::testGroupEnded(TestGroupStats const &testGroupStats) { + double suiteTime = suiteTimer.getElapsedSeconds(); + CumulativeReporterBase::testGroupEnded(testGroupStats); + writeGroup(*m_testGroups.back(), suiteTime); + } -void JunitReporter::testRunEndedCumulative() { - xml.endElement(); -} + void JunitReporter::testRunEndedCumulative() { + xml.endElement(); + } -void JunitReporter::writeGroup(TestGroupNode const &groupNode, double suiteTime) { - XmlWriter::ScopedElement e = xml.scopedElement("testsuite"); - - TestGroupStats const &stats = groupNode.value; - xml.writeAttribute("name", stats.groupInfo.name); - xml.writeAttribute("errors", unexpectedExceptions); - xml.writeAttribute("failures", stats.totals.assertions.failed - unexpectedExceptions); - xml.writeAttribute("tests", stats.totals.assertions.total()); - xml.writeAttribute("hostname", "tbd"); // !TBD - if (m_config->showDurations() == ShowDurations::Never) - xml.writeAttribute("time", ""); - else - xml.writeAttribute("time", formatDuration(suiteTime)); - xml.writeAttribute("timestamp", getCurrentTimestamp()); - - // Write properties if there are any - if (m_config->hasTestFilters() || m_config->rngSeed() != 0) { - auto properties = xml.scopedElement("properties"); - if (m_config->hasTestFilters()) { - xml.scopedElement("property") - .writeAttribute("name", "filters") - .writeAttribute("value", serializeFilters(m_config->getTestsOrTags())); - } - if (m_config->rngSeed() != 0) { - xml.scopedElement("property") - .writeAttribute("name", "random-seed") - .writeAttribute("value", m_config->rngSeed()); - } - } - - // Write test cases - for (auto const &child : groupNode.children) - writeTestCase(*child); - - xml.scopedElement("system-out").writeText(trim(stdOutForSuite), XmlFormatting::Newline); - xml.scopedElement("system-err").writeText(trim(stdErrForSuite), XmlFormatting::Newline); -} + void JunitReporter::writeGroup(TestGroupNode const &groupNode, double suiteTime) { + XmlWriter::ScopedElement e = xml.scopedElement("testsuite"); + + TestGroupStats const &stats = groupNode.value; + xml.writeAttribute("name", stats.groupInfo.name); + xml.writeAttribute("errors", unexpectedExceptions); + xml.writeAttribute("failures", stats.totals.assertions.failed - unexpectedExceptions); + xml.writeAttribute("tests", stats.totals.assertions.total()); + xml.writeAttribute("hostname", "tbd"); // !TBD + if (m_config->showDurations() == ShowDurations::Never) + xml.writeAttribute("time", ""); + else + xml.writeAttribute("time", formatDuration(suiteTime)); + xml.writeAttribute("timestamp", getCurrentTimestamp()); + + // Write properties if there are any + if (m_config->hasTestFilters() || m_config->rngSeed() != 0) { + auto properties = xml.scopedElement("properties"); + if (m_config->hasTestFilters()) { + xml.scopedElement("property") + .writeAttribute("name", "filters") + .writeAttribute("value", serializeFilters(m_config->getTestsOrTags())); + } + if (m_config->rngSeed() != 0) { + xml.scopedElement("property") + .writeAttribute("name", "random-seed") + .writeAttribute("value", m_config->rngSeed()); + } + } -void JunitReporter::writeTestCase(TestCaseNode const &testCaseNode) { - TestCaseStats const &stats = testCaseNode.value; + // Write test cases + for (auto const &child: groupNode.children) + writeTestCase(*child); - // All test cases have exactly one section - which represents the - // test case itself. That section may have 0-n nested sections - assert(testCaseNode.children.size() == 1); - SectionNode const &rootSection = *testCaseNode.children.front(); + xml.scopedElement("system-out").writeText(trim(stdOutForSuite), XmlFormatting::Newline); + xml.scopedElement("system-err").writeText(trim(stdErrForSuite), XmlFormatting::Newline); + } - std::string className = stats.testInfo.className; + void JunitReporter::writeTestCase(TestCaseNode const &testCaseNode) { + TestCaseStats const &stats = testCaseNode.value; - if (className.empty()) { - className = fileNameTag(stats.testInfo.tags); - if (className.empty()) - className = "global"; - } + // All test cases have exactly one section - which represents the + // test case itself. That section may have 0-n nested sections + assert(testCaseNode.children.size() == 1); + SectionNode const &rootSection = *testCaseNode.children.front(); - if (!m_config->name().empty()) - className = m_config->name() + "." + className; + std::string className = stats.testInfo.className; - writeSection(className, "", rootSection, stats.testInfo.okToFail()); -} + if (className.empty()) { + className = fileNameTag(stats.testInfo.tags); + if (className.empty()) + className = "global"; + } -void JunitReporter::writeSection(std::string const &className, - std::string const &rootName, - SectionNode const §ionNode, - bool testOkToFail) { - std::string name = trim(sectionNode.stats.sectionInfo.name); - if (!rootName.empty()) - name = rootName + '/' + name; - - if (!sectionNode.assertions.empty() || - !sectionNode.stdOut.empty() || - !sectionNode.stdErr.empty()) { - XmlWriter::ScopedElement e = xml.scopedElement("testcase"); - if (className.empty()) { - xml.writeAttribute("classname", name); - xml.writeAttribute("name", "root"); - } else { - xml.writeAttribute("classname", className); - xml.writeAttribute("name", name); - } - xml.writeAttribute("time", formatDuration(sectionNode.stats.durationInSeconds)); - // This is not ideal, but it should be enough to mimic gtest's - // junit output. - // Ideally the JUnit reporter would also handle `skipTest` - // events and write those out appropriately. - xml.writeAttribute("status", "run"); - - if (sectionNode.stats.assertions.failedButOk) { - xml.scopedElement("skipped") - .writeAttribute("message", "TEST_CASE tagged with !mayfail"); - } - - writeAssertions(sectionNode); - - if (!sectionNode.stdOut.empty()) - xml.scopedElement("system-out").writeText(trim(sectionNode.stdOut), XmlFormatting::Newline); - if (!sectionNode.stdErr.empty()) - xml.scopedElement("system-err").writeText(trim(sectionNode.stdErr), XmlFormatting::Newline); - } - for (auto const &childNode : sectionNode.childSections) - if (className.empty()) - writeSection(name, "", *childNode, testOkToFail); - else - writeSection(className, name, *childNode, testOkToFail); -} + if (!m_config->name().empty()) + className = m_config->name() + "." + className; -void JunitReporter::writeAssertions(SectionNode const §ionNode) { - for (auto const &assertion : sectionNode.assertions) - writeAssertion(assertion); -} + writeSection(className, "", rootSection, stats.testInfo.okToFail()); + } -void JunitReporter::writeAssertion(AssertionStats const &stats) { - AssertionResult const &result = stats.assertionResult; - if (!result.isOk()) { - std::string elementName; - switch (result.getResultType()) { - case ResultWas::ThrewException: - case ResultWas::FatalErrorCondition:elementName = "error"; - break; - case ResultWas::ExplicitFailure: - case ResultWas::ExpressionFailed: - case ResultWas::DidntThrowException:elementName = "failure"; - break; - - // We should never see these here: - case ResultWas::Info: - case ResultWas::Warning: - case ResultWas::Ok: - case ResultWas::Unknown: - case ResultWas::FailureBit: - case ResultWas::Exception:elementName = "internalError"; - break; - } - - XmlWriter::ScopedElement e = xml.scopedElement(elementName); - - xml.writeAttribute("message", result.getExpression()); - xml.writeAttribute("type", result.getTestMacroName()); - - ReusableStringStream rss; - if (stats.totals.assertions.total() > 0) { - rss << "FAILED" << ":\n"; - if (result.hasExpression()) { - rss << " "; - rss << result.getExpressionInMacro(); - rss << '\n'; - } - if (result.hasExpandedExpression()) { - rss << "with expansion:\n"; - rss << Column(result.getExpandedExpression()).indent(2) << '\n'; - } - } else { - rss << '\n'; + void JunitReporter::writeSection(std::string const &className, + std::string const &rootName, + SectionNode const §ionNode, + bool testOkToFail) { + std::string name = trim(sectionNode.stats.sectionInfo.name); + if (!rootName.empty()) + name = rootName + '/' + name; + + if (!sectionNode.assertions.empty() || + !sectionNode.stdOut.empty() || + !sectionNode.stdErr.empty()) { + XmlWriter::ScopedElement e = xml.scopedElement("testcase"); + if (className.empty()) { + xml.writeAttribute("classname", name); + xml.writeAttribute("name", "root"); + } else { + xml.writeAttribute("classname", className); + xml.writeAttribute("name", name); + } + xml.writeAttribute("time", formatDuration(sectionNode.stats.durationInSeconds)); + // This is not ideal, but it should be enough to mimic gtest's + // junit output. + // Ideally the JUnit reporter would also handle `skipTest` + // events and write those out appropriately. + xml.writeAttribute("status", "run"); + + if (sectionNode.stats.assertions.failedButOk) { + xml.scopedElement("skipped") + .writeAttribute("message", "TEST_CASE tagged with !mayfail"); + } + + writeAssertions(sectionNode); + + if (!sectionNode.stdOut.empty()) + xml.scopedElement("system-out").writeText(trim(sectionNode.stdOut), XmlFormatting::Newline); + if (!sectionNode.stdErr.empty()) + xml.scopedElement("system-err").writeText(trim(sectionNode.stdErr), XmlFormatting::Newline); + } + for (auto const &childNode: sectionNode.childSections) + if (className.empty()) + writeSection(name, "", *childNode, testOkToFail); + else + writeSection(className, name, *childNode, testOkToFail); } - if (!result.getMessage().empty()) - rss << result.getMessage() << '\n'; - for (auto const &msg : stats.infoMessages) - if (msg.type == ResultWas::Info) - rss << msg.message << '\n'; + void JunitReporter::writeAssertions(SectionNode const §ionNode) { + for (auto const &assertion: sectionNode.assertions) + writeAssertion(assertion); + } - rss << "at " << result.getSourceInfo(); - xml.writeText(rss.str(), XmlFormatting::Newline); - } -} + void JunitReporter::writeAssertion(AssertionStats const &stats) { + AssertionResult const &result = stats.assertionResult; + if (!result.isOk()) { + std::string elementName; + switch (result.getResultType()) { + case ResultWas::ThrewException: + case ResultWas::FatalErrorCondition: + elementName = "error"; + break; + case ResultWas::ExplicitFailure: + case ResultWas::ExpressionFailed: + case ResultWas::DidntThrowException: + elementName = "failure"; + break; + + // We should never see these here: + case ResultWas::Info: + case ResultWas::Warning: + case ResultWas::Ok: + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: + elementName = "internalError"; + break; + } + + XmlWriter::ScopedElement e = xml.scopedElement(elementName); + + xml.writeAttribute("message", result.getExpression()); + xml.writeAttribute("type", result.getTestMacroName()); + + ReusableStringStream rss; + if (stats.totals.assertions.total() > 0) { + rss << "FAILED" << ":\n"; + if (result.hasExpression()) { + rss << " "; + rss << result.getExpressionInMacro(); + rss << '\n'; + } + if (result.hasExpandedExpression()) { + rss << "with expansion:\n"; + rss << Column(result.getExpandedExpression()).indent(2) << '\n'; + } + } else { + rss << '\n'; + } + + if (!result.getMessage().empty()) + rss << result.getMessage() << '\n'; + for (auto const &msg: stats.infoMessages) + if (msg.type == ResultWas::Info) + rss << msg.message << '\n'; + + rss << "at " << result.getSourceInfo(); + xml.writeText(rss.str(), XmlFormatting::Newline); + } + } -CATCH_REGISTER_REPORTER("junit", JunitReporter) + CATCH_REGISTER_REPORTER("junit", JunitReporter) } // end namespace Catch // end catch_reporter_junit.cpp @@ -17004,45 +17941,45 @@ CATCH_REGISTER_REPORTER("junit", JunitReporter) namespace Catch { -ListeningReporter::ListeningReporter() { - // We will assume that listeners will always want all assertions - m_preferences.shouldReportAllAssertions = true; -} + ListeningReporter::ListeningReporter() { + // We will assume that listeners will always want all assertions + m_preferences.shouldReportAllAssertions = true; + } -void ListeningReporter::addListener(IStreamingReporterPtr &&listener) { - m_listeners.push_back(std::move(listener)); -} + void ListeningReporter::addListener(IStreamingReporterPtr &&listener) { + m_listeners.push_back(std::move(listener)); + } -void ListeningReporter::addReporter(IStreamingReporterPtr &&reporter) { - assert(!m_reporter && "Listening reporter can wrap only 1 real reporter"); - m_reporter = std::move(reporter); - m_preferences.shouldRedirectStdOut = m_reporter->getPreferences().shouldRedirectStdOut; -} + void ListeningReporter::addReporter(IStreamingReporterPtr &&reporter) { + assert(!m_reporter && "Listening reporter can wrap only 1 real reporter"); + m_reporter = std::move(reporter); + m_preferences.shouldRedirectStdOut = m_reporter->getPreferences().shouldRedirectStdOut; + } -ReporterPreferences ListeningReporter::getPreferences() const { - return m_preferences; -} + ReporterPreferences ListeningReporter::getPreferences() const { + return m_preferences; + } -std::set ListeningReporter::getSupportedVerbosities() { - return std::set{}; -} + std::set ListeningReporter::getSupportedVerbosities() { + return std::set{}; + } -void ListeningReporter::noMatchingTestCases(std::string const &spec) { - for (auto const &listener : m_listeners) { - listener->noMatchingTestCases(spec); - } - m_reporter->noMatchingTestCases(spec); -} + void ListeningReporter::noMatchingTestCases(std::string const &spec) { + for (auto const &listener: m_listeners) { + listener->noMatchingTestCases(spec); + } + m_reporter->noMatchingTestCases(spec); + } -void ListeningReporter::reportInvalidArguments(std::string const &arg) { - for (auto const &listener : m_listeners) { - listener->reportInvalidArguments(arg); - } - m_reporter->reportInvalidArguments(arg); -} + void ListeningReporter::reportInvalidArguments(std::string const &arg) { + for (auto const &listener: m_listeners) { + listener->reportInvalidArguments(arg); + } + m_reporter->reportInvalidArguments(arg); + } #if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) - void ListeningReporter::benchmarkPreparing( std::string const& name ) { + void ListeningReporter::benchmarkPreparing( std::string const& name ) { for (auto const& listener : m_listeners) { listener->benchmarkPreparing(name); } @@ -17069,87 +18006,87 @@ void ListeningReporter::reportInvalidArguments(std::string const &arg) { } #endif // CATCH_CONFIG_ENABLE_BENCHMARKING -void ListeningReporter::testRunStarting(TestRunInfo const &testRunInfo) { - for (auto const &listener : m_listeners) { - listener->testRunStarting(testRunInfo); - } - m_reporter->testRunStarting(testRunInfo); -} + void ListeningReporter::testRunStarting(TestRunInfo const &testRunInfo) { + for (auto const &listener: m_listeners) { + listener->testRunStarting(testRunInfo); + } + m_reporter->testRunStarting(testRunInfo); + } -void ListeningReporter::testGroupStarting(GroupInfo const &groupInfo) { - for (auto const &listener : m_listeners) { - listener->testGroupStarting(groupInfo); - } - m_reporter->testGroupStarting(groupInfo); -} + void ListeningReporter::testGroupStarting(GroupInfo const &groupInfo) { + for (auto const &listener: m_listeners) { + listener->testGroupStarting(groupInfo); + } + m_reporter->testGroupStarting(groupInfo); + } -void ListeningReporter::testCaseStarting(TestCaseInfo const &testInfo) { - for (auto const &listener : m_listeners) { - listener->testCaseStarting(testInfo); - } - m_reporter->testCaseStarting(testInfo); -} + void ListeningReporter::testCaseStarting(TestCaseInfo const &testInfo) { + for (auto const &listener: m_listeners) { + listener->testCaseStarting(testInfo); + } + m_reporter->testCaseStarting(testInfo); + } -void ListeningReporter::sectionStarting(SectionInfo const §ionInfo) { - for (auto const &listener : m_listeners) { - listener->sectionStarting(sectionInfo); - } - m_reporter->sectionStarting(sectionInfo); -} + void ListeningReporter::sectionStarting(SectionInfo const §ionInfo) { + for (auto const &listener: m_listeners) { + listener->sectionStarting(sectionInfo); + } + m_reporter->sectionStarting(sectionInfo); + } -void ListeningReporter::assertionStarting(AssertionInfo const &assertionInfo) { - for (auto const &listener : m_listeners) { - listener->assertionStarting(assertionInfo); - } - m_reporter->assertionStarting(assertionInfo); -} + void ListeningReporter::assertionStarting(AssertionInfo const &assertionInfo) { + for (auto const &listener: m_listeners) { + listener->assertionStarting(assertionInfo); + } + m_reporter->assertionStarting(assertionInfo); + } // The return value indicates if the messages buffer should be cleared: -bool ListeningReporter::assertionEnded(AssertionStats const &assertionStats) { - for (auto const &listener : m_listeners) { - static_cast( listener->assertionEnded(assertionStats)); - } - return m_reporter->assertionEnded(assertionStats); -} + bool ListeningReporter::assertionEnded(AssertionStats const &assertionStats) { + for (auto const &listener: m_listeners) { + static_cast( listener->assertionEnded(assertionStats)); + } + return m_reporter->assertionEnded(assertionStats); + } -void ListeningReporter::sectionEnded(SectionStats const §ionStats) { - for (auto const &listener : m_listeners) { - listener->sectionEnded(sectionStats); - } - m_reporter->sectionEnded(sectionStats); -} + void ListeningReporter::sectionEnded(SectionStats const §ionStats) { + for (auto const &listener: m_listeners) { + listener->sectionEnded(sectionStats); + } + m_reporter->sectionEnded(sectionStats); + } -void ListeningReporter::testCaseEnded(TestCaseStats const &testCaseStats) { - for (auto const &listener : m_listeners) { - listener->testCaseEnded(testCaseStats); - } - m_reporter->testCaseEnded(testCaseStats); -} + void ListeningReporter::testCaseEnded(TestCaseStats const &testCaseStats) { + for (auto const &listener: m_listeners) { + listener->testCaseEnded(testCaseStats); + } + m_reporter->testCaseEnded(testCaseStats); + } -void ListeningReporter::testGroupEnded(TestGroupStats const &testGroupStats) { - for (auto const &listener : m_listeners) { - listener->testGroupEnded(testGroupStats); - } - m_reporter->testGroupEnded(testGroupStats); -} + void ListeningReporter::testGroupEnded(TestGroupStats const &testGroupStats) { + for (auto const &listener: m_listeners) { + listener->testGroupEnded(testGroupStats); + } + m_reporter->testGroupEnded(testGroupStats); + } -void ListeningReporter::testRunEnded(TestRunStats const &testRunStats) { - for (auto const &listener : m_listeners) { - listener->testRunEnded(testRunStats); - } - m_reporter->testRunEnded(testRunStats); -} + void ListeningReporter::testRunEnded(TestRunStats const &testRunStats) { + for (auto const &listener: m_listeners) { + listener->testRunEnded(testRunStats); + } + m_reporter->testRunEnded(testRunStats); + } -void ListeningReporter::skipTest(TestCaseInfo const &testInfo) { - for (auto const &listener : m_listeners) { - listener->skipTest(testInfo); - } - m_reporter->skipTest(testInfo); -} + void ListeningReporter::skipTest(TestCaseInfo const &testInfo) { + for (auto const &listener: m_listeners) { + listener->skipTest(testInfo); + } + m_reporter->skipTest(testInfo); + } -bool ListeningReporter::isMulti() const { - return true; -} + bool ListeningReporter::isMulti() const { + return true; + } } // end namespace Catch // end catch_reporter_listening.cpp @@ -17163,210 +18100,214 @@ bool ListeningReporter::isMulti() const { #endif namespace Catch { -XmlReporter::XmlReporter(ReporterConfig const &_config) - : StreamingReporterBase(_config), - m_xml(_config.stream()) { - m_reporterPrefs.shouldRedirectStdOut = true; - m_reporterPrefs.shouldReportAllAssertions = true; -} + XmlReporter::XmlReporter(ReporterConfig const &_config) + : StreamingReporterBase(_config), + m_xml(_config.stream()) { + m_reporterPrefs.shouldRedirectStdOut = true; + m_reporterPrefs.shouldReportAllAssertions = true; + } -XmlReporter::~XmlReporter() = default; + XmlReporter::~XmlReporter() = default; -std::string XmlReporter::getDescription() { - return "Reports test results as an XML document"; -} + std::string XmlReporter::getDescription() { + return "Reports test results as an XML document"; + } -std::string XmlReporter::getStylesheetRef() const { - return std::string(); -} + std::string XmlReporter::getStylesheetRef() const { + return std::string(); + } -void XmlReporter::writeSourceInfo(SourceLineInfo const &sourceInfo) { - m_xml - .writeAttribute("filename", sourceInfo.file) - .writeAttribute("line", sourceInfo.line); -} + void XmlReporter::writeSourceInfo(SourceLineInfo const &sourceInfo) { + m_xml + .writeAttribute("filename", sourceInfo.file) + .writeAttribute("line", sourceInfo.line); + } -void XmlReporter::noMatchingTestCases(std::string const &s) { - StreamingReporterBase::noMatchingTestCases(s); -} + void XmlReporter::noMatchingTestCases(std::string const &s) { + StreamingReporterBase::noMatchingTestCases(s); + } -void XmlReporter::testRunStarting(TestRunInfo const &testInfo) { - StreamingReporterBase::testRunStarting(testInfo); - std::string stylesheetRef = getStylesheetRef(); - if (!stylesheetRef.empty()) - m_xml.writeStylesheetRef(stylesheetRef); - m_xml.startElement("Catch"); - if (!m_config->name().empty()) - m_xml.writeAttribute("name", m_config->name()); - if (m_config->testSpec().hasFilters()) - m_xml.writeAttribute("filters", serializeFilters(m_config->getTestsOrTags())); - if (m_config->rngSeed() != 0) - m_xml.scopedElement("Randomness") - .writeAttribute("seed", m_config->rngSeed()); -} + void XmlReporter::testRunStarting(TestRunInfo const &testInfo) { + StreamingReporterBase::testRunStarting(testInfo); + std::string stylesheetRef = getStylesheetRef(); + if (!stylesheetRef.empty()) + m_xml.writeStylesheetRef(stylesheetRef); + m_xml.startElement("Catch"); + if (!m_config->name().empty()) + m_xml.writeAttribute("name", m_config->name()); + if (m_config->testSpec().hasFilters()) + m_xml.writeAttribute("filters", serializeFilters(m_config->getTestsOrTags())); + if (m_config->rngSeed() != 0) + m_xml.scopedElement("Randomness") + .writeAttribute("seed", m_config->rngSeed()); + } -void XmlReporter::testGroupStarting(GroupInfo const &groupInfo) { - StreamingReporterBase::testGroupStarting(groupInfo); - m_xml.startElement("Group") - .writeAttribute("name", groupInfo.name); -} + void XmlReporter::testGroupStarting(GroupInfo const &groupInfo) { + StreamingReporterBase::testGroupStarting(groupInfo); + m_xml.startElement("Group") + .writeAttribute("name", groupInfo.name); + } -void XmlReporter::testCaseStarting(TestCaseInfo const &testInfo) { - StreamingReporterBase::testCaseStarting(testInfo); - m_xml.startElement("TestCase") - .writeAttribute("name", trim(testInfo.name)) - .writeAttribute("description", testInfo.description) - .writeAttribute("tags", testInfo.tagsAsString()); + void XmlReporter::testCaseStarting(TestCaseInfo const &testInfo) { + StreamingReporterBase::testCaseStarting(testInfo); + m_xml.startElement("TestCase") + .writeAttribute("name", trim(testInfo.name)) + .writeAttribute("description", testInfo.description) + .writeAttribute("tags", testInfo.tagsAsString()); - writeSourceInfo(testInfo.lineInfo); + writeSourceInfo(testInfo.lineInfo); - if (m_config->showDurations() == ShowDurations::Always) - m_testCaseTimer.start(); - m_xml.ensureTagClosed(); -} + if (m_config->showDurations() == ShowDurations::Always) + m_testCaseTimer.start(); + m_xml.ensureTagClosed(); + } -void XmlReporter::sectionStarting(SectionInfo const §ionInfo) { - StreamingReporterBase::sectionStarting(sectionInfo); - if (m_sectionDepth++ > 0) { - m_xml.startElement("Section") - .writeAttribute("name", trim(sectionInfo.name)); - writeSourceInfo(sectionInfo.lineInfo); - m_xml.ensureTagClosed(); - } -} + void XmlReporter::sectionStarting(SectionInfo const §ionInfo) { + StreamingReporterBase::sectionStarting(sectionInfo); + if (m_sectionDepth++ > 0) { + m_xml.startElement("Section") + .writeAttribute("name", trim(sectionInfo.name)); + writeSourceInfo(sectionInfo.lineInfo); + m_xml.ensureTagClosed(); + } + } -void XmlReporter::assertionStarting(AssertionInfo const &) {} - -bool XmlReporter::assertionEnded(AssertionStats const &assertionStats) { - - AssertionResult const &result = assertionStats.assertionResult; - - bool includeResults = m_config->includeSuccessfulResults() || !result.isOk(); - - if (includeResults || result.getResultType() == ResultWas::Warning) { - // Print any info messages in tags. - for (auto const &msg : assertionStats.infoMessages) { - if (msg.type == ResultWas::Info && includeResults) { - m_xml.scopedElement("Info") - .writeText(msg.message); - } else if (msg.type == ResultWas::Warning) { - m_xml.scopedElement("Warning") - .writeText(msg.message); - } - } - } - - // Drop out if result was successful but we're not printing them. - if (!includeResults && result.getResultType() != ResultWas::Warning) - return true; - - // Print the expression if there is one. - if (result.hasExpression()) { - m_xml.startElement("Expression") - .writeAttribute("success", result.succeeded()) - .writeAttribute("type", result.getTestMacroName()); - - writeSourceInfo(result.getSourceInfo()); - - m_xml.scopedElement("Original") - .writeText(result.getExpression()); - m_xml.scopedElement("Expanded") - .writeText(result.getExpandedExpression()); - } - - // And... Print a result applicable to each result type. - switch (result.getResultType()) { - case ResultWas::ThrewException:m_xml.startElement("Exception"); - writeSourceInfo(result.getSourceInfo()); - m_xml.writeText(result.getMessage()); - m_xml.endElement(); - break; - case ResultWas::FatalErrorCondition:m_xml.startElement("FatalErrorCondition"); - writeSourceInfo(result.getSourceInfo()); - m_xml.writeText(result.getMessage()); - m_xml.endElement(); - break; - case ResultWas::Info: - m_xml.scopedElement("Info") - .writeText(result.getMessage()); - break; - case ResultWas::Warning: - // Warning will already have been written - break; - case ResultWas::ExplicitFailure:m_xml.startElement("Failure"); - writeSourceInfo(result.getSourceInfo()); - m_xml.writeText(result.getMessage()); - m_xml.endElement(); - break; - default:break; - } - - if (result.hasExpression()) - m_xml.endElement(); - - return true; -} + void XmlReporter::assertionStarting(AssertionInfo const &) {} -void XmlReporter::sectionEnded(SectionStats const §ionStats) { - StreamingReporterBase::sectionEnded(sectionStats); - if (--m_sectionDepth > 0) { - XmlWriter::ScopedElement e = m_xml.scopedElement("OverallResults"); - e.writeAttribute("successes", sectionStats.assertions.passed); - e.writeAttribute("failures", sectionStats.assertions.failed); - e.writeAttribute("expectedFailures", sectionStats.assertions.failedButOk); + bool XmlReporter::assertionEnded(AssertionStats const &assertionStats) { - if (m_config->showDurations() == ShowDurations::Always) - e.writeAttribute("durationInSeconds", sectionStats.durationInSeconds); + AssertionResult const &result = assertionStats.assertionResult; - m_xml.endElement(); - } -} + bool includeResults = m_config->includeSuccessfulResults() || !result.isOk(); -void XmlReporter::testCaseEnded(TestCaseStats const &testCaseStats) { - StreamingReporterBase::testCaseEnded(testCaseStats); - XmlWriter::ScopedElement e = m_xml.scopedElement("OverallResult"); - e.writeAttribute("success", testCaseStats.totals.assertions.allOk()); + if (includeResults || result.getResultType() == ResultWas::Warning) { + // Print any info messages in tags. + for (auto const &msg: assertionStats.infoMessages) { + if (msg.type == ResultWas::Info && includeResults) { + m_xml.scopedElement("Info") + .writeText(msg.message); + } else if (msg.type == ResultWas::Warning) { + m_xml.scopedElement("Warning") + .writeText(msg.message); + } + } + } - if (m_config->showDurations() == ShowDurations::Always) - e.writeAttribute("durationInSeconds", m_testCaseTimer.getElapsedSeconds()); + // Drop out if result was successful but we're not printing them. + if (!includeResults && result.getResultType() != ResultWas::Warning) + return true; - if (!testCaseStats.stdOut.empty()) - m_xml.scopedElement("StdOut").writeText(trim(testCaseStats.stdOut), XmlFormatting::Newline); - if (!testCaseStats.stdErr.empty()) - m_xml.scopedElement("StdErr").writeText(trim(testCaseStats.stdErr), XmlFormatting::Newline); + // Print the expression if there is one. + if (result.hasExpression()) { + m_xml.startElement("Expression") + .writeAttribute("success", result.succeeded()) + .writeAttribute("type", result.getTestMacroName()); - m_xml.endElement(); -} + writeSourceInfo(result.getSourceInfo()); -void XmlReporter::testGroupEnded(TestGroupStats const &testGroupStats) { - StreamingReporterBase::testGroupEnded(testGroupStats); - // TODO: Check testGroupStats.aborting and act accordingly. - m_xml.scopedElement("OverallResults") - .writeAttribute("successes", testGroupStats.totals.assertions.passed) - .writeAttribute("failures", testGroupStats.totals.assertions.failed) - .writeAttribute("expectedFailures", testGroupStats.totals.assertions.failedButOk); - m_xml.scopedElement("OverallResultsCases") - .writeAttribute("successes", testGroupStats.totals.testCases.passed) - .writeAttribute("failures", testGroupStats.totals.testCases.failed) - .writeAttribute("expectedFailures", testGroupStats.totals.testCases.failedButOk); - m_xml.endElement(); -} + m_xml.scopedElement("Original") + .writeText(result.getExpression()); + m_xml.scopedElement("Expanded") + .writeText(result.getExpandedExpression()); + } -void XmlReporter::testRunEnded(TestRunStats const &testRunStats) { - StreamingReporterBase::testRunEnded(testRunStats); - m_xml.scopedElement("OverallResults") - .writeAttribute("successes", testRunStats.totals.assertions.passed) - .writeAttribute("failures", testRunStats.totals.assertions.failed) - .writeAttribute("expectedFailures", testRunStats.totals.assertions.failedButOk); - m_xml.scopedElement("OverallResultsCases") - .writeAttribute("successes", testRunStats.totals.testCases.passed) - .writeAttribute("failures", testRunStats.totals.testCases.failed) - .writeAttribute("expectedFailures", testRunStats.totals.testCases.failedButOk); - m_xml.endElement(); -} + // And... Print a result applicable to each result type. + switch (result.getResultType()) { + case ResultWas::ThrewException: + m_xml.startElement("Exception"); + writeSourceInfo(result.getSourceInfo()); + m_xml.writeText(result.getMessage()); + m_xml.endElement(); + break; + case ResultWas::FatalErrorCondition: + m_xml.startElement("FatalErrorCondition"); + writeSourceInfo(result.getSourceInfo()); + m_xml.writeText(result.getMessage()); + m_xml.endElement(); + break; + case ResultWas::Info: + m_xml.scopedElement("Info") + .writeText(result.getMessage()); + break; + case ResultWas::Warning: + // Warning will already have been written + break; + case ResultWas::ExplicitFailure: + m_xml.startElement("Failure"); + writeSourceInfo(result.getSourceInfo()); + m_xml.writeText(result.getMessage()); + m_xml.endElement(); + break; + default: + break; + } + + if (result.hasExpression()) + m_xml.endElement(); + + return true; + } + + void XmlReporter::sectionEnded(SectionStats const §ionStats) { + StreamingReporterBase::sectionEnded(sectionStats); + if (--m_sectionDepth > 0) { + XmlWriter::ScopedElement e = m_xml.scopedElement("OverallResults"); + e.writeAttribute("successes", sectionStats.assertions.passed); + e.writeAttribute("failures", sectionStats.assertions.failed); + e.writeAttribute("expectedFailures", sectionStats.assertions.failedButOk); + + if (m_config->showDurations() == ShowDurations::Always) + e.writeAttribute("durationInSeconds", sectionStats.durationInSeconds); + + m_xml.endElement(); + } + } + + void XmlReporter::testCaseEnded(TestCaseStats const &testCaseStats) { + StreamingReporterBase::testCaseEnded(testCaseStats); + XmlWriter::ScopedElement e = m_xml.scopedElement("OverallResult"); + e.writeAttribute("success", testCaseStats.totals.assertions.allOk()); + + if (m_config->showDurations() == ShowDurations::Always) + e.writeAttribute("durationInSeconds", m_testCaseTimer.getElapsedSeconds()); + + if (!testCaseStats.stdOut.empty()) + m_xml.scopedElement("StdOut").writeText(trim(testCaseStats.stdOut), XmlFormatting::Newline); + if (!testCaseStats.stdErr.empty()) + m_xml.scopedElement("StdErr").writeText(trim(testCaseStats.stdErr), XmlFormatting::Newline); + + m_xml.endElement(); + } + + void XmlReporter::testGroupEnded(TestGroupStats const &testGroupStats) { + StreamingReporterBase::testGroupEnded(testGroupStats); + // TODO: Check testGroupStats.aborting and act accordingly. + m_xml.scopedElement("OverallResults") + .writeAttribute("successes", testGroupStats.totals.assertions.passed) + .writeAttribute("failures", testGroupStats.totals.assertions.failed) + .writeAttribute("expectedFailures", testGroupStats.totals.assertions.failedButOk); + m_xml.scopedElement("OverallResultsCases") + .writeAttribute("successes", testGroupStats.totals.testCases.passed) + .writeAttribute("failures", testGroupStats.totals.testCases.failed) + .writeAttribute("expectedFailures", testGroupStats.totals.testCases.failedButOk); + m_xml.endElement(); + } + + void XmlReporter::testRunEnded(TestRunStats const &testRunStats) { + StreamingReporterBase::testRunEnded(testRunStats); + m_xml.scopedElement("OverallResults") + .writeAttribute("successes", testRunStats.totals.assertions.passed) + .writeAttribute("failures", testRunStats.totals.assertions.failed) + .writeAttribute("expectedFailures", testRunStats.totals.assertions.failedButOk); + m_xml.scopedElement("OverallResultsCases") + .writeAttribute("successes", testRunStats.totals.testCases.passed) + .writeAttribute("failures", testRunStats.totals.testCases.failed) + .writeAttribute("expectedFailures", testRunStats.totals.testCases.failedButOk); + m_xml.endElement(); + } #if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) - void XmlReporter::benchmarkPreparing(std::string const& name) { + void XmlReporter::benchmarkPreparing(std::string const& name) { m_xml.startElement("BenchmarkResults") .writeAttribute("name", name); } @@ -17410,7 +18351,7 @@ void XmlReporter::testRunEnded(TestRunStats const &testRunStats) { } #endif // CATCH_CONFIG_ENABLE_BENCHMARKING -CATCH_REGISTER_REPORTER("xml", XmlReporter) + CATCH_REGISTER_REPORTER("xml", XmlReporter) } // end namespace Catch @@ -17420,7 +18361,7 @@ CATCH_REGISTER_REPORTER("xml", XmlReporter) // end catch_reporter_xml.cpp namespace Catch { -LeakDetector leakDetector; + LeakDetector leakDetector; } #ifdef __clang__ @@ -17447,11 +18388,12 @@ LeakDetector leakDetector; // Standard C/C++ Win32 Unicode wmain entry point extern "C" int CATCH_INTERNAL_CDECL wmain (int argc, wchar_t * argv[], wchar_t * []) { #else + // Standard C/C++ main entry point int CATCH_INTERNAL_CDECL main(int argc, char *argv[]) { #endif - return Catch::Session().run(argc, argv); + return Catch::Session().run(argc, argv); } #else // __OBJC__ diff --git a/test/SystemTest/catch_reporter_automake.hpp b/test/SystemTest/catch_reporter_automake.hpp index 2880451d..6dc66e92 100644 --- a/test/SystemTest/catch_reporter_automake.hpp +++ b/test/SystemTest/catch_reporter_automake.hpp @@ -16,45 +16,45 @@ namespace Catch { -struct AutomakeReporter : StreamingReporterBase { - AutomakeReporter(ReporterConfig const &_config) - : StreamingReporterBase(_config) {} + struct AutomakeReporter : StreamingReporterBase { + AutomakeReporter(ReporterConfig const &_config) + : StreamingReporterBase(_config) {} - ~AutomakeReporter() override; + ~AutomakeReporter() override; - static std::string getDescription() { - return "Reports test results in the format of Automake .trs files"; - } + static std::string getDescription() { + return "Reports test results in the format of Automake .trs files"; + } - void assertionStarting(AssertionInfo const &) override {} + void assertionStarting(AssertionInfo const &) override {} - bool assertionEnded(AssertionStats const & /*_assertionStats*/ ) override { return true; } + bool assertionEnded(AssertionStats const & /*_assertionStats*/ ) override { return true; } - void testCaseEnded(TestCaseStats const &_testCaseStats) override { - // Possible values to emit are PASS, XFAIL, SKIP, FAIL, XPASS and ERROR. - stream << ":test-result: "; - if (_testCaseStats.totals.assertions.allPassed()) { - stream << "PASS"; - } else if (_testCaseStats.totals.assertions.allOk()) { - stream << "XFAIL"; - } else { - stream << "FAIL"; - } - stream << ' ' << _testCaseStats.testInfo.name << '\n'; - StreamingReporterBase::testCaseEnded(_testCaseStats); - } + void testCaseEnded(TestCaseStats const &_testCaseStats) override { + // Possible values to emit are PASS, XFAIL, SKIP, FAIL, XPASS and ERROR. + stream << ":test-result: "; + if (_testCaseStats.totals.assertions.allPassed()) { + stream << "PASS"; + } else if (_testCaseStats.totals.assertions.allOk()) { + stream << "XFAIL"; + } else { + stream << "FAIL"; + } + stream << ' ' << _testCaseStats.testInfo.name << '\n'; + StreamingReporterBase::testCaseEnded(_testCaseStats); + } - void skipTest(TestCaseInfo const &testInfo) override { - stream << ":test-result: SKIP " << testInfo.name << '\n'; - } + void skipTest(TestCaseInfo const &testInfo) override { + stream << ":test-result: SKIP " << testInfo.name << '\n'; + } -}; + }; #ifdef CATCH_IMPL -AutomakeReporter::~AutomakeReporter() {} + AutomakeReporter::~AutomakeReporter() {} #endif -CATCH_REGISTER_REPORTER( "automake", AutomakeReporter) + CATCH_REGISTER_REPORTER( "automake", AutomakeReporter) } // end namespace Catch diff --git a/test/SystemTest/catch_reporter_sonarqube.hpp b/test/SystemTest/catch_reporter_sonarqube.hpp index 48571846..5b4b4a92 100644 --- a/test/SystemTest/catch_reporter_sonarqube.hpp +++ b/test/SystemTest/catch_reporter_sonarqube.hpp @@ -19,155 +19,160 @@ namespace Catch { -struct SonarQubeReporter : CumulativeReporterBase { - - SonarQubeReporter(ReporterConfig const &config) - : CumulativeReporterBase(config), xml(config.stream()) { - m_reporterPrefs.shouldRedirectStdOut = true; - m_reporterPrefs.shouldReportAllAssertions = true; - } - - ~SonarQubeReporter() override; - - static std::string getDescription() { - return "Reports test results in the Generic Test Data SonarQube XML format"; - } - - static std::set getSupportedVerbosities() { - return {Verbosity::Normal}; - } - - void noMatchingTestCases(std::string const & /*spec*/) override {} - - void testRunStarting(TestRunInfo const &testRunInfo) override { - CumulativeReporterBase::testRunStarting(testRunInfo); - xml.startElement("testExecutions"); - xml.writeAttribute("version", "1"); - } - - void testGroupEnded(TestGroupStats const &testGroupStats) override { - CumulativeReporterBase::testGroupEnded(testGroupStats); - writeGroup(*m_testGroups.back()); - } - - void testRunEndedCumulative() override { - xml.endElement(); - } - - void writeGroup(TestGroupNode const &groupNode) { - std::map testsPerFile; - for (auto const &child : groupNode.children) - testsPerFile[child->value.testInfo.lineInfo.file].push_back(child); - - for (auto const &kv : testsPerFile) - writeTestFile(kv.first.c_str(), kv.second); - } - - void writeTestFile(const char *filename, TestGroupNode::ChildNodes const &testCaseNodes) { - XmlWriter::ScopedElement e = xml.scopedElement("file"); - xml.writeAttribute("path", filename); - - for (auto const &child : testCaseNodes) - writeTestCase(*child); - } - - void writeTestCase(TestCaseNode const &testCaseNode) { - // All test cases have exactly one section - which represents the - // test case itself. That section may have 0-n nested sections - assert(testCaseNode.children.size() == 1); - SectionNode const &rootSection = *testCaseNode.children.front(); - writeSection("", rootSection, testCaseNode.value.testInfo.okToFail()); - } - - void writeSection(std::string const &rootName, SectionNode const §ionNode, bool okToFail) { - std::string name = trim(sectionNode.stats.sectionInfo.name); - if (!rootName.empty()) - name = rootName + '/' + name; - - if (!sectionNode.assertions.empty() || !sectionNode.stdOut.empty() || !sectionNode.stdErr.empty()) { - XmlWriter::ScopedElement e = xml.scopedElement("testCase"); - xml.writeAttribute("name", name); - xml.writeAttribute("duration", static_cast(sectionNode.stats.durationInSeconds * 1000)); - - writeAssertions(sectionNode, okToFail); - } - - for (auto const &childNode : sectionNode.childSections) - writeSection(name, *childNode, okToFail); - } - - void writeAssertions(SectionNode const §ionNode, bool okToFail) { - for (auto const &assertion : sectionNode.assertions) - writeAssertion(assertion, okToFail); - } - - void writeAssertion(AssertionStats const &stats, bool okToFail) { - AssertionResult const &result = stats.assertionResult; - if (!result.isOk()) { - std::string elementName; - if (okToFail) { - elementName = "skipped"; - } else { - switch (result.getResultType()) { - case ResultWas::ThrewException: - case ResultWas::FatalErrorCondition:elementName = "error"; - break; - case ResultWas::ExplicitFailure:elementName = "failure"; - break; - case ResultWas::ExpressionFailed:elementName = "failure"; - break; - case ResultWas::DidntThrowException:elementName = "failure"; - break; - - // We should never see these here: - case ResultWas::Info: - case ResultWas::Warning: - case ResultWas::Ok: - case ResultWas::Unknown: - case ResultWas::FailureBit: - case ResultWas::Exception:elementName = "internalError"; - break; + struct SonarQubeReporter : CumulativeReporterBase { + + SonarQubeReporter(ReporterConfig const &config) + : CumulativeReporterBase(config), xml(config.stream()) { + m_reporterPrefs.shouldRedirectStdOut = true; + m_reporterPrefs.shouldReportAllAssertions = true; + } + + ~SonarQubeReporter() override; + + static std::string getDescription() { + return "Reports test results in the Generic Test Data SonarQube XML format"; + } + + static std::set getSupportedVerbosities() { + return {Verbosity::Normal}; + } + + void noMatchingTestCases(std::string const & /*spec*/) override {} + + void testRunStarting(TestRunInfo const &testRunInfo) override { + CumulativeReporterBase::testRunStarting(testRunInfo); + xml.startElement("testExecutions"); + xml.writeAttribute("version", "1"); + } + + void testGroupEnded(TestGroupStats const &testGroupStats) override { + CumulativeReporterBase::testGroupEnded(testGroupStats); + writeGroup(*m_testGroups.back()); } - } - XmlWriter::ScopedElement e = xml.scopedElement(elementName); + void testRunEndedCumulative() override { + xml.endElement(); + } + + void writeGroup(TestGroupNode const &groupNode) { + std::map testsPerFile; + for (auto const &child: groupNode.children) + testsPerFile[child->value.testInfo.lineInfo.file].push_back(child); + + for (auto const &kv: testsPerFile) + writeTestFile(kv.first.c_str(), kv.second); + } - ReusableStringStream messageRss; - messageRss << result.getTestMacroName() << "(" << result.getExpression() << ")"; - xml.writeAttribute("message", messageRss.str()); + void writeTestFile(const char *filename, TestGroupNode::ChildNodes const &testCaseNodes) { + XmlWriter::ScopedElement e = xml.scopedElement("file"); + xml.writeAttribute("path", filename); - ReusableStringStream textRss; - if (stats.totals.assertions.total() > 0) { - textRss << "FAILED:\n"; - if (result.hasExpression()) { - textRss << "\t" << result.getExpressionInMacro() << "\n"; + for (auto const &child: testCaseNodes) + writeTestCase(*child); } - if (result.hasExpandedExpression()) { - textRss << "with expansion:\n\t" << result.getExpandedExpression() << "\n"; + + void writeTestCase(TestCaseNode const &testCaseNode) { + // All test cases have exactly one section - which represents the + // test case itself. That section may have 0-n nested sections + assert(testCaseNode.children.size() == 1); + SectionNode const &rootSection = *testCaseNode.children.front(); + writeSection("", rootSection, testCaseNode.value.testInfo.okToFail()); } - } - if (!result.getMessage().empty()) - textRss << result.getMessage() << "\n"; + void writeSection(std::string const &rootName, SectionNode const §ionNode, bool okToFail) { + std::string name = trim(sectionNode.stats.sectionInfo.name); + if (!rootName.empty()) + name = rootName + '/' + name; + + if (!sectionNode.assertions.empty() || !sectionNode.stdOut.empty() || !sectionNode.stdErr.empty()) { + XmlWriter::ScopedElement e = xml.scopedElement("testCase"); + xml.writeAttribute("name", name); + xml.writeAttribute("duration", static_cast(sectionNode.stats.durationInSeconds * 1000)); - for (auto const &msg : stats.infoMessages) - if (msg.type == ResultWas::Info) - textRss << msg.message << "\n"; + writeAssertions(sectionNode, okToFail); + } - textRss << "at " << result.getSourceInfo(); - xml.writeText(textRss.str(), XmlFormatting::Newline); - } - } + for (auto const &childNode: sectionNode.childSections) + writeSection(name, *childNode, okToFail); + } + + void writeAssertions(SectionNode const §ionNode, bool okToFail) { + for (auto const &assertion: sectionNode.assertions) + writeAssertion(assertion, okToFail); + } + + void writeAssertion(AssertionStats const &stats, bool okToFail) { + AssertionResult const &result = stats.assertionResult; + if (!result.isOk()) { + std::string elementName; + if (okToFail) { + elementName = "skipped"; + } else { + switch (result.getResultType()) { + case ResultWas::ThrewException: + case ResultWas::FatalErrorCondition: + elementName = "error"; + break; + case ResultWas::ExplicitFailure: + elementName = "failure"; + break; + case ResultWas::ExpressionFailed: + elementName = "failure"; + break; + case ResultWas::DidntThrowException: + elementName = "failure"; + break; + + // We should never see these here: + case ResultWas::Info: + case ResultWas::Warning: + case ResultWas::Ok: + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: + elementName = "internalError"; + break; + } + } + + XmlWriter::ScopedElement e = xml.scopedElement(elementName); + + ReusableStringStream messageRss; + messageRss << result.getTestMacroName() << "(" << result.getExpression() << ")"; + xml.writeAttribute("message", messageRss.str()); + + ReusableStringStream textRss; + if (stats.totals.assertions.total() > 0) { + textRss << "FAILED:\n"; + if (result.hasExpression()) { + textRss << "\t" << result.getExpressionInMacro() << "\n"; + } + if (result.hasExpandedExpression()) { + textRss << "with expansion:\n\t" << result.getExpandedExpression() << "\n"; + } + } + + if (!result.getMessage().empty()) + textRss << result.getMessage() << "\n"; + + for (auto const &msg: stats.infoMessages) + if (msg.type == ResultWas::Info) + textRss << msg.message << "\n"; + + textRss << "at " << result.getSourceInfo(); + xml.writeText(textRss.str(), XmlFormatting::Newline); + } + } - private: - XmlWriter xml; -}; + private: + XmlWriter xml; + }; #ifdef CATCH_IMPL -SonarQubeReporter::~SonarQubeReporter() {} + SonarQubeReporter::~SonarQubeReporter() {} #endif -CATCH_REGISTER_REPORTER( "sonarqube", SonarQubeReporter ) + CATCH_REGISTER_REPORTER( "sonarqube", SonarQubeReporter ) } // end namespace Catch diff --git a/test/SystemTest/catch_reporter_tap.hpp b/test/SystemTest/catch_reporter_tap.hpp index f0567464..63b04dca 100644 --- a/test/SystemTest/catch_reporter_tap.hpp +++ b/test/SystemTest/catch_reporter_tap.hpp @@ -19,226 +19,238 @@ namespace Catch { -struct TAPReporter : StreamingReporterBase { - - using StreamingReporterBase::StreamingReporterBase; - - TAPReporter(ReporterConfig const &config) : - StreamingReporterBase(config) { - m_reporterPrefs.shouldReportAllAssertions = true; - } - - ~TAPReporter() override; - - static std::string getDescription() { - return "Reports test results in TAP format, suitable for test harnesses"; - } - - void noMatchingTestCases(std::string const &spec) override { - stream << "# No test cases matched '" << spec << "'" << std::endl; - } - - void assertionStarting(AssertionInfo const &) override {} - - bool assertionEnded(AssertionStats const &_assertionStats) override { - ++counter; - - stream << "# " << currentTestCaseInfo->name << std::endl; - AssertionPrinter printer(stream, _assertionStats, counter); - printer.print(); - - stream << std::endl; - return true; - } - - void testRunEnded(TestRunStats const &_testRunStats) override { - printTotals(_testRunStats.totals); - stream << "\n" << std::endl; - StreamingReporterBase::testRunEnded(_testRunStats); - } - - private: - std::size_t counter = 0; - class AssertionPrinter { - public: - AssertionPrinter &operator=(AssertionPrinter const &) = delete; - AssertionPrinter(AssertionPrinter const &) = delete; - AssertionPrinter(std::ostream &_stream, AssertionStats const &_stats, std::size_t _counter) - : stream(_stream), - result(_stats.assertionResult), - messages(_stats.infoMessages), - itMessage(_stats.infoMessages.begin()), - printInfoMessages(true), - counter(_counter) {} - - void print() { - itMessage = messages.begin(); - - switch (result.getResultType()) { - case ResultWas::Ok:printResultType(passedString()); - printOriginalExpression(); - printReconstructedExpression(); - if (!result.hasExpression()) - printRemainingMessages(Colour::None); - else - printRemainingMessages(); - break; - case ResultWas::ExpressionFailed: - if (result.isOk()) { - printResultType(passedString()); - } else { - printResultType(failedString()); - } - printOriginalExpression(); - printReconstructedExpression(); - if (result.isOk()) { - printIssue(" # TODO"); - } - printRemainingMessages(); - break; - case ResultWas::ThrewException:printResultType(failedString()); - printIssue("unexpected exception with message:"); - printMessage(); - printExpressionWas(); - printRemainingMessages(); - break; - case ResultWas::FatalErrorCondition:printResultType(failedString()); - printIssue("fatal error condition with message:"); - printMessage(); - printExpressionWas(); - printRemainingMessages(); - break; - case ResultWas::DidntThrowException:printResultType(failedString()); - printIssue("expected exception, got none"); - printExpressionWas(); - printRemainingMessages(); - break; - case ResultWas::Info:printResultType("info"); - printMessage(); - printRemainingMessages(); - break; - case ResultWas::Warning:printResultType("warning"); - printMessage(); - printRemainingMessages(); - break; - case ResultWas::ExplicitFailure:printResultType(failedString()); - printIssue("explicitly"); - printRemainingMessages(Colour::None); - break; - // These cases are here to prevent compiler warnings - case ResultWas::Unknown: - case ResultWas::FailureBit: - case ResultWas::Exception:printResultType("** internal error **"); - break; - } - } - - private: - static Colour::Code dimColour() { return Colour::FileName; } - - static const char *failedString() { return "not ok"; } - static const char *passedString() { return "ok"; } - - void printSourceInfo() const { - Colour colourGuard(dimColour()); - stream << result.getSourceInfo() << ":"; - } - - void printResultType(std::string const &passOrFail) const { - if (!passOrFail.empty()) { - stream << passOrFail << ' ' << counter << " -"; - } - } - - void printIssue(std::string const &issue) const { - stream << " " << issue; - } - - void printExpressionWas() { - if (result.hasExpression()) { - stream << ";"; - { - Colour colour(dimColour()); - stream << " expression was:"; + struct TAPReporter : StreamingReporterBase { + + using StreamingReporterBase::StreamingReporterBase; + + TAPReporter(ReporterConfig const &config) : + StreamingReporterBase(config) { + m_reporterPrefs.shouldReportAllAssertions = true; + } + + ~TAPReporter() override; + + static std::string getDescription() { + return "Reports test results in TAP format, suitable for test harnesses"; + } + + void noMatchingTestCases(std::string const &spec) override { + stream << "# No test cases matched '" << spec << "'" << std::endl; } - printOriginalExpression(); - } - } - - void printOriginalExpression() const { - if (result.hasExpression()) { - stream << " " << result.getExpression(); - } - } - - void printReconstructedExpression() const { - if (result.hasExpandedExpression()) { - { - Colour colour(dimColour()); - stream << " for: "; + + void assertionStarting(AssertionInfo const &) override {} + + bool assertionEnded(AssertionStats const &_assertionStats) override { + ++counter; + + stream << "# " << currentTestCaseInfo->name << std::endl; + AssertionPrinter printer(stream, _assertionStats, counter); + printer.print(); + + stream << std::endl; + return true; } - std::string expr = result.getExpandedExpression(); - std::replace(expr.begin(), expr.end(), '\n', ' '); - stream << expr; - } - } - - void printMessage() { - if (itMessage != messages.end()) { - stream << " '" << itMessage->message << "'"; - ++itMessage; - } - } - - void printRemainingMessages(Colour::Code colour = dimColour()) { - if (itMessage == messages.end()) { - return; - } - - const auto itEnd = messages.cend(); - const auto N = static_cast( std::distance(itMessage, itEnd)); - - { - Colour colourGuard(colour); - stream << " with " << pluralise(N, "message") << ":"; - } - - while (itMessage != itEnd) { - // If this assertion is a warning ignore any INFO messages - if (printInfoMessages || itMessage->type != ResultWas::Info) { - stream << " '" << itMessage->message << "'"; - if (++itMessage != itEnd) { - Colour colourGuard(dimColour()); - stream << " and"; - } - continue; + + void testRunEnded(TestRunStats const &_testRunStats) override { + printTotals(_testRunStats.totals); + stream << "\n" << std::endl; + StreamingReporterBase::testRunEnded(_testRunStats); + } + + private: + std::size_t counter = 0; + + class AssertionPrinter { + public: + AssertionPrinter &operator=(AssertionPrinter const &) = delete; + + AssertionPrinter(AssertionPrinter const &) = delete; + + AssertionPrinter(std::ostream &_stream, AssertionStats const &_stats, std::size_t _counter) + : stream(_stream), + result(_stats.assertionResult), + messages(_stats.infoMessages), + itMessage(_stats.infoMessages.begin()), + printInfoMessages(true), + counter(_counter) {} + + void print() { + itMessage = messages.begin(); + + switch (result.getResultType()) { + case ResultWas::Ok: + printResultType(passedString()); + printOriginalExpression(); + printReconstructedExpression(); + if (!result.hasExpression()) + printRemainingMessages(Colour::None); + else + printRemainingMessages(); + break; + case ResultWas::ExpressionFailed: + if (result.isOk()) { + printResultType(passedString()); + } else { + printResultType(failedString()); + } + printOriginalExpression(); + printReconstructedExpression(); + if (result.isOk()) { + printIssue(" # TODO"); + } + printRemainingMessages(); + break; + case ResultWas::ThrewException: + printResultType(failedString()); + printIssue("unexpected exception with message:"); + printMessage(); + printExpressionWas(); + printRemainingMessages(); + break; + case ResultWas::FatalErrorCondition: + printResultType(failedString()); + printIssue("fatal error condition with message:"); + printMessage(); + printExpressionWas(); + printRemainingMessages(); + break; + case ResultWas::DidntThrowException: + printResultType(failedString()); + printIssue("expected exception, got none"); + printExpressionWas(); + printRemainingMessages(); + break; + case ResultWas::Info: + printResultType("info"); + printMessage(); + printRemainingMessages(); + break; + case ResultWas::Warning: + printResultType("warning"); + printMessage(); + printRemainingMessages(); + break; + case ResultWas::ExplicitFailure: + printResultType(failedString()); + printIssue("explicitly"); + printRemainingMessages(Colour::None); + break; + // These cases are here to prevent compiler warnings + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: + printResultType("** internal error **"); + break; + } + } + + private: + static Colour::Code dimColour() { return Colour::FileName; } + + static const char *failedString() { return "not ok"; } + + static const char *passedString() { return "ok"; } + + void printSourceInfo() const { + Colour colourGuard(dimColour()); + stream << result.getSourceInfo() << ":"; + } + + void printResultType(std::string const &passOrFail) const { + if (!passOrFail.empty()) { + stream << passOrFail << ' ' << counter << " -"; + } + } + + void printIssue(std::string const &issue) const { + stream << " " << issue; + } + + void printExpressionWas() { + if (result.hasExpression()) { + stream << ";"; + { + Colour colour(dimColour()); + stream << " expression was:"; + } + printOriginalExpression(); + } + } + + void printOriginalExpression() const { + if (result.hasExpression()) { + stream << " " << result.getExpression(); + } + } + + void printReconstructedExpression() const { + if (result.hasExpandedExpression()) { + { + Colour colour(dimColour()); + stream << " for: "; + } + std::string expr = result.getExpandedExpression(); + std::replace(expr.begin(), expr.end(), '\n', ' '); + stream << expr; + } + } + + void printMessage() { + if (itMessage != messages.end()) { + stream << " '" << itMessage->message << "'"; + ++itMessage; + } + } + + void printRemainingMessages(Colour::Code colour = dimColour()) { + if (itMessage == messages.end()) { + return; + } + + const auto itEnd = messages.cend(); + const auto N = static_cast( std::distance(itMessage, itEnd)); + + { + Colour colourGuard(colour); + stream << " with " << pluralise(N, "message") << ":"; + } + + while (itMessage != itEnd) { + // If this assertion is a warning ignore any INFO messages + if (printInfoMessages || itMessage->type != ResultWas::Info) { + stream << " '" << itMessage->message << "'"; + if (++itMessage != itEnd) { + Colour colourGuard(dimColour()); + stream << " and"; + } + continue; + } + ++itMessage; + } + } + + private: + std::ostream &stream; + AssertionResult const &result; + std::vector messages; + std::vector::const_iterator itMessage; + bool printInfoMessages; + std::size_t counter; + }; + + void printTotals(const Totals &totals) const { + stream << "1.." << totals.assertions.total(); + if (totals.testCases.total() == 0) { + stream << " # Skipped: No tests ran."; + } } - ++itMessage; - } - } - - private: - std::ostream &stream; - AssertionResult const &result; - std::vector messages; - std::vector::const_iterator itMessage; - bool printInfoMessages; - std::size_t counter; - }; - - void printTotals(const Totals &totals) const { - stream << "1.." << totals.assertions.total(); - if (totals.testCases.total() == 0) { - stream << " # Skipped: No tests ran."; - } - } -}; + }; #ifdef CATCH_IMPL -TAPReporter::~TAPReporter() {} + TAPReporter::~TAPReporter() {} #endif -CATCH_REGISTER_REPORTER( "tap", TAPReporter ) + CATCH_REGISTER_REPORTER( "tap", TAPReporter ) } // end namespace Catch diff --git a/test/SystemTest/catch_reporter_teamcity.hpp b/test/SystemTest/catch_reporter_teamcity.hpp index 50eb4fb5..a57b0587 100644 --- a/test/SystemTest/catch_reporter_teamcity.hpp +++ b/test/SystemTest/catch_reporter_teamcity.hpp @@ -23,181 +23,191 @@ namespace Catch { -struct TeamCityReporter : StreamingReporterBase { - TeamCityReporter(ReporterConfig const &_config) - : StreamingReporterBase(_config) { - m_reporterPrefs.shouldRedirectStdOut = true; - } - - static std::string escape(std::string const &str) { - std::string escaped = str; - replaceInPlace(escaped, "|", "||"); - replaceInPlace(escaped, "'", "|'"); - replaceInPlace(escaped, "\n", "|n"); - replaceInPlace(escaped, "\r", "|r"); - replaceInPlace(escaped, "[", "|["); - replaceInPlace(escaped, "]", "|]"); - return escaped; - } - ~TeamCityReporter() override; - - static std::string getDescription() { - return "Reports test results as TeamCity service messages"; - } - - void skipTest(TestCaseInfo const & /* testInfo */ ) override { - } - - void noMatchingTestCases(std::string const & /* spec */ ) override {} - - void testGroupStarting(GroupInfo const &groupInfo) override { - StreamingReporterBase::testGroupStarting(groupInfo); - stream << "##teamcity[testSuiteStarted name='" - << escape(groupInfo.name) << "']\n"; - } - void testGroupEnded(TestGroupStats const &testGroupStats) override { - StreamingReporterBase::testGroupEnded(testGroupStats); - stream << "##teamcity[testSuiteFinished name='" - << escape(testGroupStats.groupInfo.name) << "']\n"; - } - - void assertionStarting(AssertionInfo const &) override {} - - bool assertionEnded(AssertionStats const &assertionStats) override { - AssertionResult const &result = assertionStats.assertionResult; - if (!result.isOk()) { - - ReusableStringStream msg; - if (!m_headerPrintedForThisSection) - printSectionHeader(msg.get()); - m_headerPrintedForThisSection = true; - - msg << result.getSourceInfo() << "\n"; - - switch (result.getResultType()) { - case ResultWas::ExpressionFailed:msg << "expression failed"; - break; - case ResultWas::ThrewException:msg << "unexpected exception"; - break; - case ResultWas::FatalErrorCondition:msg << "fatal error condition"; - break; - case ResultWas::DidntThrowException:msg << "no exception was thrown where one was expected"; - break; - case ResultWas::ExplicitFailure:msg << "explicit failure"; - break; - - // We shouldn't get here because of the isOk() test - case ResultWas::Ok: - case ResultWas::Info: - case ResultWas::Warning:CATCH_ERROR("Internal error in TeamCity reporter"); - // These cases are here to prevent compiler warnings - case ResultWas::Unknown: - case ResultWas::FailureBit: - case ResultWas::Exception:CATCH_ERROR("Not implemented"); - } - if (assertionStats.infoMessages.size() == 1) - msg << " with message:"; - if (assertionStats.infoMessages.size() > 1) - msg << " with messages:"; - for (auto const &messageInfo : assertionStats.infoMessages) - msg << "\n \"" << messageInfo.message << "\""; - - if (result.hasExpression()) { - msg << - "\n " << result.getExpressionInMacro() << "\n" - "with expansion:\n" << - " " << result.getExpandedExpression() << "\n"; - } - - if (currentTestCaseInfo->okToFail()) { - msg << "- failure ignore as test marked as 'ok to fail'\n"; - stream << "##teamcity[testIgnored" - << " name='" << escape(currentTestCaseInfo->name) << "'" - << " message='" << escape(msg.str()) << "'" - << "]\n"; - } else { - stream << "##teamcity[testFailed" - << " name='" << escape(currentTestCaseInfo->name) << "'" - << " message='" << escape(msg.str()) << "'" - << "]\n"; - } - } - stream.flush(); - return true; - } - - void sectionStarting(SectionInfo const §ionInfo) override { - m_headerPrintedForThisSection = false; - StreamingReporterBase::sectionStarting(sectionInfo); - } - - void testCaseStarting(TestCaseInfo const &testInfo) override { - m_testTimer.start(); - StreamingReporterBase::testCaseStarting(testInfo); - stream << "##teamcity[testStarted name='" - << escape(testInfo.name) << "']\n"; - stream.flush(); - } - - void testCaseEnded(TestCaseStats const &testCaseStats) override { - StreamingReporterBase::testCaseEnded(testCaseStats); - if (!testCaseStats.stdOut.empty()) - stream << "##teamcity[testStdOut name='" - << escape(testCaseStats.testInfo.name) - << "' out='" << escape(testCaseStats.stdOut) << "']\n"; - if (!testCaseStats.stdErr.empty()) - stream << "##teamcity[testStdErr name='" - << escape(testCaseStats.testInfo.name) - << "' out='" << escape(testCaseStats.stdErr) << "']\n"; - stream << "##teamcity[testFinished name='" - << escape(testCaseStats.testInfo.name) << "' duration='" - << m_testTimer.getElapsedMilliseconds() << "']\n"; - stream.flush(); - } - - private: - void printSectionHeader(std::ostream &os) { - assert(!m_sectionStack.empty()); - - if (m_sectionStack.size() > 1) { - os << getLineOfChars<'-'>() << "\n"; - - std::vector::const_iterator - it = m_sectionStack.begin() + 1, // Skip first section (test case) - itEnd = m_sectionStack.end(); - for (; it != itEnd; ++it) - printHeaderString(os, it->name); - os << getLineOfChars<'-'>() << "\n"; - } - - SourceLineInfo lineInfo = m_sectionStack.front().lineInfo; - - os << lineInfo << "\n"; - os << getLineOfChars<'.'>() << "\n\n"; - } - - // if string has a : in first line will set indent to follow it on - // subsequent lines - static void printHeaderString(std::ostream &os, std::string const &_string, std::size_t indent = 0) { - std::size_t i = _string.find(": "); - if (i != std::string::npos) - i += 2; - else - i = 0; - os << Column(_string) - .indent(indent + i) - .initialIndent(indent) << "\n"; - } - private: - bool m_headerPrintedForThisSection = false; - Timer m_testTimer; -}; + struct TeamCityReporter : StreamingReporterBase { + TeamCityReporter(ReporterConfig const &_config) + : StreamingReporterBase(_config) { + m_reporterPrefs.shouldRedirectStdOut = true; + } + + static std::string escape(std::string const &str) { + std::string escaped = str; + replaceInPlace(escaped, "|", "||"); + replaceInPlace(escaped, "'", "|'"); + replaceInPlace(escaped, "\n", "|n"); + replaceInPlace(escaped, "\r", "|r"); + replaceInPlace(escaped, "[", "|["); + replaceInPlace(escaped, "]", "|]"); + return escaped; + } + + ~TeamCityReporter() override; + + static std::string getDescription() { + return "Reports test results as TeamCity service messages"; + } + + void skipTest(TestCaseInfo const & /* testInfo */ ) override { + } + + void noMatchingTestCases(std::string const & /* spec */ ) override {} + + void testGroupStarting(GroupInfo const &groupInfo) override { + StreamingReporterBase::testGroupStarting(groupInfo); + stream << "##teamcity[testSuiteStarted name='" + << escape(groupInfo.name) << "']\n"; + } + + void testGroupEnded(TestGroupStats const &testGroupStats) override { + StreamingReporterBase::testGroupEnded(testGroupStats); + stream << "##teamcity[testSuiteFinished name='" + << escape(testGroupStats.groupInfo.name) << "']\n"; + } + + void assertionStarting(AssertionInfo const &) override {} + + bool assertionEnded(AssertionStats const &assertionStats) override { + AssertionResult const &result = assertionStats.assertionResult; + if (!result.isOk()) { + + ReusableStringStream msg; + if (!m_headerPrintedForThisSection) + printSectionHeader(msg.get()); + m_headerPrintedForThisSection = true; + + msg << result.getSourceInfo() << "\n"; + + switch (result.getResultType()) { + case ResultWas::ExpressionFailed: + msg << "expression failed"; + break; + case ResultWas::ThrewException: + msg << "unexpected exception"; + break; + case ResultWas::FatalErrorCondition: + msg << "fatal error condition"; + break; + case ResultWas::DidntThrowException: + msg << "no exception was thrown where one was expected"; + break; + case ResultWas::ExplicitFailure: + msg << "explicit failure"; + break; + + // We shouldn't get here because of the isOk() test + case ResultWas::Ok: + case ResultWas::Info: + case ResultWas::Warning: + CATCH_ERROR("Internal error in TeamCity reporter"); + // These cases are here to prevent compiler warnings + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: + CATCH_ERROR("Not implemented"); + } + if (assertionStats.infoMessages.size() == 1) + msg << " with message:"; + if (assertionStats.infoMessages.size() > 1) + msg << " with messages:"; + for (auto const &messageInfo: assertionStats.infoMessages) + msg << "\n \"" << messageInfo.message << "\""; + + if (result.hasExpression()) { + msg << + "\n " << result.getExpressionInMacro() << "\n" + "with expansion:\n" << + " " << result.getExpandedExpression() << "\n"; + } + + if (currentTestCaseInfo->okToFail()) { + msg << "- failure ignore as test marked as 'ok to fail'\n"; + stream << "##teamcity[testIgnored" + << " name='" << escape(currentTestCaseInfo->name) << "'" + << " message='" << escape(msg.str()) << "'" + << "]\n"; + } else { + stream << "##teamcity[testFailed" + << " name='" << escape(currentTestCaseInfo->name) << "'" + << " message='" << escape(msg.str()) << "'" + << "]\n"; + } + } + stream.flush(); + return true; + } + + void sectionStarting(SectionInfo const §ionInfo) override { + m_headerPrintedForThisSection = false; + StreamingReporterBase::sectionStarting(sectionInfo); + } + + void testCaseStarting(TestCaseInfo const &testInfo) override { + m_testTimer.start(); + StreamingReporterBase::testCaseStarting(testInfo); + stream << "##teamcity[testStarted name='" + << escape(testInfo.name) << "']\n"; + stream.flush(); + } + + void testCaseEnded(TestCaseStats const &testCaseStats) override { + StreamingReporterBase::testCaseEnded(testCaseStats); + if (!testCaseStats.stdOut.empty()) + stream << "##teamcity[testStdOut name='" + << escape(testCaseStats.testInfo.name) + << "' out='" << escape(testCaseStats.stdOut) << "']\n"; + if (!testCaseStats.stdErr.empty()) + stream << "##teamcity[testStdErr name='" + << escape(testCaseStats.testInfo.name) + << "' out='" << escape(testCaseStats.stdErr) << "']\n"; + stream << "##teamcity[testFinished name='" + << escape(testCaseStats.testInfo.name) << "' duration='" + << m_testTimer.getElapsedMilliseconds() << "']\n"; + stream.flush(); + } + + private: + void printSectionHeader(std::ostream &os) { + assert(!m_sectionStack.empty()); + + if (m_sectionStack.size() > 1) { + os << getLineOfChars<'-'>() << "\n"; + + std::vector::const_iterator + it = m_sectionStack.begin() + 1, // Skip first section (test case) + itEnd = m_sectionStack.end(); + for (; it != itEnd; ++it) + printHeaderString(os, it->name); + os << getLineOfChars<'-'>() << "\n"; + } + + SourceLineInfo lineInfo = m_sectionStack.front().lineInfo; + + os << lineInfo << "\n"; + os << getLineOfChars<'.'>() << "\n\n"; + } + + // if string has a : in first line will set indent to follow it on + // subsequent lines + static void printHeaderString(std::ostream &os, std::string const &_string, std::size_t indent = 0) { + std::size_t i = _string.find(": "); + if (i != std::string::npos) + i += 2; + else + i = 0; + os << Column(_string) + .indent(indent + i) + .initialIndent(indent) << "\n"; + } + + private: + bool m_headerPrintedForThisSection = false; + Timer m_testTimer; + }; #ifdef CATCH_IMPL -TeamCityReporter::~TeamCityReporter() {} + TeamCityReporter::~TeamCityReporter() {} #endif -CATCH_REGISTER_REPORTER( "teamcity", TeamCityReporter ) + CATCH_REGISTER_REPORTER( "teamcity", TeamCityReporter ) } // end namespace Catch diff --git a/test/torchscripts/PQ/PQ.cpp b/test/torchscripts/PQ/PQ.cpp index 68026f33..857fd598 100644 --- a/test/torchscripts/PQ/PQ.cpp +++ b/test/torchscripts/PQ/PQ.cpp @@ -15,8 +15,8 @@ int main() { std::vector prototypes; std::ifstream in_file("/home/haolan/PQ/prototypes.pt", std::ios::binary); if (!in_file.is_open()) { - std::cerr << "Error opening file prototypes.pt\n"; - return 1; + std::cerr << "Error opening file prototypes.pt\n"; + return 1; } torch::load(prototypes, in_file); diff --git a/test/torchscripts/PQ/PQ.py b/test/torchscripts/PQ/PQ.py index 1d88d548..3747667f 100644 --- a/test/torchscripts/PQ/PQ.py +++ b/test/torchscripts/PQ/PQ.py @@ -2,6 +2,7 @@ import time + def kmeans(X, K, max_iters=100): N, D = X.shape @@ -21,28 +22,34 @@ def kmeans(X, K, max_iters=100): centroids[k] = torch.mean(X[labels == k], dim=0) return centroids, labels + + class MyModule(torch.nn.Module): - def __init__(self,prototypes): + def __init__(self, prototypes): super(MyModule, self).__init__() - #self.fc = torch.nn.Linear(10, 5) - self.prototypes=torch.nn.Parameter(prototypes) - self.QA=torch.nn.Parameter(torch.rand(5,5)) - #self.register_parameter("prototypes", self.prototypes) - def forward(self,A: torch.Tensor): - return torch.matmul(A,A.T)+torch.sum(self.prototypes[0])+torch.sum(self.QA) - + # self.fc = torch.nn.Linear(10, 5) + self.prototypes = torch.nn.Parameter(prototypes) + self.QA = torch.nn.Parameter(torch.rand(5, 5)) + # self.register_parameter("prototypes", self.prototypes) + + def forward(self, A: torch.Tensor): + return torch.matmul(A, A.T) + torch.sum(self.prototypes[0]) + torch.sum(self.QA) + + def save_model(model, path, X): tx = X.to('cpu') # tx=X model2 = model.to('cpu') # model2=model - #model2.eval() + # model2.eval() X = torch.tensor([1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0]) traced_model = torch.jit.script(model2) ru = traced_model(tx) - + traced_model.save(path) + + def main(): learning = True @@ -73,7 +80,7 @@ def main(): if trainingSet != None: print("Starting prototypes learning on training set size of " - + str(trainingSet.shape[0]) + ", " + str(trainingSet.shape[1])) + + str(trainingSet.shape[0]) + ", " + str(trainingSet.shape[1])) t = time.time() for c in range(C): @@ -87,13 +94,13 @@ def main(): A_subspace_np = A_subspace.detach().numpy() # Run KMeans on the subspace - centroids_torch,LB = kmeans(A_subspace,K, 10) + centroids_torch, LB = kmeans(A_subspace, K, 10) # Get the centroids (prototypes) - #centroids = kmeans.cluster_centers_ + # centroids = kmeans.cluster_centers_ # Convert back to PyTorch tensor - #centroids_torch = torch.from_numpy(centroids) + # centroids_torch = torch.from_numpy(centroids) # Append to the list of prototypes prototypes.append(centroids_torch) @@ -102,9 +109,9 @@ def main(): print("\nPrototype Learning: " + str(time.time() - t) + "s") model = MyModule(torch.stack(prototypes)) - save_model(model.to('cpu'), "prototypes.pt",torch.zeros(5,5)) - #model.save_model("prototypes.pt") - #torch.save(prototypes, 'prototypes.pt') + save_model(model.to('cpu'), "prototypes.pt", torch.zeros(5, 5)) + # model.save_model("prototypes.pt") + # torch.save(prototypes, 'prototypes.pt') else: print("Loading prototypes from serialized pickle file\n") @@ -122,9 +129,9 @@ def main(): for c in range(C): # Get the prototypes for this subspace prototypes_c = prototypes[c] - + # Get the subvector for this subspace - a_subvector = a[c * D_c : (c + 1) * D_c] + a_subvector = a[c * D_c: (c + 1) * D_c] # Calculate the distances from the subvector to each prototype distances = torch.norm(prototypes_c - a_subvector, dim=1) @@ -150,7 +157,6 @@ def main(): # Assume that B is a PyTorch tensor of size D x M - # Initialize a list to store the tables for each subspace tables = [] @@ -161,7 +167,7 @@ def main(): prototypes_c = prototypes[c] # Slice B to get the corresponding subspace - B_subspace = B[c * D_c : (c + 1) * D_c, :] + B_subspace = B[c * D_c: (c + 1) * D_c, :] # Initialize an empty list to store the table for this subspace table_c = [] @@ -213,7 +219,6 @@ def main(): # Convert the list of all rows to a PyTorch tensor result_tensor = torch.stack(result) - print("Aggregation: " + str(time.time() - t) + "s") print("\nTotal: " + str(time.time() - total) + "s") @@ -227,6 +232,8 @@ def main(): print("Exact time: " + str(time.time() - t) + "s") print(eResult) print("\nerror: " + str(torch.norm(result_tensor - eResult, p='fro').item())) - print(len(prototypes),torch.stack(prototypes).size()) + print(len(prototypes), torch.stack(prototypes).size()) + + if __name__ == '__main__': main() From 0def735f2416d1f4a6eb3c723f9975920fb50e4d Mon Sep 17 00:00:00 2001 From: tony <292224750@qq.com> Date: Sat, 24 Jun 2023 15:16:40 +0800 Subject: [PATCH 4/4] 1. now make the streaming on both A and B possible --- .github/workflows/cmake.yml | 3 +- benchmark/scripts/scanACol/config_CPPCRS.csv | 8 + benchmark/scripts/scanACol/config_FDAMM.csv | 6 + .../scripts/scanAColCPP/config_BCoAMM.csv | 6 + .../scripts/scanAColCPP/config_BerCRS.csv | 6 + benchmark/scripts/scanAColCPP/config_CRS.csv | 6 + .../scripts/scanAColCPP/config_CoAMM.csv | 6 + .../scanAColCPP/config_CounterSketch.csv | 6 + benchmark/scripts/scanAColCPP/config_EWS.csv | 6 + .../scripts/scanAColCPP/config_FDAMM.csv | 6 + .../scripts/scanAColCPP/config_RAWMM.csv | 6 + benchmark/scripts/scanAColCPP/config_WCR.csv | 6 + benchmark/scripts/scanARow/config_FDAMM.csv | 6 + .../scripts/scanARowCPP/config_BCoAMM.csv | 6 + .../scripts/scanARowCPP/config_BerCRS.csv | 6 + benchmark/scripts/scanARowCPP/config_CRS.csv | 6 + .../scripts/scanARowCPP/config_CoAMM.csv | 6 + .../scanARowCPP/config_CounterSketch.csv | 6 + benchmark/scripts/scanARowCPP/config_EWS.csv | 6 + .../scripts/scanARowCPP/config_FDAMM.csv | 6 + .../scripts/scanARowCPP/config_RAWMM.csv | 6 + benchmark/scripts/scanARowCPP/config_WCR.csv | 6 + .../OoOCommon.py | 125 ++++++ .../accuBar.py | 328 ++++++++++++++++ .../autoParase.py | 87 +++++ .../config_BCoAMM.csv | 6 + .../config_BerCRS.csv | 6 + .../config_CPPBCOOFD.csv | 10 + .../config_CPPBCRS.csv | 10 + .../config_CPPCOFD.csv | 10 + .../config_CPPCOUNTERSKETCH.csv | 10 + .../config_CPPCRS.csv | 13 + .../config_CPPINT8.csv | 10 + .../config_CPPMM.csv | 13 + .../config_CPPSMPPCA.csv | 11 + .../config_CoAMM.csv | 6 + .../config_CounterSketch.csv | 6 + .../drawTogether.py | 179 +++++++++ .../groupBar.py | 236 +++++++++++ .../groupBar2.py | 232 +++++++++++ .../groupLine.py | 368 ++++++++++++++++++ .../perfRu.csv | 7 + .../OoOCommon.py | 125 ++++++ .../accuBar.py | 328 ++++++++++++++++ .../autoParase.py | 87 +++++ .../config_BCoAMM.csv | 6 + .../config_BerCRS.csv | 6 + .../config_CPPBCOOFD.csv | 10 + .../config_CPPBCRS.csv | 10 + .../config_CPPCOFD.csv | 10 + .../config_CPPCOUNTERSKETCH.csv | 10 + .../config_CPPCRS.csv | 14 + .../config_CPPINT8.csv | 10 + .../config_CPPMM.csv | 14 + .../config_CPPSMPPCA.csv | 12 + .../config_CoAMM.csv | 6 + .../config_CounterSketch.csv | 6 + .../drawTogether.py | 179 +++++++++ .../groupBar.py | 236 +++++++++++ .../groupBar2.py | 232 +++++++++++ .../groupLine.py | 368 ++++++++++++++++++ .../perfRu.csv | 7 + .../OoOCommon.py | 125 ++++++ .../accuBar.py | 328 ++++++++++++++++ .../autoParase.py | 87 +++++ .../config_BCoAMM.csv | 6 + .../config_BerCRS.csv | 6 + .../config_CPPBCOOFD.csv | 10 + .../config_CPPBCRS.csv | 10 + .../config_CPPCOFD.csv | 10 + .../config_CPPCOUNTERSKETCH.csv | 10 + .../config_CPPCRS.csv | 13 + .../config_CPPINT8.csv | 10 + .../config_CPPMM.csv | 13 + .../config_CPPSMPPCA.csv | 11 + .../config_CoAMM.csv | 6 + .../config_CounterSketch.csv | 6 + .../drawTogether.py | 180 +++++++++ .../groupBar.py | 236 +++++++++++ .../groupBar2.py | 232 +++++++++++ .../groupLine.py | 368 ++++++++++++++++++ .../perfRu.csv | 7 + .../OoOCommon.py | 125 ++++++ .../accuBar.py | 328 ++++++++++++++++ .../autoParase.py | 87 +++++ .../config_BCoAMM.csv | 6 + .../config_BerCRS.csv | 6 + .../config_CPPBCOOFD.csv | 10 + .../config_CPPBCRS.csv | 10 + .../config_CPPCOFD.csv | 10 + .../config_CPPCOUNTERSKETCH.csv | 10 + .../config_CPPCRS.csv | 14 + .../config_CPPINT8.csv | 10 + .../config_CPPMM.csv | 14 + .../config_CPPSMPPCA.csv | 12 + .../config_CoAMM.csv | 6 + .../config_CounterSketch.csv | 6 + .../drawTogether.py | 180 +++++++++ .../groupBar.py | 236 +++++++++++ .../groupBar2.py | 232 +++++++++++ .../groupLine.py | 368 ++++++++++++++++++ .../perfRu.csv | 7 + .../scanThreadsRockPi5/config_CPPBCO.csv | 9 + .../config_CPPCounterSketch.csv | 9 + benchmark/scripts/scanThreadsRockPi5/main.c | 14 + benchmark/scripts/scanThreadsRockPi5/tb | Bin 0 -> 8904 bytes benchmark/src/Benchmark.cpp | 14 +- commit_info | 2 +- .../batchSizestream_cpp_fro.pdf | Bin 0 -> 7708 bytes .../batchSizestream_cpp_lat95.pdf | Bin 0 -> 8292 bytes .../batchSizestream_cpp_thr.pdf | Bin 0 -> 7796 bytes .../batchSizeCPP/batchSizestream_cpp_fro.pdf | Bin 0 -> 7884 bytes .../batchSizestream_cpp_lat95.pdf | Bin 0 -> 8078 bytes .../batchSizeCPP/batchSizestream_cpp_thr.pdf | Bin 0 -> 7979 bytes .../eventRateTpsstream_cpp_fro.pdf | Bin 0 -> 7783 bytes .../eventRateTpsstream_cpp_lat95.pdf | Bin 0 -> 8207 bytes .../eventRateTpsstream_cpp_thr.pdf | Bin 0 -> 7991 bytes .../eventRateTpsstream_cpp_fro.pdf | Bin 0 -> 7784 bytes .../eventRateTpsstream_cpp_lat95.pdf | Bin 0 -> 8282 bytes .../eventRateTpsstream_cpp_thr.pdf | Bin 0 -> 8001 bytes include/AMMBench.h | 4 + include/Streaming/SingleThreadStreamer.h | 11 + include/Streaming/TimeStamper.h | 10 +- src/Streaming/SingleThreadStreamer.cpp | 95 ++++- test/SystemTest/StreamingTest.cpp | 24 +- 125 files changed, 7014 insertions(+), 9 deletions(-) create mode 100644 benchmark/scripts/scanACol/config_CPPCRS.csv create mode 100644 benchmark/scripts/scanACol/config_FDAMM.csv create mode 100644 benchmark/scripts/scanAColCPP/config_BCoAMM.csv create mode 100644 benchmark/scripts/scanAColCPP/config_BerCRS.csv create mode 100644 benchmark/scripts/scanAColCPP/config_CRS.csv create mode 100644 benchmark/scripts/scanAColCPP/config_CoAMM.csv create mode 100644 benchmark/scripts/scanAColCPP/config_CounterSketch.csv create mode 100644 benchmark/scripts/scanAColCPP/config_EWS.csv create mode 100644 benchmark/scripts/scanAColCPP/config_FDAMM.csv create mode 100644 benchmark/scripts/scanAColCPP/config_RAWMM.csv create mode 100644 benchmark/scripts/scanAColCPP/config_WCR.csv create mode 100644 benchmark/scripts/scanARow/config_FDAMM.csv create mode 100644 benchmark/scripts/scanARowCPP/config_BCoAMM.csv create mode 100644 benchmark/scripts/scanARowCPP/config_BerCRS.csv create mode 100644 benchmark/scripts/scanARowCPP/config_CRS.csv create mode 100644 benchmark/scripts/scanARowCPP/config_CoAMM.csv create mode 100644 benchmark/scripts/scanARowCPP/config_CounterSketch.csv create mode 100644 benchmark/scripts/scanARowCPP/config_EWS.csv create mode 100644 benchmark/scripts/scanARowCPP/config_FDAMM.csv create mode 100644 benchmark/scripts/scanARowCPP/config_RAWMM.csv create mode 100644 benchmark/scripts/scanARowCPP/config_WCR.csv create mode 100755 benchmark/scripts/scanBatchSizeSingleThreadStream/OoOCommon.py create mode 100755 benchmark/scripts/scanBatchSizeSingleThreadStream/accuBar.py create mode 100755 benchmark/scripts/scanBatchSizeSingleThreadStream/autoParase.py create mode 100644 benchmark/scripts/scanBatchSizeSingleThreadStream/config_BCoAMM.csv create mode 100644 benchmark/scripts/scanBatchSizeSingleThreadStream/config_BerCRS.csv create mode 100644 benchmark/scripts/scanBatchSizeSingleThreadStream/config_CPPBCOOFD.csv create mode 100644 benchmark/scripts/scanBatchSizeSingleThreadStream/config_CPPBCRS.csv create mode 100644 benchmark/scripts/scanBatchSizeSingleThreadStream/config_CPPCOFD.csv create mode 100644 benchmark/scripts/scanBatchSizeSingleThreadStream/config_CPPCOUNTERSKETCH.csv create mode 100644 benchmark/scripts/scanBatchSizeSingleThreadStream/config_CPPCRS.csv create mode 100644 benchmark/scripts/scanBatchSizeSingleThreadStream/config_CPPINT8.csv create mode 100644 benchmark/scripts/scanBatchSizeSingleThreadStream/config_CPPMM.csv create mode 100644 benchmark/scripts/scanBatchSizeSingleThreadStream/config_CPPSMPPCA.csv create mode 100644 benchmark/scripts/scanBatchSizeSingleThreadStream/config_CoAMM.csv create mode 100644 benchmark/scripts/scanBatchSizeSingleThreadStream/config_CounterSketch.csv create mode 100755 benchmark/scripts/scanBatchSizeSingleThreadStream/drawTogether.py create mode 100755 benchmark/scripts/scanBatchSizeSingleThreadStream/groupBar.py create mode 100755 benchmark/scripts/scanBatchSizeSingleThreadStream/groupBar2.py create mode 100755 benchmark/scripts/scanBatchSizeSingleThreadStream/groupLine.py create mode 100644 benchmark/scripts/scanBatchSizeSingleThreadStream/perfRu.csv create mode 100755 benchmark/scripts/scanBatchSizeSingleThreadStream2S/OoOCommon.py create mode 100755 benchmark/scripts/scanBatchSizeSingleThreadStream2S/accuBar.py create mode 100755 benchmark/scripts/scanBatchSizeSingleThreadStream2S/autoParase.py create mode 100644 benchmark/scripts/scanBatchSizeSingleThreadStream2S/config_BCoAMM.csv create mode 100644 benchmark/scripts/scanBatchSizeSingleThreadStream2S/config_BerCRS.csv create mode 100644 benchmark/scripts/scanBatchSizeSingleThreadStream2S/config_CPPBCOOFD.csv create mode 100644 benchmark/scripts/scanBatchSizeSingleThreadStream2S/config_CPPBCRS.csv create mode 100644 benchmark/scripts/scanBatchSizeSingleThreadStream2S/config_CPPCOFD.csv create mode 100644 benchmark/scripts/scanBatchSizeSingleThreadStream2S/config_CPPCOUNTERSKETCH.csv create mode 100644 benchmark/scripts/scanBatchSizeSingleThreadStream2S/config_CPPCRS.csv create mode 100644 benchmark/scripts/scanBatchSizeSingleThreadStream2S/config_CPPINT8.csv create mode 100644 benchmark/scripts/scanBatchSizeSingleThreadStream2S/config_CPPMM.csv create mode 100644 benchmark/scripts/scanBatchSizeSingleThreadStream2S/config_CPPSMPPCA.csv create mode 100644 benchmark/scripts/scanBatchSizeSingleThreadStream2S/config_CoAMM.csv create mode 100644 benchmark/scripts/scanBatchSizeSingleThreadStream2S/config_CounterSketch.csv create mode 100755 benchmark/scripts/scanBatchSizeSingleThreadStream2S/drawTogether.py create mode 100755 benchmark/scripts/scanBatchSizeSingleThreadStream2S/groupBar.py create mode 100755 benchmark/scripts/scanBatchSizeSingleThreadStream2S/groupBar2.py create mode 100755 benchmark/scripts/scanBatchSizeSingleThreadStream2S/groupLine.py create mode 100644 benchmark/scripts/scanBatchSizeSingleThreadStream2S/perfRu.csv create mode 100755 benchmark/scripts/scanEventRateSingleThreadStream/OoOCommon.py create mode 100755 benchmark/scripts/scanEventRateSingleThreadStream/accuBar.py create mode 100755 benchmark/scripts/scanEventRateSingleThreadStream/autoParase.py create mode 100644 benchmark/scripts/scanEventRateSingleThreadStream/config_BCoAMM.csv create mode 100644 benchmark/scripts/scanEventRateSingleThreadStream/config_BerCRS.csv create mode 100644 benchmark/scripts/scanEventRateSingleThreadStream/config_CPPBCOOFD.csv create mode 100644 benchmark/scripts/scanEventRateSingleThreadStream/config_CPPBCRS.csv create mode 100644 benchmark/scripts/scanEventRateSingleThreadStream/config_CPPCOFD.csv create mode 100644 benchmark/scripts/scanEventRateSingleThreadStream/config_CPPCOUNTERSKETCH.csv create mode 100644 benchmark/scripts/scanEventRateSingleThreadStream/config_CPPCRS.csv create mode 100644 benchmark/scripts/scanEventRateSingleThreadStream/config_CPPINT8.csv create mode 100644 benchmark/scripts/scanEventRateSingleThreadStream/config_CPPMM.csv create mode 100644 benchmark/scripts/scanEventRateSingleThreadStream/config_CPPSMPPCA.csv create mode 100644 benchmark/scripts/scanEventRateSingleThreadStream/config_CoAMM.csv create mode 100644 benchmark/scripts/scanEventRateSingleThreadStream/config_CounterSketch.csv create mode 100755 benchmark/scripts/scanEventRateSingleThreadStream/drawTogether.py create mode 100755 benchmark/scripts/scanEventRateSingleThreadStream/groupBar.py create mode 100755 benchmark/scripts/scanEventRateSingleThreadStream/groupBar2.py create mode 100755 benchmark/scripts/scanEventRateSingleThreadStream/groupLine.py create mode 100644 benchmark/scripts/scanEventRateSingleThreadStream/perfRu.csv create mode 100755 benchmark/scripts/scanEventRateSingleThreadStream2S/OoOCommon.py create mode 100755 benchmark/scripts/scanEventRateSingleThreadStream2S/accuBar.py create mode 100755 benchmark/scripts/scanEventRateSingleThreadStream2S/autoParase.py create mode 100644 benchmark/scripts/scanEventRateSingleThreadStream2S/config_BCoAMM.csv create mode 100644 benchmark/scripts/scanEventRateSingleThreadStream2S/config_BerCRS.csv create mode 100644 benchmark/scripts/scanEventRateSingleThreadStream2S/config_CPPBCOOFD.csv create mode 100644 benchmark/scripts/scanEventRateSingleThreadStream2S/config_CPPBCRS.csv create mode 100644 benchmark/scripts/scanEventRateSingleThreadStream2S/config_CPPCOFD.csv create mode 100644 benchmark/scripts/scanEventRateSingleThreadStream2S/config_CPPCOUNTERSKETCH.csv create mode 100644 benchmark/scripts/scanEventRateSingleThreadStream2S/config_CPPCRS.csv create mode 100644 benchmark/scripts/scanEventRateSingleThreadStream2S/config_CPPINT8.csv create mode 100644 benchmark/scripts/scanEventRateSingleThreadStream2S/config_CPPMM.csv create mode 100644 benchmark/scripts/scanEventRateSingleThreadStream2S/config_CPPSMPPCA.csv create mode 100644 benchmark/scripts/scanEventRateSingleThreadStream2S/config_CoAMM.csv create mode 100644 benchmark/scripts/scanEventRateSingleThreadStream2S/config_CounterSketch.csv create mode 100755 benchmark/scripts/scanEventRateSingleThreadStream2S/drawTogether.py create mode 100755 benchmark/scripts/scanEventRateSingleThreadStream2S/groupBar.py create mode 100755 benchmark/scripts/scanEventRateSingleThreadStream2S/groupBar2.py create mode 100755 benchmark/scripts/scanEventRateSingleThreadStream2S/groupLine.py create mode 100644 benchmark/scripts/scanEventRateSingleThreadStream2S/perfRu.csv create mode 100644 benchmark/scripts/scanThreadsRockPi5/config_CPPBCO.csv create mode 100644 benchmark/scripts/scanThreadsRockPi5/config_CPPCounterSketch.csv create mode 100644 benchmark/scripts/scanThreadsRockPi5/main.c create mode 100755 benchmark/scripts/scanThreadsRockPi5/tb create mode 100644 figures/batchSize2sCPP/batchSizestream_cpp_fro.pdf create mode 100644 figures/batchSize2sCPP/batchSizestream_cpp_lat95.pdf create mode 100644 figures/batchSize2sCPP/batchSizestream_cpp_thr.pdf create mode 100644 figures/batchSizeCPP/batchSizestream_cpp_fro.pdf create mode 100644 figures/batchSizeCPP/batchSizestream_cpp_lat95.pdf create mode 100644 figures/batchSizeCPP/batchSizestream_cpp_thr.pdf create mode 100644 figures/eventRateTps2sCPP/eventRateTpsstream_cpp_fro.pdf create mode 100644 figures/eventRateTps2sCPP/eventRateTpsstream_cpp_lat95.pdf create mode 100644 figures/eventRateTps2sCPP/eventRateTpsstream_cpp_thr.pdf create mode 100644 figures/eventRateTpsCPP/eventRateTpsstream_cpp_fro.pdf create mode 100644 figures/eventRateTpsCPP/eventRateTpsstream_cpp_lat95.pdf create mode 100644 figures/eventRateTpsCPP/eventRateTpsstream_cpp_thr.pdf diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 90742372..61eae8ff 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -48,4 +48,5 @@ jobs: ./sketch_test "--success" ./crs_test "--success" ./weighted_cr_test "--success" - ./block_partition_test "--success" \ No newline at end of file + ./block_partition_test "--success" + ./streaming_test "--success" \ No newline at end of file diff --git a/benchmark/scripts/scanACol/config_CPPCRS.csv b/benchmark/scripts/scanACol/config_CPPCRS.csv new file mode 100644 index 00000000..a9b928d2 --- /dev/null +++ b/benchmark/scripts/scanACol/config_CPPCRS.csv @@ -0,0 +1,8 @@ +key,value,type +aRow,1000,U64 +aCol,1000,U64 +bCol,500,U64 +sketchDimension,25,U64 +ptFile,torchscripts/CRS.pt,String +useCPP,1,U64 +cppAlgoTag,crs,String \ No newline at end of file diff --git a/benchmark/scripts/scanACol/config_FDAMM.csv b/benchmark/scripts/scanACol/config_FDAMM.csv new file mode 100644 index 00000000..2dac7db7 --- /dev/null +++ b/benchmark/scripts/scanACol/config_FDAMM.csv @@ -0,0 +1,6 @@ +key,value,type +aRow,1000,U64 +aCol,1000,U64 +bCol,500,U64 +sketchDimension,25,U64 +ptFile,torchscripts/FDAMM.pt,String \ No newline at end of file diff --git a/benchmark/scripts/scanAColCPP/config_BCoAMM.csv b/benchmark/scripts/scanAColCPP/config_BCoAMM.csv new file mode 100644 index 00000000..aa481d3d --- /dev/null +++ b/benchmark/scripts/scanAColCPP/config_BCoAMM.csv @@ -0,0 +1,6 @@ +key,value,type +aRow,1000,U64 +aCol,1000,U64 +bCol,500,U64 +sketchDimension,25,U64 +ptFile,torchscripts/BetaCoOccurringFD.pt,String \ No newline at end of file diff --git a/benchmark/scripts/scanAColCPP/config_BerCRS.csv b/benchmark/scripts/scanAColCPP/config_BerCRS.csv new file mode 100644 index 00000000..14a0cdc1 --- /dev/null +++ b/benchmark/scripts/scanAColCPP/config_BerCRS.csv @@ -0,0 +1,6 @@ +key,value,type +aRow,1000,U64 +aCol,1000,U64 +bCol,500,U64 +sketchDimension,25,U64 +ptFile,torchscripts/BernoulliCRS.pt,String \ No newline at end of file diff --git a/benchmark/scripts/scanAColCPP/config_CRS.csv b/benchmark/scripts/scanAColCPP/config_CRS.csv new file mode 100644 index 00000000..620247f2 --- /dev/null +++ b/benchmark/scripts/scanAColCPP/config_CRS.csv @@ -0,0 +1,6 @@ +key,value,type +aRow,1000,U64 +aCol,1000,U64 +bCol,500,U64 +sketchDimension,25,U64 +ptFile,torchscripts/CRS.pt,String \ No newline at end of file diff --git a/benchmark/scripts/scanAColCPP/config_CoAMM.csv b/benchmark/scripts/scanAColCPP/config_CoAMM.csv new file mode 100644 index 00000000..765e54de --- /dev/null +++ b/benchmark/scripts/scanAColCPP/config_CoAMM.csv @@ -0,0 +1,6 @@ +key,value,type +aRow,1000,U64 +aCol,1000,U64 +bCol,500,U64 +sketchDimension,25,U64 +ptFile,torchscripts/CoOccurringFD.pt,String \ No newline at end of file diff --git a/benchmark/scripts/scanAColCPP/config_CounterSketch.csv b/benchmark/scripts/scanAColCPP/config_CounterSketch.csv new file mode 100644 index 00000000..79eabd46 --- /dev/null +++ b/benchmark/scripts/scanAColCPP/config_CounterSketch.csv @@ -0,0 +1,6 @@ +key,value,type +aRow,1000,U64 +aCol,1000,U64 +bCol,500,U64 +sketchDimension,25,U64 +ptFile,torchscripts/CountSketch.pt,String diff --git a/benchmark/scripts/scanAColCPP/config_EWS.csv b/benchmark/scripts/scanAColCPP/config_EWS.csv new file mode 100644 index 00000000..0a752db9 --- /dev/null +++ b/benchmark/scripts/scanAColCPP/config_EWS.csv @@ -0,0 +1,6 @@ +key,value,type +aRow,1000,U64 +aCol,1000,U64 +bCol,500,U64 +sketchDimension,25,U64 +ptFile,torchscripts/EWS.pt,String \ No newline at end of file diff --git a/benchmark/scripts/scanAColCPP/config_FDAMM.csv b/benchmark/scripts/scanAColCPP/config_FDAMM.csv new file mode 100644 index 00000000..2dac7db7 --- /dev/null +++ b/benchmark/scripts/scanAColCPP/config_FDAMM.csv @@ -0,0 +1,6 @@ +key,value,type +aRow,1000,U64 +aCol,1000,U64 +bCol,500,U64 +sketchDimension,25,U64 +ptFile,torchscripts/FDAMM.pt,String \ No newline at end of file diff --git a/benchmark/scripts/scanAColCPP/config_RAWMM.csv b/benchmark/scripts/scanAColCPP/config_RAWMM.csv new file mode 100644 index 00000000..57e126f7 --- /dev/null +++ b/benchmark/scripts/scanAColCPP/config_RAWMM.csv @@ -0,0 +1,6 @@ +key,value,type +aRow,1000,U64 +aCol,1000,U64 +bCol,500,U64 +sketchDimension,25,U64 +ptFile,torchscripts/RAWMM.pt,String \ No newline at end of file diff --git a/benchmark/scripts/scanAColCPP/config_WCR.csv b/benchmark/scripts/scanAColCPP/config_WCR.csv new file mode 100644 index 00000000..a2b6b73c --- /dev/null +++ b/benchmark/scripts/scanAColCPP/config_WCR.csv @@ -0,0 +1,6 @@ +key,value,type +aRow,1000,U64 +aCol,1000,U64 +bCol,500,U64 +sketchDimension,25,U64 +ptFile,torchscripts/WeightedCR.pt,String \ No newline at end of file diff --git a/benchmark/scripts/scanARow/config_FDAMM.csv b/benchmark/scripts/scanARow/config_FDAMM.csv new file mode 100644 index 00000000..18b9efde --- /dev/null +++ b/benchmark/scripts/scanARow/config_FDAMM.csv @@ -0,0 +1,6 @@ +key,value,type +aRow,100,U64 +aCol,1000,U64 +bCol,500,U64 +sketchDimension,25,U64 +ptFile,torchscripts/FDAMM.pt,String \ No newline at end of file diff --git a/benchmark/scripts/scanARowCPP/config_BCoAMM.csv b/benchmark/scripts/scanARowCPP/config_BCoAMM.csv new file mode 100644 index 00000000..b65317af --- /dev/null +++ b/benchmark/scripts/scanARowCPP/config_BCoAMM.csv @@ -0,0 +1,6 @@ +key,value,type +aRow,100,U64 +aCol,1000,U64 +bCol,500,U64 +sketchDimension,25,U64 +ptFile,torchscripts/BetaCoOccurringFD.pt,String \ No newline at end of file diff --git a/benchmark/scripts/scanARowCPP/config_BerCRS.csv b/benchmark/scripts/scanARowCPP/config_BerCRS.csv new file mode 100644 index 00000000..d2b758a5 --- /dev/null +++ b/benchmark/scripts/scanARowCPP/config_BerCRS.csv @@ -0,0 +1,6 @@ +key,value,type +aRow,100,U64 +aCol,1000,U64 +bCol,500,U64 +sketchDimension,25,U64 +ptFile,torchscripts/BernoulliCRS.pt,String \ No newline at end of file diff --git a/benchmark/scripts/scanARowCPP/config_CRS.csv b/benchmark/scripts/scanARowCPP/config_CRS.csv new file mode 100644 index 00000000..b6afe1ef --- /dev/null +++ b/benchmark/scripts/scanARowCPP/config_CRS.csv @@ -0,0 +1,6 @@ +key,value,type +aRow,100,U64 +aCol,1000,U64 +bCol,500,U64 +sketchDimension,25,U64 +ptFile,torchscripts/CRS.pt,String \ No newline at end of file diff --git a/benchmark/scripts/scanARowCPP/config_CoAMM.csv b/benchmark/scripts/scanARowCPP/config_CoAMM.csv new file mode 100644 index 00000000..f41d7ddd --- /dev/null +++ b/benchmark/scripts/scanARowCPP/config_CoAMM.csv @@ -0,0 +1,6 @@ +key,value,type +aRow,100,U64 +aCol,1000,U64 +bCol,500,U64 +sketchDimension,25,U64 +ptFile,torchscripts/CoOccurringFD.pt,String \ No newline at end of file diff --git a/benchmark/scripts/scanARowCPP/config_CounterSketch.csv b/benchmark/scripts/scanARowCPP/config_CounterSketch.csv new file mode 100644 index 00000000..50cf2770 --- /dev/null +++ b/benchmark/scripts/scanARowCPP/config_CounterSketch.csv @@ -0,0 +1,6 @@ +key,value,type +aRow,100,U64 +aCol,1000,U64 +bCol,500,U64 +sketchDimension,25,U64 +ptFile,torchscripts/CountSketch.pt,String diff --git a/benchmark/scripts/scanARowCPP/config_EWS.csv b/benchmark/scripts/scanARowCPP/config_EWS.csv new file mode 100644 index 00000000..8e09f17b --- /dev/null +++ b/benchmark/scripts/scanARowCPP/config_EWS.csv @@ -0,0 +1,6 @@ +key,value,type +aRow,100,U64 +aCol,1000,U64 +bCol,500,U64 +sketchDimension,25,U64 +ptFile,torchscripts/EWS.pt,String \ No newline at end of file diff --git a/benchmark/scripts/scanARowCPP/config_FDAMM.csv b/benchmark/scripts/scanARowCPP/config_FDAMM.csv new file mode 100644 index 00000000..18b9efde --- /dev/null +++ b/benchmark/scripts/scanARowCPP/config_FDAMM.csv @@ -0,0 +1,6 @@ +key,value,type +aRow,100,U64 +aCol,1000,U64 +bCol,500,U64 +sketchDimension,25,U64 +ptFile,torchscripts/FDAMM.pt,String \ No newline at end of file diff --git a/benchmark/scripts/scanARowCPP/config_RAWMM.csv b/benchmark/scripts/scanARowCPP/config_RAWMM.csv new file mode 100644 index 00000000..aeb092a8 --- /dev/null +++ b/benchmark/scripts/scanARowCPP/config_RAWMM.csv @@ -0,0 +1,6 @@ +key,value,type +aRow,100,U64 +aCol,1000,U64 +bCol,500,U64 +sketchDimension,25,U64 +ptFile,torchscripts/RAWMM.pt,String \ No newline at end of file diff --git a/benchmark/scripts/scanARowCPP/config_WCR.csv b/benchmark/scripts/scanARowCPP/config_WCR.csv new file mode 100644 index 00000000..d9b055fb --- /dev/null +++ b/benchmark/scripts/scanARowCPP/config_WCR.csv @@ -0,0 +1,6 @@ +key,value,type +aRow,100,U64 +aCol,1000,U64 +bCol,500,U64 +sketchDimension,25,U64 +ptFile,torchscripts/WeightedCR.pt,String \ No newline at end of file diff --git a/benchmark/scripts/scanBatchSizeSingleThreadStream/OoOCommon.py b/benchmark/scripts/scanBatchSizeSingleThreadStream/OoOCommon.py new file mode 100755 index 00000000..70f4cf33 --- /dev/null +++ b/benchmark/scripts/scanBatchSizeSingleThreadStream/OoOCommon.py @@ -0,0 +1,125 @@ +import csv +import numpy as np +import matplotlib.pyplot as plt +import itertools as it +import os + +import matplotlib +import matplotlib.pyplot as plt +import numpy as np +import pylab +from matplotlib.font_manager import FontProperties +from matplotlib.ticker import LogLocator, LinearLocator +import os +import pandas as pd +import sys +import matplotlib.ticker as mtick + +OPT_FONT_NAME = 'Helvetica' +TICK_FONT_SIZE = 22 +LABEL_FONT_SIZE = 28 +LEGEND_FONT_SIZE = 30 +LABEL_FP = FontProperties(style='normal', size=LABEL_FONT_SIZE) +LEGEND_FP = FontProperties(style='normal', size=LEGEND_FONT_SIZE) +TICK_FP = FontProperties(style='normal', size=TICK_FONT_SIZE) + +MARKERS = (['*', '|', 'v', "^", "", "h", "<", ">", "+", "d", "<", "|", "", "+", "_"]) +# you may want to change the color map for different figures +COLOR_MAP = ( + '#B03A2E', '#2874A6', '#239B56', '#7D3C98', '#FFFFFF', '#F1C40F', '#F5CBA7', '#82E0AA', '#AEB6BF', '#AA4499') +# you may want to change the patterns for different figures +PATTERNS = (["////", "o", "", "||", "-", "//", "\\", "o", "O", "////", ".", "|||", "o", "---", "+", "\\\\", "*"]) +LABEL_WEIGHT = 'bold' +LINE_COLORS = COLOR_MAP +LINE_WIDTH = 3.0 +MARKER_SIZE = 15.0 +MARKER_FREQUENCY = 1000 + + +def editConfig(src, dest, key, value): + df = pd.read_csv(src, header=None) + rowIdx = 0 + idxt = 0 + for cell in df[0]: + # print(cell) + if (cell == key): + rowIdx = idxt + break + idxt = idxt + 1 + df[1][rowIdx] = str(value) + df.to_csv(dest, index=False, header=False) + + +def readConfig(src, key): + df = pd.read_csv(src, header=None) + rowIdx = 0 + idxt = 0 + for cell in df[0]: + # print(cell) + if (cell == key): + rowIdx = idxt + break + idxt = idxt + 1 + return df[1][rowIdx] + + +def draw2yLine(NAME, Com, R1, R2, l1, l2, m1, m2, fname): + fig, ax1 = plt.subplots(figsize=(10, 6.4)) + lines = [None] * 2 + # ax1.set_ylim(0, 1) + print(Com) + print(R1) + lines[0], = ax1.plot(Com, R1, color=LINE_COLORS[0], \ + linewidth=LINE_WIDTH, marker=MARKERS[0], \ + markersize=MARKER_SIZE + # + ) + + # plt.show() + ax1.set_ylabel(m1, fontproperties=LABEL_FP) + ax1.set_xlabel(NAME, fontproperties=LABEL_FP) + # ax1.set_xticklabels(ax1.get_xticklabels()) # 设置共用的x轴 + plt.xticks(rotation=0, size=TICK_FONT_SIZE) + plt.yticks(rotation=0, size=TICK_FONT_SIZE) + plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号 + ax2 = ax1.twinx() + + # ax2.set_ylabel('latency/us') + # ax2.set_ylim(0, 0.5) + lines[1], = ax2.plot(Com, R2, color=LINE_COLORS[1], \ + linewidth=LINE_WIDTH, marker=MARKERS[1], \ + markersize=MARKER_SIZE) + + ax2.set_ylabel(m2, fontproperties=LABEL_FP) + # ax2.vlines(192000, min(R2)-1, max(R2)+1, colors = "GREEN", linestyles = "dashed",label='total L1 size') + # plt.grid(axis='y', color='gray') + + # style = dict(size=10, color='black') + # ax2.hlines(tset, 0, x2_list[len(x2_list)-1]+width, colors = "r", linestyles = "dashed",label="tset") + # ax2.text(4, tset, "$T_{set}$="+str(tset)+"us", ha='right', **style) + + # plt.xlabel('batch', fontproperties=LABEL_FP) + + # plt.xscale('log') + # figure.xaxis.set_major_locator(LinearLocator(5)) + ax1.yaxis.set_major_locator(LinearLocator(5)) + ax2.yaxis.set_major_locator(LinearLocator(5)) + ax1.yaxis.set_major_formatter(mtick.FormatStrFormatter('%.1f')) + ax2.yaxis.set_major_formatter(mtick.FormatStrFormatter('%.1f')) + ax1.yaxis.set_major_formatter(mtick.FormatStrFormatter('%.1f')) + ax2.yaxis.set_major_formatter(mtick.FormatStrFormatter('%.1f')) + plt.legend(lines, + [l1, l2], + prop=LEGEND_FP, + loc='upper center', + ncol=1, + bbox_to_anchor=(0.55, 1.3 + ), shadow=False, + columnspacing=0.1, + frameon=True, borderaxespad=-1.5, handlelength=1.2, + handletextpad=0.1, + labelspacing=0.1) + plt.yticks(rotation=0, size=TICK_FONT_SIZE) + plt.tight_layout() + + plt.savefig(fname + ".pdf") diff --git a/benchmark/scripts/scanBatchSizeSingleThreadStream/accuBar.py b/benchmark/scripts/scanBatchSizeSingleThreadStream/accuBar.py new file mode 100755 index 00000000..638c8a60 --- /dev/null +++ b/benchmark/scripts/scanBatchSizeSingleThreadStream/accuBar.py @@ -0,0 +1,328 @@ +import getopt +import os +import sys + +import matplotlib +import matplotlib.pyplot as plt +import numpy as np +import pylab +from matplotlib.font_manager import FontProperties +from matplotlib.ticker import LinearLocator, LogLocator, MaxNLocator, ScalarFormatter +from numpy import double + +OPT_FONT_NAME = 'Helvetica' +TICK_FONT_SIZE = 24 +LABEL_FONT_SIZE = 28 +LEGEND_FONT_SIZE = 26 +TITLE_FRONT_SIZE = 32 +LABEL_FP = FontProperties(style='normal', size=LABEL_FONT_SIZE) +LEGEND_FP = FontProperties(style='normal', size=LEGEND_FONT_SIZE) +TICK_FP = FontProperties(style='normal', size=TICK_FONT_SIZE) +TITLE_FP = FontProperties(style='normal', size=TITLE_FRONT_SIZE) +MARKERS = (['o', 's', 'v', "^", "h", "v", ">", "x", "d", "<", "|", "", "|", "_"]) +# you may want to change the color map for different figures +COLOR_MAP = ('#B03A2E', '#2874A6', '#239B56', '#7D3C98', '#F1C40F', '#F5CBA7', '#82E0AA', '#AEB6BF', '#AA4499') +# you may want to change the patterns for different figures +PATTERNS = (["\\", "///", "o", "||", "\\\\", "\\\\", "//////", "//////", ".", "\\\\\\", "\\\\\\"]) +LABEL_WEIGHT = 'bold' +LINE_COLORS = COLOR_MAP +LINE_WIDTH = 3.0 +MARKER_SIZE = 0.0 +MARKER_FREQUENCY = 1000 + +matplotlib.rcParams['ps.useafm'] = True +matplotlib.rcParams['pdf.use14corefonts'] = True +matplotlib.rcParams['xtick.labelsize'] = TICK_FONT_SIZE +matplotlib.rcParams['ytick.labelsize'] = TICK_FONT_SIZE +matplotlib.rcParams['font.family'] = OPT_FONT_NAME +matplotlib.rcParams['pdf.fonttype'] = 42 + +exp_dir = "/data1/xtra" + +FIGURE_FOLDER = exp_dir + '/results/figure' + + +# there are some embedding problems if directly exporting the pdf figure using matplotlib. +# so we generate the eps format first and convert it to pdf. +def ConvertEpsToPdf(dir_filename): + os.system("epstopdf --outfile " + dir_filename + ".pdf " + dir_filename + ".eps") + os.system("rm -rf " + dir_filename + ".eps") + + +class ScalarFormatterForceFormat(ScalarFormatter): + def _set_format(self): # Override function that finds format to use. + self.format = "%1.1f" # Give format here + + +# draw a line chart +def DrawFigure(x_values, y_values, legend_labels, x_label, y_label, filename, allow_legend, title): + # you may change the figure size on your own. + + fig = plt.figure(figsize=(16, 9)) + figure = fig.add_subplot(111) + + FIGURE_LABEL = legend_labels + + # if not os.path.exists(FIGURE_FOLDER): + # os.makedirs(FIGURE_FOLDER) + + # values in the x_xis + index = np.arange(len(x_values)) + # the bar width. + # you may need to tune it to get the best figure. + width = 0.5 + # draw the bars + bottom_base = np.zeros(len(y_values[0])) + bars = [None] * (len(FIGURE_LABEL)) + for i in range(len(y_values)): + bars[i] = plt.bar(index + width / 2, y_values[i], width, hatch=PATTERNS[i], color=LINE_COLORS[i], + label=FIGURE_LABEL[i], bottom=bottom_base, edgecolor='black', linewidth=3) + bottom_base = np.array(y_values[i]) + bottom_base + + # sometimes you may not want to draw legends. + if allow_legend == True: + plt.legend(bars, FIGURE_LABEL + # mode='expand', + # shadow=False, + # columnspacing=0.25, + # labelspacing=-2.2, + # borderpad=5, + # bbox_transform=ax.transAxes, + # frameon=False, + # columnspacing=5.5, + # handlelength=2, + ) + if allow_legend == True: + handles, labels = figure.get_legend_handles_labels() + if allow_legend == True: + leg = plt.legend(handles[::-1], labels[::-1], + loc='upper center', + # prop=#LEGEND_FP, + # ncol=3, + # bbox_to_anchor=(0.5, 1.25), + # bbox_to_anchor=(1.17, 0.5), + # handletextpad=0.1, + # borderaxespad=0.0, + # handlelength=1.8, + # labelspacing=0.3, + # columnspacing=0.3, + ) + leg.get_frame().set_linewidth(2) + leg.get_frame().set_edgecolor("black") + + # you may need to tune the xticks position to get the best figure. + plt.xticks(index + 0.6 * width, x_values) + yfmt = ScalarFormatterForceFormat() + yfmt.set_powerlimits((0, 0)) + figure.get_yaxis().set_major_formatter(yfmt) + plt.ticklabel_format(axis="y", style="sci", scilimits=(0, 0), useMathText=True) + plt.grid(axis='y', color='gray') + figure.yaxis.set_major_locator(LinearLocator(3)) + # figure.yaxis.set_major_locator(LogLocator(base=10)) + # figure.yaxis.set_major_locator(LinearLocator(6)) + + figure.get_xaxis().set_tick_params(direction='in', pad=10) + figure.get_yaxis().set_tick_params(direction='in', pad=10) + + plt.xlabel(x_label, fontproperties=LABEL_FP) + plt.ylabel(y_label, fontproperties=LABEL_FP) + + size = fig.get_size_inches() + dpi = fig.get_dpi() + plt.title(title, fontproperties=TITLE_FP) + plt.savefig(filename + ".pdf", bbox_inches='tight', format='pdf') + + +def DrawLegend(legend_labels, filename): + fig = pylab.figure() + ax1 = fig.add_subplot(111) + FIGURE_LABEL = legend_labels + LEGEND_FP = FontProperties(style='normal', size=26) + + bars = [None] * (len(FIGURE_LABEL)) + data = [1] + x_values = [1] + + width = 0.3 + for i in range(len(FIGURE_LABEL)): + bars[i] = ax1.bar(x_values, data, width, hatch=PATTERNS[i], color=LINE_COLORS[i], + linewidth=0.2) + + # LEGEND + figlegend = pylab.figure(figsize=(11, 0.5)) + figlegend.legend(bars, FIGURE_LABEL, prop=LEGEND_FP, \ + loc=9, + bbox_to_anchor=(0, 0.4, 1, 1), + ncol=len(FIGURE_LABEL), mode="expand", shadow=False, \ + frameon=False, handlelength=1.1, handletextpad=0.2, columnspacing=0.1) + + figlegend.savefig(FIGURE_FOLDER + '/' + filename + '.pdf') + + +def normalize(y_values): + y_total_values = np.zeros(len(y_values[0])) + + for i in range(len(y_values)): + y_total_values += np.array(y_values[i]) + y_norm_values = [] + + for i in range(len(y_values)): + y_norm_values.append(np.array(y_values[i]) / (y_total_values) * 100) + return y_norm_values + + +# example for reading csv file +def ReadFile(id): + # Creates a list containing w lists, each of h items, all set to 0 + w, h = 5, 3 + y = [[0 for x in range(w)] for y in range(h)] + # print(matches) + max_value = 0 + j = 0 + bound = id + 1 * w + for i in range(id, bound, 1): + cnt = 0 + print(i) + f = open(exp_dir + "/results/breakdown/PMJ_JBCR_NP_{}.txt".format(i), "r") + read = f.readlines() + others = 0 + for x in read: + value = double(x.strip("\n")) + if value > max_value: + max_value = value + elif cnt == 3: # sort + y[0][j] = value + elif cnt == 4: # merge + y[1][j] = value + elif cnt == 5: # join + y[2][j] = value + else: + others += value + # if cnt == 6: + # y[2][j] = others + cnt += 1 + j += 1 + print(y) + return y, max_value + + +if __name__ == "__main__": + id = 119 + try: + opts, args = getopt.getopt(sys.argv[1:], '-i:h', ['test id', 'help']) + except getopt.GetoptError: + print('breakdown.py -id testid') + sys.exit(2) + for opt, opt_value in opts: + if opt in ('-h', '--help'): + print("[*] Help info") + exit() + elif opt == '-i': + print('Test ID:', opt_value) + id = (int)(opt_value) + + x_values = ['10%', '20%', '30%', '40%', '50%'] # sorting step size + + y_values, max_value = ReadFile(id) # 55 + + # y_norm_values = normalize(y_values) + + # break into 4 parts + legend_labels = ['sort', 'merge', 'join'] # , 'others' + + DrawFigure(x_values, y_values, legend_labels, + 'sorting step size', 'cycles per input tuple', + 'breakdown_sort_figure', True) + + # DrawLegend(legend_labels, 'breakdown_radix_legend') + + +def DrawPercentageFigure(x_values, y_values, legend_labels, x_label, y_label, filename, allow_legend, title): + # you may change the figure size on your own. + fig = plt.figure(figsize=(9, 3)) + figure = fig.add_subplot(111) + + FIGURE_LABEL = legend_labels + + # values in the x_xis + index = np.arange(len(x_values)) + # the bar width. + # you may need to tune it to get the best figure. + width = 0.5 + # draw the bars + bottom_base = np.zeros(len(y_values[0])) + bars = [None] * (len(FIGURE_LABEL)) + for i in range(len(y_values)): + bars[i] = plt.bar(index + width / 2, y_values[i], width, hatch=PATTERNS[i], color=LINE_COLORS[i], + label=FIGURE_LABEL[i], bottom=bottom_base, edgecolor='black', linewidth=3) + bottom_base = np.array(y_values[i]) + bottom_base + + # sometimes you may not want to draw legends. + if allow_legend == True: + plt.legend(bars, FIGURE_LABEL + # mode='expand', + # shadow=False, + # columnspacing=0.25, + # labelspacing=-2.2, + # borderpad=5, + # bbox_transform=ax.transAxes, + # frameon=False, + # columnspacing=5.5, + # handlelength=2, + ) + if allow_legend == True: + handles, labels = figure.get_legend_handles_labels() + if allow_legend == True: + print(handles[::-1], labels[::-1]) + leg = plt.legend(handles[::-1], labels[::-1], + loc='center', + prop=LEGEND_FP, + ncol=3, + bbox_to_anchor=(0.5, 1.2), + handletextpad=0.1, + borderaxespad=0.0, + handlelength=1.8, + labelspacing=0.3, + columnspacing=0.3, + ) + leg.get_frame().set_linewidth(2) + leg.get_frame().set_edgecolor("black") + + # sometimes you may not want to draw legends. + # if allow_legend == True: + # leg=plt.legend(bars, + # FIGURE_LABEL, + # prop=LEGEND_FP, + # loc='right', + # ncol=1, + # # mode='expand', + # bbox_to_anchor=(0.45, 1.1), shadow=False, + # columnspacing=0.1, + # frameon=True, borderaxespad=0.0, handlelength=1.5, + # handletextpad=0.1, + # labelspacing=0.1) + # leg.get_frame().set_linewidth(2) + # leg.get_frame().set_edgecolor("black") + + plt.ylim(0, 100) + + # you may need to tune the xticks position to get the best figure. + plt.xticks(index + 0.5 * width, x_values) + plt.xticks(rotation=30) + + # plt.xlim(0,) + # plt.ylim(0,1) + + plt.grid(axis='y', color='gray') + figure.yaxis.set_major_locator(LinearLocator(6)) + + figure.get_xaxis().set_tick_params(direction='in', pad=10) + figure.get_yaxis().set_tick_params(direction='in', pad=10) + + plt.xlabel(x_label, fontproperties=LABEL_FP) + plt.ylabel(y_label, fontproperties=LABEL_FP) + + size = fig.get_size_inches() + dpi = fig.get_dpi() + + plt.savefig(filename + ".pdf", bbox_inches='tight', format='pdf') diff --git a/benchmark/scripts/scanBatchSizeSingleThreadStream/autoParase.py b/benchmark/scripts/scanBatchSizeSingleThreadStream/autoParase.py new file mode 100755 index 00000000..9375c8e0 --- /dev/null +++ b/benchmark/scripts/scanBatchSizeSingleThreadStream/autoParase.py @@ -0,0 +1,87 @@ +import csv + + +def paraseValidStageNames(a): + nameList = [] + with open(a, 'r') as f: + reader = csv.reader(f) + # reader = [each for each in csv.DictReader(f, delimiter=',')] + result = list(reader) + rows = len(result) + # print('rows=',rows) + firstRow = result[0] + # print(firstRow) + index = 0 + # define what may attract our interest + idxCpu = 0 + idxName = 0 + for i in firstRow: + # print(i) + if (i == 'cpu'): + idxCpu = index + if (i == 'name'): + idxName = index + index = index + 1 + # read the valid stages + vdataEntries = 0 + + for k in range(1, rows): + if (result[k][idxCpu] != 'NA'): + R1 = ((result[k][idxName])) + nameList.append(R1) + return nameList + + +def paraseValidColums(a, nameList, colTitle): + with open(a, 'r') as f: + reader = csv.reader(f) + # reader = [each for each in csv.DictReader(f, delimiter=',')] + result = list(reader) + rows = len(result) + # print('rows=',rows) + firstRow = result[0] + # print(firstRow) + index = 0 + # define what may attract our interest + idxCpu = 0 + idxName = 0 + idxTitle = 0 + for i in firstRow: + # print(i) + if (i == 'cpu'): + idxCpu = index + if (i == 'name'): + idxName = index + if (i == colTitle): + idxTitle = index + index = index + 1 + # read the valid stages + vdataEntries = 0 + ru = [] + for k in range(1, rows): + if (result[k][idxCpu] != 'NA'): + R1 = ((result[k][idxName])) + for j in range(len(nameList)): + if (R1 == nameList[j]): + s = int(result[k][idxTitle]) + ru.append(s) + break + return ru + + +def maxInList(a): + # a in [[1,2] [3,4]] + inLen = len(a[0]) + ru = [] + index = [] + ti = 0 + for i in range(len(a[0])): + ts = 0 + ti = 0 + for k in range(len(a)): + if (a[k][i] > ts): + ts = a[k][i] + ti = k + ru.append(ts) + index.append(ti) + return ru, index diff --git a/benchmark/scripts/scanBatchSizeSingleThreadStream/config_BCoAMM.csv b/benchmark/scripts/scanBatchSizeSingleThreadStream/config_BCoAMM.csv new file mode 100644 index 00000000..b65317af --- /dev/null +++ b/benchmark/scripts/scanBatchSizeSingleThreadStream/config_BCoAMM.csv @@ -0,0 +1,6 @@ +key,value,type +aRow,100,U64 +aCol,1000,U64 +bCol,500,U64 +sketchDimension,25,U64 +ptFile,torchscripts/BetaCoOccurringFD.pt,String \ No newline at end of file diff --git a/benchmark/scripts/scanBatchSizeSingleThreadStream/config_BerCRS.csv b/benchmark/scripts/scanBatchSizeSingleThreadStream/config_BerCRS.csv new file mode 100644 index 00000000..d2b758a5 --- /dev/null +++ b/benchmark/scripts/scanBatchSizeSingleThreadStream/config_BerCRS.csv @@ -0,0 +1,6 @@ +key,value,type +aRow,100,U64 +aCol,1000,U64 +bCol,500,U64 +sketchDimension,25,U64 +ptFile,torchscripts/BernoulliCRS.pt,String \ No newline at end of file diff --git a/benchmark/scripts/scanBatchSizeSingleThreadStream/config_CPPBCOOFD.csv b/benchmark/scripts/scanBatchSizeSingleThreadStream/config_CPPBCOOFD.csv new file mode 100644 index 00000000..d43c92f9 --- /dev/null +++ b/benchmark/scripts/scanBatchSizeSingleThreadStream/config_CPPBCOOFD.csv @@ -0,0 +1,10 @@ +key,value,type +aRow,100,U64 +aCol,1000,U64 +bCol,500,U64 +sketchDimension,25,U64 +ptFile,torchscripts/BetaCoOccurringFD.pt,String +useCPP,1,U64 +cppAlgoTag,bcooFD,String +threads,1,U64 +forceMP,1,U64 diff --git a/benchmark/scripts/scanBatchSizeSingleThreadStream/config_CPPBCRS.csv b/benchmark/scripts/scanBatchSizeSingleThreadStream/config_CPPBCRS.csv new file mode 100644 index 00000000..3b7ee42c --- /dev/null +++ b/benchmark/scripts/scanBatchSizeSingleThreadStream/config_CPPBCRS.csv @@ -0,0 +1,10 @@ +key,value,type +aRow,100,U64 +aCol,1000,U64 +bCol,500,U64 +sketchDimension,25,U64 +ptFile,torchscripts/BernoulliCRS.pt,String +useCPP,1,U64 +cppAlgoTag,bcrs,String +threads,1,U64 +forceMP,1,U64 diff --git a/benchmark/scripts/scanBatchSizeSingleThreadStream/config_CPPCOFD.csv b/benchmark/scripts/scanBatchSizeSingleThreadStream/config_CPPCOFD.csv new file mode 100644 index 00000000..cf8f066a --- /dev/null +++ b/benchmark/scripts/scanBatchSizeSingleThreadStream/config_CPPCOFD.csv @@ -0,0 +1,10 @@ +key,value,type +aRow,100,U64 +aCol,1000,U64 +bCol,500,U64 +sketchDimension,25,U64 +ptFile,torchscripts/CRS.pt,String +useCPP,1,U64 +cppAlgoTag,cooFD,String +threads,1,U64 +forceMP,1,U64 \ No newline at end of file diff --git a/benchmark/scripts/scanBatchSizeSingleThreadStream/config_CPPCOUNTERSKETCH.csv b/benchmark/scripts/scanBatchSizeSingleThreadStream/config_CPPCOUNTERSKETCH.csv new file mode 100644 index 00000000..6823e4e2 --- /dev/null +++ b/benchmark/scripts/scanBatchSizeSingleThreadStream/config_CPPCOUNTERSKETCH.csv @@ -0,0 +1,10 @@ +key,value,type +aRow,100,U64 +aCol,1000,U64 +bCol,500,U64 +sketchDimension,25,U64 +ptFile,torchscripts/CRS.pt,String +useCPP,1,U64 +cppAlgoTag,countSketch,String +threads,1,U64 +forceMP,1,U64 diff --git a/benchmark/scripts/scanBatchSizeSingleThreadStream/config_CPPCRS.csv b/benchmark/scripts/scanBatchSizeSingleThreadStream/config_CPPCRS.csv new file mode 100644 index 00000000..1c900ad8 --- /dev/null +++ b/benchmark/scripts/scanBatchSizeSingleThreadStream/config_CPPCRS.csv @@ -0,0 +1,13 @@ +key,value,type +aRow,1000,U64 +aCol,1000,U64 +bCol,1000,U64 +sketchDimension,25,U64 +ptFile,torchscripts/CRS.pt,String +useCPP,1,U64 +cppAlgoTag,crs,String +threads,1,U64 +forceMP,1,U64 +eventRateTps,200,U64 +isStreaming,1,U64 +batchSize,1,U64 \ No newline at end of file diff --git a/benchmark/scripts/scanBatchSizeSingleThreadStream/config_CPPINT8.csv b/benchmark/scripts/scanBatchSizeSingleThreadStream/config_CPPINT8.csv new file mode 100644 index 00000000..2b61c43e --- /dev/null +++ b/benchmark/scripts/scanBatchSizeSingleThreadStream/config_CPPINT8.csv @@ -0,0 +1,10 @@ +key,value,type +aRow,100,U64 +aCol,1000,U64 +bCol,500,U64 +sketchDimension,25,U64 +ptFile,torchscripts/FDAMM.pt,String +useCPP,1,U64 +cppAlgoTag,int8,String +threads,1,U64 +forceMP,1,U64 diff --git a/benchmark/scripts/scanBatchSizeSingleThreadStream/config_CPPMM.csv b/benchmark/scripts/scanBatchSizeSingleThreadStream/config_CPPMM.csv new file mode 100644 index 00000000..af4da947 --- /dev/null +++ b/benchmark/scripts/scanBatchSizeSingleThreadStream/config_CPPMM.csv @@ -0,0 +1,13 @@ +key,value,type +aRow,1000,U64 +aCol,1000,U64 +bCol,1000,U64 +sketchDimension,25,U64 +ptFile,torchscripts/RAWMM.pt,String +useCPP,1,U64 +cppAlgoTag,mm,String +threads,1,U64 +forceMP,1,U64 +eventRateTps,200,U64 +isStreaming,1,U64 +batchSize,1,U64 \ No newline at end of file diff --git a/benchmark/scripts/scanBatchSizeSingleThreadStream/config_CPPSMPPCA.csv b/benchmark/scripts/scanBatchSizeSingleThreadStream/config_CPPSMPPCA.csv new file mode 100644 index 00000000..bfe37598 --- /dev/null +++ b/benchmark/scripts/scanBatchSizeSingleThreadStream/config_CPPSMPPCA.csv @@ -0,0 +1,11 @@ +key,value,type +aRow,1000,U64 +aCol,1000,U64 +bCol,1000,U64 +sketchDimension,25,U64 +threads,1,U64 +useCPP,1,U64 +cppAlgoTag,smp-pca,String +eventRateTps,200,U64 +isStreaming,1,U64 +batchSize,1,U64 \ No newline at end of file diff --git a/benchmark/scripts/scanBatchSizeSingleThreadStream/config_CoAMM.csv b/benchmark/scripts/scanBatchSizeSingleThreadStream/config_CoAMM.csv new file mode 100644 index 00000000..f41d7ddd --- /dev/null +++ b/benchmark/scripts/scanBatchSizeSingleThreadStream/config_CoAMM.csv @@ -0,0 +1,6 @@ +key,value,type +aRow,100,U64 +aCol,1000,U64 +bCol,500,U64 +sketchDimension,25,U64 +ptFile,torchscripts/CoOccurringFD.pt,String \ No newline at end of file diff --git a/benchmark/scripts/scanBatchSizeSingleThreadStream/config_CounterSketch.csv b/benchmark/scripts/scanBatchSizeSingleThreadStream/config_CounterSketch.csv new file mode 100644 index 00000000..50cf2770 --- /dev/null +++ b/benchmark/scripts/scanBatchSizeSingleThreadStream/config_CounterSketch.csv @@ -0,0 +1,6 @@ +key,value,type +aRow,100,U64 +aCol,1000,U64 +bCol,500,U64 +sketchDimension,25,U64 +ptFile,torchscripts/CountSketch.pt,String diff --git a/benchmark/scripts/scanBatchSizeSingleThreadStream/drawTogether.py b/benchmark/scripts/scanBatchSizeSingleThreadStream/drawTogether.py new file mode 100755 index 00000000..7079e113 --- /dev/null +++ b/benchmark/scripts/scanBatchSizeSingleThreadStream/drawTogether.py @@ -0,0 +1,179 @@ +#!/usr/bin/env python3 +#I will streaming A only!!! +import csv +import numpy as np +import accuBar as accuBar +import groupBar as groupBar +import groupBar2 as groupBar2 +import groupLine as groupLine +from autoParase import * +import itertools as it +import os + +import matplotlib +import numpy as np +import pylab +import matplotlib.font_manager as fm +from matplotlib.font_manager import FontProperties +from matplotlib.ticker import LogLocator, LinearLocator +import os +import pandas as pd +import sys +from OoOCommon import * +import time + +# OPT_FONT_NAME = 'Helvetica' +TICK_FONT_SIZE = 22 +LABEL_FONT_SIZE = 28 +LEGEND_FONT_SIZE = 30 +LABEL_FP = FontProperties(style='normal', size=LABEL_FONT_SIZE) +LEGEND_FP = FontProperties(style='normal', size=LEGEND_FONT_SIZE) +TICK_FP = FontProperties(style='normal', size=TICK_FONT_SIZE) + +MARKERS = (['*', '|', 'v', "^", "", "h", "<", ">", "+", "d", "<", "|", "", "+", "_"]) +# you may want to change the color map for different figures +COLOR_MAP = ( + '#B03A2E', '#2874A6', '#239B56', '#7D3C98', '#FFFFFF', '#F1C40F', '#F5CBA7', '#82E0AA', '#AEB6BF', '#AA4499') +# you may want to change the patterns for different figures +PATTERNS = (["////", "o", "", "||", "-", "//", "\\", "o", "O", "////", ".", "|||", "o", "---", "+", "\\\\", "*"]) +LABEL_WEIGHT = 'bold' +LINE_COLORS = COLOR_MAP +LINE_WIDTH = 3.0 +MARKER_SIZE = 15.0 +MARKER_FREQUENCY = 1000 + +matplotlib.rcParams['ps.useafm'] = True +matplotlib.rcParams['pdf.use14corefonts'] = True +matplotlib.rcParams['xtick.labelsize'] = TICK_FONT_SIZE +matplotlib.rcParams['ytick.labelsize'] = TICK_FONT_SIZE +matplotlib.rcParams['font.family'] = OPT_FONT_NAME +matplotlib.rcParams['pdf.fonttype'] = 42 + +scanTag = "batchSize" + + +def singleRun(exePath, singleValue, resultPath, configTemplate): + # resultFolder="singleValueTests" + configFname = "config_" + scanTag + str(singleValue) + ".csv" + # configTemplate = "config.csv" + # clear old files + os.system("cd " + exePath + "&& sudo rm *.csv") + + # editConfig(configTemplate, exePath + configFname, "earlierEmitMs", 0) + editConfig(configTemplate, exePath + configFname, scanTag, singleValue) + # prepare new file + # run + os.system("cd " + exePath + "&& sudo ./benchmark " + configFname) + # copy result + os.system("sudo rm -rf " + resultPath + "/" + str(singleValue)) + os.system("sudo mkdir " + resultPath + "/" + str(singleValue)) + os.system("cd " + exePath + "&& sudo cp *.csv " + resultPath + "/" + str(singleValue)) + + +def runScanVector(exePath, singleValueVec, resultPath, templateName="config.csv"): + for i in singleValueVec: + singleRun(exePath, i, resultPath, templateName) + + +def readResultSingle(singleValue, resultPath): + resultFname = resultPath + "/" + str(singleValue) + "/result_streaming.csv" + throughput = readConfig(resultFname, "throughputByElements") + lat95 = readConfig(resultFname, "95%latency") + froError = readConfig(resultFname, "froError") + errorBoundRatio = readConfig(resultFname, "errorBoundRatio") + return throughput,lat95, froError, errorBoundRatio + + +def cleanPath(path): + os.system("sudo rm -rf " + path) + os.system("sudo mkdir " + path) + + +def readResultVector(singleValueVec, resultPath): + thrVec = [] + lat95Vec= [] + froErrorVec = [] + errorBoundRatioVec = [] + for i in singleValueVec: + thr,lat95,froError, errorBoundRatio = readResultSingle(i, resultPath) + thrVec.append(float(thr)) + lat95Vec.append(float(lat95)/1000.0) + froErrorVec.append(float(froError)) + errorBoundRatioVec.append(float(errorBoundRatio)) + return np.array(thrVec), np.array(lat95Vec), np.array(froErrorVec), np.array( + errorBoundRatioVec) + + +def compareMethod(exeSpace, commonPathBase, resultPaths, csvTemplates, periodVec, reRun=1): + thrAll = [] + lat95All = [] + periodAll = [] + froAll = [] + errorBoundRatioAll = [] + for i in range(len(csvTemplates)): + resultPath = commonPathBase + resultPaths[i] + if (reRun == 1): + os.system("sudo rm -rf " + resultPath) + os.system("sudo mkdir " + resultPath) + runScanVector(exeSpace, periodVec, resultPath, csvTemplates[i]) + thr,lat95,fro, eb = readResultVector(periodVec, resultPath) + thrAll.append(thr) + lat95All.append(lat95) + + periodAll.append(periodVec) + + froAll.append(fro) + errorBoundRatioAll.append(eb) + # periodAll.append(periodVec) + return np.array(thrAll),np.array(lat95All), periodAll, np.array(froAll), np.array(errorBoundRatioAll) + + +def main(): + exeSpace = os.path.abspath(os.path.join(os.getcwd(), "../..")) + "/" + commonBase = os.path.abspath(os.path.join(os.getcwd(), "../..")) + "/results/" + scanTag + "/" + figPath = os.path.abspath(os.path.join(os.getcwd(), "../..")) + "/figures/" + scanTag + "CPP" + methodTags = ["CRS", "MM","SMP-PCA"] + resultPaths = ["CRS", "mm","smp-pca"] + csvTemplates = ["config_CPPCRS.csv", "config_CPPMM.csv","config_CPPSMPPCA.csv"] + valueVec = [1,10,20,50,100,200,500] + valueVecDisp = np.array(valueVec) + # run + reRun = 0 + if (len(sys.argv) < 2): + os.system("mkdir ../../results") + os.system("mkdir ../../figures") + os.system("mkdir " + figPath) + os.system("sudo rm -rf " + commonBase) + os.system("sudo mkdir " + commonBase) + reRun = 1 + # skech + thrAll, lat95All, periodAll, fro, eb = compareMethod(exeSpace, commonBase, resultPaths, csvTemplates, valueVec, + reRun) + groupLine.DrawFigureYnormal(periodAll, thrAll/1000.0, + methodTags, + "batch size (#rows)", "throughput (K elements/s)", 0, 1, + figPath + "/" + scanTag + "stream_cpp_thr", + True) + groupLine.DrawFigure(periodAll, lat95All, + methodTags, + "batch size (#rows)", "95% latency (ms)", 0, 1, + figPath + "/" + scanTag + "stream_cpp_lat95", + True) + groupLine.DrawFigureYnormal(periodAll, fro*100.0, + methodTags, + "batch size (#rows)", "fro error (%)", 0, 1, + figPath + "/" + scanTag + "stream_cpp_fro", + True) + + # draw2yLine("watermark time (ms)",singleValueVecDisp,lat95Vec,errVec,"95% Latency (ms)","Error","ms","",figPath+"wm_lat") + # draw2yLine("watermark time (ms)",singleValueVecDisp,thrVec,errVec,"Throughput (KTp/s)","Error","KTp/s","",figPath+"wm_thr") + # draw2yLine("watermark time (ms)",singleValueVecDisp,lat95Vec,compVec,"95% Latency (ms)","Completeness","ms","",figPath+"wm_omp") + # groupLine.DrawFigureYnormal([singleValueVec,singleValueVec],[errVec,aqpErrVec],['w/o aqp',"w/ MeanAqp"],"watermark time (ms)","Error",0,1,figPath+"wm_MeanAqp",True) + # print(errVec) + # print(aqpErrVec) + # print(elapseTimeVecFD) + # readResultsingleValue(50,resultPath) + + +if __name__ == "__main__": + main() diff --git a/benchmark/scripts/scanBatchSizeSingleThreadStream/groupBar.py b/benchmark/scripts/scanBatchSizeSingleThreadStream/groupBar.py new file mode 100755 index 00000000..ef2d9842 --- /dev/null +++ b/benchmark/scripts/scanBatchSizeSingleThreadStream/groupBar.py @@ -0,0 +1,236 @@ +import itertools as it +import os + +import matplotlib +import matplotlib.pyplot as plt +import numpy as np +import pylab +from matplotlib.font_manager import FontProperties +from matplotlib.ticker import LogLocator, LinearLocator + +OPT_FONT_NAME = 'Helvetica' +TICK_FONT_SIZE = 24 +LABEL_FONT_SIZE = 28 +LEGEND_FONT_SIZE = 15 +LABEL_FP = FontProperties(style='normal', size=LABEL_FONT_SIZE) +LEGEND_FP = FontProperties(style='normal', size=LEGEND_FONT_SIZE) +TICK_FP = FontProperties(style='normal', size=TICK_FONT_SIZE) + +MARKERS = (["", 'o', 's', 'v', "^", "", "h", "<", ">", "+", "d", "<", "|", "", "+", "_"]) +# you may want to change the color map for different figures +COLOR_MAP = ( + '#7FFFFF', '#B03A2E', '#2874A6', '#FFFFFF', '#7FFFFF', '#B03A2E', '#2874A6', '#FFFFFF', '#F5CBA7', '#82E0AA', + '#AEB6BF', + '#AA4499') +# you may want to change the patterns for different figures +PATTERNS = ( + ["////", "\\\\", "//", "o", "*", "||", "-", "//", "\\", "o", "O", "////", ".", "|||", "o", "---", "+", "\\\\", "*"]) +LABEL_WEIGHT = 'bold' +LINE_COLORS = COLOR_MAP +LINE_WIDTH = 3.0 +MARKER_SIZE = 15.0 +MARKER_FREQUENCY = 1000 + +matplotlib.rcParams['ps.useafm'] = True +matplotlib.rcParams['pdf.use14corefonts'] = True +matplotlib.rcParams['xtick.labelsize'] = TICK_FONT_SIZE +matplotlib.rcParams['ytick.labelsize'] = TICK_FONT_SIZE +matplotlib.rcParams['font.family'] = OPT_FONT_NAME +matplotlib.rcParams['pdf.fonttype'] = 42 + +exp_dir = "/data1/xtra" + +FIGURE_FOLDER = exp_dir + '/results/figure' + + +def DrawLegend(legend_labels, filename): + fig = pylab.figure() + ax1 = fig.add_subplot(111) + FIGURE_LABEL = legend_labels + LEGEND_FP = FontProperties(style='normal', size=26) + figlegend = pylab.figure(figsize=(16, 0.5)) + bars = [None] * (len(FIGURE_LABEL)) + data = [1] + x_values = [1] + + width = 0.3 + for i in range(len(FIGURE_LABEL)): + bars[i] = ax1.bar(x_values, data, width, + hatch=PATTERNS[i], + color=LINE_COLORS[i], + label=FIGURE_LABEL[i], + edgecolor='black', linewidth=3) + + # LEGEND + + figlegend.legend(bars, FIGURE_LABEL, prop=LEGEND_FP, \ + loc=1, ncol=len(FIGURE_LABEL), mode="expand", shadow=True, \ + frameon=True, handlelength=2, handletextpad=0.3, columnspacing=0.5, + borderaxespad=-0.2, fancybox=True + ) + figlegend.savefig(FIGURE_FOLDER + '/' + filename + '.pdf') + + +# draw a bar chart +def DrawFigure(x_values, y_values, legend_labels, x_label, y_label, y_min, y_max, filename, allow_legend): + # you may change the figure size on your own. + fig = plt.figure(figsize=(10, 3)) + figure = fig.add_subplot(111) + + FIGURE_LABEL = legend_labels + + # values in the x_xis + index = np.arange(len(x_values)) + # the bar width. + # you may need to tune it to get the best figure. + width = 0.08 + # draw the bars + bars = [] + ts = 0 + pos = 0 + gl = len(y_values[0]) + for i in range(len(y_values)): + pos = pos + 3 * width + for j in range(len(y_values[i])): + pos = pos + width + bar = plt.bar(pos, y_values[i][j], width, hatch=PATTERNS[j], color=LINE_COLORS[j], label=FIGURE_LABEL[j], + edgecolor='black', linewidth=3) + bars.append(bar) + ts = ts + 1 + + # sometimes you may not want to draw legends. + if allow_legend == True: + plt.legend(bars, FIGURE_LABEL, + prop=LEGEND_FP, + ncol=4, + loc='upper center', + # mode='expand', + shadow=False, + bbox_to_anchor=(0.45, 1.6), + columnspacing=0.1, + handletextpad=0.2, + # bbox_transform=ax.transAxes, + # frameon=True, + # columnspacing=5.5, + # handlelength=2, + ) + + # you may need to tune the xticks position to get the best figure. + plt.xticks(index, x_values) + # plt.ticklabel_format(axis="y", style="sci", scilimits=(0, 0)) + # plt.grid(axis='y', color='gray') + # figure.get_xaxis().set_major_formatter(matplotlib.ticker.ScalarFormatter()) + + # you may need to tune the xticks position to get the best figure. + # plt.yscale('log') + # + # plt.grid(axis='y', color='gray') + figure.yaxis.set_major_locator(LinearLocator(5)) + # figure.xaxis.set_major_locator(LinearLocator(5)) + figure.get_xaxis().set_tick_params(direction='in', pad=10) + figure.get_yaxis().set_tick_params(direction='in', pad=10) + + plt.xlabel(x_label, fontproperties=LABEL_FP) + plt.ylabel(y_label, fontproperties=LABEL_FP) + plt.ylim(y_min, y_max) + plt.savefig(filename + ".pdf", bbox_inches='tight') + + +# example for reading csv file +def ReadFile(): + y = [] + col1 = [] + col2 = [] + col3 = [] + col4 = [] + col5 = [] + col6 = [] + col7 = [] + col8 = [] + col9 = [] + + for id in it.chain(range(38, 42)): + col9.append(0) + y.append(col9) # this is a lz4_pipe2 empty line to separate eager and lazy. + + for id in it.chain(range(38, 42)): + file = exp_dir + '/results/latency/NPJ_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(int(len(read) * 0.95)).strip("\n")) # get the 99th timestamp + col1.append(x) + y.append(col1) + + for id in it.chain(range(38, 42)): + file = exp_dir + '/results/latency/PRJ_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(int(len(read) * 0.95)).strip("\n")) # get the 99th timestamp + col2.append(x) + y.append(col2) + + for id in it.chain(range(38, 42)): + file = exp_dir + '/results/latency/MWAY_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(int(len(read) * 0.95)).strip("\n")) # get the 99th timestamp + col3.append(x) + y.append(col3) + + for id in it.chain(range(38, 42)): + file = exp_dir + '/results/latency/MPASS_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(int(len(read) * 0.95)).strip("\n")) # get the 99th timestamp + col4.append(x) + y.append(col4) + + y.append(col9) # this is a lz4_pipe2 empty line to separate eager and lazy. + + for id in it.chain(range(38, 42)): + file = exp_dir + '/results/latency/SHJ_JM_NP_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(int(len(read) * 0.95)).strip("\n")) # get last timestamp + col5.append(x) + y.append(col5) + + for id in it.chain(range(38, 42)): + file = exp_dir + '/results/latency/SHJ_JBCR_NP_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(int(len(read) * 0.95)).strip("\n")) # get last timestamp + col6.append(x) + y.append(col6) + + for id in it.chain(range(38, 42)): + file = exp_dir + '/results/latency/PMJ_JM_NP_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(int(len(read) * 0.95)).strip("\n")) # get last timestamp + col7.append(x) + y.append(col7) + + for id in it.chain(range(38, 42)): + file = exp_dir + '/results/latency/PMJ_JBCR_NP_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(int(len(read) * 0.95)).strip("\n")) # get last timestamp + col8.append(x) + y.append(col8) + return y + + +if __name__ == "__main__": + x_values = ["Stock", "Rovio", "YSB", "DEBS"] + + y_values = ReadFile() + + legend_labels = ['Lazy:', 'NPJ', 'PRJ', 'MWAY', 'MPASS', + 'Eager:', 'SHJ$^{JM}$', 'SHJ$^{JB}$', 'PMJ$^{JM}$', 'PMJ$^{JB}$'] + print(y_values) + DrawFigure(x_values, y_values, legend_labels, + '', 'Latency (ms)', 0, + 400, 'latency_figure_app', False) + + # DrawLegend(legend_labels, 'latency_legend') diff --git a/benchmark/scripts/scanBatchSizeSingleThreadStream/groupBar2.py b/benchmark/scripts/scanBatchSizeSingleThreadStream/groupBar2.py new file mode 100755 index 00000000..31155695 --- /dev/null +++ b/benchmark/scripts/scanBatchSizeSingleThreadStream/groupBar2.py @@ -0,0 +1,232 @@ +import itertools as it +import os + +import matplotlib +import matplotlib.pyplot as plt +import numpy as np +import pylab +from matplotlib.font_manager import FontProperties +from matplotlib.ticker import LogLocator, LinearLocator + +OPT_FONT_NAME = 'Helvetica' +TICK_FONT_SIZE = 24 +LABEL_FONT_SIZE = 28 +LEGEND_FONT_SIZE = 30 +LABEL_FP = FontProperties(style='normal', size=LABEL_FONT_SIZE) +LEGEND_FP = FontProperties(style='normal', size=LEGEND_FONT_SIZE) +TICK_FP = FontProperties(style='normal', size=TICK_FONT_SIZE) + +MARKERS = (["", 'o', 's', 'v', "^", "", "h", "<", ">", "+", "d", "<", "|", "", "+", "_"]) +# you may want to change the color map for different figures +COLOR_MAP = ( + '#FFFFFF', '#B03A2E', '#2874A6', '#239B56', '#7D3C98', '#00FFFF', '#F1C40F', '#F5CBA7', '#82E0AA', '#AEB6BF', + '#AA4499') +# you may want to change the patterns for different figures +PATTERNS = ( + ["", "////", "\\\\", "//", "o", "", "||", "-", "//", "\\", "o", "O", "////", ".", "|||", "o", "---", "+", "\\\\", + "*"]) +LABEL_WEIGHT = 'bold' +LINE_COLORS = COLOR_MAP +LINE_WIDTH = 3.0 +MARKER_SIZE = 15.0 +MARKER_FREQUENCY = 1000 + +matplotlib.rcParams['ps.useafm'] = True +matplotlib.rcParams['pdf.use14corefonts'] = True +matplotlib.rcParams['xtick.labelsize'] = TICK_FONT_SIZE +matplotlib.rcParams['ytick.labelsize'] = TICK_FONT_SIZE +matplotlib.rcParams['font.family'] = OPT_FONT_NAME +matplotlib.rcParams['pdf.fonttype'] = 42 + +exp_dir = "/data1/xtra" + +FIGURE_FOLDER = exp_dir + '/results/figure' + + +def DrawLegend(legend_labels, filename): + fig = pylab.figure() + ax1 = fig.add_subplot(111) + FIGURE_LABEL = legend_labels + LEGEND_FP = FontProperties(style='normal', size=26) + figlegend = pylab.figure(figsize=(16, 0.5)) + bars = [None] * (len(FIGURE_LABEL)) + data = [1] + x_values = [1] + + width = 0.3 + for i in range(len(FIGURE_LABEL)): + bars[i] = ax1.bar(x_values, data, width, + hatch=PATTERNS[i], + color=LINE_COLORS[i], + label=FIGURE_LABEL[i], + edgecolor='black', linewidth=3) + + # LEGEND + + figlegend.legend(bars, FIGURE_LABEL, prop=LEGEND_FP, \ + loc=1, ncol=len(FIGURE_LABEL), mode="expand", shadow=True, \ + frameon=True, handlelength=2, handletextpad=0.3, columnspacing=0.5, + borderaxespad=-0.2, fancybox=True + ) + figlegend.savefig(FIGURE_FOLDER + '/' + filename + '.pdf') + + +# draw a bar chart +def DrawFigure(x_values, y_values, legend_labels, x_label, y_label, y_min, y_max, filename, allow_legend): + # you may change the figure size on your own. + fig = plt.figure(figsize=(10, 3)) + figure = fig.add_subplot(111) + + FIGURE_LABEL = legend_labels + + # values in the x_xis + index = np.arange(len(x_values)) + # the bar width. + # you may need to tune it to get the best figure. + width = 0.12 + # draw the bars + bars = [None] * (len(FIGURE_LABEL)) + for i in range(len(y_values)): + bars[i] = plt.bar(index + i * width + width / 2, + y_values[i], width, + hatch=PATTERNS[i], + color=LINE_COLORS[i], + label=FIGURE_LABEL[i], edgecolor='black', linewidth=3) + + # sometimes you may not want to draw legends. + if allow_legend == True: + plt.legend(bars, FIGURE_LABEL, + prop=LEGEND_FP, + ncol=3, + loc='upper center', + # mode='expand', + shadow=False, + bbox_to_anchor=(0.45, 1.7), + columnspacing=0.1, + handletextpad=0.2, + # bbox_transform=ax.transAxes, + # frameon=True, + # columnspacing=5.5, + # handlelength=2, + ) + + # you may need to tune the xticks position to get the best figure. + plt.xticks(index + 3 * width, x_values, rotation=30) + + # plt.ticklabel_format(axis="y", style="sci", scilimits=(0, 0)) + # plt.grid(axis='y', color='gray') + # figure.get_xaxis().set_major_formatter(matplotlib.ticker.ScalarFormatter()) + + # you may need to tune the xticks position to get the best figure. + # plt.yscale('log') + # + # plt.grid(axis='y', color='gray') + figure.yaxis.set_major_locator(LinearLocator(10)) + # figure.xaxis.set_major_locator(LinearLocator(5)) + figure.get_xaxis().set_tick_params(direction='in', pad=10) + figure.get_yaxis().set_tick_params(direction='in', pad=10) + + plt.xlabel(x_label, fontproperties=LABEL_FP) + plt.ylabel(y_label, fontproperties=LABEL_FP) + + plt.savefig(filename + ".pdf", bbox_inches='tight') + + +# example for reading csv file +def ReadFile(): + y = [] + col1 = [] + col2 = [] + col3 = [] + col4 = [] + col5 = [] + col6 = [] + col7 = [] + col8 = [] + col9 = [] + + for id in it.chain(range(38, 42)): + col9.append(0) + y.append(col9) # this is a fake empty line to separate eager and lazy. + + for id in it.chain(range(38, 42)): + file = exp_dir + '/results/latency/NPJ_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(int(len(read) * 0.95)).strip("\n")) # get the 99th timestamp + col1.append(x) + y.append(col1) + + for id in it.chain(range(38, 42)): + file = exp_dir + '/results/latency/PRJ_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(int(len(read) * 0.95)).strip("\n")) # get the 99th timestamp + col2.append(x) + y.append(col2) + + for id in it.chain(range(38, 42)): + file = exp_dir + '/results/latency/MWAY_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(int(len(read) * 0.95)).strip("\n")) # get the 99th timestamp + col3.append(x) + y.append(col3) + + for id in it.chain(range(38, 42)): + file = exp_dir + '/results/latency/MPASS_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(int(len(read) * 0.95)).strip("\n")) # get the 99th timestamp + col4.append(x) + y.append(col4) + + y.append(col9) # this is a fake empty line to separate eager and lazy. + + for id in it.chain(range(38, 42)): + file = exp_dir + '/results/latency/SHJ_JM_NP_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(int(len(read) * 0.95)).strip("\n")) # get last timestamp + col5.append(x) + y.append(col5) + + for id in it.chain(range(38, 42)): + file = exp_dir + '/results/latency/SHJ_JBCR_NP_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(int(len(read) * 0.95)).strip("\n")) # get last timestamp + col6.append(x) + y.append(col6) + + for id in it.chain(range(38, 42)): + file = exp_dir + '/results/latency/PMJ_JM_NP_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(int(len(read) * 0.95)).strip("\n")) # get last timestamp + col7.append(x) + y.append(col7) + + for id in it.chain(range(38, 42)): + file = exp_dir + '/results/latency/PMJ_JBCR_NP_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(int(len(read) * 0.95)).strip("\n")) # get last timestamp + col8.append(x) + y.append(col8) + return y + + +if __name__ == "__main__": + x_values = ["Stock", "Rovio", "YSB", "DEBS"] + + y_values = ReadFile() + + legend_labels = ['Lazy:', 'NPJ', 'PRJ', 'MWAY', 'MPASS', + 'Eager:', 'SHJ$^{JM}$', 'SHJ$^{JB}$', 'PMJ$^{JM}$', 'PMJ$^{JB}$'] + print(y_values) + DrawFigure(x_values, y_values, legend_labels, + '', 'Latency (ms)', 0, + 400, 'latency_figure_app', False) + + # DrawLegend(legend_labels, 'latency_legend') diff --git a/benchmark/scripts/scanBatchSizeSingleThreadStream/groupLine.py b/benchmark/scripts/scanBatchSizeSingleThreadStream/groupLine.py new file mode 100755 index 00000000..77993fc1 --- /dev/null +++ b/benchmark/scripts/scanBatchSizeSingleThreadStream/groupLine.py @@ -0,0 +1,368 @@ +import itertools as it +import os +import matplotlib.pyplot as plt +import matplotlib.font_manager as fm +import matplotlib +import numpy as np +import pylab +from matplotlib.font_manager import FontProperties +from matplotlib.ticker import MaxNLocator +from matplotlib.font_manager import FontProperties +from matplotlib.ticker import LinearLocator, LogLocator, MaxNLocator, ScalarFormatter +from numpy import double +import matplotlib.ticker as mtick +# 获取系统中可用的字体路径 +# font_paths = fm.findSystemFonts() +# OPT_FONT_NAME = 'Helvetica' +TICK_FONT_SIZE = 20 +LABEL_FONT_SIZE = 20 +LEGEND_FONT_SIZE = 20 +LABEL_FP = FontProperties(style='normal', size=LABEL_FONT_SIZE) +LEGEND_FP = FontProperties(style='normal', size=LEGEND_FONT_SIZE) +TICK_FP = FontProperties(style='normal', size=TICK_FONT_SIZE) + +MARKERS = (['o', 's', 'v', "^", "h", "v", ">", "x", "d", "<", "|", "p", "+", "_", "%", "|", "|", "|", "|", "|"]) +# you may want to change the color map for different figures +COLOR_MAP = ( + '#F15854', '#5DA5DA', '#60BD68', '#B276B2', '#DECF3F', '#F17CB0', '#B2912F', '#FAA43A', '#AFAFAF', '#087878', + '#783456', + '#560012', '#431256', "#00AABB", "#AA00BB") +# you may want to change the patterns for different figures +PATTERNS = (["|", "\\", "/", "+", "-", ".", "*", "x", "o", "O", "////", ".", "|||", "o", "---", "+", "\\\\", "*"]) +LABEL_WEIGHT = 'bold' +LINE_COLORS = COLOR_MAP +LINE_WIDTH = 3.0 +MARKER_SIZE = 13.0 +MARKER_FREQUENCY = 1000 + +# matplotlib.rcParams['ps.useafm'] = True +# matplotlib.rcParams['pdf.use14corefonts'] = True +# matplotlib.rcParams['xtick.labelsize'] = TICK_FONT_SIZE +# matplotlib.rcParams['ytick.labelsize'] = TICK_FONT_SIZE +# 创建字体列表 +# Explicitly specify the font path +# 获取系统中可用的字体路径 +font_paths = fm.findSystemFonts() + +# 创建字体列表 +font_list = [] +for font_path in font_paths: + try: + font_name = fm.FontProperties(fname=font_path).get_name() + if font_name not in font_list: + font_list.append(font_name) + except: + pass + +# 配置 matplotlib 使用系统中可用的字体 +plt.rcParams['font.family'] = font_list[0] + +FIGURE_FOLDER = '/data1/xtra/results/figure' + + +# there are some embedding problems if directly exporting the pdf figure using matplotlib. +# so we generate the eps format first and convert it to pdf. +def ConvertEpsToPdf(dir_filename): + os.system("epstopdf --outfile " + dir_filename + ".pdf " + dir_filename + ".eps") + os.system("rm -rf " + dir_filename + ".eps") + + +def DrawLegend(legend_labels, filename): + fig = pylab.figure() + ax1 = fig.add_subplot(111) + FIGURE_LABEL = legend_labels + LINE_WIDTH = 8.0 + MARKER_SIZE = 12.0 + LEGEND_FP = FontProperties(style='normal', size=26) + + figlegend = pylab.figure(figsize=(12, 0.5)) + idx = 0 + lines = [None] * (len(FIGURE_LABEL)) + data = [1] + x_values = [1] + + idx = 0 + for group in range(len(FIGURE_LABEL)): + lines[idx], = ax1.plot(x_values, data, + color=LINE_COLORS[idx], linewidth=LINE_WIDTH, + marker=MARKERS[idx], markersize=MARKER_SIZE, label=str(group)) + idx = idx + 1 + + # LEGEND + figlegend.legend(lines, FIGURE_LABEL, prop=LEGEND_FP, + loc=1, ncol=len(FIGURE_LABEL), mode="expand", shadow=False, + frameon=False, borderaxespad=0.0, handlelength=2) + + if not os.path.exists(FIGURE_FOLDER): + os.makedirs(FIGURE_FOLDER) + # no need to export eps in this case. + figlegend.savefig(filename + '.pdf') + + +# draw a line chart +def DrawFigure(xvalues, yvalues, legend_labels, x_label, y_label, y_min, y_max, filename, allow_legend): + # you may change the figure size on your own. + fig = plt.figure(figsize=(10, 3)) + figure = fig.add_subplot(111) + + FIGURE_LABEL = legend_labels + + x_values = xvalues + y_values = yvalues + + lines = [None] * (len(FIGURE_LABEL)) + for i in range(len(y_values)): + lines[i], = figure.plot(x_values[i], y_values[i], color=LINE_COLORS[i], \ + linewidth=LINE_WIDTH, marker=MARKERS[i], \ + markersize=MARKER_SIZE, label=FIGURE_LABEL[i]) + + # sometimes you may not want to draw legends. + if allow_legend == True: + plt.legend(lines, + FIGURE_LABEL, + prop=LEGEND_FP, + loc='upper center', + ncol=3, + # mode='expand', + bbox_to_anchor=(0.55, 1.6), shadow=False, + columnspacing=0.1, + frameon=True, borderaxespad=0.0, handlelength=1.5, + handletextpad=0.1, + labelspacing=0.1) + plt.xscale('log') + plt.yscale('log') + # plt.yscale('log') + + # you may control the limits on your own. + + # plt.ylim(y_min, y_max) + + plt.grid(axis='y', color='gray') + figure.yaxis.set_major_locator(LogLocator(base=10)) + figure.xaxis.set_major_locator(LogLocator(base=10)) + + figure.get_xaxis().set_tick_params(direction='in', pad=10) + figure.get_yaxis().set_tick_params(direction='in', pad=10) + + plt.xlabel(x_label, fontproperties=LABEL_FP) + plt.ylabel(y_label, fontproperties=LABEL_FP) + + size = fig.get_size_inches() + dpi = fig.get_dpi() + + # plt.show() + plt.savefig(filename + ".pdf", bbox_inches='tight') + + +# draw a line chart +def DrawFigureXYnormal(xvalues, yvalues, legend_labels, x_label, y_label, y_min, y_max, filename, allow_legend): + # you may change the figure size on your own. + fig = plt.figure(figsize=(10, 3)) + figure = fig.add_subplot(111) + + FIGURE_LABEL = legend_labels + + x_values = xvalues + y_values = yvalues + + lines = [None] * (len(FIGURE_LABEL)) + for i in range(len(y_values)): + lines[i], = figure.plot(x_values[i], y_values[i], color=LINE_COLORS[i], \ + linewidth=LINE_WIDTH, marker=MARKERS[i], \ + markersize=MARKER_SIZE, label=FIGURE_LABEL[i], markeredgecolor='k') + + # sometimes you may not want to draw legends. + if allow_legend == True: + plt.legend(lines, + FIGURE_LABEL, + prop=LEGEND_FP, + loc='upper center', + ncol=3, + bbox_to_anchor=(0.55, 1.5), shadow=False, + columnspacing=0.1, + frameon=True, borderaxespad=0, handlelength=1.2, + handletextpad=0.1, + labelspacing=0.1) + # plt.xscale('log') + # plt.yscale('log') + # plt.yscale('log') + + # you may control the limits on your own. + + # plt.ylim(y_min, y_max) + + plt.grid(axis='y', color='gray') + plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号 + # figure.yaxis.set_major_locator(LogLocator(base=10)) + # figure.xaxis.set_major_locator(LogLocator(base=10)) + plt.xticks(rotation=0, fontsize=TICK_FONT_SIZE) + figure.get_xaxis().set_tick_params(direction='in', pad=10) + figure.get_yaxis().set_tick_params(direction='in', pad=10) + + plt.xlabel(x_label, fontproperties=LABEL_FP) + plt.ylabel(y_label, fontproperties=LABEL_FP) + + size = fig.get_size_inches() + dpi = fig.get_dpi() + + # plt.show() + plt.savefig(filename + ".pdf", bbox_inches='tight') + + +# draw a line chart +def DrawFigureYnormal(xvalues, yvalues, legend_labels, x_label, y_label, y_min, y_max, filename, allow_legend): + # you may change the figure size on your own. + fig = plt.figure(figsize=(10, 3)) + figure = fig.add_subplot(111) + + FIGURE_LABEL = legend_labels + + x_values = xvalues + y_values = yvalues + + lines = [None] * (len(FIGURE_LABEL)) + for i in range(len(y_values)): + lines[i], = figure.plot(x_values[i], y_values[i], color=LINE_COLORS[i], \ + linewidth=LINE_WIDTH, marker=MARKERS[i], \ + markersize=MARKER_SIZE, label=FIGURE_LABEL[i], markeredgecolor='k') + + # sometimes you may not want to draw legends. + if allow_legend == True: + plt.legend(lines, + FIGURE_LABEL, + prop=LEGEND_FP, + loc='upper center', + ncol=3, + bbox_to_anchor=(0.55, 1.5), shadow=False, + columnspacing=0.1, + frameon=True, borderaxespad=0, handlelength=1.2, + handletextpad=0.1, + labelspacing=0.1) + plt.xscale('log') + # plt.yscale('log') + # plt.yscale('log') + + # you may control the limits on your own. + yMax=np.max(y_values) + plt.ylim(y_min, yMax*1.2) + + plt.grid(axis='y', color='gray') + plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号 + figure.yaxis.set_major_formatter(mtick.FormatStrFormatter('%.1f')) + + figure.yaxis.set_major_locator(LinearLocator(numticks=5)) + # figure.xaxis.set_major_locator(LogLocator(base=10)) + plt.xticks(rotation=0, fontsize=TICK_FONT_SIZE) + figure.get_xaxis().set_tick_params(direction='in', pad=10) + figure.get_yaxis().set_tick_params(direction='in', pad=10) + + plt.xlabel(x_label, fontproperties=LABEL_FP) + plt.ylabel(y_label, fontproperties=LABEL_FP) + + size = fig.get_size_inches() + dpi = fig.get_dpi() + + # plt.show() + plt.savefig(filename + ".pdf", bbox_inches='tight') + + +# example for reading csv file +def ReadFile(): + y = [] + col1 = [] + col2 = [] + col3 = [] + col4 = [] + col5 = [] + col6 = [] + col7 = [] + col8 = [] + + for id in it.chain(range(28, 32)): + file = '/data1/xtra/results/timestamps/PRJ_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(len(read) - 1).strip("\n")) # get last timestamp + value = len(read) / x # get throughput (#items/ms) + col1.append(value) + y.append(col1) + + for id in it.chain(range(28, 32)): + file = '/data1/xtra/results/timestamps/NPJ_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(len(read) - 1).strip("\n")) # get last timestamp + value = len(read) / x # get throughput (#items/ms) + col2.append(value) + y.append(col2) + + for id in it.chain(range(28, 32)): + file = '/data1/xtra/results/timestamps/MPASS_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(len(read) - 1).strip("\n")) # get last timestamp + value = len(read) / x # get throughput (#items/ms) + col3.append(value) + y.append(col3) + + for id in it.chain(range(28, 32)): + file = '/data1/xtra/results/timestamps/MWAY_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(len(read) - 1).strip("\n")) # get last timestamp + value = len(read) / x # get throughput (#items/ms) + col4.append(value) + y.append(col4) + + for id in it.chain(range(28, 32)): + file = '/data1/xtra/results/timestamps/SHJ_JM_NP_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(len(read) - 1).strip("\n")) # get last timestamp + value = len(read) / x # get throughput (#items/ms) + col5.append(value) + y.append(col5) + + for id in it.chain(range(28, 32)): + file = '/data1/xtra/results/timestamps/SHJ_JBCR_NP_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(len(read) - 1).strip("\n")) # get last timestamp + value = len(read) / x # get throughput (#items/ms) + col6.append(value) + y.append(col6) + + for id in it.chain(range(28, 32)): + file = '/data1/xtra/results/timestamps/PMJ_JM_NP_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(len(read) - 1).strip("\n")) # get last timestamp + value = len(read) / x # get throughput (#items/ms) + col7.append(value) + y.append(col7) + + for id in it.chain(range(28, 32)): + file = '/data1/xtra/results/timestamps/PMJ_JBCR_NP_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(len(read) - 1).strip("\n")) # get last timestamp + value = len(read) / x # get throughput (#items/ms) + col8.append(value) + y.append(col8) + return y + + +if __name__ == "__main__": + # x_values = ['Unique', 'Zipf(0)', 'Zipf(0.2)', 'Zipf(0.4)', 'Zipf(0.8)', 'Zipf(1)'] + x_values = [1600, 3200, 6400, 12800, 25600] + + y_values = ReadFile() + + legend_labels = ['NPJ', 'PRJ', 'MWAY', 'MPASS', 'SHJ$^{JM}$', 'SHJ$^{JB}$', 'PMJ$^{JM}$', + 'PMJ$^{JB}$'] + + DrawFigure(x_values, y_values, legend_labels, + 'Input arrival rate of R (e/ms)', 'Tpt. (#matches/ms)', x_values[0], + x_values[4], 'throughput_figure1_1', False) + +# DrawLegend(legend_labels, 'factor_legend') diff --git a/benchmark/scripts/scanBatchSizeSingleThreadStream/perfRu.csv b/benchmark/scripts/scanBatchSizeSingleThreadStream/perfRu.csv new file mode 100644 index 00000000..9ee43623 --- /dev/null +++ b/benchmark/scripts/scanBatchSizeSingleThreadStream/perfRu.csv @@ -0,0 +1,7 @@ +key,value,type +cacheMiss,12491377,U64 +cacheRefs,35327177,U64 +cpuClock,2478812100,U64 +cpuCycle,9889422467,U64 +instructions,16997458033,U64 +taskClock,2478813000,U64 diff --git a/benchmark/scripts/scanBatchSizeSingleThreadStream2S/OoOCommon.py b/benchmark/scripts/scanBatchSizeSingleThreadStream2S/OoOCommon.py new file mode 100755 index 00000000..70f4cf33 --- /dev/null +++ b/benchmark/scripts/scanBatchSizeSingleThreadStream2S/OoOCommon.py @@ -0,0 +1,125 @@ +import csv +import numpy as np +import matplotlib.pyplot as plt +import itertools as it +import os + +import matplotlib +import matplotlib.pyplot as plt +import numpy as np +import pylab +from matplotlib.font_manager import FontProperties +from matplotlib.ticker import LogLocator, LinearLocator +import os +import pandas as pd +import sys +import matplotlib.ticker as mtick + +OPT_FONT_NAME = 'Helvetica' +TICK_FONT_SIZE = 22 +LABEL_FONT_SIZE = 28 +LEGEND_FONT_SIZE = 30 +LABEL_FP = FontProperties(style='normal', size=LABEL_FONT_SIZE) +LEGEND_FP = FontProperties(style='normal', size=LEGEND_FONT_SIZE) +TICK_FP = FontProperties(style='normal', size=TICK_FONT_SIZE) + +MARKERS = (['*', '|', 'v', "^", "", "h", "<", ">", "+", "d", "<", "|", "", "+", "_"]) +# you may want to change the color map for different figures +COLOR_MAP = ( + '#B03A2E', '#2874A6', '#239B56', '#7D3C98', '#FFFFFF', '#F1C40F', '#F5CBA7', '#82E0AA', '#AEB6BF', '#AA4499') +# you may want to change the patterns for different figures +PATTERNS = (["////", "o", "", "||", "-", "//", "\\", "o", "O", "////", ".", "|||", "o", "---", "+", "\\\\", "*"]) +LABEL_WEIGHT = 'bold' +LINE_COLORS = COLOR_MAP +LINE_WIDTH = 3.0 +MARKER_SIZE = 15.0 +MARKER_FREQUENCY = 1000 + + +def editConfig(src, dest, key, value): + df = pd.read_csv(src, header=None) + rowIdx = 0 + idxt = 0 + for cell in df[0]: + # print(cell) + if (cell == key): + rowIdx = idxt + break + idxt = idxt + 1 + df[1][rowIdx] = str(value) + df.to_csv(dest, index=False, header=False) + + +def readConfig(src, key): + df = pd.read_csv(src, header=None) + rowIdx = 0 + idxt = 0 + for cell in df[0]: + # print(cell) + if (cell == key): + rowIdx = idxt + break + idxt = idxt + 1 + return df[1][rowIdx] + + +def draw2yLine(NAME, Com, R1, R2, l1, l2, m1, m2, fname): + fig, ax1 = plt.subplots(figsize=(10, 6.4)) + lines = [None] * 2 + # ax1.set_ylim(0, 1) + print(Com) + print(R1) + lines[0], = ax1.plot(Com, R1, color=LINE_COLORS[0], \ + linewidth=LINE_WIDTH, marker=MARKERS[0], \ + markersize=MARKER_SIZE + # + ) + + # plt.show() + ax1.set_ylabel(m1, fontproperties=LABEL_FP) + ax1.set_xlabel(NAME, fontproperties=LABEL_FP) + # ax1.set_xticklabels(ax1.get_xticklabels()) # 设置共用的x轴 + plt.xticks(rotation=0, size=TICK_FONT_SIZE) + plt.yticks(rotation=0, size=TICK_FONT_SIZE) + plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号 + ax2 = ax1.twinx() + + # ax2.set_ylabel('latency/us') + # ax2.set_ylim(0, 0.5) + lines[1], = ax2.plot(Com, R2, color=LINE_COLORS[1], \ + linewidth=LINE_WIDTH, marker=MARKERS[1], \ + markersize=MARKER_SIZE) + + ax2.set_ylabel(m2, fontproperties=LABEL_FP) + # ax2.vlines(192000, min(R2)-1, max(R2)+1, colors = "GREEN", linestyles = "dashed",label='total L1 size') + # plt.grid(axis='y', color='gray') + + # style = dict(size=10, color='black') + # ax2.hlines(tset, 0, x2_list[len(x2_list)-1]+width, colors = "r", linestyles = "dashed",label="tset") + # ax2.text(4, tset, "$T_{set}$="+str(tset)+"us", ha='right', **style) + + # plt.xlabel('batch', fontproperties=LABEL_FP) + + # plt.xscale('log') + # figure.xaxis.set_major_locator(LinearLocator(5)) + ax1.yaxis.set_major_locator(LinearLocator(5)) + ax2.yaxis.set_major_locator(LinearLocator(5)) + ax1.yaxis.set_major_formatter(mtick.FormatStrFormatter('%.1f')) + ax2.yaxis.set_major_formatter(mtick.FormatStrFormatter('%.1f')) + ax1.yaxis.set_major_formatter(mtick.FormatStrFormatter('%.1f')) + ax2.yaxis.set_major_formatter(mtick.FormatStrFormatter('%.1f')) + plt.legend(lines, + [l1, l2], + prop=LEGEND_FP, + loc='upper center', + ncol=1, + bbox_to_anchor=(0.55, 1.3 + ), shadow=False, + columnspacing=0.1, + frameon=True, borderaxespad=-1.5, handlelength=1.2, + handletextpad=0.1, + labelspacing=0.1) + plt.yticks(rotation=0, size=TICK_FONT_SIZE) + plt.tight_layout() + + plt.savefig(fname + ".pdf") diff --git a/benchmark/scripts/scanBatchSizeSingleThreadStream2S/accuBar.py b/benchmark/scripts/scanBatchSizeSingleThreadStream2S/accuBar.py new file mode 100755 index 00000000..638c8a60 --- /dev/null +++ b/benchmark/scripts/scanBatchSizeSingleThreadStream2S/accuBar.py @@ -0,0 +1,328 @@ +import getopt +import os +import sys + +import matplotlib +import matplotlib.pyplot as plt +import numpy as np +import pylab +from matplotlib.font_manager import FontProperties +from matplotlib.ticker import LinearLocator, LogLocator, MaxNLocator, ScalarFormatter +from numpy import double + +OPT_FONT_NAME = 'Helvetica' +TICK_FONT_SIZE = 24 +LABEL_FONT_SIZE = 28 +LEGEND_FONT_SIZE = 26 +TITLE_FRONT_SIZE = 32 +LABEL_FP = FontProperties(style='normal', size=LABEL_FONT_SIZE) +LEGEND_FP = FontProperties(style='normal', size=LEGEND_FONT_SIZE) +TICK_FP = FontProperties(style='normal', size=TICK_FONT_SIZE) +TITLE_FP = FontProperties(style='normal', size=TITLE_FRONT_SIZE) +MARKERS = (['o', 's', 'v', "^", "h", "v", ">", "x", "d", "<", "|", "", "|", "_"]) +# you may want to change the color map for different figures +COLOR_MAP = ('#B03A2E', '#2874A6', '#239B56', '#7D3C98', '#F1C40F', '#F5CBA7', '#82E0AA', '#AEB6BF', '#AA4499') +# you may want to change the patterns for different figures +PATTERNS = (["\\", "///", "o", "||", "\\\\", "\\\\", "//////", "//////", ".", "\\\\\\", "\\\\\\"]) +LABEL_WEIGHT = 'bold' +LINE_COLORS = COLOR_MAP +LINE_WIDTH = 3.0 +MARKER_SIZE = 0.0 +MARKER_FREQUENCY = 1000 + +matplotlib.rcParams['ps.useafm'] = True +matplotlib.rcParams['pdf.use14corefonts'] = True +matplotlib.rcParams['xtick.labelsize'] = TICK_FONT_SIZE +matplotlib.rcParams['ytick.labelsize'] = TICK_FONT_SIZE +matplotlib.rcParams['font.family'] = OPT_FONT_NAME +matplotlib.rcParams['pdf.fonttype'] = 42 + +exp_dir = "/data1/xtra" + +FIGURE_FOLDER = exp_dir + '/results/figure' + + +# there are some embedding problems if directly exporting the pdf figure using matplotlib. +# so we generate the eps format first and convert it to pdf. +def ConvertEpsToPdf(dir_filename): + os.system("epstopdf --outfile " + dir_filename + ".pdf " + dir_filename + ".eps") + os.system("rm -rf " + dir_filename + ".eps") + + +class ScalarFormatterForceFormat(ScalarFormatter): + def _set_format(self): # Override function that finds format to use. + self.format = "%1.1f" # Give format here + + +# draw a line chart +def DrawFigure(x_values, y_values, legend_labels, x_label, y_label, filename, allow_legend, title): + # you may change the figure size on your own. + + fig = plt.figure(figsize=(16, 9)) + figure = fig.add_subplot(111) + + FIGURE_LABEL = legend_labels + + # if not os.path.exists(FIGURE_FOLDER): + # os.makedirs(FIGURE_FOLDER) + + # values in the x_xis + index = np.arange(len(x_values)) + # the bar width. + # you may need to tune it to get the best figure. + width = 0.5 + # draw the bars + bottom_base = np.zeros(len(y_values[0])) + bars = [None] * (len(FIGURE_LABEL)) + for i in range(len(y_values)): + bars[i] = plt.bar(index + width / 2, y_values[i], width, hatch=PATTERNS[i], color=LINE_COLORS[i], + label=FIGURE_LABEL[i], bottom=bottom_base, edgecolor='black', linewidth=3) + bottom_base = np.array(y_values[i]) + bottom_base + + # sometimes you may not want to draw legends. + if allow_legend == True: + plt.legend(bars, FIGURE_LABEL + # mode='expand', + # shadow=False, + # columnspacing=0.25, + # labelspacing=-2.2, + # borderpad=5, + # bbox_transform=ax.transAxes, + # frameon=False, + # columnspacing=5.5, + # handlelength=2, + ) + if allow_legend == True: + handles, labels = figure.get_legend_handles_labels() + if allow_legend == True: + leg = plt.legend(handles[::-1], labels[::-1], + loc='upper center', + # prop=#LEGEND_FP, + # ncol=3, + # bbox_to_anchor=(0.5, 1.25), + # bbox_to_anchor=(1.17, 0.5), + # handletextpad=0.1, + # borderaxespad=0.0, + # handlelength=1.8, + # labelspacing=0.3, + # columnspacing=0.3, + ) + leg.get_frame().set_linewidth(2) + leg.get_frame().set_edgecolor("black") + + # you may need to tune the xticks position to get the best figure. + plt.xticks(index + 0.6 * width, x_values) + yfmt = ScalarFormatterForceFormat() + yfmt.set_powerlimits((0, 0)) + figure.get_yaxis().set_major_formatter(yfmt) + plt.ticklabel_format(axis="y", style="sci", scilimits=(0, 0), useMathText=True) + plt.grid(axis='y', color='gray') + figure.yaxis.set_major_locator(LinearLocator(3)) + # figure.yaxis.set_major_locator(LogLocator(base=10)) + # figure.yaxis.set_major_locator(LinearLocator(6)) + + figure.get_xaxis().set_tick_params(direction='in', pad=10) + figure.get_yaxis().set_tick_params(direction='in', pad=10) + + plt.xlabel(x_label, fontproperties=LABEL_FP) + plt.ylabel(y_label, fontproperties=LABEL_FP) + + size = fig.get_size_inches() + dpi = fig.get_dpi() + plt.title(title, fontproperties=TITLE_FP) + plt.savefig(filename + ".pdf", bbox_inches='tight', format='pdf') + + +def DrawLegend(legend_labels, filename): + fig = pylab.figure() + ax1 = fig.add_subplot(111) + FIGURE_LABEL = legend_labels + LEGEND_FP = FontProperties(style='normal', size=26) + + bars = [None] * (len(FIGURE_LABEL)) + data = [1] + x_values = [1] + + width = 0.3 + for i in range(len(FIGURE_LABEL)): + bars[i] = ax1.bar(x_values, data, width, hatch=PATTERNS[i], color=LINE_COLORS[i], + linewidth=0.2) + + # LEGEND + figlegend = pylab.figure(figsize=(11, 0.5)) + figlegend.legend(bars, FIGURE_LABEL, prop=LEGEND_FP, \ + loc=9, + bbox_to_anchor=(0, 0.4, 1, 1), + ncol=len(FIGURE_LABEL), mode="expand", shadow=False, \ + frameon=False, handlelength=1.1, handletextpad=0.2, columnspacing=0.1) + + figlegend.savefig(FIGURE_FOLDER + '/' + filename + '.pdf') + + +def normalize(y_values): + y_total_values = np.zeros(len(y_values[0])) + + for i in range(len(y_values)): + y_total_values += np.array(y_values[i]) + y_norm_values = [] + + for i in range(len(y_values)): + y_norm_values.append(np.array(y_values[i]) / (y_total_values) * 100) + return y_norm_values + + +# example for reading csv file +def ReadFile(id): + # Creates a list containing w lists, each of h items, all set to 0 + w, h = 5, 3 + y = [[0 for x in range(w)] for y in range(h)] + # print(matches) + max_value = 0 + j = 0 + bound = id + 1 * w + for i in range(id, bound, 1): + cnt = 0 + print(i) + f = open(exp_dir + "/results/breakdown/PMJ_JBCR_NP_{}.txt".format(i), "r") + read = f.readlines() + others = 0 + for x in read: + value = double(x.strip("\n")) + if value > max_value: + max_value = value + elif cnt == 3: # sort + y[0][j] = value + elif cnt == 4: # merge + y[1][j] = value + elif cnt == 5: # join + y[2][j] = value + else: + others += value + # if cnt == 6: + # y[2][j] = others + cnt += 1 + j += 1 + print(y) + return y, max_value + + +if __name__ == "__main__": + id = 119 + try: + opts, args = getopt.getopt(sys.argv[1:], '-i:h', ['test id', 'help']) + except getopt.GetoptError: + print('breakdown.py -id testid') + sys.exit(2) + for opt, opt_value in opts: + if opt in ('-h', '--help'): + print("[*] Help info") + exit() + elif opt == '-i': + print('Test ID:', opt_value) + id = (int)(opt_value) + + x_values = ['10%', '20%', '30%', '40%', '50%'] # sorting step size + + y_values, max_value = ReadFile(id) # 55 + + # y_norm_values = normalize(y_values) + + # break into 4 parts + legend_labels = ['sort', 'merge', 'join'] # , 'others' + + DrawFigure(x_values, y_values, legend_labels, + 'sorting step size', 'cycles per input tuple', + 'breakdown_sort_figure', True) + + # DrawLegend(legend_labels, 'breakdown_radix_legend') + + +def DrawPercentageFigure(x_values, y_values, legend_labels, x_label, y_label, filename, allow_legend, title): + # you may change the figure size on your own. + fig = plt.figure(figsize=(9, 3)) + figure = fig.add_subplot(111) + + FIGURE_LABEL = legend_labels + + # values in the x_xis + index = np.arange(len(x_values)) + # the bar width. + # you may need to tune it to get the best figure. + width = 0.5 + # draw the bars + bottom_base = np.zeros(len(y_values[0])) + bars = [None] * (len(FIGURE_LABEL)) + for i in range(len(y_values)): + bars[i] = plt.bar(index + width / 2, y_values[i], width, hatch=PATTERNS[i], color=LINE_COLORS[i], + label=FIGURE_LABEL[i], bottom=bottom_base, edgecolor='black', linewidth=3) + bottom_base = np.array(y_values[i]) + bottom_base + + # sometimes you may not want to draw legends. + if allow_legend == True: + plt.legend(bars, FIGURE_LABEL + # mode='expand', + # shadow=False, + # columnspacing=0.25, + # labelspacing=-2.2, + # borderpad=5, + # bbox_transform=ax.transAxes, + # frameon=False, + # columnspacing=5.5, + # handlelength=2, + ) + if allow_legend == True: + handles, labels = figure.get_legend_handles_labels() + if allow_legend == True: + print(handles[::-1], labels[::-1]) + leg = plt.legend(handles[::-1], labels[::-1], + loc='center', + prop=LEGEND_FP, + ncol=3, + bbox_to_anchor=(0.5, 1.2), + handletextpad=0.1, + borderaxespad=0.0, + handlelength=1.8, + labelspacing=0.3, + columnspacing=0.3, + ) + leg.get_frame().set_linewidth(2) + leg.get_frame().set_edgecolor("black") + + # sometimes you may not want to draw legends. + # if allow_legend == True: + # leg=plt.legend(bars, + # FIGURE_LABEL, + # prop=LEGEND_FP, + # loc='right', + # ncol=1, + # # mode='expand', + # bbox_to_anchor=(0.45, 1.1), shadow=False, + # columnspacing=0.1, + # frameon=True, borderaxespad=0.0, handlelength=1.5, + # handletextpad=0.1, + # labelspacing=0.1) + # leg.get_frame().set_linewidth(2) + # leg.get_frame().set_edgecolor("black") + + plt.ylim(0, 100) + + # you may need to tune the xticks position to get the best figure. + plt.xticks(index + 0.5 * width, x_values) + plt.xticks(rotation=30) + + # plt.xlim(0,) + # plt.ylim(0,1) + + plt.grid(axis='y', color='gray') + figure.yaxis.set_major_locator(LinearLocator(6)) + + figure.get_xaxis().set_tick_params(direction='in', pad=10) + figure.get_yaxis().set_tick_params(direction='in', pad=10) + + plt.xlabel(x_label, fontproperties=LABEL_FP) + plt.ylabel(y_label, fontproperties=LABEL_FP) + + size = fig.get_size_inches() + dpi = fig.get_dpi() + + plt.savefig(filename + ".pdf", bbox_inches='tight', format='pdf') diff --git a/benchmark/scripts/scanBatchSizeSingleThreadStream2S/autoParase.py b/benchmark/scripts/scanBatchSizeSingleThreadStream2S/autoParase.py new file mode 100755 index 00000000..9375c8e0 --- /dev/null +++ b/benchmark/scripts/scanBatchSizeSingleThreadStream2S/autoParase.py @@ -0,0 +1,87 @@ +import csv + + +def paraseValidStageNames(a): + nameList = [] + with open(a, 'r') as f: + reader = csv.reader(f) + # reader = [each for each in csv.DictReader(f, delimiter=',')] + result = list(reader) + rows = len(result) + # print('rows=',rows) + firstRow = result[0] + # print(firstRow) + index = 0 + # define what may attract our interest + idxCpu = 0 + idxName = 0 + for i in firstRow: + # print(i) + if (i == 'cpu'): + idxCpu = index + if (i == 'name'): + idxName = index + index = index + 1 + # read the valid stages + vdataEntries = 0 + + for k in range(1, rows): + if (result[k][idxCpu] != 'NA'): + R1 = ((result[k][idxName])) + nameList.append(R1) + return nameList + + +def paraseValidColums(a, nameList, colTitle): + with open(a, 'r') as f: + reader = csv.reader(f) + # reader = [each for each in csv.DictReader(f, delimiter=',')] + result = list(reader) + rows = len(result) + # print('rows=',rows) + firstRow = result[0] + # print(firstRow) + index = 0 + # define what may attract our interest + idxCpu = 0 + idxName = 0 + idxTitle = 0 + for i in firstRow: + # print(i) + if (i == 'cpu'): + idxCpu = index + if (i == 'name'): + idxName = index + if (i == colTitle): + idxTitle = index + index = index + 1 + # read the valid stages + vdataEntries = 0 + ru = [] + for k in range(1, rows): + if (result[k][idxCpu] != 'NA'): + R1 = ((result[k][idxName])) + for j in range(len(nameList)): + if (R1 == nameList[j]): + s = int(result[k][idxTitle]) + ru.append(s) + break + return ru + + +def maxInList(a): + # a in [[1,2] [3,4]] + inLen = len(a[0]) + ru = [] + index = [] + ti = 0 + for i in range(len(a[0])): + ts = 0 + ti = 0 + for k in range(len(a)): + if (a[k][i] > ts): + ts = a[k][i] + ti = k + ru.append(ts) + index.append(ti) + return ru, index diff --git a/benchmark/scripts/scanBatchSizeSingleThreadStream2S/config_BCoAMM.csv b/benchmark/scripts/scanBatchSizeSingleThreadStream2S/config_BCoAMM.csv new file mode 100644 index 00000000..b65317af --- /dev/null +++ b/benchmark/scripts/scanBatchSizeSingleThreadStream2S/config_BCoAMM.csv @@ -0,0 +1,6 @@ +key,value,type +aRow,100,U64 +aCol,1000,U64 +bCol,500,U64 +sketchDimension,25,U64 +ptFile,torchscripts/BetaCoOccurringFD.pt,String \ No newline at end of file diff --git a/benchmark/scripts/scanBatchSizeSingleThreadStream2S/config_BerCRS.csv b/benchmark/scripts/scanBatchSizeSingleThreadStream2S/config_BerCRS.csv new file mode 100644 index 00000000..d2b758a5 --- /dev/null +++ b/benchmark/scripts/scanBatchSizeSingleThreadStream2S/config_BerCRS.csv @@ -0,0 +1,6 @@ +key,value,type +aRow,100,U64 +aCol,1000,U64 +bCol,500,U64 +sketchDimension,25,U64 +ptFile,torchscripts/BernoulliCRS.pt,String \ No newline at end of file diff --git a/benchmark/scripts/scanBatchSizeSingleThreadStream2S/config_CPPBCOOFD.csv b/benchmark/scripts/scanBatchSizeSingleThreadStream2S/config_CPPBCOOFD.csv new file mode 100644 index 00000000..d43c92f9 --- /dev/null +++ b/benchmark/scripts/scanBatchSizeSingleThreadStream2S/config_CPPBCOOFD.csv @@ -0,0 +1,10 @@ +key,value,type +aRow,100,U64 +aCol,1000,U64 +bCol,500,U64 +sketchDimension,25,U64 +ptFile,torchscripts/BetaCoOccurringFD.pt,String +useCPP,1,U64 +cppAlgoTag,bcooFD,String +threads,1,U64 +forceMP,1,U64 diff --git a/benchmark/scripts/scanBatchSizeSingleThreadStream2S/config_CPPBCRS.csv b/benchmark/scripts/scanBatchSizeSingleThreadStream2S/config_CPPBCRS.csv new file mode 100644 index 00000000..3b7ee42c --- /dev/null +++ b/benchmark/scripts/scanBatchSizeSingleThreadStream2S/config_CPPBCRS.csv @@ -0,0 +1,10 @@ +key,value,type +aRow,100,U64 +aCol,1000,U64 +bCol,500,U64 +sketchDimension,25,U64 +ptFile,torchscripts/BernoulliCRS.pt,String +useCPP,1,U64 +cppAlgoTag,bcrs,String +threads,1,U64 +forceMP,1,U64 diff --git a/benchmark/scripts/scanBatchSizeSingleThreadStream2S/config_CPPCOFD.csv b/benchmark/scripts/scanBatchSizeSingleThreadStream2S/config_CPPCOFD.csv new file mode 100644 index 00000000..cf8f066a --- /dev/null +++ b/benchmark/scripts/scanBatchSizeSingleThreadStream2S/config_CPPCOFD.csv @@ -0,0 +1,10 @@ +key,value,type +aRow,100,U64 +aCol,1000,U64 +bCol,500,U64 +sketchDimension,25,U64 +ptFile,torchscripts/CRS.pt,String +useCPP,1,U64 +cppAlgoTag,cooFD,String +threads,1,U64 +forceMP,1,U64 \ No newline at end of file diff --git a/benchmark/scripts/scanBatchSizeSingleThreadStream2S/config_CPPCOUNTERSKETCH.csv b/benchmark/scripts/scanBatchSizeSingleThreadStream2S/config_CPPCOUNTERSKETCH.csv new file mode 100644 index 00000000..6823e4e2 --- /dev/null +++ b/benchmark/scripts/scanBatchSizeSingleThreadStream2S/config_CPPCOUNTERSKETCH.csv @@ -0,0 +1,10 @@ +key,value,type +aRow,100,U64 +aCol,1000,U64 +bCol,500,U64 +sketchDimension,25,U64 +ptFile,torchscripts/CRS.pt,String +useCPP,1,U64 +cppAlgoTag,countSketch,String +threads,1,U64 +forceMP,1,U64 diff --git a/benchmark/scripts/scanBatchSizeSingleThreadStream2S/config_CPPCRS.csv b/benchmark/scripts/scanBatchSizeSingleThreadStream2S/config_CPPCRS.csv new file mode 100644 index 00000000..46d9aae3 --- /dev/null +++ b/benchmark/scripts/scanBatchSizeSingleThreadStream2S/config_CPPCRS.csv @@ -0,0 +1,14 @@ +key,value,type +aRow,1000,U64 +aCol,1000,U64 +bCol,1000,U64 +sketchDimension,25,U64 +ptFile,torchscripts/CRS.pt,String +useCPP,1,U64 +cppAlgoTag,crs,String +threads,1,U64 +forceMP,1,U64 +eventRateTps,200,U64 +isStreaming,1,U64 +streamingTwoMatrixes,1,U64 +batchSize,1,U64 \ No newline at end of file diff --git a/benchmark/scripts/scanBatchSizeSingleThreadStream2S/config_CPPINT8.csv b/benchmark/scripts/scanBatchSizeSingleThreadStream2S/config_CPPINT8.csv new file mode 100644 index 00000000..2b61c43e --- /dev/null +++ b/benchmark/scripts/scanBatchSizeSingleThreadStream2S/config_CPPINT8.csv @@ -0,0 +1,10 @@ +key,value,type +aRow,100,U64 +aCol,1000,U64 +bCol,500,U64 +sketchDimension,25,U64 +ptFile,torchscripts/FDAMM.pt,String +useCPP,1,U64 +cppAlgoTag,int8,String +threads,1,U64 +forceMP,1,U64 diff --git a/benchmark/scripts/scanBatchSizeSingleThreadStream2S/config_CPPMM.csv b/benchmark/scripts/scanBatchSizeSingleThreadStream2S/config_CPPMM.csv new file mode 100644 index 00000000..31ac118f --- /dev/null +++ b/benchmark/scripts/scanBatchSizeSingleThreadStream2S/config_CPPMM.csv @@ -0,0 +1,14 @@ +key,value,type +aRow,1000,U64 +aCol,1000,U64 +bCol,1000,U64 +sketchDimension,25,U64 +ptFile,torchscripts/RAWMM.pt,String +useCPP,1,U64 +cppAlgoTag,mm,String +threads,1,U64 +forceMP,1,U64 +eventRateTps,200,U64 +isStreaming,1,U64 +streamingTwoMatrixes,1,U64 +batchSize,1,U64 \ No newline at end of file diff --git a/benchmark/scripts/scanBatchSizeSingleThreadStream2S/config_CPPSMPPCA.csv b/benchmark/scripts/scanBatchSizeSingleThreadStream2S/config_CPPSMPPCA.csv new file mode 100644 index 00000000..7f8d0868 --- /dev/null +++ b/benchmark/scripts/scanBatchSizeSingleThreadStream2S/config_CPPSMPPCA.csv @@ -0,0 +1,12 @@ +key,value,type +aRow,1000,U64 +aCol,1000,U64 +bCol,1000,U64 +sketchDimension,25,U64 +threads,1,U64 +useCPP,1,U64 +cppAlgoTag,smp-pca,String +eventRateTps,200,U64 +isStreaming,1,U64 +streamingTwoMatrixes,1,U64 +batchSize,1,U64 \ No newline at end of file diff --git a/benchmark/scripts/scanBatchSizeSingleThreadStream2S/config_CoAMM.csv b/benchmark/scripts/scanBatchSizeSingleThreadStream2S/config_CoAMM.csv new file mode 100644 index 00000000..f41d7ddd --- /dev/null +++ b/benchmark/scripts/scanBatchSizeSingleThreadStream2S/config_CoAMM.csv @@ -0,0 +1,6 @@ +key,value,type +aRow,100,U64 +aCol,1000,U64 +bCol,500,U64 +sketchDimension,25,U64 +ptFile,torchscripts/CoOccurringFD.pt,String \ No newline at end of file diff --git a/benchmark/scripts/scanBatchSizeSingleThreadStream2S/config_CounterSketch.csv b/benchmark/scripts/scanBatchSizeSingleThreadStream2S/config_CounterSketch.csv new file mode 100644 index 00000000..50cf2770 --- /dev/null +++ b/benchmark/scripts/scanBatchSizeSingleThreadStream2S/config_CounterSketch.csv @@ -0,0 +1,6 @@ +key,value,type +aRow,100,U64 +aCol,1000,U64 +bCol,500,U64 +sketchDimension,25,U64 +ptFile,torchscripts/CountSketch.pt,String diff --git a/benchmark/scripts/scanBatchSizeSingleThreadStream2S/drawTogether.py b/benchmark/scripts/scanBatchSizeSingleThreadStream2S/drawTogether.py new file mode 100755 index 00000000..a49dc8c9 --- /dev/null +++ b/benchmark/scripts/scanBatchSizeSingleThreadStream2S/drawTogether.py @@ -0,0 +1,179 @@ +#!/usr/bin/env python3 +#I will streaming A and B!!! +import csv +import numpy as np +import accuBar as accuBar +import groupBar as groupBar +import groupBar2 as groupBar2 +import groupLine as groupLine +from autoParase import * +import itertools as it +import os + +import matplotlib +import numpy as np +import pylab +import matplotlib.font_manager as fm +from matplotlib.font_manager import FontProperties +from matplotlib.ticker import LogLocator, LinearLocator +import os +import pandas as pd +import sys +from OoOCommon import * +import time + +# OPT_FONT_NAME = 'Helvetica' +TICK_FONT_SIZE = 22 +LABEL_FONT_SIZE = 28 +LEGEND_FONT_SIZE = 30 +LABEL_FP = FontProperties(style='normal', size=LABEL_FONT_SIZE) +LEGEND_FP = FontProperties(style='normal', size=LEGEND_FONT_SIZE) +TICK_FP = FontProperties(style='normal', size=TICK_FONT_SIZE) + +MARKERS = (['*', '|', 'v', "^", "", "h", "<", ">", "+", "d", "<", "|", "", "+", "_"]) +# you may want to change the color map for different figures +COLOR_MAP = ( + '#B03A2E', '#2874A6', '#239B56', '#7D3C98', '#FFFFFF', '#F1C40F', '#F5CBA7', '#82E0AA', '#AEB6BF', '#AA4499') +# you may want to change the patterns for different figures +PATTERNS = (["////", "o", "", "||", "-", "//", "\\", "o", "O", "////", ".", "|||", "o", "---", "+", "\\\\", "*"]) +LABEL_WEIGHT = 'bold' +LINE_COLORS = COLOR_MAP +LINE_WIDTH = 3.0 +MARKER_SIZE = 15.0 +MARKER_FREQUENCY = 1000 + +matplotlib.rcParams['ps.useafm'] = True +matplotlib.rcParams['pdf.use14corefonts'] = True +matplotlib.rcParams['xtick.labelsize'] = TICK_FONT_SIZE +matplotlib.rcParams['ytick.labelsize'] = TICK_FONT_SIZE +matplotlib.rcParams['font.family'] = OPT_FONT_NAME +matplotlib.rcParams['pdf.fonttype'] = 42 + +scanTag = "batchSize" + + +def singleRun(exePath, singleValue, resultPath, configTemplate): + # resultFolder="singleValueTests" + configFname = "config_" + scanTag + str(singleValue) + ".csv" + # configTemplate = "config.csv" + # clear old files + os.system("cd " + exePath + "&& sudo rm *.csv") + + # editConfig(configTemplate, exePath + configFname, "earlierEmitMs", 0) + editConfig(configTemplate, exePath + configFname, scanTag, singleValue) + # prepare new file + # run + os.system("cd " + exePath + "&& sudo ./benchmark " + configFname) + # copy result + os.system("sudo rm -rf " + resultPath + "/" + str(singleValue)) + os.system("sudo mkdir " + resultPath + "/" + str(singleValue)) + os.system("cd " + exePath + "&& sudo cp *.csv " + resultPath + "/" + str(singleValue)) + + +def runScanVector(exePath, singleValueVec, resultPath, templateName="config.csv"): + for i in singleValueVec: + singleRun(exePath, i, resultPath, templateName) + + +def readResultSingle(singleValue, resultPath): + resultFname = resultPath + "/" + str(singleValue) + "/result_streaming.csv" + throughput = readConfig(resultFname, "throughputByElements") + lat95 = readConfig(resultFname, "95%latency") + froError = readConfig(resultFname, "froError") + errorBoundRatio = readConfig(resultFname, "errorBoundRatio") + return throughput,lat95, froError, errorBoundRatio + + +def cleanPath(path): + os.system("sudo rm -rf " + path) + os.system("sudo mkdir " + path) + + +def readResultVector(singleValueVec, resultPath): + thrVec = [] + lat95Vec= [] + froErrorVec = [] + errorBoundRatioVec = [] + for i in singleValueVec: + thr,lat95,froError, errorBoundRatio = readResultSingle(i, resultPath) + thrVec.append(float(thr)) + lat95Vec.append(float(lat95)/1000.0) + froErrorVec.append(float(froError)) + errorBoundRatioVec.append(float(errorBoundRatio)) + return np.array(thrVec), np.array(lat95Vec), np.array(froErrorVec), np.array( + errorBoundRatioVec) + + +def compareMethod(exeSpace, commonPathBase, resultPaths, csvTemplates, periodVec, reRun=1): + thrAll = [] + lat95All = [] + periodAll = [] + froAll = [] + errorBoundRatioAll = [] + for i in range(len(csvTemplates)): + resultPath = commonPathBase + resultPaths[i] + if (reRun == 1): + os.system("sudo rm -rf " + resultPath) + os.system("sudo mkdir " + resultPath) + runScanVector(exeSpace, periodVec, resultPath, csvTemplates[i]) + thr,lat95,fro, eb = readResultVector(periodVec, resultPath) + thrAll.append(thr) + lat95All.append(lat95) + + periodAll.append(periodVec) + + froAll.append(fro) + errorBoundRatioAll.append(eb) + # periodAll.append(periodVec) + return np.array(thrAll),np.array(lat95All), periodAll, np.array(froAll), np.array(errorBoundRatioAll) + + +def main(): + exeSpace = os.path.abspath(os.path.join(os.getcwd(), "../..")) + "/" + commonBase = os.path.abspath(os.path.join(os.getcwd(), "../..")) + "/results/" + scanTag+"2S" + "/" + figPath = os.path.abspath(os.path.join(os.getcwd(), "../..")) + "/figures/" + scanTag+"2s" + "CPP" + methodTags = ["CRS", "MM","SMP-PCA"] + resultPaths = ["CRS", "mm","smp-pca"] + csvTemplates = ["config_CPPCRS.csv", "config_CPPMM.csv","config_CPPSMPPCA.csv"] + valueVec = [10,20,50,100,200,500] + valueVecDisp = np.array(valueVec) + # run + reRun = 0 + if (len(sys.argv) < 2): + os.system("mkdir ../../results") + os.system("mkdir ../../figures") + os.system("mkdir " + figPath) + os.system("sudo rm -rf " + commonBase) + os.system("sudo mkdir " + commonBase) + reRun = 1 + # skech + thrAll, lat95All, periodAll, fro, eb = compareMethod(exeSpace, commonBase, resultPaths, csvTemplates, valueVec, + reRun) + groupLine.DrawFigureYnormal(periodAll, thrAll/1000.0, + methodTags, + "batch size (#rows)", "throughput (K elements/s)", 0, 1, + figPath + "/" + scanTag + "stream_cpp_thr", + True) + groupLine.DrawFigure(periodAll, lat95All, + methodTags, + "batch size (#rows)", "95% latency (ms)", 0, 1, + figPath + "/" + scanTag + "stream_cpp_lat95", + True) + groupLine.DrawFigureYnormal(periodAll, fro*100.0, + methodTags, + "batch size (#rows)", "fro error (%)", 0, 1, + figPath + "/" + scanTag + "stream_cpp_fro", + True) + + # draw2yLine("watermark time (ms)",singleValueVecDisp,lat95Vec,errVec,"95% Latency (ms)","Error","ms","",figPath+"wm_lat") + # draw2yLine("watermark time (ms)",singleValueVecDisp,thrVec,errVec,"Throughput (KTp/s)","Error","KTp/s","",figPath+"wm_thr") + # draw2yLine("watermark time (ms)",singleValueVecDisp,lat95Vec,compVec,"95% Latency (ms)","Completeness","ms","",figPath+"wm_omp") + # groupLine.DrawFigureYnormal([singleValueVec,singleValueVec],[errVec,aqpErrVec],['w/o aqp',"w/ MeanAqp"],"watermark time (ms)","Error",0,1,figPath+"wm_MeanAqp",True) + # print(errVec) + # print(aqpErrVec) + # print(elapseTimeVecFD) + # readResultsingleValue(50,resultPath) + + +if __name__ == "__main__": + main() diff --git a/benchmark/scripts/scanBatchSizeSingleThreadStream2S/groupBar.py b/benchmark/scripts/scanBatchSizeSingleThreadStream2S/groupBar.py new file mode 100755 index 00000000..ef2d9842 --- /dev/null +++ b/benchmark/scripts/scanBatchSizeSingleThreadStream2S/groupBar.py @@ -0,0 +1,236 @@ +import itertools as it +import os + +import matplotlib +import matplotlib.pyplot as plt +import numpy as np +import pylab +from matplotlib.font_manager import FontProperties +from matplotlib.ticker import LogLocator, LinearLocator + +OPT_FONT_NAME = 'Helvetica' +TICK_FONT_SIZE = 24 +LABEL_FONT_SIZE = 28 +LEGEND_FONT_SIZE = 15 +LABEL_FP = FontProperties(style='normal', size=LABEL_FONT_SIZE) +LEGEND_FP = FontProperties(style='normal', size=LEGEND_FONT_SIZE) +TICK_FP = FontProperties(style='normal', size=TICK_FONT_SIZE) + +MARKERS = (["", 'o', 's', 'v', "^", "", "h", "<", ">", "+", "d", "<", "|", "", "+", "_"]) +# you may want to change the color map for different figures +COLOR_MAP = ( + '#7FFFFF', '#B03A2E', '#2874A6', '#FFFFFF', '#7FFFFF', '#B03A2E', '#2874A6', '#FFFFFF', '#F5CBA7', '#82E0AA', + '#AEB6BF', + '#AA4499') +# you may want to change the patterns for different figures +PATTERNS = ( + ["////", "\\\\", "//", "o", "*", "||", "-", "//", "\\", "o", "O", "////", ".", "|||", "o", "---", "+", "\\\\", "*"]) +LABEL_WEIGHT = 'bold' +LINE_COLORS = COLOR_MAP +LINE_WIDTH = 3.0 +MARKER_SIZE = 15.0 +MARKER_FREQUENCY = 1000 + +matplotlib.rcParams['ps.useafm'] = True +matplotlib.rcParams['pdf.use14corefonts'] = True +matplotlib.rcParams['xtick.labelsize'] = TICK_FONT_SIZE +matplotlib.rcParams['ytick.labelsize'] = TICK_FONT_SIZE +matplotlib.rcParams['font.family'] = OPT_FONT_NAME +matplotlib.rcParams['pdf.fonttype'] = 42 + +exp_dir = "/data1/xtra" + +FIGURE_FOLDER = exp_dir + '/results/figure' + + +def DrawLegend(legend_labels, filename): + fig = pylab.figure() + ax1 = fig.add_subplot(111) + FIGURE_LABEL = legend_labels + LEGEND_FP = FontProperties(style='normal', size=26) + figlegend = pylab.figure(figsize=(16, 0.5)) + bars = [None] * (len(FIGURE_LABEL)) + data = [1] + x_values = [1] + + width = 0.3 + for i in range(len(FIGURE_LABEL)): + bars[i] = ax1.bar(x_values, data, width, + hatch=PATTERNS[i], + color=LINE_COLORS[i], + label=FIGURE_LABEL[i], + edgecolor='black', linewidth=3) + + # LEGEND + + figlegend.legend(bars, FIGURE_LABEL, prop=LEGEND_FP, \ + loc=1, ncol=len(FIGURE_LABEL), mode="expand", shadow=True, \ + frameon=True, handlelength=2, handletextpad=0.3, columnspacing=0.5, + borderaxespad=-0.2, fancybox=True + ) + figlegend.savefig(FIGURE_FOLDER + '/' + filename + '.pdf') + + +# draw a bar chart +def DrawFigure(x_values, y_values, legend_labels, x_label, y_label, y_min, y_max, filename, allow_legend): + # you may change the figure size on your own. + fig = plt.figure(figsize=(10, 3)) + figure = fig.add_subplot(111) + + FIGURE_LABEL = legend_labels + + # values in the x_xis + index = np.arange(len(x_values)) + # the bar width. + # you may need to tune it to get the best figure. + width = 0.08 + # draw the bars + bars = [] + ts = 0 + pos = 0 + gl = len(y_values[0]) + for i in range(len(y_values)): + pos = pos + 3 * width + for j in range(len(y_values[i])): + pos = pos + width + bar = plt.bar(pos, y_values[i][j], width, hatch=PATTERNS[j], color=LINE_COLORS[j], label=FIGURE_LABEL[j], + edgecolor='black', linewidth=3) + bars.append(bar) + ts = ts + 1 + + # sometimes you may not want to draw legends. + if allow_legend == True: + plt.legend(bars, FIGURE_LABEL, + prop=LEGEND_FP, + ncol=4, + loc='upper center', + # mode='expand', + shadow=False, + bbox_to_anchor=(0.45, 1.6), + columnspacing=0.1, + handletextpad=0.2, + # bbox_transform=ax.transAxes, + # frameon=True, + # columnspacing=5.5, + # handlelength=2, + ) + + # you may need to tune the xticks position to get the best figure. + plt.xticks(index, x_values) + # plt.ticklabel_format(axis="y", style="sci", scilimits=(0, 0)) + # plt.grid(axis='y', color='gray') + # figure.get_xaxis().set_major_formatter(matplotlib.ticker.ScalarFormatter()) + + # you may need to tune the xticks position to get the best figure. + # plt.yscale('log') + # + # plt.grid(axis='y', color='gray') + figure.yaxis.set_major_locator(LinearLocator(5)) + # figure.xaxis.set_major_locator(LinearLocator(5)) + figure.get_xaxis().set_tick_params(direction='in', pad=10) + figure.get_yaxis().set_tick_params(direction='in', pad=10) + + plt.xlabel(x_label, fontproperties=LABEL_FP) + plt.ylabel(y_label, fontproperties=LABEL_FP) + plt.ylim(y_min, y_max) + plt.savefig(filename + ".pdf", bbox_inches='tight') + + +# example for reading csv file +def ReadFile(): + y = [] + col1 = [] + col2 = [] + col3 = [] + col4 = [] + col5 = [] + col6 = [] + col7 = [] + col8 = [] + col9 = [] + + for id in it.chain(range(38, 42)): + col9.append(0) + y.append(col9) # this is a lz4_pipe2 empty line to separate eager and lazy. + + for id in it.chain(range(38, 42)): + file = exp_dir + '/results/latency/NPJ_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(int(len(read) * 0.95)).strip("\n")) # get the 99th timestamp + col1.append(x) + y.append(col1) + + for id in it.chain(range(38, 42)): + file = exp_dir + '/results/latency/PRJ_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(int(len(read) * 0.95)).strip("\n")) # get the 99th timestamp + col2.append(x) + y.append(col2) + + for id in it.chain(range(38, 42)): + file = exp_dir + '/results/latency/MWAY_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(int(len(read) * 0.95)).strip("\n")) # get the 99th timestamp + col3.append(x) + y.append(col3) + + for id in it.chain(range(38, 42)): + file = exp_dir + '/results/latency/MPASS_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(int(len(read) * 0.95)).strip("\n")) # get the 99th timestamp + col4.append(x) + y.append(col4) + + y.append(col9) # this is a lz4_pipe2 empty line to separate eager and lazy. + + for id in it.chain(range(38, 42)): + file = exp_dir + '/results/latency/SHJ_JM_NP_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(int(len(read) * 0.95)).strip("\n")) # get last timestamp + col5.append(x) + y.append(col5) + + for id in it.chain(range(38, 42)): + file = exp_dir + '/results/latency/SHJ_JBCR_NP_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(int(len(read) * 0.95)).strip("\n")) # get last timestamp + col6.append(x) + y.append(col6) + + for id in it.chain(range(38, 42)): + file = exp_dir + '/results/latency/PMJ_JM_NP_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(int(len(read) * 0.95)).strip("\n")) # get last timestamp + col7.append(x) + y.append(col7) + + for id in it.chain(range(38, 42)): + file = exp_dir + '/results/latency/PMJ_JBCR_NP_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(int(len(read) * 0.95)).strip("\n")) # get last timestamp + col8.append(x) + y.append(col8) + return y + + +if __name__ == "__main__": + x_values = ["Stock", "Rovio", "YSB", "DEBS"] + + y_values = ReadFile() + + legend_labels = ['Lazy:', 'NPJ', 'PRJ', 'MWAY', 'MPASS', + 'Eager:', 'SHJ$^{JM}$', 'SHJ$^{JB}$', 'PMJ$^{JM}$', 'PMJ$^{JB}$'] + print(y_values) + DrawFigure(x_values, y_values, legend_labels, + '', 'Latency (ms)', 0, + 400, 'latency_figure_app', False) + + # DrawLegend(legend_labels, 'latency_legend') diff --git a/benchmark/scripts/scanBatchSizeSingleThreadStream2S/groupBar2.py b/benchmark/scripts/scanBatchSizeSingleThreadStream2S/groupBar2.py new file mode 100755 index 00000000..31155695 --- /dev/null +++ b/benchmark/scripts/scanBatchSizeSingleThreadStream2S/groupBar2.py @@ -0,0 +1,232 @@ +import itertools as it +import os + +import matplotlib +import matplotlib.pyplot as plt +import numpy as np +import pylab +from matplotlib.font_manager import FontProperties +from matplotlib.ticker import LogLocator, LinearLocator + +OPT_FONT_NAME = 'Helvetica' +TICK_FONT_SIZE = 24 +LABEL_FONT_SIZE = 28 +LEGEND_FONT_SIZE = 30 +LABEL_FP = FontProperties(style='normal', size=LABEL_FONT_SIZE) +LEGEND_FP = FontProperties(style='normal', size=LEGEND_FONT_SIZE) +TICK_FP = FontProperties(style='normal', size=TICK_FONT_SIZE) + +MARKERS = (["", 'o', 's', 'v', "^", "", "h", "<", ">", "+", "d", "<", "|", "", "+", "_"]) +# you may want to change the color map for different figures +COLOR_MAP = ( + '#FFFFFF', '#B03A2E', '#2874A6', '#239B56', '#7D3C98', '#00FFFF', '#F1C40F', '#F5CBA7', '#82E0AA', '#AEB6BF', + '#AA4499') +# you may want to change the patterns for different figures +PATTERNS = ( + ["", "////", "\\\\", "//", "o", "", "||", "-", "//", "\\", "o", "O", "////", ".", "|||", "o", "---", "+", "\\\\", + "*"]) +LABEL_WEIGHT = 'bold' +LINE_COLORS = COLOR_MAP +LINE_WIDTH = 3.0 +MARKER_SIZE = 15.0 +MARKER_FREQUENCY = 1000 + +matplotlib.rcParams['ps.useafm'] = True +matplotlib.rcParams['pdf.use14corefonts'] = True +matplotlib.rcParams['xtick.labelsize'] = TICK_FONT_SIZE +matplotlib.rcParams['ytick.labelsize'] = TICK_FONT_SIZE +matplotlib.rcParams['font.family'] = OPT_FONT_NAME +matplotlib.rcParams['pdf.fonttype'] = 42 + +exp_dir = "/data1/xtra" + +FIGURE_FOLDER = exp_dir + '/results/figure' + + +def DrawLegend(legend_labels, filename): + fig = pylab.figure() + ax1 = fig.add_subplot(111) + FIGURE_LABEL = legend_labels + LEGEND_FP = FontProperties(style='normal', size=26) + figlegend = pylab.figure(figsize=(16, 0.5)) + bars = [None] * (len(FIGURE_LABEL)) + data = [1] + x_values = [1] + + width = 0.3 + for i in range(len(FIGURE_LABEL)): + bars[i] = ax1.bar(x_values, data, width, + hatch=PATTERNS[i], + color=LINE_COLORS[i], + label=FIGURE_LABEL[i], + edgecolor='black', linewidth=3) + + # LEGEND + + figlegend.legend(bars, FIGURE_LABEL, prop=LEGEND_FP, \ + loc=1, ncol=len(FIGURE_LABEL), mode="expand", shadow=True, \ + frameon=True, handlelength=2, handletextpad=0.3, columnspacing=0.5, + borderaxespad=-0.2, fancybox=True + ) + figlegend.savefig(FIGURE_FOLDER + '/' + filename + '.pdf') + + +# draw a bar chart +def DrawFigure(x_values, y_values, legend_labels, x_label, y_label, y_min, y_max, filename, allow_legend): + # you may change the figure size on your own. + fig = plt.figure(figsize=(10, 3)) + figure = fig.add_subplot(111) + + FIGURE_LABEL = legend_labels + + # values in the x_xis + index = np.arange(len(x_values)) + # the bar width. + # you may need to tune it to get the best figure. + width = 0.12 + # draw the bars + bars = [None] * (len(FIGURE_LABEL)) + for i in range(len(y_values)): + bars[i] = plt.bar(index + i * width + width / 2, + y_values[i], width, + hatch=PATTERNS[i], + color=LINE_COLORS[i], + label=FIGURE_LABEL[i], edgecolor='black', linewidth=3) + + # sometimes you may not want to draw legends. + if allow_legend == True: + plt.legend(bars, FIGURE_LABEL, + prop=LEGEND_FP, + ncol=3, + loc='upper center', + # mode='expand', + shadow=False, + bbox_to_anchor=(0.45, 1.7), + columnspacing=0.1, + handletextpad=0.2, + # bbox_transform=ax.transAxes, + # frameon=True, + # columnspacing=5.5, + # handlelength=2, + ) + + # you may need to tune the xticks position to get the best figure. + plt.xticks(index + 3 * width, x_values, rotation=30) + + # plt.ticklabel_format(axis="y", style="sci", scilimits=(0, 0)) + # plt.grid(axis='y', color='gray') + # figure.get_xaxis().set_major_formatter(matplotlib.ticker.ScalarFormatter()) + + # you may need to tune the xticks position to get the best figure. + # plt.yscale('log') + # + # plt.grid(axis='y', color='gray') + figure.yaxis.set_major_locator(LinearLocator(10)) + # figure.xaxis.set_major_locator(LinearLocator(5)) + figure.get_xaxis().set_tick_params(direction='in', pad=10) + figure.get_yaxis().set_tick_params(direction='in', pad=10) + + plt.xlabel(x_label, fontproperties=LABEL_FP) + plt.ylabel(y_label, fontproperties=LABEL_FP) + + plt.savefig(filename + ".pdf", bbox_inches='tight') + + +# example for reading csv file +def ReadFile(): + y = [] + col1 = [] + col2 = [] + col3 = [] + col4 = [] + col5 = [] + col6 = [] + col7 = [] + col8 = [] + col9 = [] + + for id in it.chain(range(38, 42)): + col9.append(0) + y.append(col9) # this is a fake empty line to separate eager and lazy. + + for id in it.chain(range(38, 42)): + file = exp_dir + '/results/latency/NPJ_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(int(len(read) * 0.95)).strip("\n")) # get the 99th timestamp + col1.append(x) + y.append(col1) + + for id in it.chain(range(38, 42)): + file = exp_dir + '/results/latency/PRJ_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(int(len(read) * 0.95)).strip("\n")) # get the 99th timestamp + col2.append(x) + y.append(col2) + + for id in it.chain(range(38, 42)): + file = exp_dir + '/results/latency/MWAY_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(int(len(read) * 0.95)).strip("\n")) # get the 99th timestamp + col3.append(x) + y.append(col3) + + for id in it.chain(range(38, 42)): + file = exp_dir + '/results/latency/MPASS_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(int(len(read) * 0.95)).strip("\n")) # get the 99th timestamp + col4.append(x) + y.append(col4) + + y.append(col9) # this is a fake empty line to separate eager and lazy. + + for id in it.chain(range(38, 42)): + file = exp_dir + '/results/latency/SHJ_JM_NP_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(int(len(read) * 0.95)).strip("\n")) # get last timestamp + col5.append(x) + y.append(col5) + + for id in it.chain(range(38, 42)): + file = exp_dir + '/results/latency/SHJ_JBCR_NP_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(int(len(read) * 0.95)).strip("\n")) # get last timestamp + col6.append(x) + y.append(col6) + + for id in it.chain(range(38, 42)): + file = exp_dir + '/results/latency/PMJ_JM_NP_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(int(len(read) * 0.95)).strip("\n")) # get last timestamp + col7.append(x) + y.append(col7) + + for id in it.chain(range(38, 42)): + file = exp_dir + '/results/latency/PMJ_JBCR_NP_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(int(len(read) * 0.95)).strip("\n")) # get last timestamp + col8.append(x) + y.append(col8) + return y + + +if __name__ == "__main__": + x_values = ["Stock", "Rovio", "YSB", "DEBS"] + + y_values = ReadFile() + + legend_labels = ['Lazy:', 'NPJ', 'PRJ', 'MWAY', 'MPASS', + 'Eager:', 'SHJ$^{JM}$', 'SHJ$^{JB}$', 'PMJ$^{JM}$', 'PMJ$^{JB}$'] + print(y_values) + DrawFigure(x_values, y_values, legend_labels, + '', 'Latency (ms)', 0, + 400, 'latency_figure_app', False) + + # DrawLegend(legend_labels, 'latency_legend') diff --git a/benchmark/scripts/scanBatchSizeSingleThreadStream2S/groupLine.py b/benchmark/scripts/scanBatchSizeSingleThreadStream2S/groupLine.py new file mode 100755 index 00000000..77993fc1 --- /dev/null +++ b/benchmark/scripts/scanBatchSizeSingleThreadStream2S/groupLine.py @@ -0,0 +1,368 @@ +import itertools as it +import os +import matplotlib.pyplot as plt +import matplotlib.font_manager as fm +import matplotlib +import numpy as np +import pylab +from matplotlib.font_manager import FontProperties +from matplotlib.ticker import MaxNLocator +from matplotlib.font_manager import FontProperties +from matplotlib.ticker import LinearLocator, LogLocator, MaxNLocator, ScalarFormatter +from numpy import double +import matplotlib.ticker as mtick +# 获取系统中可用的字体路径 +# font_paths = fm.findSystemFonts() +# OPT_FONT_NAME = 'Helvetica' +TICK_FONT_SIZE = 20 +LABEL_FONT_SIZE = 20 +LEGEND_FONT_SIZE = 20 +LABEL_FP = FontProperties(style='normal', size=LABEL_FONT_SIZE) +LEGEND_FP = FontProperties(style='normal', size=LEGEND_FONT_SIZE) +TICK_FP = FontProperties(style='normal', size=TICK_FONT_SIZE) + +MARKERS = (['o', 's', 'v', "^", "h", "v", ">", "x", "d", "<", "|", "p", "+", "_", "%", "|", "|", "|", "|", "|"]) +# you may want to change the color map for different figures +COLOR_MAP = ( + '#F15854', '#5DA5DA', '#60BD68', '#B276B2', '#DECF3F', '#F17CB0', '#B2912F', '#FAA43A', '#AFAFAF', '#087878', + '#783456', + '#560012', '#431256', "#00AABB", "#AA00BB") +# you may want to change the patterns for different figures +PATTERNS = (["|", "\\", "/", "+", "-", ".", "*", "x", "o", "O", "////", ".", "|||", "o", "---", "+", "\\\\", "*"]) +LABEL_WEIGHT = 'bold' +LINE_COLORS = COLOR_MAP +LINE_WIDTH = 3.0 +MARKER_SIZE = 13.0 +MARKER_FREQUENCY = 1000 + +# matplotlib.rcParams['ps.useafm'] = True +# matplotlib.rcParams['pdf.use14corefonts'] = True +# matplotlib.rcParams['xtick.labelsize'] = TICK_FONT_SIZE +# matplotlib.rcParams['ytick.labelsize'] = TICK_FONT_SIZE +# 创建字体列表 +# Explicitly specify the font path +# 获取系统中可用的字体路径 +font_paths = fm.findSystemFonts() + +# 创建字体列表 +font_list = [] +for font_path in font_paths: + try: + font_name = fm.FontProperties(fname=font_path).get_name() + if font_name not in font_list: + font_list.append(font_name) + except: + pass + +# 配置 matplotlib 使用系统中可用的字体 +plt.rcParams['font.family'] = font_list[0] + +FIGURE_FOLDER = '/data1/xtra/results/figure' + + +# there are some embedding problems if directly exporting the pdf figure using matplotlib. +# so we generate the eps format first and convert it to pdf. +def ConvertEpsToPdf(dir_filename): + os.system("epstopdf --outfile " + dir_filename + ".pdf " + dir_filename + ".eps") + os.system("rm -rf " + dir_filename + ".eps") + + +def DrawLegend(legend_labels, filename): + fig = pylab.figure() + ax1 = fig.add_subplot(111) + FIGURE_LABEL = legend_labels + LINE_WIDTH = 8.0 + MARKER_SIZE = 12.0 + LEGEND_FP = FontProperties(style='normal', size=26) + + figlegend = pylab.figure(figsize=(12, 0.5)) + idx = 0 + lines = [None] * (len(FIGURE_LABEL)) + data = [1] + x_values = [1] + + idx = 0 + for group in range(len(FIGURE_LABEL)): + lines[idx], = ax1.plot(x_values, data, + color=LINE_COLORS[idx], linewidth=LINE_WIDTH, + marker=MARKERS[idx], markersize=MARKER_SIZE, label=str(group)) + idx = idx + 1 + + # LEGEND + figlegend.legend(lines, FIGURE_LABEL, prop=LEGEND_FP, + loc=1, ncol=len(FIGURE_LABEL), mode="expand", shadow=False, + frameon=False, borderaxespad=0.0, handlelength=2) + + if not os.path.exists(FIGURE_FOLDER): + os.makedirs(FIGURE_FOLDER) + # no need to export eps in this case. + figlegend.savefig(filename + '.pdf') + + +# draw a line chart +def DrawFigure(xvalues, yvalues, legend_labels, x_label, y_label, y_min, y_max, filename, allow_legend): + # you may change the figure size on your own. + fig = plt.figure(figsize=(10, 3)) + figure = fig.add_subplot(111) + + FIGURE_LABEL = legend_labels + + x_values = xvalues + y_values = yvalues + + lines = [None] * (len(FIGURE_LABEL)) + for i in range(len(y_values)): + lines[i], = figure.plot(x_values[i], y_values[i], color=LINE_COLORS[i], \ + linewidth=LINE_WIDTH, marker=MARKERS[i], \ + markersize=MARKER_SIZE, label=FIGURE_LABEL[i]) + + # sometimes you may not want to draw legends. + if allow_legend == True: + plt.legend(lines, + FIGURE_LABEL, + prop=LEGEND_FP, + loc='upper center', + ncol=3, + # mode='expand', + bbox_to_anchor=(0.55, 1.6), shadow=False, + columnspacing=0.1, + frameon=True, borderaxespad=0.0, handlelength=1.5, + handletextpad=0.1, + labelspacing=0.1) + plt.xscale('log') + plt.yscale('log') + # plt.yscale('log') + + # you may control the limits on your own. + + # plt.ylim(y_min, y_max) + + plt.grid(axis='y', color='gray') + figure.yaxis.set_major_locator(LogLocator(base=10)) + figure.xaxis.set_major_locator(LogLocator(base=10)) + + figure.get_xaxis().set_tick_params(direction='in', pad=10) + figure.get_yaxis().set_tick_params(direction='in', pad=10) + + plt.xlabel(x_label, fontproperties=LABEL_FP) + plt.ylabel(y_label, fontproperties=LABEL_FP) + + size = fig.get_size_inches() + dpi = fig.get_dpi() + + # plt.show() + plt.savefig(filename + ".pdf", bbox_inches='tight') + + +# draw a line chart +def DrawFigureXYnormal(xvalues, yvalues, legend_labels, x_label, y_label, y_min, y_max, filename, allow_legend): + # you may change the figure size on your own. + fig = plt.figure(figsize=(10, 3)) + figure = fig.add_subplot(111) + + FIGURE_LABEL = legend_labels + + x_values = xvalues + y_values = yvalues + + lines = [None] * (len(FIGURE_LABEL)) + for i in range(len(y_values)): + lines[i], = figure.plot(x_values[i], y_values[i], color=LINE_COLORS[i], \ + linewidth=LINE_WIDTH, marker=MARKERS[i], \ + markersize=MARKER_SIZE, label=FIGURE_LABEL[i], markeredgecolor='k') + + # sometimes you may not want to draw legends. + if allow_legend == True: + plt.legend(lines, + FIGURE_LABEL, + prop=LEGEND_FP, + loc='upper center', + ncol=3, + bbox_to_anchor=(0.55, 1.5), shadow=False, + columnspacing=0.1, + frameon=True, borderaxespad=0, handlelength=1.2, + handletextpad=0.1, + labelspacing=0.1) + # plt.xscale('log') + # plt.yscale('log') + # plt.yscale('log') + + # you may control the limits on your own. + + # plt.ylim(y_min, y_max) + + plt.grid(axis='y', color='gray') + plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号 + # figure.yaxis.set_major_locator(LogLocator(base=10)) + # figure.xaxis.set_major_locator(LogLocator(base=10)) + plt.xticks(rotation=0, fontsize=TICK_FONT_SIZE) + figure.get_xaxis().set_tick_params(direction='in', pad=10) + figure.get_yaxis().set_tick_params(direction='in', pad=10) + + plt.xlabel(x_label, fontproperties=LABEL_FP) + plt.ylabel(y_label, fontproperties=LABEL_FP) + + size = fig.get_size_inches() + dpi = fig.get_dpi() + + # plt.show() + plt.savefig(filename + ".pdf", bbox_inches='tight') + + +# draw a line chart +def DrawFigureYnormal(xvalues, yvalues, legend_labels, x_label, y_label, y_min, y_max, filename, allow_legend): + # you may change the figure size on your own. + fig = plt.figure(figsize=(10, 3)) + figure = fig.add_subplot(111) + + FIGURE_LABEL = legend_labels + + x_values = xvalues + y_values = yvalues + + lines = [None] * (len(FIGURE_LABEL)) + for i in range(len(y_values)): + lines[i], = figure.plot(x_values[i], y_values[i], color=LINE_COLORS[i], \ + linewidth=LINE_WIDTH, marker=MARKERS[i], \ + markersize=MARKER_SIZE, label=FIGURE_LABEL[i], markeredgecolor='k') + + # sometimes you may not want to draw legends. + if allow_legend == True: + plt.legend(lines, + FIGURE_LABEL, + prop=LEGEND_FP, + loc='upper center', + ncol=3, + bbox_to_anchor=(0.55, 1.5), shadow=False, + columnspacing=0.1, + frameon=True, borderaxespad=0, handlelength=1.2, + handletextpad=0.1, + labelspacing=0.1) + plt.xscale('log') + # plt.yscale('log') + # plt.yscale('log') + + # you may control the limits on your own. + yMax=np.max(y_values) + plt.ylim(y_min, yMax*1.2) + + plt.grid(axis='y', color='gray') + plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号 + figure.yaxis.set_major_formatter(mtick.FormatStrFormatter('%.1f')) + + figure.yaxis.set_major_locator(LinearLocator(numticks=5)) + # figure.xaxis.set_major_locator(LogLocator(base=10)) + plt.xticks(rotation=0, fontsize=TICK_FONT_SIZE) + figure.get_xaxis().set_tick_params(direction='in', pad=10) + figure.get_yaxis().set_tick_params(direction='in', pad=10) + + plt.xlabel(x_label, fontproperties=LABEL_FP) + plt.ylabel(y_label, fontproperties=LABEL_FP) + + size = fig.get_size_inches() + dpi = fig.get_dpi() + + # plt.show() + plt.savefig(filename + ".pdf", bbox_inches='tight') + + +# example for reading csv file +def ReadFile(): + y = [] + col1 = [] + col2 = [] + col3 = [] + col4 = [] + col5 = [] + col6 = [] + col7 = [] + col8 = [] + + for id in it.chain(range(28, 32)): + file = '/data1/xtra/results/timestamps/PRJ_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(len(read) - 1).strip("\n")) # get last timestamp + value = len(read) / x # get throughput (#items/ms) + col1.append(value) + y.append(col1) + + for id in it.chain(range(28, 32)): + file = '/data1/xtra/results/timestamps/NPJ_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(len(read) - 1).strip("\n")) # get last timestamp + value = len(read) / x # get throughput (#items/ms) + col2.append(value) + y.append(col2) + + for id in it.chain(range(28, 32)): + file = '/data1/xtra/results/timestamps/MPASS_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(len(read) - 1).strip("\n")) # get last timestamp + value = len(read) / x # get throughput (#items/ms) + col3.append(value) + y.append(col3) + + for id in it.chain(range(28, 32)): + file = '/data1/xtra/results/timestamps/MWAY_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(len(read) - 1).strip("\n")) # get last timestamp + value = len(read) / x # get throughput (#items/ms) + col4.append(value) + y.append(col4) + + for id in it.chain(range(28, 32)): + file = '/data1/xtra/results/timestamps/SHJ_JM_NP_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(len(read) - 1).strip("\n")) # get last timestamp + value = len(read) / x # get throughput (#items/ms) + col5.append(value) + y.append(col5) + + for id in it.chain(range(28, 32)): + file = '/data1/xtra/results/timestamps/SHJ_JBCR_NP_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(len(read) - 1).strip("\n")) # get last timestamp + value = len(read) / x # get throughput (#items/ms) + col6.append(value) + y.append(col6) + + for id in it.chain(range(28, 32)): + file = '/data1/xtra/results/timestamps/PMJ_JM_NP_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(len(read) - 1).strip("\n")) # get last timestamp + value = len(read) / x # get throughput (#items/ms) + col7.append(value) + y.append(col7) + + for id in it.chain(range(28, 32)): + file = '/data1/xtra/results/timestamps/PMJ_JBCR_NP_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(len(read) - 1).strip("\n")) # get last timestamp + value = len(read) / x # get throughput (#items/ms) + col8.append(value) + y.append(col8) + return y + + +if __name__ == "__main__": + # x_values = ['Unique', 'Zipf(0)', 'Zipf(0.2)', 'Zipf(0.4)', 'Zipf(0.8)', 'Zipf(1)'] + x_values = [1600, 3200, 6400, 12800, 25600] + + y_values = ReadFile() + + legend_labels = ['NPJ', 'PRJ', 'MWAY', 'MPASS', 'SHJ$^{JM}$', 'SHJ$^{JB}$', 'PMJ$^{JM}$', + 'PMJ$^{JB}$'] + + DrawFigure(x_values, y_values, legend_labels, + 'Input arrival rate of R (e/ms)', 'Tpt. (#matches/ms)', x_values[0], + x_values[4], 'throughput_figure1_1', False) + +# DrawLegend(legend_labels, 'factor_legend') diff --git a/benchmark/scripts/scanBatchSizeSingleThreadStream2S/perfRu.csv b/benchmark/scripts/scanBatchSizeSingleThreadStream2S/perfRu.csv new file mode 100644 index 00000000..9ee43623 --- /dev/null +++ b/benchmark/scripts/scanBatchSizeSingleThreadStream2S/perfRu.csv @@ -0,0 +1,7 @@ +key,value,type +cacheMiss,12491377,U64 +cacheRefs,35327177,U64 +cpuClock,2478812100,U64 +cpuCycle,9889422467,U64 +instructions,16997458033,U64 +taskClock,2478813000,U64 diff --git a/benchmark/scripts/scanEventRateSingleThreadStream/OoOCommon.py b/benchmark/scripts/scanEventRateSingleThreadStream/OoOCommon.py new file mode 100755 index 00000000..70f4cf33 --- /dev/null +++ b/benchmark/scripts/scanEventRateSingleThreadStream/OoOCommon.py @@ -0,0 +1,125 @@ +import csv +import numpy as np +import matplotlib.pyplot as plt +import itertools as it +import os + +import matplotlib +import matplotlib.pyplot as plt +import numpy as np +import pylab +from matplotlib.font_manager import FontProperties +from matplotlib.ticker import LogLocator, LinearLocator +import os +import pandas as pd +import sys +import matplotlib.ticker as mtick + +OPT_FONT_NAME = 'Helvetica' +TICK_FONT_SIZE = 22 +LABEL_FONT_SIZE = 28 +LEGEND_FONT_SIZE = 30 +LABEL_FP = FontProperties(style='normal', size=LABEL_FONT_SIZE) +LEGEND_FP = FontProperties(style='normal', size=LEGEND_FONT_SIZE) +TICK_FP = FontProperties(style='normal', size=TICK_FONT_SIZE) + +MARKERS = (['*', '|', 'v', "^", "", "h", "<", ">", "+", "d", "<", "|", "", "+", "_"]) +# you may want to change the color map for different figures +COLOR_MAP = ( + '#B03A2E', '#2874A6', '#239B56', '#7D3C98', '#FFFFFF', '#F1C40F', '#F5CBA7', '#82E0AA', '#AEB6BF', '#AA4499') +# you may want to change the patterns for different figures +PATTERNS = (["////", "o", "", "||", "-", "//", "\\", "o", "O", "////", ".", "|||", "o", "---", "+", "\\\\", "*"]) +LABEL_WEIGHT = 'bold' +LINE_COLORS = COLOR_MAP +LINE_WIDTH = 3.0 +MARKER_SIZE = 15.0 +MARKER_FREQUENCY = 1000 + + +def editConfig(src, dest, key, value): + df = pd.read_csv(src, header=None) + rowIdx = 0 + idxt = 0 + for cell in df[0]: + # print(cell) + if (cell == key): + rowIdx = idxt + break + idxt = idxt + 1 + df[1][rowIdx] = str(value) + df.to_csv(dest, index=False, header=False) + + +def readConfig(src, key): + df = pd.read_csv(src, header=None) + rowIdx = 0 + idxt = 0 + for cell in df[0]: + # print(cell) + if (cell == key): + rowIdx = idxt + break + idxt = idxt + 1 + return df[1][rowIdx] + + +def draw2yLine(NAME, Com, R1, R2, l1, l2, m1, m2, fname): + fig, ax1 = plt.subplots(figsize=(10, 6.4)) + lines = [None] * 2 + # ax1.set_ylim(0, 1) + print(Com) + print(R1) + lines[0], = ax1.plot(Com, R1, color=LINE_COLORS[0], \ + linewidth=LINE_WIDTH, marker=MARKERS[0], \ + markersize=MARKER_SIZE + # + ) + + # plt.show() + ax1.set_ylabel(m1, fontproperties=LABEL_FP) + ax1.set_xlabel(NAME, fontproperties=LABEL_FP) + # ax1.set_xticklabels(ax1.get_xticklabels()) # 设置共用的x轴 + plt.xticks(rotation=0, size=TICK_FONT_SIZE) + plt.yticks(rotation=0, size=TICK_FONT_SIZE) + plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号 + ax2 = ax1.twinx() + + # ax2.set_ylabel('latency/us') + # ax2.set_ylim(0, 0.5) + lines[1], = ax2.plot(Com, R2, color=LINE_COLORS[1], \ + linewidth=LINE_WIDTH, marker=MARKERS[1], \ + markersize=MARKER_SIZE) + + ax2.set_ylabel(m2, fontproperties=LABEL_FP) + # ax2.vlines(192000, min(R2)-1, max(R2)+1, colors = "GREEN", linestyles = "dashed",label='total L1 size') + # plt.grid(axis='y', color='gray') + + # style = dict(size=10, color='black') + # ax2.hlines(tset, 0, x2_list[len(x2_list)-1]+width, colors = "r", linestyles = "dashed",label="tset") + # ax2.text(4, tset, "$T_{set}$="+str(tset)+"us", ha='right', **style) + + # plt.xlabel('batch', fontproperties=LABEL_FP) + + # plt.xscale('log') + # figure.xaxis.set_major_locator(LinearLocator(5)) + ax1.yaxis.set_major_locator(LinearLocator(5)) + ax2.yaxis.set_major_locator(LinearLocator(5)) + ax1.yaxis.set_major_formatter(mtick.FormatStrFormatter('%.1f')) + ax2.yaxis.set_major_formatter(mtick.FormatStrFormatter('%.1f')) + ax1.yaxis.set_major_formatter(mtick.FormatStrFormatter('%.1f')) + ax2.yaxis.set_major_formatter(mtick.FormatStrFormatter('%.1f')) + plt.legend(lines, + [l1, l2], + prop=LEGEND_FP, + loc='upper center', + ncol=1, + bbox_to_anchor=(0.55, 1.3 + ), shadow=False, + columnspacing=0.1, + frameon=True, borderaxespad=-1.5, handlelength=1.2, + handletextpad=0.1, + labelspacing=0.1) + plt.yticks(rotation=0, size=TICK_FONT_SIZE) + plt.tight_layout() + + plt.savefig(fname + ".pdf") diff --git a/benchmark/scripts/scanEventRateSingleThreadStream/accuBar.py b/benchmark/scripts/scanEventRateSingleThreadStream/accuBar.py new file mode 100755 index 00000000..638c8a60 --- /dev/null +++ b/benchmark/scripts/scanEventRateSingleThreadStream/accuBar.py @@ -0,0 +1,328 @@ +import getopt +import os +import sys + +import matplotlib +import matplotlib.pyplot as plt +import numpy as np +import pylab +from matplotlib.font_manager import FontProperties +from matplotlib.ticker import LinearLocator, LogLocator, MaxNLocator, ScalarFormatter +from numpy import double + +OPT_FONT_NAME = 'Helvetica' +TICK_FONT_SIZE = 24 +LABEL_FONT_SIZE = 28 +LEGEND_FONT_SIZE = 26 +TITLE_FRONT_SIZE = 32 +LABEL_FP = FontProperties(style='normal', size=LABEL_FONT_SIZE) +LEGEND_FP = FontProperties(style='normal', size=LEGEND_FONT_SIZE) +TICK_FP = FontProperties(style='normal', size=TICK_FONT_SIZE) +TITLE_FP = FontProperties(style='normal', size=TITLE_FRONT_SIZE) +MARKERS = (['o', 's', 'v', "^", "h", "v", ">", "x", "d", "<", "|", "", "|", "_"]) +# you may want to change the color map for different figures +COLOR_MAP = ('#B03A2E', '#2874A6', '#239B56', '#7D3C98', '#F1C40F', '#F5CBA7', '#82E0AA', '#AEB6BF', '#AA4499') +# you may want to change the patterns for different figures +PATTERNS = (["\\", "///", "o", "||", "\\\\", "\\\\", "//////", "//////", ".", "\\\\\\", "\\\\\\"]) +LABEL_WEIGHT = 'bold' +LINE_COLORS = COLOR_MAP +LINE_WIDTH = 3.0 +MARKER_SIZE = 0.0 +MARKER_FREQUENCY = 1000 + +matplotlib.rcParams['ps.useafm'] = True +matplotlib.rcParams['pdf.use14corefonts'] = True +matplotlib.rcParams['xtick.labelsize'] = TICK_FONT_SIZE +matplotlib.rcParams['ytick.labelsize'] = TICK_FONT_SIZE +matplotlib.rcParams['font.family'] = OPT_FONT_NAME +matplotlib.rcParams['pdf.fonttype'] = 42 + +exp_dir = "/data1/xtra" + +FIGURE_FOLDER = exp_dir + '/results/figure' + + +# there are some embedding problems if directly exporting the pdf figure using matplotlib. +# so we generate the eps format first and convert it to pdf. +def ConvertEpsToPdf(dir_filename): + os.system("epstopdf --outfile " + dir_filename + ".pdf " + dir_filename + ".eps") + os.system("rm -rf " + dir_filename + ".eps") + + +class ScalarFormatterForceFormat(ScalarFormatter): + def _set_format(self): # Override function that finds format to use. + self.format = "%1.1f" # Give format here + + +# draw a line chart +def DrawFigure(x_values, y_values, legend_labels, x_label, y_label, filename, allow_legend, title): + # you may change the figure size on your own. + + fig = plt.figure(figsize=(16, 9)) + figure = fig.add_subplot(111) + + FIGURE_LABEL = legend_labels + + # if not os.path.exists(FIGURE_FOLDER): + # os.makedirs(FIGURE_FOLDER) + + # values in the x_xis + index = np.arange(len(x_values)) + # the bar width. + # you may need to tune it to get the best figure. + width = 0.5 + # draw the bars + bottom_base = np.zeros(len(y_values[0])) + bars = [None] * (len(FIGURE_LABEL)) + for i in range(len(y_values)): + bars[i] = plt.bar(index + width / 2, y_values[i], width, hatch=PATTERNS[i], color=LINE_COLORS[i], + label=FIGURE_LABEL[i], bottom=bottom_base, edgecolor='black', linewidth=3) + bottom_base = np.array(y_values[i]) + bottom_base + + # sometimes you may not want to draw legends. + if allow_legend == True: + plt.legend(bars, FIGURE_LABEL + # mode='expand', + # shadow=False, + # columnspacing=0.25, + # labelspacing=-2.2, + # borderpad=5, + # bbox_transform=ax.transAxes, + # frameon=False, + # columnspacing=5.5, + # handlelength=2, + ) + if allow_legend == True: + handles, labels = figure.get_legend_handles_labels() + if allow_legend == True: + leg = plt.legend(handles[::-1], labels[::-1], + loc='upper center', + # prop=#LEGEND_FP, + # ncol=3, + # bbox_to_anchor=(0.5, 1.25), + # bbox_to_anchor=(1.17, 0.5), + # handletextpad=0.1, + # borderaxespad=0.0, + # handlelength=1.8, + # labelspacing=0.3, + # columnspacing=0.3, + ) + leg.get_frame().set_linewidth(2) + leg.get_frame().set_edgecolor("black") + + # you may need to tune the xticks position to get the best figure. + plt.xticks(index + 0.6 * width, x_values) + yfmt = ScalarFormatterForceFormat() + yfmt.set_powerlimits((0, 0)) + figure.get_yaxis().set_major_formatter(yfmt) + plt.ticklabel_format(axis="y", style="sci", scilimits=(0, 0), useMathText=True) + plt.grid(axis='y', color='gray') + figure.yaxis.set_major_locator(LinearLocator(3)) + # figure.yaxis.set_major_locator(LogLocator(base=10)) + # figure.yaxis.set_major_locator(LinearLocator(6)) + + figure.get_xaxis().set_tick_params(direction='in', pad=10) + figure.get_yaxis().set_tick_params(direction='in', pad=10) + + plt.xlabel(x_label, fontproperties=LABEL_FP) + plt.ylabel(y_label, fontproperties=LABEL_FP) + + size = fig.get_size_inches() + dpi = fig.get_dpi() + plt.title(title, fontproperties=TITLE_FP) + plt.savefig(filename + ".pdf", bbox_inches='tight', format='pdf') + + +def DrawLegend(legend_labels, filename): + fig = pylab.figure() + ax1 = fig.add_subplot(111) + FIGURE_LABEL = legend_labels + LEGEND_FP = FontProperties(style='normal', size=26) + + bars = [None] * (len(FIGURE_LABEL)) + data = [1] + x_values = [1] + + width = 0.3 + for i in range(len(FIGURE_LABEL)): + bars[i] = ax1.bar(x_values, data, width, hatch=PATTERNS[i], color=LINE_COLORS[i], + linewidth=0.2) + + # LEGEND + figlegend = pylab.figure(figsize=(11, 0.5)) + figlegend.legend(bars, FIGURE_LABEL, prop=LEGEND_FP, \ + loc=9, + bbox_to_anchor=(0, 0.4, 1, 1), + ncol=len(FIGURE_LABEL), mode="expand", shadow=False, \ + frameon=False, handlelength=1.1, handletextpad=0.2, columnspacing=0.1) + + figlegend.savefig(FIGURE_FOLDER + '/' + filename + '.pdf') + + +def normalize(y_values): + y_total_values = np.zeros(len(y_values[0])) + + for i in range(len(y_values)): + y_total_values += np.array(y_values[i]) + y_norm_values = [] + + for i in range(len(y_values)): + y_norm_values.append(np.array(y_values[i]) / (y_total_values) * 100) + return y_norm_values + + +# example for reading csv file +def ReadFile(id): + # Creates a list containing w lists, each of h items, all set to 0 + w, h = 5, 3 + y = [[0 for x in range(w)] for y in range(h)] + # print(matches) + max_value = 0 + j = 0 + bound = id + 1 * w + for i in range(id, bound, 1): + cnt = 0 + print(i) + f = open(exp_dir + "/results/breakdown/PMJ_JBCR_NP_{}.txt".format(i), "r") + read = f.readlines() + others = 0 + for x in read: + value = double(x.strip("\n")) + if value > max_value: + max_value = value + elif cnt == 3: # sort + y[0][j] = value + elif cnt == 4: # merge + y[1][j] = value + elif cnt == 5: # join + y[2][j] = value + else: + others += value + # if cnt == 6: + # y[2][j] = others + cnt += 1 + j += 1 + print(y) + return y, max_value + + +if __name__ == "__main__": + id = 119 + try: + opts, args = getopt.getopt(sys.argv[1:], '-i:h', ['test id', 'help']) + except getopt.GetoptError: + print('breakdown.py -id testid') + sys.exit(2) + for opt, opt_value in opts: + if opt in ('-h', '--help'): + print("[*] Help info") + exit() + elif opt == '-i': + print('Test ID:', opt_value) + id = (int)(opt_value) + + x_values = ['10%', '20%', '30%', '40%', '50%'] # sorting step size + + y_values, max_value = ReadFile(id) # 55 + + # y_norm_values = normalize(y_values) + + # break into 4 parts + legend_labels = ['sort', 'merge', 'join'] # , 'others' + + DrawFigure(x_values, y_values, legend_labels, + 'sorting step size', 'cycles per input tuple', + 'breakdown_sort_figure', True) + + # DrawLegend(legend_labels, 'breakdown_radix_legend') + + +def DrawPercentageFigure(x_values, y_values, legend_labels, x_label, y_label, filename, allow_legend, title): + # you may change the figure size on your own. + fig = plt.figure(figsize=(9, 3)) + figure = fig.add_subplot(111) + + FIGURE_LABEL = legend_labels + + # values in the x_xis + index = np.arange(len(x_values)) + # the bar width. + # you may need to tune it to get the best figure. + width = 0.5 + # draw the bars + bottom_base = np.zeros(len(y_values[0])) + bars = [None] * (len(FIGURE_LABEL)) + for i in range(len(y_values)): + bars[i] = plt.bar(index + width / 2, y_values[i], width, hatch=PATTERNS[i], color=LINE_COLORS[i], + label=FIGURE_LABEL[i], bottom=bottom_base, edgecolor='black', linewidth=3) + bottom_base = np.array(y_values[i]) + bottom_base + + # sometimes you may not want to draw legends. + if allow_legend == True: + plt.legend(bars, FIGURE_LABEL + # mode='expand', + # shadow=False, + # columnspacing=0.25, + # labelspacing=-2.2, + # borderpad=5, + # bbox_transform=ax.transAxes, + # frameon=False, + # columnspacing=5.5, + # handlelength=2, + ) + if allow_legend == True: + handles, labels = figure.get_legend_handles_labels() + if allow_legend == True: + print(handles[::-1], labels[::-1]) + leg = plt.legend(handles[::-1], labels[::-1], + loc='center', + prop=LEGEND_FP, + ncol=3, + bbox_to_anchor=(0.5, 1.2), + handletextpad=0.1, + borderaxespad=0.0, + handlelength=1.8, + labelspacing=0.3, + columnspacing=0.3, + ) + leg.get_frame().set_linewidth(2) + leg.get_frame().set_edgecolor("black") + + # sometimes you may not want to draw legends. + # if allow_legend == True: + # leg=plt.legend(bars, + # FIGURE_LABEL, + # prop=LEGEND_FP, + # loc='right', + # ncol=1, + # # mode='expand', + # bbox_to_anchor=(0.45, 1.1), shadow=False, + # columnspacing=0.1, + # frameon=True, borderaxespad=0.0, handlelength=1.5, + # handletextpad=0.1, + # labelspacing=0.1) + # leg.get_frame().set_linewidth(2) + # leg.get_frame().set_edgecolor("black") + + plt.ylim(0, 100) + + # you may need to tune the xticks position to get the best figure. + plt.xticks(index + 0.5 * width, x_values) + plt.xticks(rotation=30) + + # plt.xlim(0,) + # plt.ylim(0,1) + + plt.grid(axis='y', color='gray') + figure.yaxis.set_major_locator(LinearLocator(6)) + + figure.get_xaxis().set_tick_params(direction='in', pad=10) + figure.get_yaxis().set_tick_params(direction='in', pad=10) + + plt.xlabel(x_label, fontproperties=LABEL_FP) + plt.ylabel(y_label, fontproperties=LABEL_FP) + + size = fig.get_size_inches() + dpi = fig.get_dpi() + + plt.savefig(filename + ".pdf", bbox_inches='tight', format='pdf') diff --git a/benchmark/scripts/scanEventRateSingleThreadStream/autoParase.py b/benchmark/scripts/scanEventRateSingleThreadStream/autoParase.py new file mode 100755 index 00000000..9375c8e0 --- /dev/null +++ b/benchmark/scripts/scanEventRateSingleThreadStream/autoParase.py @@ -0,0 +1,87 @@ +import csv + + +def paraseValidStageNames(a): + nameList = [] + with open(a, 'r') as f: + reader = csv.reader(f) + # reader = [each for each in csv.DictReader(f, delimiter=',')] + result = list(reader) + rows = len(result) + # print('rows=',rows) + firstRow = result[0] + # print(firstRow) + index = 0 + # define what may attract our interest + idxCpu = 0 + idxName = 0 + for i in firstRow: + # print(i) + if (i == 'cpu'): + idxCpu = index + if (i == 'name'): + idxName = index + index = index + 1 + # read the valid stages + vdataEntries = 0 + + for k in range(1, rows): + if (result[k][idxCpu] != 'NA'): + R1 = ((result[k][idxName])) + nameList.append(R1) + return nameList + + +def paraseValidColums(a, nameList, colTitle): + with open(a, 'r') as f: + reader = csv.reader(f) + # reader = [each for each in csv.DictReader(f, delimiter=',')] + result = list(reader) + rows = len(result) + # print('rows=',rows) + firstRow = result[0] + # print(firstRow) + index = 0 + # define what may attract our interest + idxCpu = 0 + idxName = 0 + idxTitle = 0 + for i in firstRow: + # print(i) + if (i == 'cpu'): + idxCpu = index + if (i == 'name'): + idxName = index + if (i == colTitle): + idxTitle = index + index = index + 1 + # read the valid stages + vdataEntries = 0 + ru = [] + for k in range(1, rows): + if (result[k][idxCpu] != 'NA'): + R1 = ((result[k][idxName])) + for j in range(len(nameList)): + if (R1 == nameList[j]): + s = int(result[k][idxTitle]) + ru.append(s) + break + return ru + + +def maxInList(a): + # a in [[1,2] [3,4]] + inLen = len(a[0]) + ru = [] + index = [] + ti = 0 + for i in range(len(a[0])): + ts = 0 + ti = 0 + for k in range(len(a)): + if (a[k][i] > ts): + ts = a[k][i] + ti = k + ru.append(ts) + index.append(ti) + return ru, index diff --git a/benchmark/scripts/scanEventRateSingleThreadStream/config_BCoAMM.csv b/benchmark/scripts/scanEventRateSingleThreadStream/config_BCoAMM.csv new file mode 100644 index 00000000..b65317af --- /dev/null +++ b/benchmark/scripts/scanEventRateSingleThreadStream/config_BCoAMM.csv @@ -0,0 +1,6 @@ +key,value,type +aRow,100,U64 +aCol,1000,U64 +bCol,500,U64 +sketchDimension,25,U64 +ptFile,torchscripts/BetaCoOccurringFD.pt,String \ No newline at end of file diff --git a/benchmark/scripts/scanEventRateSingleThreadStream/config_BerCRS.csv b/benchmark/scripts/scanEventRateSingleThreadStream/config_BerCRS.csv new file mode 100644 index 00000000..d2b758a5 --- /dev/null +++ b/benchmark/scripts/scanEventRateSingleThreadStream/config_BerCRS.csv @@ -0,0 +1,6 @@ +key,value,type +aRow,100,U64 +aCol,1000,U64 +bCol,500,U64 +sketchDimension,25,U64 +ptFile,torchscripts/BernoulliCRS.pt,String \ No newline at end of file diff --git a/benchmark/scripts/scanEventRateSingleThreadStream/config_CPPBCOOFD.csv b/benchmark/scripts/scanEventRateSingleThreadStream/config_CPPBCOOFD.csv new file mode 100644 index 00000000..d43c92f9 --- /dev/null +++ b/benchmark/scripts/scanEventRateSingleThreadStream/config_CPPBCOOFD.csv @@ -0,0 +1,10 @@ +key,value,type +aRow,100,U64 +aCol,1000,U64 +bCol,500,U64 +sketchDimension,25,U64 +ptFile,torchscripts/BetaCoOccurringFD.pt,String +useCPP,1,U64 +cppAlgoTag,bcooFD,String +threads,1,U64 +forceMP,1,U64 diff --git a/benchmark/scripts/scanEventRateSingleThreadStream/config_CPPBCRS.csv b/benchmark/scripts/scanEventRateSingleThreadStream/config_CPPBCRS.csv new file mode 100644 index 00000000..3b7ee42c --- /dev/null +++ b/benchmark/scripts/scanEventRateSingleThreadStream/config_CPPBCRS.csv @@ -0,0 +1,10 @@ +key,value,type +aRow,100,U64 +aCol,1000,U64 +bCol,500,U64 +sketchDimension,25,U64 +ptFile,torchscripts/BernoulliCRS.pt,String +useCPP,1,U64 +cppAlgoTag,bcrs,String +threads,1,U64 +forceMP,1,U64 diff --git a/benchmark/scripts/scanEventRateSingleThreadStream/config_CPPCOFD.csv b/benchmark/scripts/scanEventRateSingleThreadStream/config_CPPCOFD.csv new file mode 100644 index 00000000..cf8f066a --- /dev/null +++ b/benchmark/scripts/scanEventRateSingleThreadStream/config_CPPCOFD.csv @@ -0,0 +1,10 @@ +key,value,type +aRow,100,U64 +aCol,1000,U64 +bCol,500,U64 +sketchDimension,25,U64 +ptFile,torchscripts/CRS.pt,String +useCPP,1,U64 +cppAlgoTag,cooFD,String +threads,1,U64 +forceMP,1,U64 \ No newline at end of file diff --git a/benchmark/scripts/scanEventRateSingleThreadStream/config_CPPCOUNTERSKETCH.csv b/benchmark/scripts/scanEventRateSingleThreadStream/config_CPPCOUNTERSKETCH.csv new file mode 100644 index 00000000..6823e4e2 --- /dev/null +++ b/benchmark/scripts/scanEventRateSingleThreadStream/config_CPPCOUNTERSKETCH.csv @@ -0,0 +1,10 @@ +key,value,type +aRow,100,U64 +aCol,1000,U64 +bCol,500,U64 +sketchDimension,25,U64 +ptFile,torchscripts/CRS.pt,String +useCPP,1,U64 +cppAlgoTag,countSketch,String +threads,1,U64 +forceMP,1,U64 diff --git a/benchmark/scripts/scanEventRateSingleThreadStream/config_CPPCRS.csv b/benchmark/scripts/scanEventRateSingleThreadStream/config_CPPCRS.csv new file mode 100644 index 00000000..12f10ca1 --- /dev/null +++ b/benchmark/scripts/scanEventRateSingleThreadStream/config_CPPCRS.csv @@ -0,0 +1,13 @@ +key,value,type +aRow,1000,U64 +aCol,1000,U64 +bCol,1000,U64 +sketchDimension,25,U64 +ptFile,torchscripts/CRS.pt,String +useCPP,1,U64 +cppAlgoTag,crs,String +threads,1,U64 +forceMP,1,U64 +eventRateTps,200,U64 +isStreaming,1,U64 +batchSize,20,U64 \ No newline at end of file diff --git a/benchmark/scripts/scanEventRateSingleThreadStream/config_CPPINT8.csv b/benchmark/scripts/scanEventRateSingleThreadStream/config_CPPINT8.csv new file mode 100644 index 00000000..2b61c43e --- /dev/null +++ b/benchmark/scripts/scanEventRateSingleThreadStream/config_CPPINT8.csv @@ -0,0 +1,10 @@ +key,value,type +aRow,100,U64 +aCol,1000,U64 +bCol,500,U64 +sketchDimension,25,U64 +ptFile,torchscripts/FDAMM.pt,String +useCPP,1,U64 +cppAlgoTag,int8,String +threads,1,U64 +forceMP,1,U64 diff --git a/benchmark/scripts/scanEventRateSingleThreadStream/config_CPPMM.csv b/benchmark/scripts/scanEventRateSingleThreadStream/config_CPPMM.csv new file mode 100644 index 00000000..b683a171 --- /dev/null +++ b/benchmark/scripts/scanEventRateSingleThreadStream/config_CPPMM.csv @@ -0,0 +1,13 @@ +key,value,type +aRow,1000,U64 +aCol,1000,U64 +bCol,1000,U64 +sketchDimension,25,U64 +ptFile,torchscripts/RAWMM.pt,String +useCPP,1,U64 +cppAlgoTag,mm,String +threads,1,U64 +forceMP,1,U64 +eventRateTps,200,U64 +isStreaming,1,U64 +batchSize,20,U64 \ No newline at end of file diff --git a/benchmark/scripts/scanEventRateSingleThreadStream/config_CPPSMPPCA.csv b/benchmark/scripts/scanEventRateSingleThreadStream/config_CPPSMPPCA.csv new file mode 100644 index 00000000..644f8571 --- /dev/null +++ b/benchmark/scripts/scanEventRateSingleThreadStream/config_CPPSMPPCA.csv @@ -0,0 +1,11 @@ +key,value,type +aRow,1000,U64 +aCol,1000,U64 +bCol,1000,U64 +sketchDimension,25,U64 +threads,1,U64 +useCPP,1,U64 +cppAlgoTag,smp-pca,String +isStreaming,1,U64 +batchSize,20,U64 +eventRateTps,200,U64 \ No newline at end of file diff --git a/benchmark/scripts/scanEventRateSingleThreadStream/config_CoAMM.csv b/benchmark/scripts/scanEventRateSingleThreadStream/config_CoAMM.csv new file mode 100644 index 00000000..f41d7ddd --- /dev/null +++ b/benchmark/scripts/scanEventRateSingleThreadStream/config_CoAMM.csv @@ -0,0 +1,6 @@ +key,value,type +aRow,100,U64 +aCol,1000,U64 +bCol,500,U64 +sketchDimension,25,U64 +ptFile,torchscripts/CoOccurringFD.pt,String \ No newline at end of file diff --git a/benchmark/scripts/scanEventRateSingleThreadStream/config_CounterSketch.csv b/benchmark/scripts/scanEventRateSingleThreadStream/config_CounterSketch.csv new file mode 100644 index 00000000..50cf2770 --- /dev/null +++ b/benchmark/scripts/scanEventRateSingleThreadStream/config_CounterSketch.csv @@ -0,0 +1,6 @@ +key,value,type +aRow,100,U64 +aCol,1000,U64 +bCol,500,U64 +sketchDimension,25,U64 +ptFile,torchscripts/CountSketch.pt,String diff --git a/benchmark/scripts/scanEventRateSingleThreadStream/drawTogether.py b/benchmark/scripts/scanEventRateSingleThreadStream/drawTogether.py new file mode 100755 index 00000000..96370382 --- /dev/null +++ b/benchmark/scripts/scanEventRateSingleThreadStream/drawTogether.py @@ -0,0 +1,180 @@ +#!/usr/bin/env python3 +#I will streaming A only!!! +import csv +import numpy as np +import accuBar as accuBar +import groupBar as groupBar +import groupBar2 as groupBar2 +import groupLine as groupLine +from autoParase import * +import itertools as it +import os + +import matplotlib +import numpy as np +import pylab +import matplotlib.font_manager as fm +from matplotlib.font_manager import FontProperties +from matplotlib.ticker import LogLocator, LinearLocator +import os +import pandas as pd +import sys +from OoOCommon import * +import time + +# OPT_FONT_NAME = 'Helvetica' +TICK_FONT_SIZE = 22 +LABEL_FONT_SIZE = 28 +LEGEND_FONT_SIZE = 30 +LABEL_FP = FontProperties(style='normal', size=LABEL_FONT_SIZE) +LEGEND_FP = FontProperties(style='normal', size=LEGEND_FONT_SIZE) +TICK_FP = FontProperties(style='normal', size=TICK_FONT_SIZE) + +MARKERS = (['*', '|', 'v', "^", "", "h", "<", ">", "+", "d", "<", "|", "", "+", "_"]) +# you may want to change the color map for different figures +COLOR_MAP = ( + '#B03A2E', '#2874A6', '#239B56', '#7D3C98', '#FFFFFF', '#F1C40F', '#F5CBA7', '#82E0AA', '#AEB6BF', '#AA4499') +# you may want to change the patterns for different figures +PATTERNS = (["////", "o", "", "||", "-", "//", "\\", "o", "O", "////", ".", "|||", "o", "---", "+", "\\\\", "*"]) +LABEL_WEIGHT = 'bold' +LINE_COLORS = COLOR_MAP +LINE_WIDTH = 3.0 +MARKER_SIZE = 15.0 +MARKER_FREQUENCY = 1000 + +matplotlib.rcParams['ps.useafm'] = True +matplotlib.rcParams['pdf.use14corefonts'] = True +matplotlib.rcParams['xtick.labelsize'] = TICK_FONT_SIZE +matplotlib.rcParams['ytick.labelsize'] = TICK_FONT_SIZE +matplotlib.rcParams['font.family'] = OPT_FONT_NAME +matplotlib.rcParams['pdf.fonttype'] = 42 + +scanTag = "eventRateTps" + + +def singleRun(exePath, singleValue, resultPath, configTemplate): + # resultFolder="singleValueTests" + configFname = "config_" + scanTag + str(singleValue) + ".csv" + # configTemplate = "config.csv" + # clear old files + os.system("cd " + exePath + "&& sudo rm *.csv") + + # editConfig(configTemplate, exePath + configFname, "earlierEmitMs", 0) + editConfig(configTemplate, exePath + configFname, scanTag, singleValue) + # prepare new file + # run + os.system("cd " + exePath + "&& sudo ./benchmark " + configFname) + # copy result + os.system("sudo rm -rf " + resultPath + "/" + str(singleValue)) + os.system("sudo mkdir " + resultPath + "/" + str(singleValue)) + os.system("cd " + exePath + "&& sudo cp *.csv " + resultPath + "/" + str(singleValue)) + + +def runScanVector(exePath, singleValueVec, resultPath, templateName="config.csv"): + for i in singleValueVec: + singleRun(exePath, i, resultPath, templateName) + + +def readResultSingle(singleValue, resultPath): + resultFname = resultPath + "/" + str(singleValue) + "/result_streaming.csv" + throughput = readConfig(resultFname, "throughputByElements") + lat95 = readConfig(resultFname, "95%latency") + froError = readConfig(resultFname, "froError") + errorBoundRatio = readConfig(resultFname, "errorBoundRatio") + return throughput,lat95, froError, errorBoundRatio + + +def cleanPath(path): + os.system("sudo rm -rf " + path) + os.system("sudo mkdir " + path) + + +def readResultVector(singleValueVec, resultPath): + thrVec = [] + lat95Vec= [] + froErrorVec = [] + errorBoundRatioVec = [] + for i in singleValueVec: + thr,lat95,froError, errorBoundRatio = readResultSingle(i, resultPath) + thrVec.append(float(thr)) + lat95Vec.append(float(lat95)/1000.0) + froErrorVec.append(float(froError)) + errorBoundRatioVec.append(float(errorBoundRatio)) + return np.array(thrVec), np.array(lat95Vec), np.array(froErrorVec), np.array( + errorBoundRatioVec) + + +def compareMethod(exeSpace, commonPathBase, resultPaths, csvTemplates, periodVec, reRun=1): + thrAll = [] + lat95All = [] + periodAll = [] + froAll = [] + errorBoundRatioAll = [] + for i in range(len(csvTemplates)): + resultPath = commonPathBase + resultPaths[i] + if (reRun == 1): + os.system("sudo rm -rf " + resultPath) + os.system("sudo mkdir " + resultPath) + runScanVector(exeSpace, periodVec, resultPath, csvTemplates[i]) + thr,lat95,fro, eb = readResultVector(periodVec, resultPath) + thrAll.append(thr) + lat95All.append(lat95) + + periodAll.append(periodVec) + + froAll.append(fro) + errorBoundRatioAll.append(eb) + # periodAll.append(periodVec) + return np.array(thrAll),np.array(lat95All), periodAll, np.array(froAll), np.array(errorBoundRatioAll) + + +def main(): + exeSpace = os.path.abspath(os.path.join(os.getcwd(), "../..")) + "/" + commonBase = os.path.abspath(os.path.join(os.getcwd(), "../..")) + "/results/" + scanTag + "/" + figPath = os.path.abspath(os.path.join(os.getcwd(), "../..")) + "/figures/" + scanTag + "CPP" + methodTags = ["CRS", "MM","SMP-PCA"] + resultPaths = ["CRS", "mm","smp-pca"] + csvTemplates = ["config_CPPCRS.csv", "config_CPPMM.csv","config_CPPSMPPCA.csv"] + valueVec = [100,200,500,1000,2000,5000] + valueVecDisp = np.array(valueVec) + # run + reRun = 0 + if (len(sys.argv) < 2): + os.system("mkdir ../../results") + os.system("mkdir ../../figures") + os.system("mkdir " + figPath) + os.system("sudo rm -rf " + commonBase) + os.system("sudo mkdir " + commonBase) + + reRun = 1 + # skech + thrAll, lat95All, periodAll, fro, eb = compareMethod(exeSpace, commonBase, resultPaths, csvTemplates, valueVec, + reRun) + groupLine.DrawFigureYnormal(periodAll, thrAll/1000.0, + methodTags, + "event rate (#rows/s)", "throughput (K elements/s)", 0, 1, + figPath + "/" + scanTag + "stream_cpp_thr", + True) + groupLine.DrawFigure(periodAll, lat95All, + methodTags, + "event rate (#rows/s)", "95% latency (ms)", 0, 1, + figPath + "/" + scanTag + "stream_cpp_lat95", + True) + groupLine.DrawFigureYnormal(periodAll, fro*100.0, + methodTags, + "event rate (#rows)", "fro error (%)", 0, 1, + figPath + "/" + scanTag + "stream_cpp_fro", + True) + + # draw2yLine("watermark time (ms)",singleValueVecDisp,lat95Vec,errVec,"95% Latency (ms)","Error","ms","",figPath+"wm_lat") + # draw2yLine("watermark time (ms)",singleValueVecDisp,thrVec,errVec,"Throughput (KTp/s)","Error","KTp/s","",figPath+"wm_thr") + # draw2yLine("watermark time (ms)",singleValueVecDisp,lat95Vec,compVec,"95% Latency (ms)","Completeness","ms","",figPath+"wm_omp") + # groupLine.DrawFigureYnormal([singleValueVec,singleValueVec],[errVec,aqpErrVec],['w/o aqp',"w/ MeanAqp"],"watermark time (ms)","Error",0,1,figPath+"wm_MeanAqp",True) + # print(errVec) + # print(aqpErrVec) + # print(elapseTimeVecFD) + # readResultsingleValue(50,resultPath) + + +if __name__ == "__main__": + main() diff --git a/benchmark/scripts/scanEventRateSingleThreadStream/groupBar.py b/benchmark/scripts/scanEventRateSingleThreadStream/groupBar.py new file mode 100755 index 00000000..ef2d9842 --- /dev/null +++ b/benchmark/scripts/scanEventRateSingleThreadStream/groupBar.py @@ -0,0 +1,236 @@ +import itertools as it +import os + +import matplotlib +import matplotlib.pyplot as plt +import numpy as np +import pylab +from matplotlib.font_manager import FontProperties +from matplotlib.ticker import LogLocator, LinearLocator + +OPT_FONT_NAME = 'Helvetica' +TICK_FONT_SIZE = 24 +LABEL_FONT_SIZE = 28 +LEGEND_FONT_SIZE = 15 +LABEL_FP = FontProperties(style='normal', size=LABEL_FONT_SIZE) +LEGEND_FP = FontProperties(style='normal', size=LEGEND_FONT_SIZE) +TICK_FP = FontProperties(style='normal', size=TICK_FONT_SIZE) + +MARKERS = (["", 'o', 's', 'v', "^", "", "h", "<", ">", "+", "d", "<", "|", "", "+", "_"]) +# you may want to change the color map for different figures +COLOR_MAP = ( + '#7FFFFF', '#B03A2E', '#2874A6', '#FFFFFF', '#7FFFFF', '#B03A2E', '#2874A6', '#FFFFFF', '#F5CBA7', '#82E0AA', + '#AEB6BF', + '#AA4499') +# you may want to change the patterns for different figures +PATTERNS = ( + ["////", "\\\\", "//", "o", "*", "||", "-", "//", "\\", "o", "O", "////", ".", "|||", "o", "---", "+", "\\\\", "*"]) +LABEL_WEIGHT = 'bold' +LINE_COLORS = COLOR_MAP +LINE_WIDTH = 3.0 +MARKER_SIZE = 15.0 +MARKER_FREQUENCY = 1000 + +matplotlib.rcParams['ps.useafm'] = True +matplotlib.rcParams['pdf.use14corefonts'] = True +matplotlib.rcParams['xtick.labelsize'] = TICK_FONT_SIZE +matplotlib.rcParams['ytick.labelsize'] = TICK_FONT_SIZE +matplotlib.rcParams['font.family'] = OPT_FONT_NAME +matplotlib.rcParams['pdf.fonttype'] = 42 + +exp_dir = "/data1/xtra" + +FIGURE_FOLDER = exp_dir + '/results/figure' + + +def DrawLegend(legend_labels, filename): + fig = pylab.figure() + ax1 = fig.add_subplot(111) + FIGURE_LABEL = legend_labels + LEGEND_FP = FontProperties(style='normal', size=26) + figlegend = pylab.figure(figsize=(16, 0.5)) + bars = [None] * (len(FIGURE_LABEL)) + data = [1] + x_values = [1] + + width = 0.3 + for i in range(len(FIGURE_LABEL)): + bars[i] = ax1.bar(x_values, data, width, + hatch=PATTERNS[i], + color=LINE_COLORS[i], + label=FIGURE_LABEL[i], + edgecolor='black', linewidth=3) + + # LEGEND + + figlegend.legend(bars, FIGURE_LABEL, prop=LEGEND_FP, \ + loc=1, ncol=len(FIGURE_LABEL), mode="expand", shadow=True, \ + frameon=True, handlelength=2, handletextpad=0.3, columnspacing=0.5, + borderaxespad=-0.2, fancybox=True + ) + figlegend.savefig(FIGURE_FOLDER + '/' + filename + '.pdf') + + +# draw a bar chart +def DrawFigure(x_values, y_values, legend_labels, x_label, y_label, y_min, y_max, filename, allow_legend): + # you may change the figure size on your own. + fig = plt.figure(figsize=(10, 3)) + figure = fig.add_subplot(111) + + FIGURE_LABEL = legend_labels + + # values in the x_xis + index = np.arange(len(x_values)) + # the bar width. + # you may need to tune it to get the best figure. + width = 0.08 + # draw the bars + bars = [] + ts = 0 + pos = 0 + gl = len(y_values[0]) + for i in range(len(y_values)): + pos = pos + 3 * width + for j in range(len(y_values[i])): + pos = pos + width + bar = plt.bar(pos, y_values[i][j], width, hatch=PATTERNS[j], color=LINE_COLORS[j], label=FIGURE_LABEL[j], + edgecolor='black', linewidth=3) + bars.append(bar) + ts = ts + 1 + + # sometimes you may not want to draw legends. + if allow_legend == True: + plt.legend(bars, FIGURE_LABEL, + prop=LEGEND_FP, + ncol=4, + loc='upper center', + # mode='expand', + shadow=False, + bbox_to_anchor=(0.45, 1.6), + columnspacing=0.1, + handletextpad=0.2, + # bbox_transform=ax.transAxes, + # frameon=True, + # columnspacing=5.5, + # handlelength=2, + ) + + # you may need to tune the xticks position to get the best figure. + plt.xticks(index, x_values) + # plt.ticklabel_format(axis="y", style="sci", scilimits=(0, 0)) + # plt.grid(axis='y', color='gray') + # figure.get_xaxis().set_major_formatter(matplotlib.ticker.ScalarFormatter()) + + # you may need to tune the xticks position to get the best figure. + # plt.yscale('log') + # + # plt.grid(axis='y', color='gray') + figure.yaxis.set_major_locator(LinearLocator(5)) + # figure.xaxis.set_major_locator(LinearLocator(5)) + figure.get_xaxis().set_tick_params(direction='in', pad=10) + figure.get_yaxis().set_tick_params(direction='in', pad=10) + + plt.xlabel(x_label, fontproperties=LABEL_FP) + plt.ylabel(y_label, fontproperties=LABEL_FP) + plt.ylim(y_min, y_max) + plt.savefig(filename + ".pdf", bbox_inches='tight') + + +# example for reading csv file +def ReadFile(): + y = [] + col1 = [] + col2 = [] + col3 = [] + col4 = [] + col5 = [] + col6 = [] + col7 = [] + col8 = [] + col9 = [] + + for id in it.chain(range(38, 42)): + col9.append(0) + y.append(col9) # this is a lz4_pipe2 empty line to separate eager and lazy. + + for id in it.chain(range(38, 42)): + file = exp_dir + '/results/latency/NPJ_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(int(len(read) * 0.95)).strip("\n")) # get the 99th timestamp + col1.append(x) + y.append(col1) + + for id in it.chain(range(38, 42)): + file = exp_dir + '/results/latency/PRJ_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(int(len(read) * 0.95)).strip("\n")) # get the 99th timestamp + col2.append(x) + y.append(col2) + + for id in it.chain(range(38, 42)): + file = exp_dir + '/results/latency/MWAY_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(int(len(read) * 0.95)).strip("\n")) # get the 99th timestamp + col3.append(x) + y.append(col3) + + for id in it.chain(range(38, 42)): + file = exp_dir + '/results/latency/MPASS_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(int(len(read) * 0.95)).strip("\n")) # get the 99th timestamp + col4.append(x) + y.append(col4) + + y.append(col9) # this is a lz4_pipe2 empty line to separate eager and lazy. + + for id in it.chain(range(38, 42)): + file = exp_dir + '/results/latency/SHJ_JM_NP_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(int(len(read) * 0.95)).strip("\n")) # get last timestamp + col5.append(x) + y.append(col5) + + for id in it.chain(range(38, 42)): + file = exp_dir + '/results/latency/SHJ_JBCR_NP_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(int(len(read) * 0.95)).strip("\n")) # get last timestamp + col6.append(x) + y.append(col6) + + for id in it.chain(range(38, 42)): + file = exp_dir + '/results/latency/PMJ_JM_NP_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(int(len(read) * 0.95)).strip("\n")) # get last timestamp + col7.append(x) + y.append(col7) + + for id in it.chain(range(38, 42)): + file = exp_dir + '/results/latency/PMJ_JBCR_NP_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(int(len(read) * 0.95)).strip("\n")) # get last timestamp + col8.append(x) + y.append(col8) + return y + + +if __name__ == "__main__": + x_values = ["Stock", "Rovio", "YSB", "DEBS"] + + y_values = ReadFile() + + legend_labels = ['Lazy:', 'NPJ', 'PRJ', 'MWAY', 'MPASS', + 'Eager:', 'SHJ$^{JM}$', 'SHJ$^{JB}$', 'PMJ$^{JM}$', 'PMJ$^{JB}$'] + print(y_values) + DrawFigure(x_values, y_values, legend_labels, + '', 'Latency (ms)', 0, + 400, 'latency_figure_app', False) + + # DrawLegend(legend_labels, 'latency_legend') diff --git a/benchmark/scripts/scanEventRateSingleThreadStream/groupBar2.py b/benchmark/scripts/scanEventRateSingleThreadStream/groupBar2.py new file mode 100755 index 00000000..31155695 --- /dev/null +++ b/benchmark/scripts/scanEventRateSingleThreadStream/groupBar2.py @@ -0,0 +1,232 @@ +import itertools as it +import os + +import matplotlib +import matplotlib.pyplot as plt +import numpy as np +import pylab +from matplotlib.font_manager import FontProperties +from matplotlib.ticker import LogLocator, LinearLocator + +OPT_FONT_NAME = 'Helvetica' +TICK_FONT_SIZE = 24 +LABEL_FONT_SIZE = 28 +LEGEND_FONT_SIZE = 30 +LABEL_FP = FontProperties(style='normal', size=LABEL_FONT_SIZE) +LEGEND_FP = FontProperties(style='normal', size=LEGEND_FONT_SIZE) +TICK_FP = FontProperties(style='normal', size=TICK_FONT_SIZE) + +MARKERS = (["", 'o', 's', 'v', "^", "", "h", "<", ">", "+", "d", "<", "|", "", "+", "_"]) +# you may want to change the color map for different figures +COLOR_MAP = ( + '#FFFFFF', '#B03A2E', '#2874A6', '#239B56', '#7D3C98', '#00FFFF', '#F1C40F', '#F5CBA7', '#82E0AA', '#AEB6BF', + '#AA4499') +# you may want to change the patterns for different figures +PATTERNS = ( + ["", "////", "\\\\", "//", "o", "", "||", "-", "//", "\\", "o", "O", "////", ".", "|||", "o", "---", "+", "\\\\", + "*"]) +LABEL_WEIGHT = 'bold' +LINE_COLORS = COLOR_MAP +LINE_WIDTH = 3.0 +MARKER_SIZE = 15.0 +MARKER_FREQUENCY = 1000 + +matplotlib.rcParams['ps.useafm'] = True +matplotlib.rcParams['pdf.use14corefonts'] = True +matplotlib.rcParams['xtick.labelsize'] = TICK_FONT_SIZE +matplotlib.rcParams['ytick.labelsize'] = TICK_FONT_SIZE +matplotlib.rcParams['font.family'] = OPT_FONT_NAME +matplotlib.rcParams['pdf.fonttype'] = 42 + +exp_dir = "/data1/xtra" + +FIGURE_FOLDER = exp_dir + '/results/figure' + + +def DrawLegend(legend_labels, filename): + fig = pylab.figure() + ax1 = fig.add_subplot(111) + FIGURE_LABEL = legend_labels + LEGEND_FP = FontProperties(style='normal', size=26) + figlegend = pylab.figure(figsize=(16, 0.5)) + bars = [None] * (len(FIGURE_LABEL)) + data = [1] + x_values = [1] + + width = 0.3 + for i in range(len(FIGURE_LABEL)): + bars[i] = ax1.bar(x_values, data, width, + hatch=PATTERNS[i], + color=LINE_COLORS[i], + label=FIGURE_LABEL[i], + edgecolor='black', linewidth=3) + + # LEGEND + + figlegend.legend(bars, FIGURE_LABEL, prop=LEGEND_FP, \ + loc=1, ncol=len(FIGURE_LABEL), mode="expand", shadow=True, \ + frameon=True, handlelength=2, handletextpad=0.3, columnspacing=0.5, + borderaxespad=-0.2, fancybox=True + ) + figlegend.savefig(FIGURE_FOLDER + '/' + filename + '.pdf') + + +# draw a bar chart +def DrawFigure(x_values, y_values, legend_labels, x_label, y_label, y_min, y_max, filename, allow_legend): + # you may change the figure size on your own. + fig = plt.figure(figsize=(10, 3)) + figure = fig.add_subplot(111) + + FIGURE_LABEL = legend_labels + + # values in the x_xis + index = np.arange(len(x_values)) + # the bar width. + # you may need to tune it to get the best figure. + width = 0.12 + # draw the bars + bars = [None] * (len(FIGURE_LABEL)) + for i in range(len(y_values)): + bars[i] = plt.bar(index + i * width + width / 2, + y_values[i], width, + hatch=PATTERNS[i], + color=LINE_COLORS[i], + label=FIGURE_LABEL[i], edgecolor='black', linewidth=3) + + # sometimes you may not want to draw legends. + if allow_legend == True: + plt.legend(bars, FIGURE_LABEL, + prop=LEGEND_FP, + ncol=3, + loc='upper center', + # mode='expand', + shadow=False, + bbox_to_anchor=(0.45, 1.7), + columnspacing=0.1, + handletextpad=0.2, + # bbox_transform=ax.transAxes, + # frameon=True, + # columnspacing=5.5, + # handlelength=2, + ) + + # you may need to tune the xticks position to get the best figure. + plt.xticks(index + 3 * width, x_values, rotation=30) + + # plt.ticklabel_format(axis="y", style="sci", scilimits=(0, 0)) + # plt.grid(axis='y', color='gray') + # figure.get_xaxis().set_major_formatter(matplotlib.ticker.ScalarFormatter()) + + # you may need to tune the xticks position to get the best figure. + # plt.yscale('log') + # + # plt.grid(axis='y', color='gray') + figure.yaxis.set_major_locator(LinearLocator(10)) + # figure.xaxis.set_major_locator(LinearLocator(5)) + figure.get_xaxis().set_tick_params(direction='in', pad=10) + figure.get_yaxis().set_tick_params(direction='in', pad=10) + + plt.xlabel(x_label, fontproperties=LABEL_FP) + plt.ylabel(y_label, fontproperties=LABEL_FP) + + plt.savefig(filename + ".pdf", bbox_inches='tight') + + +# example for reading csv file +def ReadFile(): + y = [] + col1 = [] + col2 = [] + col3 = [] + col4 = [] + col5 = [] + col6 = [] + col7 = [] + col8 = [] + col9 = [] + + for id in it.chain(range(38, 42)): + col9.append(0) + y.append(col9) # this is a fake empty line to separate eager and lazy. + + for id in it.chain(range(38, 42)): + file = exp_dir + '/results/latency/NPJ_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(int(len(read) * 0.95)).strip("\n")) # get the 99th timestamp + col1.append(x) + y.append(col1) + + for id in it.chain(range(38, 42)): + file = exp_dir + '/results/latency/PRJ_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(int(len(read) * 0.95)).strip("\n")) # get the 99th timestamp + col2.append(x) + y.append(col2) + + for id in it.chain(range(38, 42)): + file = exp_dir + '/results/latency/MWAY_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(int(len(read) * 0.95)).strip("\n")) # get the 99th timestamp + col3.append(x) + y.append(col3) + + for id in it.chain(range(38, 42)): + file = exp_dir + '/results/latency/MPASS_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(int(len(read) * 0.95)).strip("\n")) # get the 99th timestamp + col4.append(x) + y.append(col4) + + y.append(col9) # this is a fake empty line to separate eager and lazy. + + for id in it.chain(range(38, 42)): + file = exp_dir + '/results/latency/SHJ_JM_NP_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(int(len(read) * 0.95)).strip("\n")) # get last timestamp + col5.append(x) + y.append(col5) + + for id in it.chain(range(38, 42)): + file = exp_dir + '/results/latency/SHJ_JBCR_NP_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(int(len(read) * 0.95)).strip("\n")) # get last timestamp + col6.append(x) + y.append(col6) + + for id in it.chain(range(38, 42)): + file = exp_dir + '/results/latency/PMJ_JM_NP_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(int(len(read) * 0.95)).strip("\n")) # get last timestamp + col7.append(x) + y.append(col7) + + for id in it.chain(range(38, 42)): + file = exp_dir + '/results/latency/PMJ_JBCR_NP_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(int(len(read) * 0.95)).strip("\n")) # get last timestamp + col8.append(x) + y.append(col8) + return y + + +if __name__ == "__main__": + x_values = ["Stock", "Rovio", "YSB", "DEBS"] + + y_values = ReadFile() + + legend_labels = ['Lazy:', 'NPJ', 'PRJ', 'MWAY', 'MPASS', + 'Eager:', 'SHJ$^{JM}$', 'SHJ$^{JB}$', 'PMJ$^{JM}$', 'PMJ$^{JB}$'] + print(y_values) + DrawFigure(x_values, y_values, legend_labels, + '', 'Latency (ms)', 0, + 400, 'latency_figure_app', False) + + # DrawLegend(legend_labels, 'latency_legend') diff --git a/benchmark/scripts/scanEventRateSingleThreadStream/groupLine.py b/benchmark/scripts/scanEventRateSingleThreadStream/groupLine.py new file mode 100755 index 00000000..77993fc1 --- /dev/null +++ b/benchmark/scripts/scanEventRateSingleThreadStream/groupLine.py @@ -0,0 +1,368 @@ +import itertools as it +import os +import matplotlib.pyplot as plt +import matplotlib.font_manager as fm +import matplotlib +import numpy as np +import pylab +from matplotlib.font_manager import FontProperties +from matplotlib.ticker import MaxNLocator +from matplotlib.font_manager import FontProperties +from matplotlib.ticker import LinearLocator, LogLocator, MaxNLocator, ScalarFormatter +from numpy import double +import matplotlib.ticker as mtick +# 获取系统中可用的字体路径 +# font_paths = fm.findSystemFonts() +# OPT_FONT_NAME = 'Helvetica' +TICK_FONT_SIZE = 20 +LABEL_FONT_SIZE = 20 +LEGEND_FONT_SIZE = 20 +LABEL_FP = FontProperties(style='normal', size=LABEL_FONT_SIZE) +LEGEND_FP = FontProperties(style='normal', size=LEGEND_FONT_SIZE) +TICK_FP = FontProperties(style='normal', size=TICK_FONT_SIZE) + +MARKERS = (['o', 's', 'v', "^", "h", "v", ">", "x", "d", "<", "|", "p", "+", "_", "%", "|", "|", "|", "|", "|"]) +# you may want to change the color map for different figures +COLOR_MAP = ( + '#F15854', '#5DA5DA', '#60BD68', '#B276B2', '#DECF3F', '#F17CB0', '#B2912F', '#FAA43A', '#AFAFAF', '#087878', + '#783456', + '#560012', '#431256', "#00AABB", "#AA00BB") +# you may want to change the patterns for different figures +PATTERNS = (["|", "\\", "/", "+", "-", ".", "*", "x", "o", "O", "////", ".", "|||", "o", "---", "+", "\\\\", "*"]) +LABEL_WEIGHT = 'bold' +LINE_COLORS = COLOR_MAP +LINE_WIDTH = 3.0 +MARKER_SIZE = 13.0 +MARKER_FREQUENCY = 1000 + +# matplotlib.rcParams['ps.useafm'] = True +# matplotlib.rcParams['pdf.use14corefonts'] = True +# matplotlib.rcParams['xtick.labelsize'] = TICK_FONT_SIZE +# matplotlib.rcParams['ytick.labelsize'] = TICK_FONT_SIZE +# 创建字体列表 +# Explicitly specify the font path +# 获取系统中可用的字体路径 +font_paths = fm.findSystemFonts() + +# 创建字体列表 +font_list = [] +for font_path in font_paths: + try: + font_name = fm.FontProperties(fname=font_path).get_name() + if font_name not in font_list: + font_list.append(font_name) + except: + pass + +# 配置 matplotlib 使用系统中可用的字体 +plt.rcParams['font.family'] = font_list[0] + +FIGURE_FOLDER = '/data1/xtra/results/figure' + + +# there are some embedding problems if directly exporting the pdf figure using matplotlib. +# so we generate the eps format first and convert it to pdf. +def ConvertEpsToPdf(dir_filename): + os.system("epstopdf --outfile " + dir_filename + ".pdf " + dir_filename + ".eps") + os.system("rm -rf " + dir_filename + ".eps") + + +def DrawLegend(legend_labels, filename): + fig = pylab.figure() + ax1 = fig.add_subplot(111) + FIGURE_LABEL = legend_labels + LINE_WIDTH = 8.0 + MARKER_SIZE = 12.0 + LEGEND_FP = FontProperties(style='normal', size=26) + + figlegend = pylab.figure(figsize=(12, 0.5)) + idx = 0 + lines = [None] * (len(FIGURE_LABEL)) + data = [1] + x_values = [1] + + idx = 0 + for group in range(len(FIGURE_LABEL)): + lines[idx], = ax1.plot(x_values, data, + color=LINE_COLORS[idx], linewidth=LINE_WIDTH, + marker=MARKERS[idx], markersize=MARKER_SIZE, label=str(group)) + idx = idx + 1 + + # LEGEND + figlegend.legend(lines, FIGURE_LABEL, prop=LEGEND_FP, + loc=1, ncol=len(FIGURE_LABEL), mode="expand", shadow=False, + frameon=False, borderaxespad=0.0, handlelength=2) + + if not os.path.exists(FIGURE_FOLDER): + os.makedirs(FIGURE_FOLDER) + # no need to export eps in this case. + figlegend.savefig(filename + '.pdf') + + +# draw a line chart +def DrawFigure(xvalues, yvalues, legend_labels, x_label, y_label, y_min, y_max, filename, allow_legend): + # you may change the figure size on your own. + fig = plt.figure(figsize=(10, 3)) + figure = fig.add_subplot(111) + + FIGURE_LABEL = legend_labels + + x_values = xvalues + y_values = yvalues + + lines = [None] * (len(FIGURE_LABEL)) + for i in range(len(y_values)): + lines[i], = figure.plot(x_values[i], y_values[i], color=LINE_COLORS[i], \ + linewidth=LINE_WIDTH, marker=MARKERS[i], \ + markersize=MARKER_SIZE, label=FIGURE_LABEL[i]) + + # sometimes you may not want to draw legends. + if allow_legend == True: + plt.legend(lines, + FIGURE_LABEL, + prop=LEGEND_FP, + loc='upper center', + ncol=3, + # mode='expand', + bbox_to_anchor=(0.55, 1.6), shadow=False, + columnspacing=0.1, + frameon=True, borderaxespad=0.0, handlelength=1.5, + handletextpad=0.1, + labelspacing=0.1) + plt.xscale('log') + plt.yscale('log') + # plt.yscale('log') + + # you may control the limits on your own. + + # plt.ylim(y_min, y_max) + + plt.grid(axis='y', color='gray') + figure.yaxis.set_major_locator(LogLocator(base=10)) + figure.xaxis.set_major_locator(LogLocator(base=10)) + + figure.get_xaxis().set_tick_params(direction='in', pad=10) + figure.get_yaxis().set_tick_params(direction='in', pad=10) + + plt.xlabel(x_label, fontproperties=LABEL_FP) + plt.ylabel(y_label, fontproperties=LABEL_FP) + + size = fig.get_size_inches() + dpi = fig.get_dpi() + + # plt.show() + plt.savefig(filename + ".pdf", bbox_inches='tight') + + +# draw a line chart +def DrawFigureXYnormal(xvalues, yvalues, legend_labels, x_label, y_label, y_min, y_max, filename, allow_legend): + # you may change the figure size on your own. + fig = plt.figure(figsize=(10, 3)) + figure = fig.add_subplot(111) + + FIGURE_LABEL = legend_labels + + x_values = xvalues + y_values = yvalues + + lines = [None] * (len(FIGURE_LABEL)) + for i in range(len(y_values)): + lines[i], = figure.plot(x_values[i], y_values[i], color=LINE_COLORS[i], \ + linewidth=LINE_WIDTH, marker=MARKERS[i], \ + markersize=MARKER_SIZE, label=FIGURE_LABEL[i], markeredgecolor='k') + + # sometimes you may not want to draw legends. + if allow_legend == True: + plt.legend(lines, + FIGURE_LABEL, + prop=LEGEND_FP, + loc='upper center', + ncol=3, + bbox_to_anchor=(0.55, 1.5), shadow=False, + columnspacing=0.1, + frameon=True, borderaxespad=0, handlelength=1.2, + handletextpad=0.1, + labelspacing=0.1) + # plt.xscale('log') + # plt.yscale('log') + # plt.yscale('log') + + # you may control the limits on your own. + + # plt.ylim(y_min, y_max) + + plt.grid(axis='y', color='gray') + plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号 + # figure.yaxis.set_major_locator(LogLocator(base=10)) + # figure.xaxis.set_major_locator(LogLocator(base=10)) + plt.xticks(rotation=0, fontsize=TICK_FONT_SIZE) + figure.get_xaxis().set_tick_params(direction='in', pad=10) + figure.get_yaxis().set_tick_params(direction='in', pad=10) + + plt.xlabel(x_label, fontproperties=LABEL_FP) + plt.ylabel(y_label, fontproperties=LABEL_FP) + + size = fig.get_size_inches() + dpi = fig.get_dpi() + + # plt.show() + plt.savefig(filename + ".pdf", bbox_inches='tight') + + +# draw a line chart +def DrawFigureYnormal(xvalues, yvalues, legend_labels, x_label, y_label, y_min, y_max, filename, allow_legend): + # you may change the figure size on your own. + fig = plt.figure(figsize=(10, 3)) + figure = fig.add_subplot(111) + + FIGURE_LABEL = legend_labels + + x_values = xvalues + y_values = yvalues + + lines = [None] * (len(FIGURE_LABEL)) + for i in range(len(y_values)): + lines[i], = figure.plot(x_values[i], y_values[i], color=LINE_COLORS[i], \ + linewidth=LINE_WIDTH, marker=MARKERS[i], \ + markersize=MARKER_SIZE, label=FIGURE_LABEL[i], markeredgecolor='k') + + # sometimes you may not want to draw legends. + if allow_legend == True: + plt.legend(lines, + FIGURE_LABEL, + prop=LEGEND_FP, + loc='upper center', + ncol=3, + bbox_to_anchor=(0.55, 1.5), shadow=False, + columnspacing=0.1, + frameon=True, borderaxespad=0, handlelength=1.2, + handletextpad=0.1, + labelspacing=0.1) + plt.xscale('log') + # plt.yscale('log') + # plt.yscale('log') + + # you may control the limits on your own. + yMax=np.max(y_values) + plt.ylim(y_min, yMax*1.2) + + plt.grid(axis='y', color='gray') + plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号 + figure.yaxis.set_major_formatter(mtick.FormatStrFormatter('%.1f')) + + figure.yaxis.set_major_locator(LinearLocator(numticks=5)) + # figure.xaxis.set_major_locator(LogLocator(base=10)) + plt.xticks(rotation=0, fontsize=TICK_FONT_SIZE) + figure.get_xaxis().set_tick_params(direction='in', pad=10) + figure.get_yaxis().set_tick_params(direction='in', pad=10) + + plt.xlabel(x_label, fontproperties=LABEL_FP) + plt.ylabel(y_label, fontproperties=LABEL_FP) + + size = fig.get_size_inches() + dpi = fig.get_dpi() + + # plt.show() + plt.savefig(filename + ".pdf", bbox_inches='tight') + + +# example for reading csv file +def ReadFile(): + y = [] + col1 = [] + col2 = [] + col3 = [] + col4 = [] + col5 = [] + col6 = [] + col7 = [] + col8 = [] + + for id in it.chain(range(28, 32)): + file = '/data1/xtra/results/timestamps/PRJ_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(len(read) - 1).strip("\n")) # get last timestamp + value = len(read) / x # get throughput (#items/ms) + col1.append(value) + y.append(col1) + + for id in it.chain(range(28, 32)): + file = '/data1/xtra/results/timestamps/NPJ_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(len(read) - 1).strip("\n")) # get last timestamp + value = len(read) / x # get throughput (#items/ms) + col2.append(value) + y.append(col2) + + for id in it.chain(range(28, 32)): + file = '/data1/xtra/results/timestamps/MPASS_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(len(read) - 1).strip("\n")) # get last timestamp + value = len(read) / x # get throughput (#items/ms) + col3.append(value) + y.append(col3) + + for id in it.chain(range(28, 32)): + file = '/data1/xtra/results/timestamps/MWAY_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(len(read) - 1).strip("\n")) # get last timestamp + value = len(read) / x # get throughput (#items/ms) + col4.append(value) + y.append(col4) + + for id in it.chain(range(28, 32)): + file = '/data1/xtra/results/timestamps/SHJ_JM_NP_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(len(read) - 1).strip("\n")) # get last timestamp + value = len(read) / x # get throughput (#items/ms) + col5.append(value) + y.append(col5) + + for id in it.chain(range(28, 32)): + file = '/data1/xtra/results/timestamps/SHJ_JBCR_NP_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(len(read) - 1).strip("\n")) # get last timestamp + value = len(read) / x # get throughput (#items/ms) + col6.append(value) + y.append(col6) + + for id in it.chain(range(28, 32)): + file = '/data1/xtra/results/timestamps/PMJ_JM_NP_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(len(read) - 1).strip("\n")) # get last timestamp + value = len(read) / x # get throughput (#items/ms) + col7.append(value) + y.append(col7) + + for id in it.chain(range(28, 32)): + file = '/data1/xtra/results/timestamps/PMJ_JBCR_NP_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(len(read) - 1).strip("\n")) # get last timestamp + value = len(read) / x # get throughput (#items/ms) + col8.append(value) + y.append(col8) + return y + + +if __name__ == "__main__": + # x_values = ['Unique', 'Zipf(0)', 'Zipf(0.2)', 'Zipf(0.4)', 'Zipf(0.8)', 'Zipf(1)'] + x_values = [1600, 3200, 6400, 12800, 25600] + + y_values = ReadFile() + + legend_labels = ['NPJ', 'PRJ', 'MWAY', 'MPASS', 'SHJ$^{JM}$', 'SHJ$^{JB}$', 'PMJ$^{JM}$', + 'PMJ$^{JB}$'] + + DrawFigure(x_values, y_values, legend_labels, + 'Input arrival rate of R (e/ms)', 'Tpt. (#matches/ms)', x_values[0], + x_values[4], 'throughput_figure1_1', False) + +# DrawLegend(legend_labels, 'factor_legend') diff --git a/benchmark/scripts/scanEventRateSingleThreadStream/perfRu.csv b/benchmark/scripts/scanEventRateSingleThreadStream/perfRu.csv new file mode 100644 index 00000000..9ee43623 --- /dev/null +++ b/benchmark/scripts/scanEventRateSingleThreadStream/perfRu.csv @@ -0,0 +1,7 @@ +key,value,type +cacheMiss,12491377,U64 +cacheRefs,35327177,U64 +cpuClock,2478812100,U64 +cpuCycle,9889422467,U64 +instructions,16997458033,U64 +taskClock,2478813000,U64 diff --git a/benchmark/scripts/scanEventRateSingleThreadStream2S/OoOCommon.py b/benchmark/scripts/scanEventRateSingleThreadStream2S/OoOCommon.py new file mode 100755 index 00000000..70f4cf33 --- /dev/null +++ b/benchmark/scripts/scanEventRateSingleThreadStream2S/OoOCommon.py @@ -0,0 +1,125 @@ +import csv +import numpy as np +import matplotlib.pyplot as plt +import itertools as it +import os + +import matplotlib +import matplotlib.pyplot as plt +import numpy as np +import pylab +from matplotlib.font_manager import FontProperties +from matplotlib.ticker import LogLocator, LinearLocator +import os +import pandas as pd +import sys +import matplotlib.ticker as mtick + +OPT_FONT_NAME = 'Helvetica' +TICK_FONT_SIZE = 22 +LABEL_FONT_SIZE = 28 +LEGEND_FONT_SIZE = 30 +LABEL_FP = FontProperties(style='normal', size=LABEL_FONT_SIZE) +LEGEND_FP = FontProperties(style='normal', size=LEGEND_FONT_SIZE) +TICK_FP = FontProperties(style='normal', size=TICK_FONT_SIZE) + +MARKERS = (['*', '|', 'v', "^", "", "h", "<", ">", "+", "d", "<", "|", "", "+", "_"]) +# you may want to change the color map for different figures +COLOR_MAP = ( + '#B03A2E', '#2874A6', '#239B56', '#7D3C98', '#FFFFFF', '#F1C40F', '#F5CBA7', '#82E0AA', '#AEB6BF', '#AA4499') +# you may want to change the patterns for different figures +PATTERNS = (["////", "o", "", "||", "-", "//", "\\", "o", "O", "////", ".", "|||", "o", "---", "+", "\\\\", "*"]) +LABEL_WEIGHT = 'bold' +LINE_COLORS = COLOR_MAP +LINE_WIDTH = 3.0 +MARKER_SIZE = 15.0 +MARKER_FREQUENCY = 1000 + + +def editConfig(src, dest, key, value): + df = pd.read_csv(src, header=None) + rowIdx = 0 + idxt = 0 + for cell in df[0]: + # print(cell) + if (cell == key): + rowIdx = idxt + break + idxt = idxt + 1 + df[1][rowIdx] = str(value) + df.to_csv(dest, index=False, header=False) + + +def readConfig(src, key): + df = pd.read_csv(src, header=None) + rowIdx = 0 + idxt = 0 + for cell in df[0]: + # print(cell) + if (cell == key): + rowIdx = idxt + break + idxt = idxt + 1 + return df[1][rowIdx] + + +def draw2yLine(NAME, Com, R1, R2, l1, l2, m1, m2, fname): + fig, ax1 = plt.subplots(figsize=(10, 6.4)) + lines = [None] * 2 + # ax1.set_ylim(0, 1) + print(Com) + print(R1) + lines[0], = ax1.plot(Com, R1, color=LINE_COLORS[0], \ + linewidth=LINE_WIDTH, marker=MARKERS[0], \ + markersize=MARKER_SIZE + # + ) + + # plt.show() + ax1.set_ylabel(m1, fontproperties=LABEL_FP) + ax1.set_xlabel(NAME, fontproperties=LABEL_FP) + # ax1.set_xticklabels(ax1.get_xticklabels()) # 设置共用的x轴 + plt.xticks(rotation=0, size=TICK_FONT_SIZE) + plt.yticks(rotation=0, size=TICK_FONT_SIZE) + plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号 + ax2 = ax1.twinx() + + # ax2.set_ylabel('latency/us') + # ax2.set_ylim(0, 0.5) + lines[1], = ax2.plot(Com, R2, color=LINE_COLORS[1], \ + linewidth=LINE_WIDTH, marker=MARKERS[1], \ + markersize=MARKER_SIZE) + + ax2.set_ylabel(m2, fontproperties=LABEL_FP) + # ax2.vlines(192000, min(R2)-1, max(R2)+1, colors = "GREEN", linestyles = "dashed",label='total L1 size') + # plt.grid(axis='y', color='gray') + + # style = dict(size=10, color='black') + # ax2.hlines(tset, 0, x2_list[len(x2_list)-1]+width, colors = "r", linestyles = "dashed",label="tset") + # ax2.text(4, tset, "$T_{set}$="+str(tset)+"us", ha='right', **style) + + # plt.xlabel('batch', fontproperties=LABEL_FP) + + # plt.xscale('log') + # figure.xaxis.set_major_locator(LinearLocator(5)) + ax1.yaxis.set_major_locator(LinearLocator(5)) + ax2.yaxis.set_major_locator(LinearLocator(5)) + ax1.yaxis.set_major_formatter(mtick.FormatStrFormatter('%.1f')) + ax2.yaxis.set_major_formatter(mtick.FormatStrFormatter('%.1f')) + ax1.yaxis.set_major_formatter(mtick.FormatStrFormatter('%.1f')) + ax2.yaxis.set_major_formatter(mtick.FormatStrFormatter('%.1f')) + plt.legend(lines, + [l1, l2], + prop=LEGEND_FP, + loc='upper center', + ncol=1, + bbox_to_anchor=(0.55, 1.3 + ), shadow=False, + columnspacing=0.1, + frameon=True, borderaxespad=-1.5, handlelength=1.2, + handletextpad=0.1, + labelspacing=0.1) + plt.yticks(rotation=0, size=TICK_FONT_SIZE) + plt.tight_layout() + + plt.savefig(fname + ".pdf") diff --git a/benchmark/scripts/scanEventRateSingleThreadStream2S/accuBar.py b/benchmark/scripts/scanEventRateSingleThreadStream2S/accuBar.py new file mode 100755 index 00000000..638c8a60 --- /dev/null +++ b/benchmark/scripts/scanEventRateSingleThreadStream2S/accuBar.py @@ -0,0 +1,328 @@ +import getopt +import os +import sys + +import matplotlib +import matplotlib.pyplot as plt +import numpy as np +import pylab +from matplotlib.font_manager import FontProperties +from matplotlib.ticker import LinearLocator, LogLocator, MaxNLocator, ScalarFormatter +from numpy import double + +OPT_FONT_NAME = 'Helvetica' +TICK_FONT_SIZE = 24 +LABEL_FONT_SIZE = 28 +LEGEND_FONT_SIZE = 26 +TITLE_FRONT_SIZE = 32 +LABEL_FP = FontProperties(style='normal', size=LABEL_FONT_SIZE) +LEGEND_FP = FontProperties(style='normal', size=LEGEND_FONT_SIZE) +TICK_FP = FontProperties(style='normal', size=TICK_FONT_SIZE) +TITLE_FP = FontProperties(style='normal', size=TITLE_FRONT_SIZE) +MARKERS = (['o', 's', 'v', "^", "h", "v", ">", "x", "d", "<", "|", "", "|", "_"]) +# you may want to change the color map for different figures +COLOR_MAP = ('#B03A2E', '#2874A6', '#239B56', '#7D3C98', '#F1C40F', '#F5CBA7', '#82E0AA', '#AEB6BF', '#AA4499') +# you may want to change the patterns for different figures +PATTERNS = (["\\", "///", "o", "||", "\\\\", "\\\\", "//////", "//////", ".", "\\\\\\", "\\\\\\"]) +LABEL_WEIGHT = 'bold' +LINE_COLORS = COLOR_MAP +LINE_WIDTH = 3.0 +MARKER_SIZE = 0.0 +MARKER_FREQUENCY = 1000 + +matplotlib.rcParams['ps.useafm'] = True +matplotlib.rcParams['pdf.use14corefonts'] = True +matplotlib.rcParams['xtick.labelsize'] = TICK_FONT_SIZE +matplotlib.rcParams['ytick.labelsize'] = TICK_FONT_SIZE +matplotlib.rcParams['font.family'] = OPT_FONT_NAME +matplotlib.rcParams['pdf.fonttype'] = 42 + +exp_dir = "/data1/xtra" + +FIGURE_FOLDER = exp_dir + '/results/figure' + + +# there are some embedding problems if directly exporting the pdf figure using matplotlib. +# so we generate the eps format first and convert it to pdf. +def ConvertEpsToPdf(dir_filename): + os.system("epstopdf --outfile " + dir_filename + ".pdf " + dir_filename + ".eps") + os.system("rm -rf " + dir_filename + ".eps") + + +class ScalarFormatterForceFormat(ScalarFormatter): + def _set_format(self): # Override function that finds format to use. + self.format = "%1.1f" # Give format here + + +# draw a line chart +def DrawFigure(x_values, y_values, legend_labels, x_label, y_label, filename, allow_legend, title): + # you may change the figure size on your own. + + fig = plt.figure(figsize=(16, 9)) + figure = fig.add_subplot(111) + + FIGURE_LABEL = legend_labels + + # if not os.path.exists(FIGURE_FOLDER): + # os.makedirs(FIGURE_FOLDER) + + # values in the x_xis + index = np.arange(len(x_values)) + # the bar width. + # you may need to tune it to get the best figure. + width = 0.5 + # draw the bars + bottom_base = np.zeros(len(y_values[0])) + bars = [None] * (len(FIGURE_LABEL)) + for i in range(len(y_values)): + bars[i] = plt.bar(index + width / 2, y_values[i], width, hatch=PATTERNS[i], color=LINE_COLORS[i], + label=FIGURE_LABEL[i], bottom=bottom_base, edgecolor='black', linewidth=3) + bottom_base = np.array(y_values[i]) + bottom_base + + # sometimes you may not want to draw legends. + if allow_legend == True: + plt.legend(bars, FIGURE_LABEL + # mode='expand', + # shadow=False, + # columnspacing=0.25, + # labelspacing=-2.2, + # borderpad=5, + # bbox_transform=ax.transAxes, + # frameon=False, + # columnspacing=5.5, + # handlelength=2, + ) + if allow_legend == True: + handles, labels = figure.get_legend_handles_labels() + if allow_legend == True: + leg = plt.legend(handles[::-1], labels[::-1], + loc='upper center', + # prop=#LEGEND_FP, + # ncol=3, + # bbox_to_anchor=(0.5, 1.25), + # bbox_to_anchor=(1.17, 0.5), + # handletextpad=0.1, + # borderaxespad=0.0, + # handlelength=1.8, + # labelspacing=0.3, + # columnspacing=0.3, + ) + leg.get_frame().set_linewidth(2) + leg.get_frame().set_edgecolor("black") + + # you may need to tune the xticks position to get the best figure. + plt.xticks(index + 0.6 * width, x_values) + yfmt = ScalarFormatterForceFormat() + yfmt.set_powerlimits((0, 0)) + figure.get_yaxis().set_major_formatter(yfmt) + plt.ticklabel_format(axis="y", style="sci", scilimits=(0, 0), useMathText=True) + plt.grid(axis='y', color='gray') + figure.yaxis.set_major_locator(LinearLocator(3)) + # figure.yaxis.set_major_locator(LogLocator(base=10)) + # figure.yaxis.set_major_locator(LinearLocator(6)) + + figure.get_xaxis().set_tick_params(direction='in', pad=10) + figure.get_yaxis().set_tick_params(direction='in', pad=10) + + plt.xlabel(x_label, fontproperties=LABEL_FP) + plt.ylabel(y_label, fontproperties=LABEL_FP) + + size = fig.get_size_inches() + dpi = fig.get_dpi() + plt.title(title, fontproperties=TITLE_FP) + plt.savefig(filename + ".pdf", bbox_inches='tight', format='pdf') + + +def DrawLegend(legend_labels, filename): + fig = pylab.figure() + ax1 = fig.add_subplot(111) + FIGURE_LABEL = legend_labels + LEGEND_FP = FontProperties(style='normal', size=26) + + bars = [None] * (len(FIGURE_LABEL)) + data = [1] + x_values = [1] + + width = 0.3 + for i in range(len(FIGURE_LABEL)): + bars[i] = ax1.bar(x_values, data, width, hatch=PATTERNS[i], color=LINE_COLORS[i], + linewidth=0.2) + + # LEGEND + figlegend = pylab.figure(figsize=(11, 0.5)) + figlegend.legend(bars, FIGURE_LABEL, prop=LEGEND_FP, \ + loc=9, + bbox_to_anchor=(0, 0.4, 1, 1), + ncol=len(FIGURE_LABEL), mode="expand", shadow=False, \ + frameon=False, handlelength=1.1, handletextpad=0.2, columnspacing=0.1) + + figlegend.savefig(FIGURE_FOLDER + '/' + filename + '.pdf') + + +def normalize(y_values): + y_total_values = np.zeros(len(y_values[0])) + + for i in range(len(y_values)): + y_total_values += np.array(y_values[i]) + y_norm_values = [] + + for i in range(len(y_values)): + y_norm_values.append(np.array(y_values[i]) / (y_total_values) * 100) + return y_norm_values + + +# example for reading csv file +def ReadFile(id): + # Creates a list containing w lists, each of h items, all set to 0 + w, h = 5, 3 + y = [[0 for x in range(w)] for y in range(h)] + # print(matches) + max_value = 0 + j = 0 + bound = id + 1 * w + for i in range(id, bound, 1): + cnt = 0 + print(i) + f = open(exp_dir + "/results/breakdown/PMJ_JBCR_NP_{}.txt".format(i), "r") + read = f.readlines() + others = 0 + for x in read: + value = double(x.strip("\n")) + if value > max_value: + max_value = value + elif cnt == 3: # sort + y[0][j] = value + elif cnt == 4: # merge + y[1][j] = value + elif cnt == 5: # join + y[2][j] = value + else: + others += value + # if cnt == 6: + # y[2][j] = others + cnt += 1 + j += 1 + print(y) + return y, max_value + + +if __name__ == "__main__": + id = 119 + try: + opts, args = getopt.getopt(sys.argv[1:], '-i:h', ['test id', 'help']) + except getopt.GetoptError: + print('breakdown.py -id testid') + sys.exit(2) + for opt, opt_value in opts: + if opt in ('-h', '--help'): + print("[*] Help info") + exit() + elif opt == '-i': + print('Test ID:', opt_value) + id = (int)(opt_value) + + x_values = ['10%', '20%', '30%', '40%', '50%'] # sorting step size + + y_values, max_value = ReadFile(id) # 55 + + # y_norm_values = normalize(y_values) + + # break into 4 parts + legend_labels = ['sort', 'merge', 'join'] # , 'others' + + DrawFigure(x_values, y_values, legend_labels, + 'sorting step size', 'cycles per input tuple', + 'breakdown_sort_figure', True) + + # DrawLegend(legend_labels, 'breakdown_radix_legend') + + +def DrawPercentageFigure(x_values, y_values, legend_labels, x_label, y_label, filename, allow_legend, title): + # you may change the figure size on your own. + fig = plt.figure(figsize=(9, 3)) + figure = fig.add_subplot(111) + + FIGURE_LABEL = legend_labels + + # values in the x_xis + index = np.arange(len(x_values)) + # the bar width. + # you may need to tune it to get the best figure. + width = 0.5 + # draw the bars + bottom_base = np.zeros(len(y_values[0])) + bars = [None] * (len(FIGURE_LABEL)) + for i in range(len(y_values)): + bars[i] = plt.bar(index + width / 2, y_values[i], width, hatch=PATTERNS[i], color=LINE_COLORS[i], + label=FIGURE_LABEL[i], bottom=bottom_base, edgecolor='black', linewidth=3) + bottom_base = np.array(y_values[i]) + bottom_base + + # sometimes you may not want to draw legends. + if allow_legend == True: + plt.legend(bars, FIGURE_LABEL + # mode='expand', + # shadow=False, + # columnspacing=0.25, + # labelspacing=-2.2, + # borderpad=5, + # bbox_transform=ax.transAxes, + # frameon=False, + # columnspacing=5.5, + # handlelength=2, + ) + if allow_legend == True: + handles, labels = figure.get_legend_handles_labels() + if allow_legend == True: + print(handles[::-1], labels[::-1]) + leg = plt.legend(handles[::-1], labels[::-1], + loc='center', + prop=LEGEND_FP, + ncol=3, + bbox_to_anchor=(0.5, 1.2), + handletextpad=0.1, + borderaxespad=0.0, + handlelength=1.8, + labelspacing=0.3, + columnspacing=0.3, + ) + leg.get_frame().set_linewidth(2) + leg.get_frame().set_edgecolor("black") + + # sometimes you may not want to draw legends. + # if allow_legend == True: + # leg=plt.legend(bars, + # FIGURE_LABEL, + # prop=LEGEND_FP, + # loc='right', + # ncol=1, + # # mode='expand', + # bbox_to_anchor=(0.45, 1.1), shadow=False, + # columnspacing=0.1, + # frameon=True, borderaxespad=0.0, handlelength=1.5, + # handletextpad=0.1, + # labelspacing=0.1) + # leg.get_frame().set_linewidth(2) + # leg.get_frame().set_edgecolor("black") + + plt.ylim(0, 100) + + # you may need to tune the xticks position to get the best figure. + plt.xticks(index + 0.5 * width, x_values) + plt.xticks(rotation=30) + + # plt.xlim(0,) + # plt.ylim(0,1) + + plt.grid(axis='y', color='gray') + figure.yaxis.set_major_locator(LinearLocator(6)) + + figure.get_xaxis().set_tick_params(direction='in', pad=10) + figure.get_yaxis().set_tick_params(direction='in', pad=10) + + plt.xlabel(x_label, fontproperties=LABEL_FP) + plt.ylabel(y_label, fontproperties=LABEL_FP) + + size = fig.get_size_inches() + dpi = fig.get_dpi() + + plt.savefig(filename + ".pdf", bbox_inches='tight', format='pdf') diff --git a/benchmark/scripts/scanEventRateSingleThreadStream2S/autoParase.py b/benchmark/scripts/scanEventRateSingleThreadStream2S/autoParase.py new file mode 100755 index 00000000..9375c8e0 --- /dev/null +++ b/benchmark/scripts/scanEventRateSingleThreadStream2S/autoParase.py @@ -0,0 +1,87 @@ +import csv + + +def paraseValidStageNames(a): + nameList = [] + with open(a, 'r') as f: + reader = csv.reader(f) + # reader = [each for each in csv.DictReader(f, delimiter=',')] + result = list(reader) + rows = len(result) + # print('rows=',rows) + firstRow = result[0] + # print(firstRow) + index = 0 + # define what may attract our interest + idxCpu = 0 + idxName = 0 + for i in firstRow: + # print(i) + if (i == 'cpu'): + idxCpu = index + if (i == 'name'): + idxName = index + index = index + 1 + # read the valid stages + vdataEntries = 0 + + for k in range(1, rows): + if (result[k][idxCpu] != 'NA'): + R1 = ((result[k][idxName])) + nameList.append(R1) + return nameList + + +def paraseValidColums(a, nameList, colTitle): + with open(a, 'r') as f: + reader = csv.reader(f) + # reader = [each for each in csv.DictReader(f, delimiter=',')] + result = list(reader) + rows = len(result) + # print('rows=',rows) + firstRow = result[0] + # print(firstRow) + index = 0 + # define what may attract our interest + idxCpu = 0 + idxName = 0 + idxTitle = 0 + for i in firstRow: + # print(i) + if (i == 'cpu'): + idxCpu = index + if (i == 'name'): + idxName = index + if (i == colTitle): + idxTitle = index + index = index + 1 + # read the valid stages + vdataEntries = 0 + ru = [] + for k in range(1, rows): + if (result[k][idxCpu] != 'NA'): + R1 = ((result[k][idxName])) + for j in range(len(nameList)): + if (R1 == nameList[j]): + s = int(result[k][idxTitle]) + ru.append(s) + break + return ru + + +def maxInList(a): + # a in [[1,2] [3,4]] + inLen = len(a[0]) + ru = [] + index = [] + ti = 0 + for i in range(len(a[0])): + ts = 0 + ti = 0 + for k in range(len(a)): + if (a[k][i] > ts): + ts = a[k][i] + ti = k + ru.append(ts) + index.append(ti) + return ru, index diff --git a/benchmark/scripts/scanEventRateSingleThreadStream2S/config_BCoAMM.csv b/benchmark/scripts/scanEventRateSingleThreadStream2S/config_BCoAMM.csv new file mode 100644 index 00000000..b65317af --- /dev/null +++ b/benchmark/scripts/scanEventRateSingleThreadStream2S/config_BCoAMM.csv @@ -0,0 +1,6 @@ +key,value,type +aRow,100,U64 +aCol,1000,U64 +bCol,500,U64 +sketchDimension,25,U64 +ptFile,torchscripts/BetaCoOccurringFD.pt,String \ No newline at end of file diff --git a/benchmark/scripts/scanEventRateSingleThreadStream2S/config_BerCRS.csv b/benchmark/scripts/scanEventRateSingleThreadStream2S/config_BerCRS.csv new file mode 100644 index 00000000..d2b758a5 --- /dev/null +++ b/benchmark/scripts/scanEventRateSingleThreadStream2S/config_BerCRS.csv @@ -0,0 +1,6 @@ +key,value,type +aRow,100,U64 +aCol,1000,U64 +bCol,500,U64 +sketchDimension,25,U64 +ptFile,torchscripts/BernoulliCRS.pt,String \ No newline at end of file diff --git a/benchmark/scripts/scanEventRateSingleThreadStream2S/config_CPPBCOOFD.csv b/benchmark/scripts/scanEventRateSingleThreadStream2S/config_CPPBCOOFD.csv new file mode 100644 index 00000000..d43c92f9 --- /dev/null +++ b/benchmark/scripts/scanEventRateSingleThreadStream2S/config_CPPBCOOFD.csv @@ -0,0 +1,10 @@ +key,value,type +aRow,100,U64 +aCol,1000,U64 +bCol,500,U64 +sketchDimension,25,U64 +ptFile,torchscripts/BetaCoOccurringFD.pt,String +useCPP,1,U64 +cppAlgoTag,bcooFD,String +threads,1,U64 +forceMP,1,U64 diff --git a/benchmark/scripts/scanEventRateSingleThreadStream2S/config_CPPBCRS.csv b/benchmark/scripts/scanEventRateSingleThreadStream2S/config_CPPBCRS.csv new file mode 100644 index 00000000..3b7ee42c --- /dev/null +++ b/benchmark/scripts/scanEventRateSingleThreadStream2S/config_CPPBCRS.csv @@ -0,0 +1,10 @@ +key,value,type +aRow,100,U64 +aCol,1000,U64 +bCol,500,U64 +sketchDimension,25,U64 +ptFile,torchscripts/BernoulliCRS.pt,String +useCPP,1,U64 +cppAlgoTag,bcrs,String +threads,1,U64 +forceMP,1,U64 diff --git a/benchmark/scripts/scanEventRateSingleThreadStream2S/config_CPPCOFD.csv b/benchmark/scripts/scanEventRateSingleThreadStream2S/config_CPPCOFD.csv new file mode 100644 index 00000000..cf8f066a --- /dev/null +++ b/benchmark/scripts/scanEventRateSingleThreadStream2S/config_CPPCOFD.csv @@ -0,0 +1,10 @@ +key,value,type +aRow,100,U64 +aCol,1000,U64 +bCol,500,U64 +sketchDimension,25,U64 +ptFile,torchscripts/CRS.pt,String +useCPP,1,U64 +cppAlgoTag,cooFD,String +threads,1,U64 +forceMP,1,U64 \ No newline at end of file diff --git a/benchmark/scripts/scanEventRateSingleThreadStream2S/config_CPPCOUNTERSKETCH.csv b/benchmark/scripts/scanEventRateSingleThreadStream2S/config_CPPCOUNTERSKETCH.csv new file mode 100644 index 00000000..6823e4e2 --- /dev/null +++ b/benchmark/scripts/scanEventRateSingleThreadStream2S/config_CPPCOUNTERSKETCH.csv @@ -0,0 +1,10 @@ +key,value,type +aRow,100,U64 +aCol,1000,U64 +bCol,500,U64 +sketchDimension,25,U64 +ptFile,torchscripts/CRS.pt,String +useCPP,1,U64 +cppAlgoTag,countSketch,String +threads,1,U64 +forceMP,1,U64 diff --git a/benchmark/scripts/scanEventRateSingleThreadStream2S/config_CPPCRS.csv b/benchmark/scripts/scanEventRateSingleThreadStream2S/config_CPPCRS.csv new file mode 100644 index 00000000..e6dff688 --- /dev/null +++ b/benchmark/scripts/scanEventRateSingleThreadStream2S/config_CPPCRS.csv @@ -0,0 +1,14 @@ +key,value,type +aRow,1000,U64 +aCol,1000,U64 +bCol,1000,U64 +sketchDimension,25,U64 +ptFile,torchscripts/CRS.pt,String +useCPP,1,U64 +cppAlgoTag,crs,String +threads,1,U64 +forceMP,1,U64 +eventRateTps,200,U64 +isStreaming,1,U64 +streamingTwoMatrixes,1,U64 +batchSize,100,U64 \ No newline at end of file diff --git a/benchmark/scripts/scanEventRateSingleThreadStream2S/config_CPPINT8.csv b/benchmark/scripts/scanEventRateSingleThreadStream2S/config_CPPINT8.csv new file mode 100644 index 00000000..2b61c43e --- /dev/null +++ b/benchmark/scripts/scanEventRateSingleThreadStream2S/config_CPPINT8.csv @@ -0,0 +1,10 @@ +key,value,type +aRow,100,U64 +aCol,1000,U64 +bCol,500,U64 +sketchDimension,25,U64 +ptFile,torchscripts/FDAMM.pt,String +useCPP,1,U64 +cppAlgoTag,int8,String +threads,1,U64 +forceMP,1,U64 diff --git a/benchmark/scripts/scanEventRateSingleThreadStream2S/config_CPPMM.csv b/benchmark/scripts/scanEventRateSingleThreadStream2S/config_CPPMM.csv new file mode 100644 index 00000000..e844ccd8 --- /dev/null +++ b/benchmark/scripts/scanEventRateSingleThreadStream2S/config_CPPMM.csv @@ -0,0 +1,14 @@ +key,value,type +aRow,1000,U64 +aCol,1000,U64 +bCol,1000,U64 +sketchDimension,25,U64 +ptFile,torchscripts/RAWMM.pt,String +useCPP,1,U64 +cppAlgoTag,mm,String +threads,1,U64 +forceMP,1,U64 +eventRateTps,200,U64 +isStreaming,1,U64 +streamingTwoMatrixes,1,U64 +batchSize,100,U64 \ No newline at end of file diff --git a/benchmark/scripts/scanEventRateSingleThreadStream2S/config_CPPSMPPCA.csv b/benchmark/scripts/scanEventRateSingleThreadStream2S/config_CPPSMPPCA.csv new file mode 100644 index 00000000..406a960e --- /dev/null +++ b/benchmark/scripts/scanEventRateSingleThreadStream2S/config_CPPSMPPCA.csv @@ -0,0 +1,12 @@ +key,value,type +aRow,1000,U64 +aCol,1000,U64 +bCol,1000,U64 +sketchDimension,25,U64 +threads,1,U64 +useCPP,1,U64 +cppAlgoTag,smp-pca,String +isStreaming,1,U64 +streamingTwoMatrixes,1,U64 +batchSize,100,U64 +eventRateTps,200,U64 \ No newline at end of file diff --git a/benchmark/scripts/scanEventRateSingleThreadStream2S/config_CoAMM.csv b/benchmark/scripts/scanEventRateSingleThreadStream2S/config_CoAMM.csv new file mode 100644 index 00000000..f41d7ddd --- /dev/null +++ b/benchmark/scripts/scanEventRateSingleThreadStream2S/config_CoAMM.csv @@ -0,0 +1,6 @@ +key,value,type +aRow,100,U64 +aCol,1000,U64 +bCol,500,U64 +sketchDimension,25,U64 +ptFile,torchscripts/CoOccurringFD.pt,String \ No newline at end of file diff --git a/benchmark/scripts/scanEventRateSingleThreadStream2S/config_CounterSketch.csv b/benchmark/scripts/scanEventRateSingleThreadStream2S/config_CounterSketch.csv new file mode 100644 index 00000000..50cf2770 --- /dev/null +++ b/benchmark/scripts/scanEventRateSingleThreadStream2S/config_CounterSketch.csv @@ -0,0 +1,6 @@ +key,value,type +aRow,100,U64 +aCol,1000,U64 +bCol,500,U64 +sketchDimension,25,U64 +ptFile,torchscripts/CountSketch.pt,String diff --git a/benchmark/scripts/scanEventRateSingleThreadStream2S/drawTogether.py b/benchmark/scripts/scanEventRateSingleThreadStream2S/drawTogether.py new file mode 100755 index 00000000..2d2e4ff2 --- /dev/null +++ b/benchmark/scripts/scanEventRateSingleThreadStream2S/drawTogether.py @@ -0,0 +1,180 @@ +#!/usr/bin/env python3 +#I will streaming A and B!!! +import csv +import numpy as np +import accuBar as accuBar +import groupBar as groupBar +import groupBar2 as groupBar2 +import groupLine as groupLine +from autoParase import * +import itertools as it +import os + +import matplotlib +import numpy as np +import pylab +import matplotlib.font_manager as fm +from matplotlib.font_manager import FontProperties +from matplotlib.ticker import LogLocator, LinearLocator +import os +import pandas as pd +import sys +from OoOCommon import * +import time + +# OPT_FONT_NAME = 'Helvetica' +TICK_FONT_SIZE = 22 +LABEL_FONT_SIZE = 28 +LEGEND_FONT_SIZE = 30 +LABEL_FP = FontProperties(style='normal', size=LABEL_FONT_SIZE) +LEGEND_FP = FontProperties(style='normal', size=LEGEND_FONT_SIZE) +TICK_FP = FontProperties(style='normal', size=TICK_FONT_SIZE) + +MARKERS = (['*', '|', 'v', "^", "", "h", "<", ">", "+", "d", "<", "|", "", "+", "_"]) +# you may want to change the color map for different figures +COLOR_MAP = ( + '#B03A2E', '#2874A6', '#239B56', '#7D3C98', '#FFFFFF', '#F1C40F', '#F5CBA7', '#82E0AA', '#AEB6BF', '#AA4499') +# you may want to change the patterns for different figures +PATTERNS = (["////", "o", "", "||", "-", "//", "\\", "o", "O", "////", ".", "|||", "o", "---", "+", "\\\\", "*"]) +LABEL_WEIGHT = 'bold' +LINE_COLORS = COLOR_MAP +LINE_WIDTH = 3.0 +MARKER_SIZE = 15.0 +MARKER_FREQUENCY = 1000 + +matplotlib.rcParams['ps.useafm'] = True +matplotlib.rcParams['pdf.use14corefonts'] = True +matplotlib.rcParams['xtick.labelsize'] = TICK_FONT_SIZE +matplotlib.rcParams['ytick.labelsize'] = TICK_FONT_SIZE +matplotlib.rcParams['font.family'] = OPT_FONT_NAME +matplotlib.rcParams['pdf.fonttype'] = 42 + +scanTag = "eventRateTps" + + +def singleRun(exePath, singleValue, resultPath, configTemplate): + # resultFolder="singleValueTests" + configFname = "config_" + scanTag + str(singleValue) + ".csv" + # configTemplate = "config.csv" + # clear old files + os.system("cd " + exePath + "&& sudo rm *.csv") + + # editConfig(configTemplate, exePath + configFname, "earlierEmitMs", 0) + editConfig(configTemplate, exePath + configFname, scanTag, singleValue) + # prepare new file + # run + os.system("cd " + exePath + "&& sudo ./benchmark " + configFname) + # copy result + os.system("sudo rm -rf " + resultPath + "/" + str(singleValue)) + os.system("sudo mkdir " + resultPath + "/" + str(singleValue)) + os.system("cd " + exePath + "&& sudo cp *.csv " + resultPath + "/" + str(singleValue)) + + +def runScanVector(exePath, singleValueVec, resultPath, templateName="config.csv"): + for i in singleValueVec: + singleRun(exePath, i, resultPath, templateName) + + +def readResultSingle(singleValue, resultPath): + resultFname = resultPath + "/" + str(singleValue) + "/result_streaming.csv" + throughput = readConfig(resultFname, "throughputByElements") + lat95 = readConfig(resultFname, "95%latency") + froError = readConfig(resultFname, "froError") + errorBoundRatio = readConfig(resultFname, "errorBoundRatio") + return throughput,lat95, froError, errorBoundRatio + + +def cleanPath(path): + os.system("sudo rm -rf " + path) + os.system("sudo mkdir " + path) + + +def readResultVector(singleValueVec, resultPath): + thrVec = [] + lat95Vec= [] + froErrorVec = [] + errorBoundRatioVec = [] + for i in singleValueVec: + thr,lat95,froError, errorBoundRatio = readResultSingle(i, resultPath) + thrVec.append(float(thr)) + lat95Vec.append(float(lat95)/1000.0) + froErrorVec.append(float(froError)) + errorBoundRatioVec.append(float(errorBoundRatio)) + return np.array(thrVec), np.array(lat95Vec), np.array(froErrorVec), np.array( + errorBoundRatioVec) + + +def compareMethod(exeSpace, commonPathBase, resultPaths, csvTemplates, periodVec, reRun=1): + thrAll = [] + lat95All = [] + periodAll = [] + froAll = [] + errorBoundRatioAll = [] + for i in range(len(csvTemplates)): + resultPath = commonPathBase + resultPaths[i] + if (reRun == 1): + os.system("sudo rm -rf " + resultPath) + os.system("sudo mkdir " + resultPath) + runScanVector(exeSpace, periodVec, resultPath, csvTemplates[i]) + thr,lat95,fro, eb = readResultVector(periodVec, resultPath) + thrAll.append(thr) + lat95All.append(lat95) + + periodAll.append(periodVec) + + froAll.append(fro) + errorBoundRatioAll.append(eb) + # periodAll.append(periodVec) + return np.array(thrAll),np.array(lat95All), periodAll, np.array(froAll), np.array(errorBoundRatioAll) + + +def main(): + exeSpace = os.path.abspath(os.path.join(os.getcwd(), "../..")) + "/" + commonBase = os.path.abspath(os.path.join(os.getcwd(), "../..")) + "/results/" + scanTag+"2S" + "/" + figPath = os.path.abspath(os.path.join(os.getcwd(), "../..")) + "/figures/" + scanTag+"2s" + "CPP" + methodTags = ["CRS", "MM","SMP-PCA"] + resultPaths = ["CRS", "mm","smp-pca"] + csvTemplates = ["config_CPPCRS.csv", "config_CPPMM.csv","config_CPPSMPPCA.csv"] + valueVec = [100,200,500,1000,2000,5000] + valueVecDisp = np.array(valueVec) + # run + reRun = 0 + if (len(sys.argv) < 2): + os.system("mkdir ../../results") + os.system("mkdir ../../figures") + os.system("mkdir " + figPath) + os.system("sudo rm -rf " + commonBase) + os.system("sudo mkdir " + commonBase) + + reRun = 1 + # skech + thrAll, lat95All, periodAll, fro, eb = compareMethod(exeSpace, commonBase, resultPaths, csvTemplates, valueVec, + reRun) + groupLine.DrawFigureYnormal(periodAll, thrAll/1000.0, + methodTags, + "event rate (#rows/s)", "throughput (K elements/s)", 0, 1, + figPath + "/" + scanTag + "stream_cpp_thr", + True) + groupLine.DrawFigure(periodAll, lat95All, + methodTags, + "event rate (#rows/s)", "95% latency (ms)", 0, 1, + figPath + "/" + scanTag + "stream_cpp_lat95", + True) + groupLine.DrawFigureYnormal(periodAll, fro*100.0, + methodTags, + "event rate (#rows)", "fro error (%)", 0, 1, + figPath + "/" + scanTag + "stream_cpp_fro", + True) + + # draw2yLine("watermark time (ms)",singleValueVecDisp,lat95Vec,errVec,"95% Latency (ms)","Error","ms","",figPath+"wm_lat") + # draw2yLine("watermark time (ms)",singleValueVecDisp,thrVec,errVec,"Throughput (KTp/s)","Error","KTp/s","",figPath+"wm_thr") + # draw2yLine("watermark time (ms)",singleValueVecDisp,lat95Vec,compVec,"95% Latency (ms)","Completeness","ms","",figPath+"wm_omp") + # groupLine.DrawFigureYnormal([singleValueVec,singleValueVec],[errVec,aqpErrVec],['w/o aqp',"w/ MeanAqp"],"watermark time (ms)","Error",0,1,figPath+"wm_MeanAqp",True) + # print(errVec) + # print(aqpErrVec) + # print(elapseTimeVecFD) + # readResultsingleValue(50,resultPath) + + +if __name__ == "__main__": + main() diff --git a/benchmark/scripts/scanEventRateSingleThreadStream2S/groupBar.py b/benchmark/scripts/scanEventRateSingleThreadStream2S/groupBar.py new file mode 100755 index 00000000..ef2d9842 --- /dev/null +++ b/benchmark/scripts/scanEventRateSingleThreadStream2S/groupBar.py @@ -0,0 +1,236 @@ +import itertools as it +import os + +import matplotlib +import matplotlib.pyplot as plt +import numpy as np +import pylab +from matplotlib.font_manager import FontProperties +from matplotlib.ticker import LogLocator, LinearLocator + +OPT_FONT_NAME = 'Helvetica' +TICK_FONT_SIZE = 24 +LABEL_FONT_SIZE = 28 +LEGEND_FONT_SIZE = 15 +LABEL_FP = FontProperties(style='normal', size=LABEL_FONT_SIZE) +LEGEND_FP = FontProperties(style='normal', size=LEGEND_FONT_SIZE) +TICK_FP = FontProperties(style='normal', size=TICK_FONT_SIZE) + +MARKERS = (["", 'o', 's', 'v', "^", "", "h", "<", ">", "+", "d", "<", "|", "", "+", "_"]) +# you may want to change the color map for different figures +COLOR_MAP = ( + '#7FFFFF', '#B03A2E', '#2874A6', '#FFFFFF', '#7FFFFF', '#B03A2E', '#2874A6', '#FFFFFF', '#F5CBA7', '#82E0AA', + '#AEB6BF', + '#AA4499') +# you may want to change the patterns for different figures +PATTERNS = ( + ["////", "\\\\", "//", "o", "*", "||", "-", "//", "\\", "o", "O", "////", ".", "|||", "o", "---", "+", "\\\\", "*"]) +LABEL_WEIGHT = 'bold' +LINE_COLORS = COLOR_MAP +LINE_WIDTH = 3.0 +MARKER_SIZE = 15.0 +MARKER_FREQUENCY = 1000 + +matplotlib.rcParams['ps.useafm'] = True +matplotlib.rcParams['pdf.use14corefonts'] = True +matplotlib.rcParams['xtick.labelsize'] = TICK_FONT_SIZE +matplotlib.rcParams['ytick.labelsize'] = TICK_FONT_SIZE +matplotlib.rcParams['font.family'] = OPT_FONT_NAME +matplotlib.rcParams['pdf.fonttype'] = 42 + +exp_dir = "/data1/xtra" + +FIGURE_FOLDER = exp_dir + '/results/figure' + + +def DrawLegend(legend_labels, filename): + fig = pylab.figure() + ax1 = fig.add_subplot(111) + FIGURE_LABEL = legend_labels + LEGEND_FP = FontProperties(style='normal', size=26) + figlegend = pylab.figure(figsize=(16, 0.5)) + bars = [None] * (len(FIGURE_LABEL)) + data = [1] + x_values = [1] + + width = 0.3 + for i in range(len(FIGURE_LABEL)): + bars[i] = ax1.bar(x_values, data, width, + hatch=PATTERNS[i], + color=LINE_COLORS[i], + label=FIGURE_LABEL[i], + edgecolor='black', linewidth=3) + + # LEGEND + + figlegend.legend(bars, FIGURE_LABEL, prop=LEGEND_FP, \ + loc=1, ncol=len(FIGURE_LABEL), mode="expand", shadow=True, \ + frameon=True, handlelength=2, handletextpad=0.3, columnspacing=0.5, + borderaxespad=-0.2, fancybox=True + ) + figlegend.savefig(FIGURE_FOLDER + '/' + filename + '.pdf') + + +# draw a bar chart +def DrawFigure(x_values, y_values, legend_labels, x_label, y_label, y_min, y_max, filename, allow_legend): + # you may change the figure size on your own. + fig = plt.figure(figsize=(10, 3)) + figure = fig.add_subplot(111) + + FIGURE_LABEL = legend_labels + + # values in the x_xis + index = np.arange(len(x_values)) + # the bar width. + # you may need to tune it to get the best figure. + width = 0.08 + # draw the bars + bars = [] + ts = 0 + pos = 0 + gl = len(y_values[0]) + for i in range(len(y_values)): + pos = pos + 3 * width + for j in range(len(y_values[i])): + pos = pos + width + bar = plt.bar(pos, y_values[i][j], width, hatch=PATTERNS[j], color=LINE_COLORS[j], label=FIGURE_LABEL[j], + edgecolor='black', linewidth=3) + bars.append(bar) + ts = ts + 1 + + # sometimes you may not want to draw legends. + if allow_legend == True: + plt.legend(bars, FIGURE_LABEL, + prop=LEGEND_FP, + ncol=4, + loc='upper center', + # mode='expand', + shadow=False, + bbox_to_anchor=(0.45, 1.6), + columnspacing=0.1, + handletextpad=0.2, + # bbox_transform=ax.transAxes, + # frameon=True, + # columnspacing=5.5, + # handlelength=2, + ) + + # you may need to tune the xticks position to get the best figure. + plt.xticks(index, x_values) + # plt.ticklabel_format(axis="y", style="sci", scilimits=(0, 0)) + # plt.grid(axis='y', color='gray') + # figure.get_xaxis().set_major_formatter(matplotlib.ticker.ScalarFormatter()) + + # you may need to tune the xticks position to get the best figure. + # plt.yscale('log') + # + # plt.grid(axis='y', color='gray') + figure.yaxis.set_major_locator(LinearLocator(5)) + # figure.xaxis.set_major_locator(LinearLocator(5)) + figure.get_xaxis().set_tick_params(direction='in', pad=10) + figure.get_yaxis().set_tick_params(direction='in', pad=10) + + plt.xlabel(x_label, fontproperties=LABEL_FP) + plt.ylabel(y_label, fontproperties=LABEL_FP) + plt.ylim(y_min, y_max) + plt.savefig(filename + ".pdf", bbox_inches='tight') + + +# example for reading csv file +def ReadFile(): + y = [] + col1 = [] + col2 = [] + col3 = [] + col4 = [] + col5 = [] + col6 = [] + col7 = [] + col8 = [] + col9 = [] + + for id in it.chain(range(38, 42)): + col9.append(0) + y.append(col9) # this is a lz4_pipe2 empty line to separate eager and lazy. + + for id in it.chain(range(38, 42)): + file = exp_dir + '/results/latency/NPJ_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(int(len(read) * 0.95)).strip("\n")) # get the 99th timestamp + col1.append(x) + y.append(col1) + + for id in it.chain(range(38, 42)): + file = exp_dir + '/results/latency/PRJ_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(int(len(read) * 0.95)).strip("\n")) # get the 99th timestamp + col2.append(x) + y.append(col2) + + for id in it.chain(range(38, 42)): + file = exp_dir + '/results/latency/MWAY_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(int(len(read) * 0.95)).strip("\n")) # get the 99th timestamp + col3.append(x) + y.append(col3) + + for id in it.chain(range(38, 42)): + file = exp_dir + '/results/latency/MPASS_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(int(len(read) * 0.95)).strip("\n")) # get the 99th timestamp + col4.append(x) + y.append(col4) + + y.append(col9) # this is a lz4_pipe2 empty line to separate eager and lazy. + + for id in it.chain(range(38, 42)): + file = exp_dir + '/results/latency/SHJ_JM_NP_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(int(len(read) * 0.95)).strip("\n")) # get last timestamp + col5.append(x) + y.append(col5) + + for id in it.chain(range(38, 42)): + file = exp_dir + '/results/latency/SHJ_JBCR_NP_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(int(len(read) * 0.95)).strip("\n")) # get last timestamp + col6.append(x) + y.append(col6) + + for id in it.chain(range(38, 42)): + file = exp_dir + '/results/latency/PMJ_JM_NP_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(int(len(read) * 0.95)).strip("\n")) # get last timestamp + col7.append(x) + y.append(col7) + + for id in it.chain(range(38, 42)): + file = exp_dir + '/results/latency/PMJ_JBCR_NP_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(int(len(read) * 0.95)).strip("\n")) # get last timestamp + col8.append(x) + y.append(col8) + return y + + +if __name__ == "__main__": + x_values = ["Stock", "Rovio", "YSB", "DEBS"] + + y_values = ReadFile() + + legend_labels = ['Lazy:', 'NPJ', 'PRJ', 'MWAY', 'MPASS', + 'Eager:', 'SHJ$^{JM}$', 'SHJ$^{JB}$', 'PMJ$^{JM}$', 'PMJ$^{JB}$'] + print(y_values) + DrawFigure(x_values, y_values, legend_labels, + '', 'Latency (ms)', 0, + 400, 'latency_figure_app', False) + + # DrawLegend(legend_labels, 'latency_legend') diff --git a/benchmark/scripts/scanEventRateSingleThreadStream2S/groupBar2.py b/benchmark/scripts/scanEventRateSingleThreadStream2S/groupBar2.py new file mode 100755 index 00000000..31155695 --- /dev/null +++ b/benchmark/scripts/scanEventRateSingleThreadStream2S/groupBar2.py @@ -0,0 +1,232 @@ +import itertools as it +import os + +import matplotlib +import matplotlib.pyplot as plt +import numpy as np +import pylab +from matplotlib.font_manager import FontProperties +from matplotlib.ticker import LogLocator, LinearLocator + +OPT_FONT_NAME = 'Helvetica' +TICK_FONT_SIZE = 24 +LABEL_FONT_SIZE = 28 +LEGEND_FONT_SIZE = 30 +LABEL_FP = FontProperties(style='normal', size=LABEL_FONT_SIZE) +LEGEND_FP = FontProperties(style='normal', size=LEGEND_FONT_SIZE) +TICK_FP = FontProperties(style='normal', size=TICK_FONT_SIZE) + +MARKERS = (["", 'o', 's', 'v', "^", "", "h", "<", ">", "+", "d", "<", "|", "", "+", "_"]) +# you may want to change the color map for different figures +COLOR_MAP = ( + '#FFFFFF', '#B03A2E', '#2874A6', '#239B56', '#7D3C98', '#00FFFF', '#F1C40F', '#F5CBA7', '#82E0AA', '#AEB6BF', + '#AA4499') +# you may want to change the patterns for different figures +PATTERNS = ( + ["", "////", "\\\\", "//", "o", "", "||", "-", "//", "\\", "o", "O", "////", ".", "|||", "o", "---", "+", "\\\\", + "*"]) +LABEL_WEIGHT = 'bold' +LINE_COLORS = COLOR_MAP +LINE_WIDTH = 3.0 +MARKER_SIZE = 15.0 +MARKER_FREQUENCY = 1000 + +matplotlib.rcParams['ps.useafm'] = True +matplotlib.rcParams['pdf.use14corefonts'] = True +matplotlib.rcParams['xtick.labelsize'] = TICK_FONT_SIZE +matplotlib.rcParams['ytick.labelsize'] = TICK_FONT_SIZE +matplotlib.rcParams['font.family'] = OPT_FONT_NAME +matplotlib.rcParams['pdf.fonttype'] = 42 + +exp_dir = "/data1/xtra" + +FIGURE_FOLDER = exp_dir + '/results/figure' + + +def DrawLegend(legend_labels, filename): + fig = pylab.figure() + ax1 = fig.add_subplot(111) + FIGURE_LABEL = legend_labels + LEGEND_FP = FontProperties(style='normal', size=26) + figlegend = pylab.figure(figsize=(16, 0.5)) + bars = [None] * (len(FIGURE_LABEL)) + data = [1] + x_values = [1] + + width = 0.3 + for i in range(len(FIGURE_LABEL)): + bars[i] = ax1.bar(x_values, data, width, + hatch=PATTERNS[i], + color=LINE_COLORS[i], + label=FIGURE_LABEL[i], + edgecolor='black', linewidth=3) + + # LEGEND + + figlegend.legend(bars, FIGURE_LABEL, prop=LEGEND_FP, \ + loc=1, ncol=len(FIGURE_LABEL), mode="expand", shadow=True, \ + frameon=True, handlelength=2, handletextpad=0.3, columnspacing=0.5, + borderaxespad=-0.2, fancybox=True + ) + figlegend.savefig(FIGURE_FOLDER + '/' + filename + '.pdf') + + +# draw a bar chart +def DrawFigure(x_values, y_values, legend_labels, x_label, y_label, y_min, y_max, filename, allow_legend): + # you may change the figure size on your own. + fig = plt.figure(figsize=(10, 3)) + figure = fig.add_subplot(111) + + FIGURE_LABEL = legend_labels + + # values in the x_xis + index = np.arange(len(x_values)) + # the bar width. + # you may need to tune it to get the best figure. + width = 0.12 + # draw the bars + bars = [None] * (len(FIGURE_LABEL)) + for i in range(len(y_values)): + bars[i] = plt.bar(index + i * width + width / 2, + y_values[i], width, + hatch=PATTERNS[i], + color=LINE_COLORS[i], + label=FIGURE_LABEL[i], edgecolor='black', linewidth=3) + + # sometimes you may not want to draw legends. + if allow_legend == True: + plt.legend(bars, FIGURE_LABEL, + prop=LEGEND_FP, + ncol=3, + loc='upper center', + # mode='expand', + shadow=False, + bbox_to_anchor=(0.45, 1.7), + columnspacing=0.1, + handletextpad=0.2, + # bbox_transform=ax.transAxes, + # frameon=True, + # columnspacing=5.5, + # handlelength=2, + ) + + # you may need to tune the xticks position to get the best figure. + plt.xticks(index + 3 * width, x_values, rotation=30) + + # plt.ticklabel_format(axis="y", style="sci", scilimits=(0, 0)) + # plt.grid(axis='y', color='gray') + # figure.get_xaxis().set_major_formatter(matplotlib.ticker.ScalarFormatter()) + + # you may need to tune the xticks position to get the best figure. + # plt.yscale('log') + # + # plt.grid(axis='y', color='gray') + figure.yaxis.set_major_locator(LinearLocator(10)) + # figure.xaxis.set_major_locator(LinearLocator(5)) + figure.get_xaxis().set_tick_params(direction='in', pad=10) + figure.get_yaxis().set_tick_params(direction='in', pad=10) + + plt.xlabel(x_label, fontproperties=LABEL_FP) + plt.ylabel(y_label, fontproperties=LABEL_FP) + + plt.savefig(filename + ".pdf", bbox_inches='tight') + + +# example for reading csv file +def ReadFile(): + y = [] + col1 = [] + col2 = [] + col3 = [] + col4 = [] + col5 = [] + col6 = [] + col7 = [] + col8 = [] + col9 = [] + + for id in it.chain(range(38, 42)): + col9.append(0) + y.append(col9) # this is a fake empty line to separate eager and lazy. + + for id in it.chain(range(38, 42)): + file = exp_dir + '/results/latency/NPJ_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(int(len(read) * 0.95)).strip("\n")) # get the 99th timestamp + col1.append(x) + y.append(col1) + + for id in it.chain(range(38, 42)): + file = exp_dir + '/results/latency/PRJ_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(int(len(read) * 0.95)).strip("\n")) # get the 99th timestamp + col2.append(x) + y.append(col2) + + for id in it.chain(range(38, 42)): + file = exp_dir + '/results/latency/MWAY_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(int(len(read) * 0.95)).strip("\n")) # get the 99th timestamp + col3.append(x) + y.append(col3) + + for id in it.chain(range(38, 42)): + file = exp_dir + '/results/latency/MPASS_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(int(len(read) * 0.95)).strip("\n")) # get the 99th timestamp + col4.append(x) + y.append(col4) + + y.append(col9) # this is a fake empty line to separate eager and lazy. + + for id in it.chain(range(38, 42)): + file = exp_dir + '/results/latency/SHJ_JM_NP_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(int(len(read) * 0.95)).strip("\n")) # get last timestamp + col5.append(x) + y.append(col5) + + for id in it.chain(range(38, 42)): + file = exp_dir + '/results/latency/SHJ_JBCR_NP_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(int(len(read) * 0.95)).strip("\n")) # get last timestamp + col6.append(x) + y.append(col6) + + for id in it.chain(range(38, 42)): + file = exp_dir + '/results/latency/PMJ_JM_NP_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(int(len(read) * 0.95)).strip("\n")) # get last timestamp + col7.append(x) + y.append(col7) + + for id in it.chain(range(38, 42)): + file = exp_dir + '/results/latency/PMJ_JBCR_NP_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(int(len(read) * 0.95)).strip("\n")) # get last timestamp + col8.append(x) + y.append(col8) + return y + + +if __name__ == "__main__": + x_values = ["Stock", "Rovio", "YSB", "DEBS"] + + y_values = ReadFile() + + legend_labels = ['Lazy:', 'NPJ', 'PRJ', 'MWAY', 'MPASS', + 'Eager:', 'SHJ$^{JM}$', 'SHJ$^{JB}$', 'PMJ$^{JM}$', 'PMJ$^{JB}$'] + print(y_values) + DrawFigure(x_values, y_values, legend_labels, + '', 'Latency (ms)', 0, + 400, 'latency_figure_app', False) + + # DrawLegend(legend_labels, 'latency_legend') diff --git a/benchmark/scripts/scanEventRateSingleThreadStream2S/groupLine.py b/benchmark/scripts/scanEventRateSingleThreadStream2S/groupLine.py new file mode 100755 index 00000000..77993fc1 --- /dev/null +++ b/benchmark/scripts/scanEventRateSingleThreadStream2S/groupLine.py @@ -0,0 +1,368 @@ +import itertools as it +import os +import matplotlib.pyplot as plt +import matplotlib.font_manager as fm +import matplotlib +import numpy as np +import pylab +from matplotlib.font_manager import FontProperties +from matplotlib.ticker import MaxNLocator +from matplotlib.font_manager import FontProperties +from matplotlib.ticker import LinearLocator, LogLocator, MaxNLocator, ScalarFormatter +from numpy import double +import matplotlib.ticker as mtick +# 获取系统中可用的字体路径 +# font_paths = fm.findSystemFonts() +# OPT_FONT_NAME = 'Helvetica' +TICK_FONT_SIZE = 20 +LABEL_FONT_SIZE = 20 +LEGEND_FONT_SIZE = 20 +LABEL_FP = FontProperties(style='normal', size=LABEL_FONT_SIZE) +LEGEND_FP = FontProperties(style='normal', size=LEGEND_FONT_SIZE) +TICK_FP = FontProperties(style='normal', size=TICK_FONT_SIZE) + +MARKERS = (['o', 's', 'v', "^", "h", "v", ">", "x", "d", "<", "|", "p", "+", "_", "%", "|", "|", "|", "|", "|"]) +# you may want to change the color map for different figures +COLOR_MAP = ( + '#F15854', '#5DA5DA', '#60BD68', '#B276B2', '#DECF3F', '#F17CB0', '#B2912F', '#FAA43A', '#AFAFAF', '#087878', + '#783456', + '#560012', '#431256', "#00AABB", "#AA00BB") +# you may want to change the patterns for different figures +PATTERNS = (["|", "\\", "/", "+", "-", ".", "*", "x", "o", "O", "////", ".", "|||", "o", "---", "+", "\\\\", "*"]) +LABEL_WEIGHT = 'bold' +LINE_COLORS = COLOR_MAP +LINE_WIDTH = 3.0 +MARKER_SIZE = 13.0 +MARKER_FREQUENCY = 1000 + +# matplotlib.rcParams['ps.useafm'] = True +# matplotlib.rcParams['pdf.use14corefonts'] = True +# matplotlib.rcParams['xtick.labelsize'] = TICK_FONT_SIZE +# matplotlib.rcParams['ytick.labelsize'] = TICK_FONT_SIZE +# 创建字体列表 +# Explicitly specify the font path +# 获取系统中可用的字体路径 +font_paths = fm.findSystemFonts() + +# 创建字体列表 +font_list = [] +for font_path in font_paths: + try: + font_name = fm.FontProperties(fname=font_path).get_name() + if font_name not in font_list: + font_list.append(font_name) + except: + pass + +# 配置 matplotlib 使用系统中可用的字体 +plt.rcParams['font.family'] = font_list[0] + +FIGURE_FOLDER = '/data1/xtra/results/figure' + + +# there are some embedding problems if directly exporting the pdf figure using matplotlib. +# so we generate the eps format first and convert it to pdf. +def ConvertEpsToPdf(dir_filename): + os.system("epstopdf --outfile " + dir_filename + ".pdf " + dir_filename + ".eps") + os.system("rm -rf " + dir_filename + ".eps") + + +def DrawLegend(legend_labels, filename): + fig = pylab.figure() + ax1 = fig.add_subplot(111) + FIGURE_LABEL = legend_labels + LINE_WIDTH = 8.0 + MARKER_SIZE = 12.0 + LEGEND_FP = FontProperties(style='normal', size=26) + + figlegend = pylab.figure(figsize=(12, 0.5)) + idx = 0 + lines = [None] * (len(FIGURE_LABEL)) + data = [1] + x_values = [1] + + idx = 0 + for group in range(len(FIGURE_LABEL)): + lines[idx], = ax1.plot(x_values, data, + color=LINE_COLORS[idx], linewidth=LINE_WIDTH, + marker=MARKERS[idx], markersize=MARKER_SIZE, label=str(group)) + idx = idx + 1 + + # LEGEND + figlegend.legend(lines, FIGURE_LABEL, prop=LEGEND_FP, + loc=1, ncol=len(FIGURE_LABEL), mode="expand", shadow=False, + frameon=False, borderaxespad=0.0, handlelength=2) + + if not os.path.exists(FIGURE_FOLDER): + os.makedirs(FIGURE_FOLDER) + # no need to export eps in this case. + figlegend.savefig(filename + '.pdf') + + +# draw a line chart +def DrawFigure(xvalues, yvalues, legend_labels, x_label, y_label, y_min, y_max, filename, allow_legend): + # you may change the figure size on your own. + fig = plt.figure(figsize=(10, 3)) + figure = fig.add_subplot(111) + + FIGURE_LABEL = legend_labels + + x_values = xvalues + y_values = yvalues + + lines = [None] * (len(FIGURE_LABEL)) + for i in range(len(y_values)): + lines[i], = figure.plot(x_values[i], y_values[i], color=LINE_COLORS[i], \ + linewidth=LINE_WIDTH, marker=MARKERS[i], \ + markersize=MARKER_SIZE, label=FIGURE_LABEL[i]) + + # sometimes you may not want to draw legends. + if allow_legend == True: + plt.legend(lines, + FIGURE_LABEL, + prop=LEGEND_FP, + loc='upper center', + ncol=3, + # mode='expand', + bbox_to_anchor=(0.55, 1.6), shadow=False, + columnspacing=0.1, + frameon=True, borderaxespad=0.0, handlelength=1.5, + handletextpad=0.1, + labelspacing=0.1) + plt.xscale('log') + plt.yscale('log') + # plt.yscale('log') + + # you may control the limits on your own. + + # plt.ylim(y_min, y_max) + + plt.grid(axis='y', color='gray') + figure.yaxis.set_major_locator(LogLocator(base=10)) + figure.xaxis.set_major_locator(LogLocator(base=10)) + + figure.get_xaxis().set_tick_params(direction='in', pad=10) + figure.get_yaxis().set_tick_params(direction='in', pad=10) + + plt.xlabel(x_label, fontproperties=LABEL_FP) + plt.ylabel(y_label, fontproperties=LABEL_FP) + + size = fig.get_size_inches() + dpi = fig.get_dpi() + + # plt.show() + plt.savefig(filename + ".pdf", bbox_inches='tight') + + +# draw a line chart +def DrawFigureXYnormal(xvalues, yvalues, legend_labels, x_label, y_label, y_min, y_max, filename, allow_legend): + # you may change the figure size on your own. + fig = plt.figure(figsize=(10, 3)) + figure = fig.add_subplot(111) + + FIGURE_LABEL = legend_labels + + x_values = xvalues + y_values = yvalues + + lines = [None] * (len(FIGURE_LABEL)) + for i in range(len(y_values)): + lines[i], = figure.plot(x_values[i], y_values[i], color=LINE_COLORS[i], \ + linewidth=LINE_WIDTH, marker=MARKERS[i], \ + markersize=MARKER_SIZE, label=FIGURE_LABEL[i], markeredgecolor='k') + + # sometimes you may not want to draw legends. + if allow_legend == True: + plt.legend(lines, + FIGURE_LABEL, + prop=LEGEND_FP, + loc='upper center', + ncol=3, + bbox_to_anchor=(0.55, 1.5), shadow=False, + columnspacing=0.1, + frameon=True, borderaxespad=0, handlelength=1.2, + handletextpad=0.1, + labelspacing=0.1) + # plt.xscale('log') + # plt.yscale('log') + # plt.yscale('log') + + # you may control the limits on your own. + + # plt.ylim(y_min, y_max) + + plt.grid(axis='y', color='gray') + plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号 + # figure.yaxis.set_major_locator(LogLocator(base=10)) + # figure.xaxis.set_major_locator(LogLocator(base=10)) + plt.xticks(rotation=0, fontsize=TICK_FONT_SIZE) + figure.get_xaxis().set_tick_params(direction='in', pad=10) + figure.get_yaxis().set_tick_params(direction='in', pad=10) + + plt.xlabel(x_label, fontproperties=LABEL_FP) + plt.ylabel(y_label, fontproperties=LABEL_FP) + + size = fig.get_size_inches() + dpi = fig.get_dpi() + + # plt.show() + plt.savefig(filename + ".pdf", bbox_inches='tight') + + +# draw a line chart +def DrawFigureYnormal(xvalues, yvalues, legend_labels, x_label, y_label, y_min, y_max, filename, allow_legend): + # you may change the figure size on your own. + fig = plt.figure(figsize=(10, 3)) + figure = fig.add_subplot(111) + + FIGURE_LABEL = legend_labels + + x_values = xvalues + y_values = yvalues + + lines = [None] * (len(FIGURE_LABEL)) + for i in range(len(y_values)): + lines[i], = figure.plot(x_values[i], y_values[i], color=LINE_COLORS[i], \ + linewidth=LINE_WIDTH, marker=MARKERS[i], \ + markersize=MARKER_SIZE, label=FIGURE_LABEL[i], markeredgecolor='k') + + # sometimes you may not want to draw legends. + if allow_legend == True: + plt.legend(lines, + FIGURE_LABEL, + prop=LEGEND_FP, + loc='upper center', + ncol=3, + bbox_to_anchor=(0.55, 1.5), shadow=False, + columnspacing=0.1, + frameon=True, borderaxespad=0, handlelength=1.2, + handletextpad=0.1, + labelspacing=0.1) + plt.xscale('log') + # plt.yscale('log') + # plt.yscale('log') + + # you may control the limits on your own. + yMax=np.max(y_values) + plt.ylim(y_min, yMax*1.2) + + plt.grid(axis='y', color='gray') + plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号 + figure.yaxis.set_major_formatter(mtick.FormatStrFormatter('%.1f')) + + figure.yaxis.set_major_locator(LinearLocator(numticks=5)) + # figure.xaxis.set_major_locator(LogLocator(base=10)) + plt.xticks(rotation=0, fontsize=TICK_FONT_SIZE) + figure.get_xaxis().set_tick_params(direction='in', pad=10) + figure.get_yaxis().set_tick_params(direction='in', pad=10) + + plt.xlabel(x_label, fontproperties=LABEL_FP) + plt.ylabel(y_label, fontproperties=LABEL_FP) + + size = fig.get_size_inches() + dpi = fig.get_dpi() + + # plt.show() + plt.savefig(filename + ".pdf", bbox_inches='tight') + + +# example for reading csv file +def ReadFile(): + y = [] + col1 = [] + col2 = [] + col3 = [] + col4 = [] + col5 = [] + col6 = [] + col7 = [] + col8 = [] + + for id in it.chain(range(28, 32)): + file = '/data1/xtra/results/timestamps/PRJ_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(len(read) - 1).strip("\n")) # get last timestamp + value = len(read) / x # get throughput (#items/ms) + col1.append(value) + y.append(col1) + + for id in it.chain(range(28, 32)): + file = '/data1/xtra/results/timestamps/NPJ_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(len(read) - 1).strip("\n")) # get last timestamp + value = len(read) / x # get throughput (#items/ms) + col2.append(value) + y.append(col2) + + for id in it.chain(range(28, 32)): + file = '/data1/xtra/results/timestamps/MPASS_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(len(read) - 1).strip("\n")) # get last timestamp + value = len(read) / x # get throughput (#items/ms) + col3.append(value) + y.append(col3) + + for id in it.chain(range(28, 32)): + file = '/data1/xtra/results/timestamps/MWAY_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(len(read) - 1).strip("\n")) # get last timestamp + value = len(read) / x # get throughput (#items/ms) + col4.append(value) + y.append(col4) + + for id in it.chain(range(28, 32)): + file = '/data1/xtra/results/timestamps/SHJ_JM_NP_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(len(read) - 1).strip("\n")) # get last timestamp + value = len(read) / x # get throughput (#items/ms) + col5.append(value) + y.append(col5) + + for id in it.chain(range(28, 32)): + file = '/data1/xtra/results/timestamps/SHJ_JBCR_NP_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(len(read) - 1).strip("\n")) # get last timestamp + value = len(read) / x # get throughput (#items/ms) + col6.append(value) + y.append(col6) + + for id in it.chain(range(28, 32)): + file = '/data1/xtra/results/timestamps/PMJ_JM_NP_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(len(read) - 1).strip("\n")) # get last timestamp + value = len(read) / x # get throughput (#items/ms) + col7.append(value) + y.append(col7) + + for id in it.chain(range(28, 32)): + file = '/data1/xtra/results/timestamps/PMJ_JBCR_NP_{}.txt'.format(id) + f = open(file, "r") + read = f.readlines() + x = float(read.pop(len(read) - 1).strip("\n")) # get last timestamp + value = len(read) / x # get throughput (#items/ms) + col8.append(value) + y.append(col8) + return y + + +if __name__ == "__main__": + # x_values = ['Unique', 'Zipf(0)', 'Zipf(0.2)', 'Zipf(0.4)', 'Zipf(0.8)', 'Zipf(1)'] + x_values = [1600, 3200, 6400, 12800, 25600] + + y_values = ReadFile() + + legend_labels = ['NPJ', 'PRJ', 'MWAY', 'MPASS', 'SHJ$^{JM}$', 'SHJ$^{JB}$', 'PMJ$^{JM}$', + 'PMJ$^{JB}$'] + + DrawFigure(x_values, y_values, legend_labels, + 'Input arrival rate of R (e/ms)', 'Tpt. (#matches/ms)', x_values[0], + x_values[4], 'throughput_figure1_1', False) + +# DrawLegend(legend_labels, 'factor_legend') diff --git a/benchmark/scripts/scanEventRateSingleThreadStream2S/perfRu.csv b/benchmark/scripts/scanEventRateSingleThreadStream2S/perfRu.csv new file mode 100644 index 00000000..9ee43623 --- /dev/null +++ b/benchmark/scripts/scanEventRateSingleThreadStream2S/perfRu.csv @@ -0,0 +1,7 @@ +key,value,type +cacheMiss,12491377,U64 +cacheRefs,35327177,U64 +cpuClock,2478812100,U64 +cpuCycle,9889422467,U64 +instructions,16997458033,U64 +taskClock,2478813000,U64 diff --git a/benchmark/scripts/scanThreadsRockPi5/config_CPPBCO.csv b/benchmark/scripts/scanThreadsRockPi5/config_CPPBCO.csv new file mode 100644 index 00000000..37943e33 --- /dev/null +++ b/benchmark/scripts/scanThreadsRockPi5/config_CPPBCO.csv @@ -0,0 +1,9 @@ +key,value,type +aRow,10000,U64 +aCol,1000,U64 +bCol,500,U64 +sketchDimension,25,U64 +threads,1,U64 +ptFile,torchscripts/CRS.pt,String +useCPP,1,U64 +cppAlgoTag,bcoofd,String \ No newline at end of file diff --git a/benchmark/scripts/scanThreadsRockPi5/config_CPPCounterSketch.csv b/benchmark/scripts/scanThreadsRockPi5/config_CPPCounterSketch.csv new file mode 100644 index 00000000..f41b4df5 --- /dev/null +++ b/benchmark/scripts/scanThreadsRockPi5/config_CPPCounterSketch.csv @@ -0,0 +1,9 @@ +key,value,type +aRow,10000,U64 +aCol,1000,U64 +bCol,500,U64 +sketchDimension,25,U64 +threads,1,U64 +ptFile,torchscripts/RAWMM.pt,String +useCPP,1,U64 +cppAlgoTag,count-sketch,String \ No newline at end of file diff --git a/benchmark/scripts/scanThreadsRockPi5/main.c b/benchmark/scripts/scanThreadsRockPi5/main.c new file mode 100644 index 00000000..35fa90f7 --- /dev/null +++ b/benchmark/scripts/scanThreadsRockPi5/main.c @@ -0,0 +1,14 @@ +#include +#include +int isFP32Supported() { + float32x4_t a; + // Perform a NEON vector addition of FP32 values + float32_t result = vaddvq_f32(a); + printf("ok\r\n"); + // If compilation succeeds, FP32 operations are supported + return 1; +} +void main() { + + isFP32Supported(); +} diff --git a/benchmark/scripts/scanThreadsRockPi5/tb b/benchmark/scripts/scanThreadsRockPi5/tb new file mode 100755 index 0000000000000000000000000000000000000000..208ced9bbbc5f7213d9093bf20f0cf6362afe264 GIT binary patch literal 8904 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 literal 0 HcmV?d00001 diff --git a/benchmark/src/Benchmark.cpp b/benchmark/src/Benchmark.cpp index 9b604c27..5f7e0c06 100644 --- a/benchmark/src/Benchmark.cpp +++ b/benchmark/src/Benchmark.cpp @@ -15,8 +15,18 @@ using namespace DIVERSE_METER; void streamingTest(ConfigMapPtr cfg, torch::Tensor A, torch::Tensor B, uint64_t sketchSize = 1) { AMMBench::SingleThreadStreamer ss; ss.setConfig(cfg); - auto ssC = ss.streamingAmm(A, B, sketchSize); - + uint64_t streamingTwoMatrixes=cfg->tryU64("streamingTwoMatrixes", 0, true); + torch::Tensor ssC; + if(streamingTwoMatrixes) + { + INTELLI_INFO("Both A,B will be streaming" ); + ssC = ss.streamingAmm2S(A, B, sketchSize); + } + else + { + INTELLI_INFO("Only A will be streaming" ); + ssC = ss.streamingAmm(A, B, sketchSize); + } auto resultCsv = newConfigMap();; resultCsv->edit("throughput", (double) ss.getThroughput()); resultCsv->edit("throughputByElements", (double) (ss.getThroughput() * A.size(1))); diff --git a/commit_info b/commit_info index dbf6d99e..6522b437 100644 --- a/commit_info +++ b/commit_info @@ -1 +1 @@ -1. add the experimental feature of single thread streaming \ No newline at end of file +1. now make the streaming on both A and B possible \ No newline at end of file diff --git a/figures/batchSize2sCPP/batchSizestream_cpp_fro.pdf b/figures/batchSize2sCPP/batchSizestream_cpp_fro.pdf new file mode 100644 index 0000000000000000000000000000000000000000..5989c96f8e162be6d471421ce3d9755deed9fa20 GIT binary patch literal 7708 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 VG?}sAW)Je*pPXfa?GN literal 0 HcmV?d00001 diff --git a/figures/batchSize2sCPP/batchSizestream_cpp_lat95.pdf b/figures/batchSize2sCPP/batchSizestream_cpp_lat95.pdf new file mode 100644 index 0000000000000000000000000000000000000000..3e2c6a0669c812031143e19d0b4a59efd0282f70 GIT binary patch literal 8292 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 DLU7pR literal 0 HcmV?d00001 diff --git a/figures/batchSizeCPP/batchSizestream_cpp_lat95.pdf b/figures/batchSizeCPP/batchSizestream_cpp_lat95.pdf new file mode 100644 index 0000000000000000000000000000000000000000..fa6d8ce6c4f5ad4d3bcec136496b57c757a63b47 GIT binary patch literal 8078 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 literal 0 HcmV?d00001 diff --git a/figures/batchSizeCPP/batchSizestream_cpp_thr.pdf b/figures/batchSizeCPP/batchSizestream_cpp_thr.pdf new file mode 100644 index 0000000000000000000000000000000000000000..47e71ac7472c833cde8056180f7d20ffe8253dcc GIT binary patch literal 7979 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 literal 0 HcmV?d00001 diff --git a/figures/eventRateTps2sCPP/eventRateTpsstream_cpp_fro.pdf b/figures/eventRateTps2sCPP/eventRateTpsstream_cpp_fro.pdf new file mode 100644 index 0000000000000000000000000000000000000000..9f4a44b88fb462ecc40b0dad8cb5f0d198a99e62 GIT binary patch literal 7783 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 LmseR^1@Qj>!fuNV literal 0 HcmV?d00001 diff --git a/figures/eventRateTps2sCPP/eventRateTpsstream_cpp_thr.pdf b/figures/eventRateTps2sCPP/eventRateTpsstream_cpp_thr.pdf new file mode 100644 index 0000000000000000000000000000000000000000..21ab17cf8a33924eca239f7ceeef646b89ecd091 GIT binary patch literal 7991 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 C<0RYw literal 0 HcmV?d00001 diff --git a/figures/eventRateTpsCPP/eventRateTpsstream_cpp_fro.pdf b/figures/eventRateTpsCPP/eventRateTpsstream_cpp_fro.pdf new file mode 100644 index 0000000000000000000000000000000000000000..288c37d4726ecc20338a4f1deb8f22782c3b153a GIT binary patch literal 7784 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 Ht_b=c=NQWA literal 0 HcmV?d00001 diff --git a/figures/eventRateTpsCPP/eventRateTpsstream_cpp_lat95.pdf b/figures/eventRateTpsCPP/eventRateTpsstream_cpp_lat95.pdf new file mode 100644 index 0000000000000000000000000000000000000000..3d8d87d5a3940a25c659cbf5ba2e9c98e88581cc GIT binary patch literal 8282 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 if you want to manually config the static power of the device * - meterAddress (String) set this to the file system path of the meter, if it is different from the meter's default + * - isStreaming (U64) whether or not use streaming, default 0 + * @note this will only be run under single thread now, and perf or energy meter is not avaliable when setting to 1 + * @note by default, we only make A matrix streaming, if also want yo streaming B, please also set streamingTwoMatrixes to 1 + * - streamingTwoMatrixes (U64) whether make B matrix also streaming, default 0, will only affect when isStreaming=1 * @warning For some platforms, the staticPower automatically measured by sleep is not accurate. Please do this mannulally. See also the template config.csv * @section subsec_extend_operator How to extend a new algorithm (pt-based) diff --git a/include/Streaming/SingleThreadStreamer.h b/include/Streaming/SingleThreadStreamer.h index 2fea9e17..59c47601 100644 --- a/include/Streaming/SingleThreadStreamer.h +++ b/include/Streaming/SingleThreadStreamer.h @@ -40,6 +40,10 @@ namespace AMMBench { * @brief the timestamps to trace the streaming process */ std::vector myTs; + /** + * @brief the additional timestamps to trace the streaming process, if B is also stream + */ + std::vector myTsB; /** * @brief Set the GLOBAL config map related to this TimerStamper @@ -55,6 +59,13 @@ namespace AMMBench { * @return bool whether the config is successfully set */ virtual torch::Tensor streamingAmm(torch::Tensor A, torch::Tensor B, uint64_t sketchSize = 1); + /** +* @brief To run a streaming Amm, assuming the rows of A coming in a streaming manner and the cols of B coming in a streaming manner +* @param A The A matrix +* @param B The B matrix + * @return bool whether the config is successfully set +*/ + virtual torch::Tensor streamingAmm2S(torch::Tensor A, torch::Tensor B, uint64_t sketchSize = 1); /** * @brief to get the throughput of last streaming process, the unit is rows/second diff --git a/include/Streaming/TimeStamper.h b/include/Streaming/TimeStamper.h index 2939ffbb..191607fd 100644 --- a/include/Streaming/TimeStamper.h +++ b/include/Streaming/TimeStamper.h @@ -71,6 +71,7 @@ namespace AMMBench { * - timeStamper_zipfEventFactor, Double, the zpf factor for event rate, default 0.1, should be 0~1 * @note Default behavior * - create + * - call @ref setSetSeed if you want different seed, default seed is 114514 * - call @ref setConfig to generate the timestamp under instructions * - call @ref getTimeStamps to get the timestamp */ @@ -114,7 +115,14 @@ namespace AMMBench { ~TimeStamper() {} std::vector myTs; - + /** + * @brief to set the seed of this timestamer + * @param _seed + */ + void setSeed(uint64_t _seed) + { + seed=_seed; + } /** * @brief Set the GLOBAL config map related to this TimerStamper * @param cfg The config map diff --git a/src/Streaming/SingleThreadStreamer.cpp b/src/Streaming/SingleThreadStreamer.cpp index 52779e71..97deeacb 100644 --- a/src/Streaming/SingleThreadStreamer.cpp +++ b/src/Streaming/SingleThreadStreamer.cpp @@ -34,7 +34,7 @@ torch::Tensor AMMBench::SingleThreadStreamer::streamingAmm(torch::Tensor A, torc matC = newTensor(torch::zeros({A.size(0), B.size(1)})); struct timeval tstart; //INTELLI_INFO("I am mm"); - INTELLI_INFO("Start Streaming"); + INTELLI_INFO("Start Streaming A rows"); uint64_t startRow = 0; uint64_t endRow = startRow + batchSize; uint64_t tNow = 0; @@ -47,7 +47,7 @@ torch::Tensor AMMBench::SingleThreadStreamer::streamingAmm(torch::Tensor A, torc auto subA = A.slice(0, startRow, endRow); while (tNow < tEXpectedArrival) { tNow = INTELLI::UtilityFunctions::timeLastUs(tstart); - usleep(1); + //usleep(1); } /** * @brief now, the whole batch has arrived, compute @@ -74,6 +74,97 @@ torch::Tensor AMMBench::SingleThreadStreamer::streamingAmm(torch::Tensor A, torc return *matC; } + +torch::Tensor AMMBench::SingleThreadStreamer::streamingAmm2S(torch::Tensor A, torch::Tensor B, uint64_t sketchSize) { + assert(sketchSize); + uint64_t aRows = A.size(0); + cfgGlobal->edit("streamingTupleCnt", (uint64_t) aRows); + if (batchSize > aRows) { + batchSize = aRows; + } + AMMBench::TimeStamper tsGen,tsGenB; + tsGen.setConfig(cfgGlobal); + myTs = tsGen.getTimeStamps(); + + tsGenB.setSeed(7758258); + tsGenB.setConfig(cfgGlobal); + myTsB = tsGenB.getTimeStamps(); + INTELLI_INFO("Generate time stamps for two streams done"); + matC = newTensor(torch::zeros({A.size(0), B.size(1)})); + struct timeval tstart; + //INTELLI_INFO("I am mm"); + INTELLI_INFO("Start Streaming A rows and B cols"); + uint64_t startRow = 0; + uint64_t endRow = startRow + batchSize; + uint64_t tNow = 0; + uint64_t tEXpectedArrival = myTs[endRow - 1]->arrivalTime; + if(myTsB[endRow-1]->arrivalTime>tEXpectedArrival) + { + tEXpectedArrival=myTsB[endRow-1]->arrivalTime; + } + uint64_t tp = 0; + uint64_t tDone = 0; + gettimeofday(&tstart, NULL); + uint64_t iterationCnt=0; + torch::Tensor incomingA,incomingB,newArrivedB,oldArrivedA; + uint64_t aBCols=0,lastABCols=0; + while (startRow < aRows) { + tNow = INTELLI::UtilityFunctions::timeLastUs(tstart); + //auto subA = A.slice(0, startRow, endRow); + incomingA =A.slice(0, startRow, endRow); + incomingB=B.slice(1,startRow,endRow); + newArrivedB=B.slice(1,0,endRow); + while (tNow < tEXpectedArrival) { + tNow = INTELLI::UtilityFunctions::timeLastUs(tstart); + //usleep(1); + } + /** + * @brief now, the whole batch has arrived, compute + */ + /** + * @brief do the incomingA*newArrivedB part + */ + auto aB=cppAlgoPtr->amm(incomingA, newArrivedB, sketchSize); + lastABCols=aBCols; + aBCols=aB.size(1); + matC->slice(0,startRow,endRow).slice(1,0,aBCols).copy_(aB); + /** + * @brief do the oldArrivedA*incomingB part + */ + if(iterationCnt!=0) + { + auto aB2=cppAlgoPtr->amm(oldArrivedA, incomingB, sketchSize); + uint64_t aB2Rows=aB2.size(0); + uint64_t aB2Cols=aB2.size(1); + matC->slice(0,0,aB2Rows).slice(1,lastABCols,lastABCols+aB2Cols).copy_(aB2); + } + oldArrivedA=A.slice(0, 0, endRow); + //matC->slice(0, startRow, endRow) = cppAlgoPtr->amm(subA, B, sketchSize); + tp = INTELLI::UtilityFunctions::timeLastUs(tstart); + for (size_t i = startRow; i < endRow; i++) { + myTs[i]->processedTime = tp; + } + /** + * @brief update the indexes + */ + startRow += batchSize; + endRow += batchSize; + if (endRow >= aRows) { + endRow = aRows; + } + tEXpectedArrival = myTs[endRow - 1]->arrivalTime; + if(myTsB[endRow-1]->arrivalTime>tEXpectedArrival) + { + tEXpectedArrival=myTsB[endRow-1]->arrivalTime; + } + iterationCnt++; + } + tDone = INTELLI::UtilityFunctions::timeLastUs(tstart); + INTELLI_INFO("Done in " + to_string(tDone) + "us"); + throughput = aRows; + throughput = throughput * 1e6 / tDone; + return *matC; +} double AMMBench::SingleThreadStreamer::getLatencyPercentage(double fraction) { size_t rLen = myTs.size(); size_t nonZeroCnt = 0; diff --git a/test/SystemTest/StreamingTest.cpp b/test/SystemTest/StreamingTest.cpp index 78443b1e..21eda4d9 100644 --- a/test/SystemTest/StreamingTest.cpp +++ b/test/SystemTest/StreamingTest.cpp @@ -8,7 +8,7 @@ using namespace std; using namespace INTELLI; using namespace torch; - +/* TEST_CASE("Test the basic streaming batch 1", "[short]") { ConfigMapPtr cfg = newConfigMap(); @@ -28,7 +28,7 @@ TEST_CASE("Test the basic streaming batch 1", "[short]") std::cout << "throughput=" + to_string(ss.getThroughput()) << std::endl; // REQUIRE(froError < 0.5); } - +*/ TEST_CASE("Test the basic streaming batch 2", "[short]") { @@ -49,3 +49,23 @@ TEST_CASE("Test the basic streaming batch 2", "[short]") std::cout << "throughput=" + to_string(ss.getThroughput()) << std::endl; // REQUIRE(froError < 0.5); } +TEST_CASE("Test the basic streaming batch 1, 2 matrix in streaming", "[short]") +{ + ConfigMapPtr cfg = newConfigMap(); + torch::manual_seed(114514); + auto A = torch::rand({(long) 4, (long) 4}); + auto B = torch::rand({(long) 4, (long) 4}); + auto rawC = torch::matmul(A, B); + AMMBench::SingleThreadStreamer ss; + cfg->edit("batchSize", (uint64_t) 2); + //cfg->edit("",(uint64_t)100); + ss.setConfig(cfg); + auto ssC = ss.streamingAmm2S(A, B); + std::cout << "raw C:" << std::endl; + std::cout << rawC << std::endl; + std::cout << "streaming C:" << std::endl; + std::cout << ssC << std::endl; + std::cout << "95% latency=" + to_string(ss.getLatencyPercentage(0.95)) << std::endl; + std::cout << "throughput=" + to_string(ss.getThroughput()) << std::endl; + // REQUIRE(froError < 0.5); +} \ No newline at end of file