Skip to content

Commit

Permalink
Add more API options to the P4Testgen api. (p4lang#4451)
Browse files Browse the repository at this point in the history
  • Loading branch information
fruffy-g authored Feb 24, 2024
1 parent 3a7db09 commit 8829fa0
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 26 deletions.
3 changes: 2 additions & 1 deletion backends/p4tools/modules/testgen/options.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#define BACKENDS_P4TOOLS_MODULES_TESTGEN_OPTIONS_H_

#include <cstdint>
#include <filesystem>
#include <optional>
#include <set>
#include <string>
Expand Down Expand Up @@ -36,7 +37,7 @@ class TestgenOptions : public AbstractP4cToolOptions {
static TestgenOptions &get();

/// Directory for generated tests. Defaults to PWD.
cstring outputDir = nullptr;
std::optional<std::filesystem::path> outputDir = std::nullopt;

/// Fail on unimplemented features instead of trying the next branch
bool strict = false;
Expand Down
99 changes: 76 additions & 23 deletions backends/p4tools/modules/testgen/testgen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@

#include "backends/p4tools/common/compiler/context.h"
#include "backends/p4tools/common/core/z3_solver.h"
#include "backends/p4tools/common/lib/util.h"
#include "frontends/common/parser_options.h"
#include "ir/solver.h"
#include "lib/cstring.h"
Expand Down Expand Up @@ -101,22 +100,19 @@ std::optional<AbstractTestList> generateAndCollectAbstractTests(

int generateAndWriteAbstractTests(const TestgenOptions &testgenOptions,
const ProgramInfo &programInfo) {
cstring inputFile = P4CContext::get().options().file;
if (inputFile == nullptr) {
::error("No input file provided.");
return EXIT_FAILURE;
}

std::filesystem::path testPath;
/// If the test name is not provided, use the steam of the input file name as test name.
auto testPath = std::filesystem::path(inputFile.c_str()).stem();
if (testgenOptions.testBaseName.has_value()) {
testPath = testPath.replace_filename(testgenOptions.testBaseName.value().c_str());
testPath = testgenOptions.testBaseName.value().c_str();
} else if (cstring inputFile = P4CContext::get().options().file) {
testPath = std::filesystem::path(inputFile.c_str()).stem();
} else {
::error("Neither a file nor test base name was set. Can not infer a test name.");
}

// Create the directory, if the directory string is valid and if it does not exist.
cstring testDirStr = testgenOptions.outputDir;
if (!testDirStr.isNullOrEmpty()) {
auto testDir = std::filesystem::path(testDirStr.c_str());
if (testgenOptions.outputDir.has_value()) {
auto testDir = testgenOptions.outputDir.value();
try {
std::filesystem::create_directories(testDir);
} catch (const std::exception &err) {
Expand Down Expand Up @@ -146,9 +142,10 @@ int generateAndWriteAbstractTests(const TestgenOptions &testgenOptions,
return postProcess(testgenOptions, *testBackend);
}

std::optional<AbstractTestList> generateTestsImpl(const std::string &program,
std::optional<AbstractTestList> generateTestsImpl(std::optional<std::string_view> program,
const CompilerOptions &compilerOptions,
const TestgenOptions &testgenOptions) {
const TestgenOptions &testgenOptions,
bool writeTests) {
// Register supported compiler targets.
registerCompilerTargets();

Expand All @@ -161,11 +158,19 @@ std::optional<AbstractTestList> generateTestsImpl(const std::string &program,
auto *compileContext = new CompileContext<CompilerOptions>();
compileContext->options() = compilerOptions;
AutoCompileContext autoContext(compileContext);
// Run the compiler to get an IR and invoke the tool.
const auto compilerResultOpt = P4Tools::CompilerTarget::runCompiler(program);
if (!compilerResultOpt.has_value()) {
return std::nullopt;
CompilerResultOrError compilerResultOpt;
if (program.has_value()) {
// Run the compiler to get an IR and invoke the tool.
compilerResultOpt = P4Tools::CompilerTarget::runCompiler(std::string(program.value()));
} else {
if (!compilerOptions.file.isNullOrEmpty()) {
::error("Expected a file input.");
return std::nullopt;
}
// Run the compiler to get an IR and invoke the tool.
compilerResultOpt = P4Tools::CompilerTarget::runCompiler();
}

const auto *testgenCompilerResult =
compilerResultOpt.value().get().checkedTo<TestgenCompilerResult>();

Expand All @@ -175,6 +180,13 @@ std::optional<AbstractTestList> generateTestsImpl(const std::string &program,
return std::nullopt;
}

if (writeTests) {
int result = generateAndWriteAbstractTests(testgenOptions, *programInfo);
if (result != EXIT_SUCCESS) {
return std::nullopt;
}
return {};
}
return generateAndCollectAbstractTests(testgenOptions, *programInfo);
}

Expand All @@ -199,16 +211,27 @@ int Testgen::mainImpl(const CompilerResult &compilerResult) {
::error("P4Testgen encountered errors during preprocessing.");
return EXIT_FAILURE;
}

const auto &testgenOptions = TestgenOptions::get();
return generateAndWriteAbstractTests(testgenOptions, *programInfo);
return generateAndWriteAbstractTests(TestgenOptions::get(), *programInfo);
}

std::optional<AbstractTestList> Testgen::generateTests(const std::string &program,
std::optional<AbstractTestList> Testgen::generateTests(std::string_view program,
const CompilerOptions &compilerOptions,
const TestgenOptions &testgenOptions) {
try {
return generateTestsImpl(program, compilerOptions, testgenOptions);
return generateTestsImpl(program, compilerOptions, testgenOptions, false);
} catch (const std::exception &e) {
std::cerr << "Internal error: " << e.what() << "\n";
return std::nullopt;
} catch (...) {
return std::nullopt;
}
return std::nullopt;
}

std::optional<AbstractTestList> Testgen::generateTests(const CompilerOptions &compilerOptions,
const TestgenOptions &testgenOptions) {
try {
return generateTestsImpl(std::nullopt, compilerOptions, testgenOptions, false);
} catch (const std::exception &e) {
std::cerr << "Internal error: " << e.what() << "\n";
return std::nullopt;
Expand All @@ -218,4 +241,34 @@ std::optional<AbstractTestList> Testgen::generateTests(const std::string &progra
return std::nullopt;
}

int Testgen::writeTests(std::string_view program, const CompilerOptions &compilerOptions,
const TestgenOptions &testgenOptions) {
try {
if (generateTestsImpl(program, compilerOptions, testgenOptions, true).has_value()) {
return EXIT_SUCCESS;
}
} catch (const std::exception &e) {
std::cerr << "Internal error: " << e.what() << "\n";
return EXIT_FAILURE;
} catch (...) {
return EXIT_FAILURE;
}
return EXIT_FAILURE;
}

int Testgen::writeTests(const CompilerOptions &compilerOptions,
const TestgenOptions &testgenOptions) {
try {
if (generateTestsImpl(std::nullopt, compilerOptions, testgenOptions, true).has_value()) {
return EXIT_SUCCESS;
}
} catch (const std::exception &e) {
std::cerr << "Internal error: " << e.what() << "\n";
return EXIT_FAILURE;
} catch (...) {
return EXIT_FAILURE;
}
return EXIT_FAILURE;
}

} // namespace P4Tools::P4Testgen
28 changes: 26 additions & 2 deletions backends/p4tools/modules/testgen/testgen.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,35 @@ class Testgen : public AbstractP4cTool<TestgenOptions> {
int mainImpl(const CompilerResult &compilerResult) override;

public:
///
static std::optional<AbstractTestList> generateTests(const std::string &program,
/// Invokes P4Testgen and returns a list of abstract tests which are generated based on the
/// input TestgenOptions. The abstract tests can be further specialized depending on the select
/// test back end. CompilerOptions is required to invoke the correct preprocessor and P4
/// compiler. It is assumed that `.file` in the compiler options is set.
static std::optional<AbstractTestList> generateTests(const CompilerOptions &options,
const TestgenOptions &testgenOptions);
/// Invokes P4Testgen and returns a list of abstract tests which are generated based on the
/// input TestgenOptions. The abstract tests can be further specialized depending on the select
/// test back end. CompilerOptions is required to invoke the correct P4 compiler. This function
/// assumes that @param program is already preprocessed. P4Testgen will directly parse the input
/// program.
static std::optional<AbstractTestList> generateTests(std::string_view program,
const CompilerOptions &options,
const TestgenOptions &testgenOptions);

/// Invokes P4Testgen and writes a list of abstract tests to a specified output directory which
/// are generated based on the input TestgenOptions. The abstract tests can be further
/// specialized depending on the select test back end. CompilerOptions is required to invoke the
/// correct preprocessor and P4 compiler. It is assumed that `.file` in the compiler options is
/// set.
static int writeTests(const CompilerOptions &options, const TestgenOptions &testgenOptions);

/// Invokes P4Testgen and writes a list of abstract tests to a specified output directory which
/// are generated based on the input TestgenOptions. CompilerOptions is required to invoke the
/// correct P4 compiler. This function assumes that @param program is already preprocessed.
/// P4Testgen will directly parse the input program.
static int writeTests(std::string_view program, const CompilerOptions &options,
const TestgenOptions &testgenOptions);

virtual ~Testgen() = default;
};

Expand Down

0 comments on commit 8829fa0

Please sign in to comment.