/* * Copyright 2023 WebAssembly Community Group participants * * 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 #include #include #include "support/dfa_minimization.h" #include "gtest/gtest.h" using namespace wasm; using State = DFA::State; using Graph = std::vector>; using Results = std::vector>; using SetResults = std::set>; // We don't care about order between or within the output partitions. SetResults settify(const Results& vecs) { SetResults sets; for (const auto& vec : vecs) { sets.emplace(vec.begin(), vec.end()); } return sets; } // Check that the same values appear in the input and output and that each value // appears once. void validate(const Graph& graph, const Results& results) { std::set inputVals; for (const auto& partition : graph) { for (const auto& state : partition) { bool inserted = inputVals.insert(state.val).second; ASSERT_TRUE(inserted); } } std::set outputVals; for (const auto& partition : results) { for (const auto& val : partition) { ASSERT_NE(inputVals.find(val), inputVals.end()); ASSERT_TRUE(outputVals.insert(val).second); } } ASSERT_EQ(outputVals.size(), inputVals.size()); } TEST(DFATest, Empty) { Graph graph = {}; auto results = DFA::refinePartitions(graph); validate(graph, results); EXPECT_EQ(settify(results), SetResults{}); } TEST(DFATest, Singleton) { Graph graph = {{{"A", {}}}}; auto results = DFA::refinePartitions(graph); validate(graph, results); EXPECT_EQ(settify(results), SetResults{{"A"}}); } TEST(DFATest, CyclePartitioned) { // The partitions are already maximally refined, so shouldn't change. State a{"A", {"B"}}; State b{"B", {"C"}}; State c{"C", {"A"}}; Graph graph = {{a}, {b}, {c}}; auto results = DFA::refinePartitions(graph); validate(graph, results); EXPECT_EQ(settify(results), (SetResults{{"A"}, {"B"}, {"C"}})); } TEST(DFATest, CycleGrouped) { // All the states are identical, so the the partition shouldn't change. State a{"A", {"B"}}; State b{"B", {"C"}}; State c{"C", {"A"}}; Graph graph = {{a, b, c}}; auto results = DFA::refinePartitions(graph); validate(graph, results); EXPECT_EQ(settify(results), (SetResults{{"A", "B", "C"}})); } TEST(DFATest, CycleRefined) { // A and B are distinguishable, so the partitions should be refined. State a{"A", {"B"}}; State b{"B", {"C"}}; State c{"C", {"A"}}; Graph graph = {{a, b}, {c}}; auto results = DFA::refinePartitions(graph); validate(graph, results); EXPECT_EQ(settify(results), (SetResults{{"A"}, {"B"}, {"C"}})); } TEST(DFATest, ChainZip) { // Chain A->B->C->D is identical to chain W->X->Y->Z. State a{"A", {"B"}}, w{"W", {"X"}}; State b{"B", {"C"}}, x{"X", {"Y"}}; State c{"C", {"D"}}, y{"Y", {"Z"}}; State d{"D", {}}, z{"Z", {}}; Graph graph = {{a, b, c, d, w, x, y, z}}; auto results = DFA::refinePartitions(graph); validate(graph, results); EXPECT_EQ(settify(results), (SetResults{{"A", "W"}, {"B", "X"}, {"C", "Y"}, {"D", "Z"}})); } TEST(DFATest, ChainUnzip) { // Chain A->B->C->D looks a lot like chain W->X->Y->Z, but since Z is // distinguishable, the full chains end up being separated. State a{"A", {"B"}}, w{"W", {"X"}}; State b{"B", {"C"}}, x{"X", {"Y"}}; State c{"C", {"D"}}, y{"Y", {"Z"}}; State d{"D", {}}, z{"Z", {}}; Graph graph = {{a, b, c, d, w, x, y}, {z}}; auto results = DFA::refinePartitions(graph); validate(graph, results); EXPECT_EQ( settify(results), (SetResults{{"A"}, {"W"}, {"B"}, {"X"}, {"C"}, {"Y"}, {"D"}, {"Z"}})); }