From 8740303d5966dcac1cbcd99c99bdf9ffb22fb34d Mon Sep 17 00:00:00 2001 From: fruffy Date: Fri, 11 Nov 2022 14:47:04 -0500 Subject: [PATCH] Refactor test frameworks and use a utility library. --- backends/p4tools/testgen/lib/tf.h | 102 ++++++++++++++- .../bmv2/backend/protobuf/protobuf.cpp | 98 +++------------ .../targets/bmv2/backend/protobuf/protobuf.h | 20 ++- .../testgen/targets/bmv2/backend/ptf/ptf.cpp | 119 ++++-------------- .../testgen/targets/bmv2/backend/ptf/ptf.h | 5 +- .../testgen/targets/bmv2/backend/stf/stf.cpp | 113 +++-------------- .../testgen/targets/bmv2/backend/stf/stf.h | 5 +- .../testgen/targets/ebpf/backend/stf/stf.cpp | 30 ++--- .../testgen/targets/ebpf/backend/stf/stf.h | 5 +- 9 files changed, 187 insertions(+), 310 deletions(-) diff --git a/backends/p4tools/testgen/lib/tf.h b/backends/p4tools/testgen/lib/tf.h index eae360de57e..2acf2b65a8b 100644 --- a/backends/p4tools/testgen/lib/tf.h +++ b/backends/p4tools/testgen/lib/tf.h @@ -4,7 +4,9 @@ #include #include +#include +#include "backends/p4tools/common/lib/format_int.h" #include "lib/cstring.h" #include "backends/p4tools/testgen/lib/test_spec.h" @@ -14,7 +16,7 @@ namespace P4Tools { namespace P4Testgen { /// THe default base class for the various test frameworks (TF). Every test framework has a test -/// name and a seed associated with it. +/// name and a seed associated with it. Also contains a variety of common utility functions. class TF { protected: /// The @testName to be used in test case generation. @@ -26,6 +28,104 @@ class TF { /// Creates a generic test framework. explicit TF(cstring, boost::optional); + /// Converts the traces of this test into a string representation and Inja object. + static inja::json getTrace(const TestSpec* testSpec) { + inja::json traceList = inja::json::array(); + const auto* traces = testSpec->getTraces(); + if ((traces != nullptr) && !traces->empty()) { + for (const auto& trace : *traces) { + std::stringstream ss; + ss << *trace; + traceList.push_back(ss.str()); + } + } + return traceList; + } + + /// Checks whether a table object has an action profile or selector associated with it. + /// If that is the case, we set a boolean flag for this particular inja object. + template + static void checkForTableActionProfile(inja::json& tblJson, std::map& apAsMap, + const TableConfig* tblConfig) { + const auto* apObject = tblConfig->getProperty("action_profile", false); + if (apObject != nullptr) { + const auto* actionProfile = apObject->checkedTo(); + tblJson["has_ap"] = true; + // Check if we have an Action Selector too. + // TODO: Change this to check in ActionSelector with table + // property "action_selectors". + const auto* asObject = tblConfig->getProperty("action_selector", false); + if (asObject != nullptr) { + const auto* actionSelector = asObject->checkedTo(); + apAsMap[actionProfile->getProfileDecl()->controlPlaneName()] = + actionSelector->getSelectorDecl()->controlPlaneName(); + tblJson["has_as"] = true; + } + } + } + + /// Check whether the table object has an overridden default action. + /// In this case, we assume there are no keys and we just set the default action of the table. + static void checkForDefaultActionOverride(inja::json& tblJson, const TableConfig* tblConfig) { + const auto* defaultOverrideObj = tblConfig->getProperty("overriden_default_action", false); + if (defaultOverrideObj != nullptr) { + const auto* defaultAction = defaultOverrideObj->checkedTo(); + inja::json a; + a["action_name"] = defaultAction->getActionName(); + auto const* actionArgs = defaultAction->getArgs(); + inja::json b = inja::json::array(); + for (const auto& actArg : *actionArgs) { + inja::json j; + j["param"] = actArg.getActionParamName().c_str(); + j["value"] = formatHexExpr(actArg.getEvaluatedValue()); + b.push_back(j); + } + a["act_args"] = b; + tblJson["default_override"] = a; + } + } + + /// Collect all the action profile objects. These will have to be declared in the test. + template + static void collectActionProfileDeclarations(const TestSpec* testSpec, + inja::json& controlPlaneJson, + const std::map& apAsMap) { + auto actionProfiles = testSpec->getTestObjectCategory("action_profiles"); + if (!actionProfiles.empty()) { + controlPlaneJson["action_profiles"] = inja::json::array(); + } + for (auto const& testObject : actionProfiles) { + const auto* const actionProfile = testObject.second->checkedTo(); + const auto* actions = actionProfile->getActions(); + inja::json j; + j["profile"] = actionProfile->getProfileDecl()->controlPlaneName(); + j["actions"] = inja::json::array(); + for (size_t idx = 0; idx < actions->size(); ++idx) { + const auto& action = actions->at(idx); + auto actionName = action.first; + auto actionArgs = action.second; + inja::json a; + a["action_name"] = actionName; + a["action_idx"] = std::to_string(idx); + inja::json b = inja::json::array(); + for (const auto& actArg : actionArgs) { + inja::json c; + c["param"] = actArg.getActionParamName().c_str(); + c["value"] = formatHexExpr(actArg.getEvaluatedValue()).c_str(); + b.push_back(c); + } + a["act_args"] = b; + j["actions"].push_back(a); + } + // Look up the selectors associated with the profile. + if (apAsMap.find(actionProfile->getProfileDecl()->controlPlaneName()) != + apAsMap.end()) { + j["selector"] = apAsMap.at(actionProfile->getProfileDecl()->controlPlaneName()); + } + controlPlaneJson["action_profiles"].push_back(j); + } + } + public: /// The method used to output the test case to be implemented by /// all the test frameworks (eg. STF, PTF, etc.). diff --git a/backends/p4tools/testgen/targets/bmv2/backend/protobuf/protobuf.cpp b/backends/p4tools/testgen/targets/bmv2/backend/protobuf/protobuf.cpp index ab4d83730a7..fa386eebaa6 100644 --- a/backends/p4tools/testgen/targets/bmv2/backend/protobuf/protobuf.cpp +++ b/backends/p4tools/testgen/targets/bmv2/backend/protobuf/protobuf.cpp @@ -17,7 +17,6 @@ #include "backends/p4tools/common/lib/format_int.h" #include "backends/p4tools/common/lib/trace_events.h" #include "backends/p4tools/common/lib/util.h" -#include "control-plane/p4RuntimeArchStandard.h" #include "gsl/gsl-lite.hpp" #include "ir/ir.h" #include "lib/big_int_util.h" @@ -34,10 +33,6 @@ namespace P4Testgen { namespace Bmv2 { -using P4::ControlPlaneAPI::p4rt_id_t; -using P4::ControlPlaneAPI::P4RuntimeSymbolType; -using P4::ControlPlaneAPI::Standard::SymbolType; - /// Wrapper helper function that automatically inserts separators for hex strings. std::string formatHexExprWithSep(const IR::Expression* expr) { return insertHexSeparators(formatHexExpr(expr, false, true, false)); @@ -46,22 +41,7 @@ std::string formatHexExprWithSep(const IR::Expression* expr) { Protobuf::Protobuf(cstring testName, boost::optional seed = boost::none) : TF(testName, seed) {} -inja::json Protobuf::getTrace(const TestSpec* testSpec) { - inja::json traceList = inja::json::array(); - const auto* traces = testSpec->getTraces(); - if ((traces != nullptr) && !traces->empty()) { - for (const auto& trace : *traces) { - std::stringstream ss; - ss << *trace; - traceList.push_back(ss.str()); - } - } - return traceList; -} - -/// @return the id allocated to the object through the @id annotation if any, or -/// boost::none. -static boost::optional getIdAnnotation(const IR::IAnnotated* node) { +boost::optional Protobuf::getIdAnnotation(const IR::IAnnotated* node) { const auto* idAnnotation = node->getAnnotation("id"); if (idAnnotation == nullptr) { return boost::none; @@ -75,11 +55,8 @@ static boost::optional getIdAnnotation(const IR::IAnnotated* node) { return static_cast(idConstant->value); } -/// @return the value of any P4 '@id' annotation @declaration may have, and -/// ensure that the value is correct with respect to the P4Runtime -/// specification. The name 'externalId' is in analogy with externalName(). -static boost::optional externalId(const P4RuntimeSymbolType& type, - const IR::IDeclaration* declaration) { +boost::optional Protobuf::externalId(const P4RuntimeSymbolType& type, + const IR::IDeclaration* declaration) { CHECK_NULL(declaration); if (!declaration->is()) { return boost::none; // Assign an id later; see below. @@ -133,7 +110,7 @@ inja::json Protobuf::getControlPlane(const TestSpec* testSpec) { inja::json controlPlaneJson = inja::json::object(); // Map of actionProfiles and actionSelectors for easy reference. - std::map apASMap; + std::map apAsMap; auto tables = testSpec->getTestObjectCategory("tables"); if (!tables.empty()) { @@ -163,70 +140,25 @@ inja::json Protobuf::getControlPlane(const TestSpec* testSpec) { actionDecl); rule["action_id"] = *p4RuntimeId; auto j = getControlPlaneForTable(*matches, *actionArgs); - rule["priority"] = tblRule.getPriority(); rule["rules"] = std::move(j); + rule["priority"] = tblRule.getPriority(); tblJson["rules"].push_back(rule); } - // Action Profile - const auto* apObject = tblConfig->getProperty("action_profile", false); - const Bmv2_V1ModelActionProfile* actionProfile = nullptr; - const Bmv2_V1ModelActionSelector* actionSelector = nullptr; - if (apObject != nullptr) { - actionProfile = apObject->checkedTo(); - // Check if we have an Action Selector too. - // TODO: Change this to check in ActionSelector with table - // property "action_selectors". - const auto* asObject = tblConfig->getProperty("action_selector", false); - if (asObject != nullptr) { - actionSelector = asObject->checkedTo(); - apASMap[actionProfile->getProfileDecl()->controlPlaneName()] = - actionSelector->getSelectorDecl()->controlPlaneName(); - } - } - if (actionProfile != nullptr) { - tblJson["has_ap"] = true; - } + // Collect action profiles and selectors associated with the table. + checkForTableActionProfile( + tblJson, apAsMap, tblConfig); - if (actionSelector != nullptr) { - tblJson["has_as"] = true; - } + // Check whether the default action is overridden for this table. + checkForDefaultActionOverride(tblJson, tblConfig); controlPlaneJson["tables"].push_back(tblJson); } - auto actionProfiles = testSpec->getTestObjectCategory("action_profiles"); - if (!actionProfiles.empty()) { - controlPlaneJson["action_profiles"] = inja::json::array(); - } - for (auto const& testObject : actionProfiles) { - const auto* const actionProfile = testObject.second->checkedTo(); - const auto* actions = actionProfile->getActions(); - inja::json j; - const auto* profileDecl = actionProfile->getProfileDecl(); - j["profile"] = profileDecl->controlPlaneName(); - j["actions"] = inja::json::array(); - for (size_t idx = 0; idx < actions->size(); ++idx) { - const auto& action = actions->at(idx); - auto actionName = action.first; - auto actionArgs = action.second; - inja::json a; - a["action_name"] = actionName; - a["action_idx"] = std::to_string(idx); - inja::json b = inja::json::array(); - for (const auto& actArg : actionArgs) { - inja::json c; - c["param"] = actArg.getActionParamName().c_str(); - c["value"] = formatHexExprWithSep(actArg.getEvaluatedValue()).c_str(); - b.push_back(c); - } - a["action_args"] = b; - j["actions"].push_back(a); - } - if (apASMap.find(actionProfile->getProfileDecl()->controlPlaneName()) != apASMap.end()) { - j["selector"] = apASMap[actionProfile->getProfileDecl()->controlPlaneName()]; - } - controlPlaneJson["action_profiles"].push_back(j); - } + + // Collect declarations of action profiles. + collectActionProfileDeclarations(testSpec, controlPlaneJson, + apAsMap); + return controlPlaneJson; } diff --git a/backends/p4tools/testgen/targets/bmv2/backend/protobuf/protobuf.h b/backends/p4tools/testgen/targets/bmv2/backend/protobuf/protobuf.h index d12abacf6cb..7dbd2217baa 100644 --- a/backends/p4tools/testgen/targets/bmv2/backend/protobuf/protobuf.h +++ b/backends/p4tools/testgen/targets/bmv2/backend/protobuf/protobuf.h @@ -11,7 +11,7 @@ #include #include -/// Inja +#include "control-plane/p4RuntimeArchStandard.h" #include "ir/ir.h" #include "lib/cstring.h" @@ -24,6 +24,10 @@ namespace P4Testgen { namespace Bmv2 { +using P4::ControlPlaneAPI::p4rt_id_t; +using P4::ControlPlaneAPI::P4RuntimeSymbolType; +using P4::ControlPlaneAPI::Standard::SymbolType; + /// Extracts information from the @testSpec to emit a Protobuf test case. class Protobuf : public TF { /// The output file. @@ -42,6 +46,7 @@ class Protobuf : public TF { Protobuf(cstring testName, boost::optional seed); + /// Produce a Protobuf test. void outputTest(const TestSpec* spec, cstring selectedBranches, size_t testIdx, float currentCoverage) override; @@ -58,9 +63,6 @@ class Protobuf : public TF { void emitTestcase(const TestSpec* testSpec, cstring selectedBranches, size_t testId, const std::string& testCase, float currentCoverage); - /// Converts the traces of this test into a string representation and Inja object. - static inja::json getTrace(const TestSpec* testSpec); - /// Converts all the control plane objects into Inja format. static inja::json getControlPlane(const TestSpec* testSpec); @@ -77,6 +79,16 @@ class Protobuf : public TF { /// Helper function for the control plane table inja objects. static inja::json getControlPlaneForTable(const std::map& matches, const std::vector& args); + + /// @return the id allocated to the object through the @id annotation if any, or + /// boost::none. + static boost::optional getIdAnnotation(const IR::IAnnotated* node); + + /// @return the value of any P4 '@id' annotation @declaration may have, and + /// ensure that the value is correct with respect to the P4Runtime + /// specification. The name 'externalId' is in analogy with externalName(). + static boost::optional externalId(const P4RuntimeSymbolType& type, + const IR::IDeclaration* declaration); }; } // namespace Bmv2 diff --git a/backends/p4tools/testgen/targets/bmv2/backend/ptf/ptf.cpp b/backends/p4tools/testgen/targets/bmv2/backend/ptf/ptf.cpp index b267ddd4a50..1f5a04f580a 100644 --- a/backends/p4tools/testgen/targets/bmv2/backend/ptf/ptf.cpp +++ b/backends/p4tools/testgen/targets/bmv2/backend/ptf/ptf.cpp @@ -34,19 +34,6 @@ PTF::PTF(cstring testName, boost::optional seed = boost::none) : T cstring testNameOnly(testFile.stem().c_str()); } -inja::json PTF::getTrace(const TestSpec* testSpec) { - inja::json traceList = inja::json::array(); - const auto* traces = testSpec->getTraces(); - if ((traces != nullptr) && !traces->empty()) { - for (const auto& trace : *traces) { - std::stringstream ss; - ss << *trace; - traceList.push_back(ss.str()); - } - } - return traceList; -} - std::vector> PTF::getIgnoreMasks(const IR::Constant* mask) { std::vector> ignoreMasks; if (mask == nullptr) { @@ -75,87 +62,44 @@ inja::json PTF::getControlPlane(const TestSpec* testSpec) { inja::json controlPlaneJson = inja::json::object(); // Map of actionProfiles and actionSelectors for easy reference. - std::map apASMap; + std::map apAsMap; auto tables = testSpec->getTestObjectCategory("tables"); if (!tables.empty()) { controlPlaneJson["tables"] = inja::json::array(); } - for (auto const& testObject : tables) { + for (const auto& testObject : tables) { inja::json tblJson; tblJson["table_name"] = testObject.first.c_str(); const auto* const tblConfig = testObject.second->checkedTo(); - auto const* tblRules = tblConfig->getRules(); + const auto* tblRules = tblConfig->getRules(); tblJson["rules"] = inja::json::array(); - for (const auto& tblRules : *tblRules) { + for (const auto& tblRule : *tblRules) { inja::json rule; - auto const* matches = tblRules.getMatches(); - auto const* actionCall = tblRules.getActionCall(); - auto const* actionArgs = actionCall->getArgs(); + const auto* matches = tblRule.getMatches(); + const auto* actionCall = tblRule.getActionCall(); + const auto* actionArgs = actionCall->getArgs(); rule["action_name"] = actionCall->getActionName().c_str(); auto j = getControlPlaneForTable(*matches, *actionArgs); rule["rules"] = std::move(j); + rule["priority"] = tblRule.getPriority(); tblJson["rules"].push_back(rule); } - // Action Profile - const auto* apObject = tblConfig->getProperty("action_profile", false); - const Bmv2_V1ModelActionProfile* actionProfile = nullptr; - const Bmv2_V1ModelActionSelector* actionSelector = nullptr; - if (apObject != nullptr) { - actionProfile = apObject->checkedTo(); - // Check if we have an Action Selector too. - // TODO: Change this to check in ActionSelector with table - // property "action_selectors". - const auto* asObject = tblConfig->getProperty("action_selector", false); - if (asObject != nullptr) { - actionSelector = asObject->checkedTo(); - apASMap[actionProfile->getProfileDecl()->controlPlaneName()] = - actionSelector->getSelectorDecl()->controlPlaneName(); - } - } - if (actionProfile != nullptr) { - tblJson["has_ap"] = true; - } + // Collect action profiles and selectors associated with the table. + checkForTableActionProfile( + tblJson, apAsMap, tblConfig); - if (actionSelector != nullptr) { - tblJson["has_as"] = true; - } + // Check whether the default action is overridden for this table. + checkForDefaultActionOverride(tblJson, tblConfig); controlPlaneJson["tables"].push_back(tblJson); } - auto actionProfiles = testSpec->getTestObjectCategory("action_profiles"); - if (!actionProfiles.empty()) { - controlPlaneJson["action_profiles"] = inja::json::array(); - } - for (auto const& testObject : actionProfiles) { - const auto* const actionProfile = testObject.second->checkedTo(); - const auto* actions = actionProfile->getActions(); - inja::json j; - j["profile"] = actionProfile->getProfileDecl()->controlPlaneName(); - j["actions"] = inja::json::array(); - for (size_t idx = 0; idx < actions->size(); ++idx) { - const auto& action = actions->at(idx); - auto actionName = action.first; - auto actionArgs = action.second; - inja::json a; - a["action_name"] = actionName; - a["action_idx"] = std::to_string(idx); - inja::json b = inja::json::array(); - for (const auto& actArg : actionArgs) { - inja::json c; - c["param"] = actArg.getActionParamName().c_str(); - c["value"] = formatHexExpr(actArg.getEvaluatedValue()).c_str(); - b.push_back(c); - } - a["action_args"] = b; - j["actions"].push_back(a); - } - if (apASMap.find(actionProfile->getProfileDecl()->controlPlaneName()) != apASMap.end()) { - j["selector"] = apASMap[actionProfile->getProfileDecl()->controlPlaneName()]; - } - controlPlaneJson["action_profiles"].push_back(j); - } + + // Collect declarations of action profiles. + collectActionProfileDeclarations(testSpec, controlPlaneJson, + apAsMap); + return controlPlaneJson; } @@ -171,9 +115,9 @@ inja::json PTF::getControlPlaneForTable(const std::map { @@ -360,7 +304,6 @@ class Test{{test_id}}(AbstractTest): ## endfor ## endif ## for table in control_plane.tables -## if existsIn(table, '"has_ap"') ## for rule in table.rules self.insertTableEntry( '{{table.table_name}}', @@ -378,32 +321,14 @@ class Test{{test_id}}(AbstractTest): ## for r in rule.rules.lpm_matches self.Lpm('{{r.field_name}}', {{r.value}}, {{r.prefix_len}}), ## endfor +## if existsIn(table, '"has_ap"') ], None, [ ('$ACTION_MEMBER_ID', {{rule.action_name}}), ] ) -## endfor ## else - # Table {{table.table_name}} -## for rule in table.rules - self.insertTableEntry( - '{{table.table_name}}', - [ -## for r in rule.rules.single_exact_matches - self.Exact('{{r.field_name}}', {{r.value}}), -## endfor -## for r in rule.rules.range_matches - # TODO: p4Runtime doesn't have Range match - this would fail. Need to fix. - self.Range('{{r.field_name}}', {{r.lo}}, {{r.hi}}), -## endfor -## for r in rule.rules.ternary_matches - self.Ternary('{{r.field_name}}', {{r.value}}, {{r.mask}}), -## endfor -## for r in rule.rules.lpm_matches - self.Lpm('{{r.field_name}}', {{r.value}}, {{r.prefix_len}}), -## endfor ], '{{rule.action_name}}', [ diff --git a/backends/p4tools/testgen/targets/bmv2/backend/ptf/ptf.h b/backends/p4tools/testgen/targets/bmv2/backend/ptf/ptf.h index 212280967b2..75a7845f758 100644 --- a/backends/p4tools/testgen/targets/bmv2/backend/ptf/ptf.h +++ b/backends/p4tools/testgen/targets/bmv2/backend/ptf/ptf.h @@ -44,6 +44,8 @@ class PTF : public TF { PTF& operator=(PTF&&) = delete; PTF(cstring testName, boost::optional seed); + + /// Produce a PTF test. void outputTest(const TestSpec* spec, cstring selectedBranches, size_t testIdx, float currentCoverage) override; @@ -60,9 +62,6 @@ class PTF : public TF { void emitTestcase(const TestSpec* testSpec, cstring selectedBranches, size_t testId, const std::string& testCase, float currentCoverage); - /// Converts the traces of this test into a string representation and Inja object. - static inja::json getTrace(const TestSpec* testSpec); - /// Converts all the control plane objects into Inja format. static inja::json getControlPlane(const TestSpec* testSpec); diff --git a/backends/p4tools/testgen/targets/bmv2/backend/stf/stf.cpp b/backends/p4tools/testgen/targets/bmv2/backend/stf/stf.cpp index 315fa796490..225cf6a306d 100644 --- a/backends/p4tools/testgen/targets/bmv2/backend/stf/stf.cpp +++ b/backends/p4tools/testgen/targets/bmv2/backend/stf/stf.cpp @@ -37,74 +37,6 @@ STF::STF(cstring testName, boost::optional seed = boost::none) : T cstring testNameOnly(testFile.stem().c_str()); } -inja::json STF::getTrace(const TestSpec* testSpec) { - inja::json traceList = inja::json::array(); - const auto* traces = testSpec->getTraces(); - if ((traces != nullptr) && !traces->empty()) { - for (const auto& trace : *traces) { - std::stringstream ss; - ss << *trace; - traceList.push_back(ss.str()); - } - } - return traceList; -} - -void checkForTableActionProfile(inja::json& tblJson, std::map& apAsMap, - const TableConfig* tblConfig) { - const auto* apObject = tblConfig->getProperty("action_profile", false); - if (apObject != nullptr) { - const auto* actionProfile = apObject->checkedTo(); - tblJson["has_ap"] = true; - // Check if we have an Action Selector too. - // TODO: Change this to check in ActionSelector with table - // property "action_selectors". - const auto* asObject = tblConfig->getProperty("action_selector", false); - if (asObject != nullptr) { - const auto* actionSelector = asObject->checkedTo(); - apAsMap[actionProfile->getProfileDecl()->controlPlaneName()] = - actionSelector->getSelectorDecl()->controlPlaneName(); - tblJson["has_as"] = true; - } - } -} - -void collectActionProfiles(const TestSpec* testSpec, inja::json& controlPlaneJson, - std::map& apAsMap) { - auto actionProfiles = testSpec->getTestObjectCategory("action_profiles"); - if (!actionProfiles.empty()) { - controlPlaneJson["action_profiles"] = inja::json::array(); - } - for (auto const& testObject : actionProfiles) { - const auto* const actionProfile = testObject.second->checkedTo(); - const auto* actions = actionProfile->getActions(); - inja::json j; - j["profile"] = actionProfile->getProfileDecl()->controlPlaneName(); - j["actions"] = inja::json::array(); - for (size_t idx = 0; idx < actions->size(); ++idx) { - const auto& action = actions->at(idx); - auto actionName = action.first; - auto actionArgs = action.second; - inja::json a; - a["action_name"] = actionName; - a["action_idx"] = std::to_string(idx); - inja::json b = inja::json::array(); - for (const auto& actArg : actionArgs) { - inja::json c; - c["param"] = actArg.getActionParamName().c_str(); - c["value"] = formatHexExpr(actArg.getEvaluatedValue()).c_str(); - b.push_back(c); - } - a["act_args"] = b; - j["actions"].push_back(a); - } - if (apAsMap.find(actionProfile->getProfileDecl()->controlPlaneName()) != apAsMap.end()) { - j["selector"] = apAsMap[actionProfile->getProfileDecl()->controlPlaneName()]; - } - controlPlaneJson["action_profiles"].push_back(j); - } -} - inja::json STF::getControlPlane(const TestSpec* testSpec) { inja::json controlPlaneJson = inja::json::object(); @@ -115,17 +47,17 @@ inja::json STF::getControlPlane(const TestSpec* testSpec) { if (!tables.empty()) { controlPlaneJson["tables"] = inja::json::array(); } - for (auto const& testObject : tables) { + for (const auto& testObject : tables) { inja::json tblJson; tblJson["table_name"] = testObject.first.c_str(); const auto* const tblConfig = testObject.second->checkedTo(); - auto const* tblRules = tblConfig->getRules(); + const auto* tblRules = tblConfig->getRules(); tblJson["rules"] = inja::json::array(); for (const auto& tblRule : *tblRules) { inja::json rule; - auto const* matches = tblRule.getMatches(); - auto const* actionCall = tblRule.getActionCall(); - auto const* actionArgs = actionCall->getArgs(); + const auto* matches = tblRule.getMatches(); + const auto* actionCall = tblRule.getActionCall(); + const auto* actionArgs = actionCall->getArgs(); rule["action_name"] = actionCall->getActionName().c_str(); auto j = getControlPlaneForTable(*matches, *actionArgs); rule["rules"] = std::move(j); @@ -133,30 +65,19 @@ inja::json STF::getControlPlane(const TestSpec* testSpec) { tblJson["rules"].push_back(rule); } - // Action Profile - checkForTableActionProfile(tblJson, apAsMap, tblConfig); - - const auto* defaultOverrideObj = tblConfig->getProperty("overriden_default_action", false); - if (defaultOverrideObj != nullptr) { - const auto* defaultAction = defaultOverrideObj->checkedTo(); - inja::json a; - a["action_name"] = defaultAction->getActionName(); - auto const* actionArgs = defaultAction->getArgs(); - inja::json b = inja::json::array(); - for (const auto& actArg : *actionArgs) { - inja::json j; - j["param"] = actArg.getActionParamName().c_str(); - j["value"] = formatHexExpr(actArg.getEvaluatedValue()); - b.push_back(j); - } - a["act_args"] = b; - tblJson["default_override"] = a; - } + // Collect action profiles and selectors associated with the table. + checkForTableActionProfile( + tblJson, apAsMap, tblConfig); + + // Check whether the default action is overridden for this table. + checkForDefaultActionOverride(tblJson, tblConfig); controlPlaneJson["tables"].push_back(tblJson); } - collectActionProfiles(testSpec, controlPlaneJson, apAsMap); + // Collect declarations of action profiles. + collectActionProfileDeclarations(testSpec, controlPlaneJson, + apAsMap); return controlPlaneJson; } @@ -169,9 +90,9 @@ inja::json STF::getControlPlaneForTable(const std::map { diff --git a/backends/p4tools/testgen/targets/bmv2/backend/stf/stf.h b/backends/p4tools/testgen/targets/bmv2/backend/stf/stf.h index 97f0d5a6013..3888a6b3cbe 100644 --- a/backends/p4tools/testgen/targets/bmv2/backend/stf/stf.h +++ b/backends/p4tools/testgen/targets/bmv2/backend/stf/stf.h @@ -41,6 +41,8 @@ class STF : public TF { STF& operator=(STF&&) = delete; STF(cstring testName, boost::optional seed); + + /// Produce an STF test. void outputTest(const TestSpec* spec, cstring selectedBranches, size_t testIdx, float currentCoverage) override; @@ -53,9 +55,6 @@ class STF : public TF { void emitTestcase(const TestSpec* testSpec, cstring selectedBranches, size_t testId, const std::string& testCase, float currentCoverage); - /// Converts the traces of this test into a string representation and Inja object. - static inja::json getTrace(const TestSpec* testSpec); - /// Converts all the control plane objects into Inja format. static inja::json getControlPlane(const TestSpec* testSpec); diff --git a/backends/p4tools/testgen/targets/ebpf/backend/stf/stf.cpp b/backends/p4tools/testgen/targets/ebpf/backend/stf/stf.cpp index 01ccd53eaef..fbdfa0792b1 100644 --- a/backends/p4tools/testgen/targets/ebpf/backend/stf/stf.cpp +++ b/backends/p4tools/testgen/targets/ebpf/backend/stf/stf.cpp @@ -38,37 +38,27 @@ STF::STF(cstring testName, boost::optional seed = boost::none) : T cstring testNameOnly(testFile.stem().c_str()); } -inja::json STF::getTrace(const TestSpec* testSpec) { - inja::json traceList = inja::json::array(); - const auto* traces = testSpec->getTraces(); - if ((traces != nullptr) && !traces->empty()) { - for (const auto& trace : *traces) { - std::stringstream ss; - ss << *trace; - traceList.push_back(ss.str()); - } - } - return traceList; -} - inja::json STF::getControlPlane(const TestSpec* testSpec) { inja::json controlPlaneJson = inja::json::object(); + // Map of actionProfiles and actionSelectors for easy reference. + std::map apAsMap; + auto tables = testSpec->getTestObjectCategory("tables"); if (!tables.empty()) { controlPlaneJson["tables"] = inja::json::array(); } - for (auto const& testObject : tables) { + for (const auto& testObject : tables) { inja::json tblJson; tblJson["table_name"] = testObject.first.c_str(); const auto* const tblConfig = testObject.second->checkedTo(); - auto const* tblRules = tblConfig->getRules(); + const auto* tblRules = tblConfig->getRules(); tblJson["rules"] = inja::json::array(); for (const auto& tblRule : *tblRules) { inja::json rule; - auto const* matches = tblRule.getMatches(); - auto const* actionCall = tblRule.getActionCall(); - auto const* actionArgs = actionCall->getArgs(); + const auto* matches = tblRule.getMatches(); + const auto* actionCall = tblRule.getActionCall(); + const auto* actionArgs = actionCall->getArgs(); rule["action_name"] = actionCall->getActionName().c_str(); auto j = getControlPlaneForTable(*matches, *actionArgs); rule["rules"] = std::move(j); @@ -90,9 +80,9 @@ inja::json STF::getControlPlaneForTable(const std::map] with hdr$. // TODO: This is a limitation of the stf parser. We should fix this. diff --git a/backends/p4tools/testgen/targets/ebpf/backend/stf/stf.h b/backends/p4tools/testgen/targets/ebpf/backend/stf/stf.h index 95ecf6f9a01..7a8891387cf 100644 --- a/backends/p4tools/testgen/targets/ebpf/backend/stf/stf.h +++ b/backends/p4tools/testgen/targets/ebpf/backend/stf/stf.h @@ -41,6 +41,8 @@ class STF : public TF { STF& operator=(STF&&) = delete; STF(cstring testName, boost::optional seed); + + /// Produce an STF test. void outputTest(const TestSpec* spec, cstring selectedBranches, size_t testIdx, float currentCoverage) override; @@ -53,9 +55,6 @@ class STF : public TF { void emitTestcase(const TestSpec* testSpec, cstring selectedBranches, size_t testId, const std::string& testCase, float currentCoverage); - /// Converts the traces of this test into a string representation and Inja object. - static inja::json getTrace(const TestSpec* testSpec); - /// Converts all the control plane objects into Inja format. static inja::json getControlPlane(const TestSpec* testSpec);