From 1297646b93d773be26a92c2c8a08c2cf05ae6b44 Mon Sep 17 00:00:00 2001 From: Han Wang Date: Sat, 8 Apr 2017 22:57:13 -0700 Subject: [PATCH] A few gtest cases for PSA (#459) --- frontends/p4/p4-parse.ypp | 5 +- frontends/p4/symbol_table.cpp | 2 + lib/source_file.cpp | 5 + lib/source_file.h | 3 + test/gtest/Makefile.am | 5 +- test/gtest/arch_test.cpp | 348 +++++++++++++++++++++++++++ test/gtest/{helper.h => helpers.cpp} | 8 +- test/gtest/helpers.h | 25 ++ test/gtest/midend_test.cpp | 2 +- test/gtest/typecheck_test.cpp | 75 ------ 10 files changed, 392 insertions(+), 86 deletions(-) create mode 100644 test/gtest/arch_test.cpp rename test/gtest/{helper.h => helpers.cpp} (83%) create mode 100644 test/gtest/helpers.h delete mode 100644 test/gtest/typecheck_test.cpp diff --git a/frontends/p4/p4-parse.ypp b/frontends/p4/p4-parse.ypp index 5a2c70ab46a..ca06a89757a 100644 --- a/frontends/p4/p4-parse.ypp +++ b/frontends/p4/p4-parse.ypp @@ -1035,13 +1035,14 @@ const IR::P4Program *parse_P4_16_file(const char *name, FILE *in) { const IR::P4Program *parse_string(const std::string &pgm) { int errors = 0; + Util::InputSources::reset(); clearErrorReporter(); + structure.clear(); + allErrors = nullptr; declarations = new IR::IndexedVector(); YY_BUFFER_STATE state = yy_scan_string(pgm.c_str()); errors |= yyparse(); yy_delete_buffer(state); - structure.clear(); - allErrors = nullptr; if (errors) { return nullptr; } diff --git a/frontends/p4/symbol_table.cpp b/frontends/p4/symbol_table.cpp index 6e432a8aed0..70acd835a0d 100644 --- a/frontends/p4/symbol_table.cpp +++ b/frontends/p4/symbol_table.cpp @@ -244,5 +244,7 @@ cstring ProgramStructure::toString() const { void ProgramStructure::clear() { rootNamespace->clear(); + currentNamespace = rootNamespace; + debugStream = stderr; } } // namespace Util diff --git a/lib/source_file.cpp b/lib/source_file.cpp index eb1927de42e..cf398e8ae41 100644 --- a/lib/source_file.cpp +++ b/lib/source_file.cpp @@ -62,6 +62,11 @@ InputSources::InputSources() : this->contents.push_back(""); } +/* static */ +void InputSources::reset() { + instance = new InputSources; +} + // prevent further changes void InputSources::seal() { if (this->sealed) diff --git a/lib/source_file.h b/lib/source_file.h index 452e44a803d..95cc866c8e4 100644 --- a/lib/source_file.h +++ b/lib/source_file.h @@ -271,6 +271,9 @@ class InputSources final { cstring toDebugString() const; + // Reset all source information, so that a new file can be parsed. + static void reset(); + static InputSources* instance; private: diff --git a/test/gtest/Makefile.am b/test/gtest/Makefile.am index 5d7f80b7c4e..fc097f5c7ff 100644 --- a/test/gtest/Makefile.am +++ b/test/gtest/Makefile.am @@ -15,9 +15,10 @@ # General GTest unit tests. Add tests here if they don't have a logical home # elsewhere in the codebase. gtest_unittest_UNIFIED = \ + test/gtest/helpers.cpp \ test/gtest/opeq_test.cpp \ - test/gtest/typecheck_test.cpp \ - test/gtest/midend_test.cpp + test/gtest/midend_test.cpp \ + test/gtest/arch_test.cpp cpplint_FILES += $(gtest_unittest_UNIFIED) gtest_SOURCES += $(gtest_unittest_SOURCES) diff --git a/test/gtest/arch_test.cpp b/test/gtest/arch_test.cpp new file mode 100644 index 00000000000..0d5a7fd4b45 --- /dev/null +++ b/test/gtest/arch_test.cpp @@ -0,0 +1,348 @@ +/* +Copyright 2013-present Barefoot Networks, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#include "gtest/gtest.h" +#include "ir/ir.h" +#include "helpers.h" +#include "lib/log.h" + +#include "frontends/p4/typeMap.h" +#include "frontends/common/resolveReferences/referenceMap.h" +#include "frontends/common/resolveReferences/resolveReferences.h" + +#include "p4/createBuiltins.h" +#include "p4/typeChecking/typeChecker.h" + +using namespace P4; + +TEST(arch, packet_out) { + std::string program = R"( + // simplified core.p4 + extern packet_out { + void emit (in T hdr); + } + // simplified v1model.p4 + control Deparser (packet_out b, in H hdr); + package PSA (Deparser p); + // user program + struct ParsedHeaders { + bit<32> hdr; + } + control MyDeparser(packet_out b, in ParsedHeaders h){ + apply { + b.emit(h.hdr); + } + } + PSA(MyDeparser()) main; + )"; + const IR::P4Program* pgm = parse_string(program); + + ReferenceMap refMap; + TypeMap typeMap; + PassManager passes({ + new TypeChecking(&refMap, &typeMap) + }); + + pgm = pgm->apply(passes); + ASSERT_NE(nullptr, pgm); +} + +// Potential bug +TEST(arch, duplicatedDeclarationBug) { + std::string program = R"( + // simplified core.p4 + extern packet_out { + void emit (in T hdr); + } + // simplified v1model.p4 + control Deparser (packet_out b, in H hdr); + package PSA (Deparser p); + // user program + struct ParsedHeaders { + bit<32> hdr; + } + // BUG: duplicated name with 'Deparser' + control Deparser(packet_out b, in ParsedHeaders h){ + apply { + b.emit(h.hdr); + } + } + PSA(Deparser()) main; + )"; + const IR::P4Program* pgm = parse_string(program); + + ReferenceMap refMap; + TypeMap typeMap; + PassManager passes({ + new TypeChecking(&refMap, &typeMap) + }); + + pgm = pgm->apply(passes); + ASSERT_EQ(nullptr, pgm); +} + +TEST(arch, instantiation) { + std::string program = R"( + // simplified core.p4 + extern packet_in { + void extract (out T hdr); + } + extern packet_out { + void emit (in T hdr); + } + // simplified v1model.p4 + parser Parser (packet_in b, out M meta); + control Ingress (inout H hdr, inout M meta); + control Deparser (packet_out b, in H hdr); + package PSA (Parser p, Ingress ig, Deparser dp); + // user program + struct ParsedHeaders { + bit<32> hdr; + } + struct Metadata { + bit<32> hdr; + } + parser MyParser(packet_in b, out Metadata m) { + state start {} + } + control MyIngress(inout ParsedHeaders h, inout Metadata m) { + apply { + } + } + control MyDeparser(packet_out b, in ParsedHeaders h){ + apply { + b.emit(h.hdr); + } + } + MyParser() p; + MyIngress() ig; + MyDeparser() dp; + PSA(p, ig, dp) main; + )"; + const IR::P4Program* pgm = parse_string(program); + + ReferenceMap refMap; + TypeMap typeMap; + PassManager passes({ + new TypeChecking(&refMap, &typeMap) + }); + + pgm = pgm->apply(passes); + ASSERT_NE(nullptr, pgm); +} + +TEST(arch, psa_package_with_body) { + std::string program = R"( + // simplified core.p4 + // simplified v1model.p4 + control Ingress (inout H hdr, inout M meta); + package PSA (Ingress ig) {} + // user program + struct ParsedHeaders { + bit<32> hdr; + } + struct Metadata { + bit<32> hdr; + } + control MyIngress(inout ParsedHeaders h, inout Metadata m) (bit<32> x) { + apply { + } + } + MyIngress(2) ig; + PSA(ig) main; + )"; + const IR::P4Program* pgm = parse_string(program); + + ReferenceMap refMap; + TypeMap typeMap; + PassManager passes({ + new TypeChecking(&refMap, &typeMap) + }); + pgm = pgm->apply(passes); + ASSERT_EQ(nullptr, pgm); +} + +TEST(arch, psa_control_in_control) { + std::string program = R"( + // simplified core.p4 + // simplified v1model.p4 + control Ingress (inout H hdr, inout M meta); + control Egress (inout M meta); + package PSA (Ingress ig); + // user program + struct ParsedHeaders { + bit<32> hdr; + } + struct Metadata { + bit<32> hdr; + } + control MyIngress(inout ParsedHeaders h, inout Metadata m) { + apply { + } + } + control MyEgress(inout Metadata m) (MyIngress ig) { + apply {} + } + MyIngress() ig; + MyEgress(ig) eg; + PSA(ig) main; + )"; + const IR::P4Program* pgm = parse_string(program); + + ReferenceMap refMap; + TypeMap typeMap; + PassManager passes({ + new TypeChecking(&refMap, &typeMap) + }); + pgm = pgm->apply(passes); + ASSERT_NE(nullptr, pgm); +} + +TEST(arch, psa_clone_as_param_to_package) { + std::string program = R"( + extern clone { + clone(); + void execute(in bit<32> sessions); + } + package PSA (clone c); + // user program + struct ParsedHeaders { + bit<32> hdr; + } + clone() c; + PSA(c) main; + )"; + const IR::P4Program* pgm = parse_string(program); + + ReferenceMap refMap; + TypeMap typeMap; + PassManager passes({ + new TypeChecking(&refMap, &typeMap) + }); + pgm = pgm->apply(passes); + ASSERT_NE(nullptr, pgm); +} + +TEST(arch, psa_clone_as_param_to_control) { + std::string program = R"( + extern clone { + // constructor + clone(); + // methods + void clone(in bit<32> sessions); + void clone(in bit<32> sessions, in T data); + } + control ingress (inout H hdr, inout M meta); + package PSA (ingress ig); + // user program + struct ParsedHeaders { + bit<32> hdr; + } + struct Metadata { + bit<32> md; + } + control MyIngress (inout ParsedHeaders p, inout Metadata m) (clone> c) { + apply{} + } + MyIngress(clone>()) ig; + PSA(ig) main; + )"; + const IR::P4Program* pgm = parse_string(program); + + ReferenceMap refMap; + TypeMap typeMap; + PassManager passes({ + new TypeChecking(&refMap, &typeMap) + }); + pgm = pgm->apply(passes); + ASSERT_NE(nullptr, pgm); +} + +TEST(arch, psa_clone_as_param_to_extern) { + std::string program = R"( + extern clone { + // constructor + clone(); + // methods + void clone(in bit<32> sessions); + void clone(in bit<32> sessions, in T data); + } + extern PRE { + PRE(); + void clone(in bit<32> sessions); /* same as invoking c.clone(sessions); */ + void clone(in bit<32> sessions, in T data); /* same as invoking c.clone(sessions, data) */ + // ... + // drop, multicast api here + } + control ingress (inout H hdr, inout M meta); + package PSA (ingress ig); + // user program + struct ParsedHeaders { + bit<32> hdr; + } + struct Metadata { + bit<32> md; + } + control MyIngress (inout ParsedHeaders p, inout Metadata m) (PRE> pre) { + apply{} + } + PRE>() pre; + MyIngress(pre) ig; + PSA(ig) main; + )"; + const IR::P4Program* pgm = parse_string(program); + + ReferenceMap refMap; + TypeMap typeMap; + PassManager passes({ + new TypeChecking(&refMap, &typeMap) + }); + pgm = pgm->apply(passes); + ASSERT_NE(nullptr, pgm); +} + +TEST(arch, clone_as_extern_method) { + std::string program = R"( + extern void clone(in bit<32> sessions); + extern void clone3(in bit<32> sessions, in T data); + control ingress (inout H hdr, inout M meta); + package PSA (ingress ig); + // user program + struct ParsedHeaders { + bit<32> hdr; + } + struct Metadata { + bit<32> md; + } + control MyIngress (inout ParsedHeaders p, inout Metadata m) { + apply{ + // invoke clone3 triggers a compiler bug. + } + } + MyIngress() ig; + PSA(ig) main; + )"; + const IR::P4Program* pgm = parse_string(program); + + ReferenceMap refMap; + TypeMap typeMap; + PassManager passes({ + new TypeChecking(&refMap, &typeMap) + }); + pgm = pgm->apply(passes); + ASSERT_NE(nullptr, pgm); +} + diff --git a/test/gtest/helper.h b/test/gtest/helpers.cpp similarity index 83% rename from test/gtest/helper.h rename to test/gtest/helpers.cpp index 28e1e9354c9..658715066e1 100644 --- a/test/gtest/helper.h +++ b/test/gtest/helpers.cpp @@ -14,17 +14,13 @@ See the License for the specific language governing permissions and limitations under the License. */ -#ifndef _TEST_GTEST_HELPER_ -#define _TEST_GTEST_HELPER_ - #include /* preprocessing by prepending the content of core.p4 to test program */ -std::string with_core_p4 (const std::string& pgm) { +std::string with_core_p4(const std::string& pgm) { std::ifstream input("p4include/core.p4"); std::stringstream sstr; - while(input >> sstr.rdbuf()); + while (input >> sstr.rdbuf()) {} return sstr.str() + pgm; } -#endif diff --git a/test/gtest/helpers.h b/test/gtest/helpers.h new file mode 100644 index 00000000000..2699edd9853 --- /dev/null +++ b/test/gtest/helpers.h @@ -0,0 +1,25 @@ +/* +Copyright 2013-present Barefoot Networks, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#ifndef _TEST_GTEST_HELPER_ +#define _TEST_GTEST_HELPER_ + +#include + +/* preprocessing by prepending the content of core.p4 to test program */ +std::string with_core_p4 (const std::string& pgm); + +#endif diff --git a/test/gtest/midend_test.cpp b/test/gtest/midend_test.cpp index ce7563e6704..1afaeda5a15 100644 --- a/test/gtest/midend_test.cpp +++ b/test/gtest/midend_test.cpp @@ -16,7 +16,7 @@ limitations under the License. #include "gtest/gtest.h" #include "ir/ir.h" -#include "helper.h" +#include "helpers.h" #include "lib/log.h" #include "frontends/p4/typeMap.h" diff --git a/test/gtest/typecheck_test.cpp b/test/gtest/typecheck_test.cpp deleted file mode 100644 index 8331fa814c1..00000000000 --- a/test/gtest/typecheck_test.cpp +++ /dev/null @@ -1,75 +0,0 @@ -/* -Copyright 2013-present Barefoot Networks, Inc. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -#include "gtest/gtest.h" - -#include "ir/ir.h" -#include "p4/p4-parse.h" -#include "frontends/p4/typeMap.h" -#include "frontends/common/constantFolding.h" -#include "frontends/p4/typeChecking/bindVariables.h" -#include "frontends/common/resolveReferences/resolveReferences.h" - -#include "p4/validateParsedProgram.h" -#include "p4/createBuiltins.h" -#include "p4/unusedDeclarations.h" -#include "p4/typeChecking/typeChecker.h" - -#include "helper.h" - -using namespace P4; - -TEST(UNITTEST, helloworld) { - std::string program = with_core_p4( - "parser Parser (packet_in p){ state start{} };\n" - "control empty() { apply {} };\n" - "package top(empty e);\n" - "top(empty()) main;\n"); - - const IR::P4Program* pgm = parse_string(program); - ASSERT_NE(nullptr, pgm); - - ReferenceMap refMap; - TypeMap typeMap; - - PassManager passes = { - new ValidateParsedProgram(false), - new CreateBuiltins(), - new ResolveReferences(&refMap, true), - new ConstantFolding(&refMap, nullptr), - new ResolveReferences(&refMap), -// new TypeInference(&refMap, &typeMap), - }; - pgm = pgm->apply(passes); - - ASSERT_NE(nullptr, pgm); -} - -TEST(UNITTEST, package) { - std::string program = with_core_p4( - "parser Parser (packet_in p){ state start{} };\n" - "control empty() { apply {} };\n" - "package top(empty e);\n"); - - const IR::P4Program* pgm = parse_string(program); - - ASSERT_NE(nullptr, pgm); - - PassManager passes = { - new CreateBuiltins(), - }; - pgm = pgm->apply(passes); -}