1515
1616#include " netkat/evaluator.h"
1717
18+ #include " absl/container/flat_hash_set.h"
19+ #include " fuzztest/fuzztest.h"
20+ #include " gmock/gmock.h"
1821#include " gtest/gtest.h"
1922#include " netkat/netkat.pb.h"
23+ #include " netkat/netkat_proto_constructors.h"
2024
2125namespace netkat {
2226namespace {
2327
24- TEST (EvaluatorTest, TrueIsTrueOnAnyPackets) {
28+ using ::fuzztest::Arbitrary;
29+ using ::fuzztest::InRange;
30+ using ::testing::ContainerEq;
31+ using ::testing::IsEmpty;
32+ using ::testing::IsSupersetOf;
33+ using ::testing::UnorderedElementsAre;
34+
35+ TEST (EvaluatePredicateProtoTest, TrueIsTrueOnAnyPackets) {
2536 PredicateProto true_predicate;
2637 true_predicate.mutable_bool_constant ()->set_value (true );
2738
@@ -31,7 +42,7 @@ TEST(EvaluatorTest, TrueIsTrueOnAnyPackets) {
3142 Packet ({{" field1" , 1 }, {" field2" , 2 }, {" field3" , 3 }})));
3243}
3344
34- TEST (EvaluatorTest , FalseIsFalseOnAnyPacket) {
45+ TEST (EvaluatePredicateProtoTest , FalseIsFalseOnAnyPacket) {
3546 PredicateProto false_predicate;
3647 false_predicate.mutable_bool_constant ()->set_value (false );
3748
@@ -41,7 +52,7 @@ TEST(EvaluatorTest, FalseIsFalseOnAnyPacket) {
4152 Packet ({{" field1" , 1 }, {" field2" , 2 }, {" field3" , 3 }})));
4253}
4354
44- TEST (EvaluatorTest , EmptyPredicateIsFalseOnAnyPacket) {
55+ TEST (EvaluatePredicateProtoTest , EmptyPredicateIsFalseOnAnyPacket) {
4556 PredicateProto empty_predicate;
4657
4758 EXPECT_FALSE (Evaluate (empty_predicate, Packet ()));
@@ -50,7 +61,7 @@ TEST(EvaluatorTest, EmptyPredicateIsFalseOnAnyPacket) {
5061 Packet ({{" field1" , 1 }, {" field2" , 2 }, {" field3" , 3 }})));
5162}
5263
53- TEST (EvaluatorTest , NotTrueIsFalseOnAnyPackets) {
64+ TEST (EvaluatePredicateProtoTest , NotTrueIsFalseOnAnyPackets) {
5465 PredicateProto not_true_predicate;
5566 not_true_predicate.mutable_not_op ()
5667 ->mutable_negand ()
@@ -63,7 +74,7 @@ TEST(EvaluatorTest, NotTrueIsFalseOnAnyPackets) {
6374 Packet ({{" field1" , 1 }, {" field2" , 2 }, {" field3" , 3 }})));
6475}
6576
66- TEST (EvaluatorTest , NotFalseIsTrueOnAnyPacket) {
77+ TEST (EvaluatePredicateProtoTest , NotFalseIsTrueOnAnyPacket) {
6778 PredicateProto not_false_predicate;
6879 not_false_predicate.mutable_not_op ()
6980 ->mutable_negand ()
@@ -76,7 +87,7 @@ TEST(EvaluatorTest, NotFalseIsTrueOnAnyPacket) {
7687 Packet ({{" field1" , 1 }, {" field2" , 2 }, {" field3" , 3 }})));
7788}
7889
79- TEST (EvaluatorTest , NotNotTrueIsTrueOnAnyPackets) {
90+ TEST (EvaluatePredicateProtoTest , NotNotTrueIsTrueOnAnyPackets) {
8091 PredicateProto not_not_true_predicate;
8192 not_not_true_predicate.mutable_not_op ()
8293 ->mutable_negand ()
@@ -91,7 +102,7 @@ TEST(EvaluatorTest, NotNotTrueIsTrueOnAnyPackets) {
91102 Packet ({{" field1" , 1 }, {" field2" , 2 }, {" field3" , 3 }})));
92103}
93104
94- TEST (EvaluatorTest , MatchesFieldWithCorrectValue) {
105+ TEST (EvaluatePredicateProtoTest , MatchesFieldWithCorrectValue) {
95106 PredicateProto match_predicate;
96107 match_predicate.mutable_match ()->set_field (" field1" );
97108 match_predicate.mutable_match ()->set_value (1 );
@@ -102,7 +113,7 @@ TEST(EvaluatorTest, MatchesFieldWithCorrectValue) {
102113 Packet ({{" field1" , 1 }, {" field2" , 2 }, {" field3" , 3 }})));
103114}
104115
105- TEST (EvaluatorTest , DoesNotMatchFieldWithWrongValue) {
116+ TEST (EvaluatePredicateProtoTest , DoesNotMatchFieldWithWrongValue) {
106117 PredicateProto match_predicate;
107118 match_predicate.mutable_match ()->set_field (" field1" );
108119 match_predicate.mutable_match ()->set_value (2 );
@@ -113,7 +124,7 @@ TEST(EvaluatorTest, DoesNotMatchFieldWithWrongValue) {
113124 Packet ({{" field1" , 1 }, {" field2" , 2 }, {" field3" , 3 }})));
114125}
115126
116- TEST (EvaluatorTest , AndIsLogicallyCorrect) {
127+ TEST (EvaluatePredicateProtoTest , AndIsLogicallyCorrect) {
117128 PredicateProto true_and_true_predicate;
118129 true_and_true_predicate.mutable_and_op ()
119130 ->mutable_left ()
@@ -160,7 +171,7 @@ TEST(EvaluatorTest, AndIsLogicallyCorrect) {
160171 Packet ({{" field1" , 1 }, {" field2" , 2 }, {" field3" , 3 }})));
161172}
162173
163- TEST (EvaluatorTest , OrIsLogicallyCorrect) {
174+ TEST (EvaluatePredicateProtoTest , OrIsLogicallyCorrect) {
164175 PredicateProto true_or_true_predicate;
165176 true_or_true_predicate.mutable_or_op ()
166177 ->mutable_left ()
@@ -207,7 +218,7 @@ TEST(EvaluatorTest, OrIsLogicallyCorrect) {
207218 Packet ({{" field1" , 1 }, {" field2" , 2 }, {" field3" , 3 }})));
208219}
209220
210- TEST (EvaluatorTest , DeMorganHolds) {
221+ TEST (EvaluatePredicateProtoTest , DeMorganHolds) {
211222 const Packet kEmptyPacket = Packet ();
212223 const Packet kOneFieldPacket = Packet ({{" field1" , 1 }});
213224 const Packet kThreeFieldsPacket =
@@ -294,5 +305,136 @@ TEST(EvaluatorTest, DeMorganHolds) {
294305 }
295306}
296307
308+ /* --- Basic policy properties ------------------------------------------------*/
309+
310+ void LiftedEvaluationIsCorrect (absl::flat_hash_set<Packet> packets,
311+ PolicyProto policy) {
312+ absl::flat_hash_set<Packet> expected_packets;
313+ for (const Packet& packet : packets) {
314+ expected_packets.merge (Evaluate (policy, packet));
315+ }
316+ EXPECT_THAT (Evaluate (policy, packets), ContainerEq (expected_packets));
317+ }
318+ FUZZ_TEST (EvaluatePolicyProtoTest, LiftedEvaluationIsCorrect);
319+
320+ void RecordIsAccept (Packet packet) {
321+ EXPECT_THAT (Evaluate (RecordProto (), packet), UnorderedElementsAre (packet));
322+ }
323+ FUZZ_TEST (EvaluatePolicyProtoTest, RecordIsAccept);
324+
325+ void UninitializedPolicyIsDeny (Packet packet) {
326+ EXPECT_THAT (Evaluate (PolicyProto (), packet), IsEmpty ());
327+ }
328+ FUZZ_TEST (EvaluatePolicyProtoTest, UninitializedPolicyIsDeny);
329+
330+ void FilterIsCorrect (Packet packet, PredicateProto predicate) {
331+ if (Evaluate (predicate, packet)) {
332+ EXPECT_THAT (Evaluate (FilterProto (predicate), packet),
333+ UnorderedElementsAre (packet));
334+ } else {
335+ EXPECT_THAT (Evaluate (FilterProto (predicate), packet), IsEmpty ());
336+ }
337+ }
338+ FUZZ_TEST (EvaluatePolicyProtoTest, FilterIsCorrect);
339+
340+ void ModifyModifies (Packet packet, std::string field, int value) {
341+ Packet expected_packet = packet;
342+ expected_packet[field] = value;
343+ EXPECT_THAT (Evaluate (ModificationProto (field, value), packet),
344+ UnorderedElementsAre (expected_packet));
345+ }
346+ FUZZ_TEST (EvaluatePolicyProtoTest, ModifyModifies);
347+
348+ void UnionCombines (Packet packet, PolicyProto left, PolicyProto right) {
349+ absl::flat_hash_set<Packet> expected_packets = Evaluate (left, packet);
350+ expected_packets.merge (Evaluate (right, packet));
351+
352+ EXPECT_THAT (Evaluate (UnionProto (left, right), packet),
353+ ContainerEq (expected_packets));
354+ }
355+ FUZZ_TEST (EvaluatePolicyProtoTest, UnionCombines);
356+
357+ void SequenceSequences (Packet packet, PolicyProto left, PolicyProto right) {
358+ absl::flat_hash_set<Packet> expected_packets =
359+ Evaluate (right, Evaluate (left, packet));
360+
361+ EXPECT_THAT (Evaluate (SequenceProto (left, right), packet),
362+ ContainerEq (expected_packets));
363+ }
364+ FUZZ_TEST (EvaluatePolicyProtoTest, SequenceSequences);
365+
366+ PolicyProto UnionUpToNthPower (PolicyProto iterable, int n) {
367+ PolicyProto union_policy = AcceptProto ();
368+ PolicyProto next_sequence = iterable;
369+ for (int i = 1 ; i <= n; ++i) {
370+ union_policy = UnionProto (union_policy, next_sequence);
371+ next_sequence = SequenceProto (iterable, next_sequence);
372+ }
373+ return union_policy;
374+ }
375+
376+ void IterateIsSupersetOfUnionOfNSequences (Packet packet, PolicyProto iterable,
377+ int n) {
378+ EXPECT_THAT (Evaluate (IterateProto (iterable), packet),
379+ IsSupersetOf (Evaluate (UnionUpToNthPower (iterable, n), packet)));
380+ }
381+ FUZZ_TEST (EvaluatePolicyProtoTest, IterateIsSupersetOfUnionOfNSequences)
382+ .WithDomains(/* packet=*/ Arbitrary<Packet>(),
383+ /* iterable=*/ Arbitrary<PolicyProto>(),
384+ /* n=*/ InRange(0 , 100 ));
385+
386+ void IterateIsUnionOfNSequencesForSomeN (Packet packet, PolicyProto iterable) {
387+ absl::flat_hash_set<Packet> iterate_output_packets =
388+ Evaluate (IterateProto (iterable), packet);
389+
390+ // Evaluate successively larger unions until we find one that matches all
391+ // packets in `iterate_packets`.
392+ absl::flat_hash_set<Packet> union_output_packets;
393+ int last_size;
394+ int n = 0 ;
395+ do {
396+ last_size = union_output_packets.size ();
397+ union_output_packets = Evaluate (UnionUpToNthPower (iterable, n++), packet);
398+ } while (iterate_output_packets != union_output_packets &&
399+ union_output_packets.size () > last_size);
400+
401+ EXPECT_THAT (iterate_output_packets, ContainerEq (union_output_packets));
402+ }
403+ FUZZ_TEST (EvaluatePolicyProtoTest, IterateIsUnionOfNSequencesForSomeN);
404+
405+ TEST (EvaluatePolicyProtoTest, SimpleIterateThroughFiltersAndModifies) {
406+ // f == 0; f:=1 + f == 1; f := 2 + f == 2; f := 3
407+ PolicyProto iterable = UnionProto (
408+ SequenceProto (FilterProto (MatchProto (" f" , 0 )), ModificationProto (" f" , 1 )),
409+ UnionProto (SequenceProto (FilterProto (MatchProto (" f" , 1 )),
410+ ModificationProto (" f" , 2 )),
411+ SequenceProto (FilterProto (MatchProto (" f" , 2 )),
412+ ModificationProto (" f" , 3 ))));
413+
414+ // If the packet contains the field, then the output is the union of the
415+ // input and the modified packets.
416+ EXPECT_THAT (Evaluate (IterateProto (iterable), Packet ({{" f" , 0 }})),
417+ UnorderedElementsAre (Packet ({{" f" , 0 }}), Packet ({{" f" , 1 }}),
418+ Packet ({{" f" , 2 }}), Packet ({{" f" , 3 }})));
419+
420+ // If the packet doesn't contain the field, then the only output is the
421+ // input.
422+ EXPECT_THAT (Evaluate (IterateProto (iterable), Packet ()),
423+ UnorderedElementsAre (Packet ()));
424+ }
425+
426+ /* --- Advanced policy properties ---------------------------------------------*/
427+ void ModifyThenMatchIsEquivalentToModify (Packet packet, std::string field,
428+ int value) {
429+ // f := n;f == n is equivalent to f := n.
430+ EXPECT_THAT (Evaluate (SequenceProto (ModificationProto (field, value),
431+ FilterProto (MatchProto (field, value))),
432+ packet),
433+ ContainerEq (Evaluate (ModificationProto (field, value), packet)));
434+ }
435+ FUZZ_TEST (EvaluatePolicyProtoTest, ModifyThenMatchIsEquivalentToModify);
436+
437+ // TODO(dilo): Add tests for each of the NetKAT axioms.
438+
297439} // namespace
298440} // namespace netkat
0 commit comments