From 10217a10a39f8a11aa97a47cc735943442a5aeeb Mon Sep 17 00:00:00 2001 From: Oliver Reiche Date: Mon, 14 Mar 2022 11:29:28 +0100 Subject: ExecutionAPI: Add common tests for local and remote api --- test/buildtool/execution_api/common/TARGETS | 12 + test/buildtool/execution_api/common/api_test.hpp | 314 +++++++++++++++++++++++ 2 files changed, 326 insertions(+) create mode 100644 test/buildtool/execution_api/common/TARGETS create mode 100644 test/buildtool/execution_api/common/api_test.hpp (limited to 'test/buildtool/execution_api/common') diff --git a/test/buildtool/execution_api/common/TARGETS b/test/buildtool/execution_api/common/TARGETS new file mode 100644 index 00000000..21095882 --- /dev/null +++ b/test/buildtool/execution_api/common/TARGETS @@ -0,0 +1,12 @@ +{ "api_test": + { "type": ["@", "rules", "CC", "library"] + , "name": ["api_test"] + , "hdrs": ["api_test.hpp"] + , "deps": + [ ["@", "catch2", "", "catch2"] + , ["src/buildtool/common", "artifact_factory"] + , ["src/buildtool/execution_api/common", "common"] + ] + , "stage": ["test", "buildtool", "execution_api", "common"] + } +} diff --git a/test/buildtool/execution_api/common/api_test.hpp b/test/buildtool/execution_api/common/api_test.hpp new file mode 100644 index 00000000..42026563 --- /dev/null +++ b/test/buildtool/execution_api/common/api_test.hpp @@ -0,0 +1,314 @@ +#include +#include + +#include "catch2/catch.hpp" +#include "src/buildtool/common/artifact_factory.hpp" +#include "src/buildtool/execution_api/common/execution_action.hpp" +#include "src/buildtool/execution_api/common/execution_api.hpp" +#include "src/buildtool/execution_api/common/execution_response.hpp" + +using ApiFactory = std::function; +using ExecProps = std::map; + +[[nodiscard]] static inline auto TestNoInputNoOutput( + ApiFactory const& api_factory, + ExecProps const& props, + bool is_hermetic = false) { + std::string test_content("test"); + + auto api = api_factory(); + + auto action = api->CreateAction( + *api->UploadTree({}), {"echo", "-n", test_content}, {}, {}, {}, props); + + SECTION("Cache execution result in action cache") { + action->SetCacheFlag(IExecutionAction::CacheFlag::CacheOutput); + + // run execution + auto response = action->Execute(); + REQUIRE(response); + + // verify result + CHECK(response->HasStdOut()); + CHECK(response->StdOut() == test_content); + + if (is_hermetic) { + CHECK(not response->IsCached()); + + SECTION("Rerun execution to verify caching") { + // run execution + auto response = action->Execute(); + REQUIRE(response); + + // verify result + CHECK(response->HasStdOut()); + CHECK(response->StdOut() == test_content); + CHECK(response->IsCached()); + } + } + } + + SECTION("Do not cache execution result in action cache") { + action->SetCacheFlag(IExecutionAction::CacheFlag::DoNotCacheOutput); + + // run execution + auto response = action->Execute(); + REQUIRE(response); + + // verify result + CHECK(response->HasStdOut()); + CHECK(response->StdOut() == test_content); + CHECK(not response->IsCached()); + + SECTION("Rerun execution to verify caching") { + // run execution + auto response = action->Execute(); + REQUIRE(response); + + // verify result + CHECK(response->HasStdOut()); + CHECK(response->StdOut() == test_content); + CHECK(not response->IsCached()); + } + } +} + +[[nodiscard]] static inline auto TestNoInputCreateOutput( + ApiFactory const& api_factory, + ExecProps const& props, + bool is_hermetic = false) { + std::string test_content("test"); + auto test_digest = ArtifactDigest::Create(test_content); + + std::string output_path{"output_file"}; + + auto api = api_factory(); + + auto action = api->CreateAction( + *api->UploadTree({}), + {"/bin/sh", + "-c", + "set -e\necho -n " + test_content + " > " + output_path}, + {output_path}, + {}, + {}, + props); + + SECTION("Cache execution result in action cache") { + action->SetCacheFlag(IExecutionAction::CacheFlag::CacheOutput); + + // run execution + auto response = action->Execute(); + REQUIRE(response); + + // verify result + auto artifacts = response->Artifacts(); + REQUIRE(artifacts.contains(output_path)); + CHECK(artifacts.at(output_path).digest == test_digest); + + if (is_hermetic) { + CHECK(not response->IsCached()); + + SECTION("Rerun execution to verify caching") { + // run execution + auto response = action->Execute(); + REQUIRE(response); + + // verify result + auto artifacts = response->Artifacts(); + REQUIRE(artifacts.contains(output_path)); + CHECK(artifacts.at(output_path).digest == test_digest); + CHECK(response->IsCached()); + } + } + } + + SECTION("Do not cache execution result in action cache") { + action->SetCacheFlag(IExecutionAction::CacheFlag::DoNotCacheOutput); + + // run execution + auto response = action->Execute(); + REQUIRE(response); + + // verify result + auto artifacts = response->Artifacts(); + REQUIRE(artifacts.contains(output_path)); + CHECK(artifacts.at(output_path).digest == test_digest); + CHECK(not response->IsCached()); + + SECTION("Rerun execution to verify caching") { + // run execution + auto response = action->Execute(); + REQUIRE(response); + + // verify result + auto artifacts = response->Artifacts(); + REQUIRE(artifacts.contains(output_path)); + CHECK(artifacts.at(output_path).digest == test_digest); + CHECK(not response->IsCached()); + } + } +} + +[[nodiscard]] static inline auto TestOneInputCopiedToOutput( + ApiFactory const& api_factory, + ExecProps const& props, + bool is_hermetic = false) { + std::string test_content("test"); + auto test_digest = ArtifactDigest::Create(test_content); + + auto input_artifact_opt = + ArtifactFactory::FromDescription(ArtifactFactory::DescribeKnownArtifact( + test_digest.hash(), test_digest.size(), ObjectType::File)); + CHECK(input_artifact_opt.has_value()); + auto input_artifact = + DependencyGraph::ArtifactNode{std::move(*input_artifact_opt)}; + + std::string input_path{"dir/subdir/input"}; + std::string output_path{"output_file"}; + + auto api = api_factory(); + CHECK(api->Upload(BlobContainer{{BazelBlob{test_digest, test_content}}}, + false)); + + auto action = + api->CreateAction(*api->UploadTree({{input_path, &input_artifact}}), + {"cp", input_path, output_path}, + {output_path}, + {}, + {}, + props); + + SECTION("Cache execution result in action cache") { + action->SetCacheFlag(IExecutionAction::CacheFlag::CacheOutput); + + // run execution + auto response = action->Execute(); + REQUIRE(response); + + // verify result + auto artifacts = response->Artifacts(); + REQUIRE(artifacts.contains(output_path)); + CHECK(artifacts.at(output_path).digest == test_digest); + + if (is_hermetic) { + CHECK(not response->IsCached()); + + SECTION("Rerun execution to verify caching") { + // run execution + auto response = action->Execute(); + REQUIRE(response); + + // verify result + auto artifacts = response->Artifacts(); + REQUIRE(artifacts.contains(output_path)); + CHECK(artifacts.at(output_path).digest == test_digest); + CHECK(response->IsCached()); + } + } + } + + SECTION("Do not cache execution result in action cache") { + action->SetCacheFlag(IExecutionAction::CacheFlag::DoNotCacheOutput); + + // run execution + auto response = action->Execute(); + REQUIRE(response); + + // verify result + auto artifacts = response->Artifacts(); + REQUIRE(artifacts.contains(output_path)); + CHECK(artifacts.at(output_path).digest == test_digest); + CHECK(not response->IsCached()); + + SECTION("Rerun execution to verify caching") { + // run execution + auto response = action->Execute(); + REQUIRE(response); + + // verify result + auto artifacts = response->Artifacts(); + REQUIRE(artifacts.contains(output_path)); + CHECK(artifacts.at(output_path).digest == test_digest); + CHECK(not response->IsCached()); + } + } +} + +[[nodiscard]] static inline auto TestNonZeroExitCodeCreateOutput( + ApiFactory const& api_factory, + ExecProps const& props) { + std::string test_content("test"); + auto test_digest = ArtifactDigest::Create(test_content); + + std::string output_path{"output_file"}; + + auto api = api_factory(); + + auto action = api->CreateAction(*api->UploadTree({}), + {"/bin/sh", + "-c", + "set -e\necho -n " + test_content + " > " + + output_path + "\nexit 1\n"}, + {output_path}, + {}, + {}, + props); + + SECTION("Cache execution result in action cache") { + action->SetCacheFlag(IExecutionAction::CacheFlag::CacheOutput); + + // run execution + auto response = action->Execute(); + REQUIRE(response); + + // verify result + CHECK(response->ExitCode() == 1); + auto artifacts = response->Artifacts(); + REQUIRE(artifacts.contains(output_path)); + CHECK(artifacts.at(output_path).digest == test_digest); + + CHECK(not response->IsCached()); + + SECTION("Rerun execution to verify that non-zero actions are rerun") { + // run execution + auto response = action->Execute(); + REQUIRE(response); + + // verify result + CHECK(response->ExitCode() == 1); + auto artifacts = response->Artifacts(); + REQUIRE(artifacts.contains(output_path)); + CHECK(artifacts.at(output_path).digest == test_digest); + CHECK(not response->IsCached()); + } + } + + SECTION("Do not cache execution result in action cache") { + action->SetCacheFlag(IExecutionAction::CacheFlag::DoNotCacheOutput); + + // run execution + auto response = action->Execute(); + REQUIRE(response); + + // verify result + CHECK(response->ExitCode() == 1); + auto artifacts = response->Artifacts(); + REQUIRE(artifacts.contains(output_path)); + CHECK(artifacts.at(output_path).digest == test_digest); + CHECK(not response->IsCached()); + + SECTION("Rerun execution to verify non-zero actions are not cached") { + // run execution + auto response = action->Execute(); + REQUIRE(response); + + // verify result + CHECK(response->ExitCode() == 1); + auto artifacts = response->Artifacts(); + REQUIRE(artifacts.contains(output_path)); + CHECK(artifacts.at(output_path).digest == test_digest); + CHECK(not response->IsCached()); + } + } +} -- cgit v1.2.3