mavtables  0.2.1
MAVLink router and firewall.
test_Chain.cpp
Go to the documentation of this file.
1 // MAVLink router and firewall.
2 // Copyright (C) 2018 Michael R. Shannon <mrshannon.aerospace@gmail.com>
3 //
4 // This program is free software; you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation; either version 2 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
16 
17 
18 #include <memory>
19 #include <utility>
20 #include <vector>
21 
22 #include <catch.hpp>
23 
24 #include "Accept.hpp"
25 #include "Action.hpp"
26 #include "Call.hpp"
27 #include "Chain.hpp"
28 #include "GoTo.hpp"
29 #include "If.hpp"
30 #include "MAVAddress.hpp"
31 #include "Packet.hpp"
32 #include "PacketVersion1.hpp"
33 #include "PacketVersion2.hpp"
34 #include "RecursionError.hpp"
35 #include "Reject.hpp"
36 #include "utility.hpp"
37 
38 #include "common_Packet.hpp"
39 
40 
41 TEST_CASE("Chain's are constructable.", "[Chain]")
42 {
43  SECTION("Without rules.")
44  {
45  REQUIRE_NOTHROW(Chain("test_chain"));
46  }
47  SECTION("With rules.")
48  {
49  std::vector<std::unique_ptr<Rule>> rules;
50  rules.push_back(std::make_unique<Accept>(If().type("PING")));
51  rules.push_back(std::make_unique<Reject>());
52  REQUIRE_NOTHROW(Chain("test_chain", std::move(rules)));
53  }
54  SECTION("Ensures the chain name does not contain whitespace.")
55  {
56  // Spaces
57  REQUIRE_THROWS_AS(Chain("test chain"), std::invalid_argument);
58  REQUIRE_THROWS_WITH(
59  Chain("test chain"), "Chain names cannot contain whitespace.");
60  // Tabs
61  REQUIRE_THROWS_AS(Chain("test\tchain"), std::invalid_argument);
62  REQUIRE_THROWS_WITH(
63  Chain("test\tchain"), "Chain names cannot contain whitespace.");
64  // Newlines
65  REQUIRE_THROWS_AS(Chain("test\nchain"), std::invalid_argument);
66  REQUIRE_THROWS_WITH(
67  Chain("test\nchain"), "Chain names cannot contain whitespace.");
68  }
69 }
70 
71 
72 TEST_CASE("Chain's are comparable.", "[Chain]")
73 {
74  SECTION("When there are no rules.")
75  {
76  REQUIRE(Chain("test_chain") == Chain("test_chain"));
77  REQUIRE_FALSE(Chain("test_chain") != Chain("test_chain"));
78  REQUIRE_FALSE(Chain("test_chain1") == Chain("test_chain2"));
79  REQUIRE(Chain("test_chain1") != Chain("test_chain2"));
80  }
81  SECTION("When the number of rules are not the same.")
82  {
83  std::vector<std::unique_ptr<Rule>> rules1;
84  rules1.push_back(std::make_unique<Accept>(If().to("192.168")));
85  rules1.push_back(std::make_unique<Reject>());
86  Chain chain1("test_chain", std::move(rules1));
87  std::vector<std::unique_ptr<Rule>> rules2;
88  rules2.push_back(std::make_unique<Accept>(If().to("192.168")));
89  Chain chain2("test_chain", std::move(rules2));
90  REQUIRE_FALSE(chain1 == chain2);
91  REQUIRE(chain1 != chain2);
92  }
93  SECTION("When the rules are the same.")
94  {
95  std::vector<std::unique_ptr<Rule>> rules1;
96  rules1.push_back(std::make_unique<Accept>(If().to("192.168")));
97  rules1.push_back(std::make_unique<Reject>());
98  Chain chain1("test_chain", std::move(rules1));
99  std::vector<std::unique_ptr<Rule>> rules2;
100  rules2.push_back(std::make_unique<Accept>(If().to("192.168")));
101  rules2.push_back(std::make_unique<Reject>());
102  Chain chain2("test_chain", std::move(rules2));
103  REQUIRE(chain1 == chain2);
104  REQUIRE_FALSE(chain1 != chain2);
105  }
106  SECTION("When the rules are not the same.")
107  {
108  std::vector<std::unique_ptr<Rule>> rules1;
109  rules1.push_back(std::make_unique<Accept>(If().to("192.168")));
110  rules1.push_back(std::make_unique<Reject>());
111  Chain chain1("test_chain", std::move(rules1));
112  std::vector<std::unique_ptr<Rule>> rules2;
113  rules2.push_back(std::make_unique<Accept>(If().from("172.16")));
114  rules2.push_back(std::make_unique<Reject>());
115  Chain chain2("test_chain", std::move(rules2));
116  REQUIRE_FALSE(chain1 == chain2);
117  REQUIRE(chain1 != chain2);
118  }
119 }
120 
121 
122 TEST_CASE("Chain's 'append' method appends a new rule to the filter chain.",
123  "[Chain]")
124 {
125  std::vector<std::unique_ptr<Rule>> rules;
126  rules.push_back(std::make_unique<Accept>(If().to("192.168")));
127  rules.push_back(std::make_unique<Reject>());
128  Chain chain1("test_chain", std::move(rules));
129  Chain chain2("test_chain");
130  chain2.append(std::make_unique<Accept>(If().to("192.168")));
131  chain2.append(std::make_unique<Reject>());
132  REQUIRE(chain1 == chain2);
133 }
134 
135 
136 TEST_CASE("Chain's 'name' method returns the name of the chain.", "[Chain]")
137 {
138  REQUIRE(Chain("crazy_chain_name").name() == "crazy_chain_name");
139 }
140 
141 
142 TEST_CASE("Chain's are copyable.", "[Chain]")
143 {
144  Chain original("test_chain");
145  original.append(std::make_unique<Accept>(If().to("192.168")));
146  original.append(std::make_unique<Reject>());
147  Chain for_comparison("test_chain");
148  for_comparison.append(std::make_unique<Accept>(If().to("192.168")));
149  for_comparison.append(std::make_unique<Reject>());
150  Chain copy(original);
151  REQUIRE(copy == for_comparison);
152 }
153 
154 
155 TEST_CASE("Chain's are movable.", "[Chain]")
156 {
157  Chain original("test_chain");
158  original.append(std::make_unique<Accept>(If().to("192.168")));
159  original.append(std::make_unique<Reject>());
160  Chain for_comparison("test_chain");
161  for_comparison.append(std::make_unique<Accept>(If().to("192.168")));
162  for_comparison.append(std::make_unique<Reject>());
163  Chain moved(std::move(original));
164  REQUIRE(moved == for_comparison);
165 }
166 
167 
168 TEST_CASE("Chain's are assignable.", "[Chain]")
169 {
170  Chain chain_a("test_chain_a");
171  chain_a.append(std::make_unique<Accept>(If().to("192.168")));
172  chain_a.append(std::make_unique<Reject>());
173  Chain chain_a_compare("test_chain_a");
174  chain_a_compare.append(std::make_unique<Accept>(If().to("192.168")));
175  chain_a_compare.append(std::make_unique<Reject>());
176  Chain chain_b("test_chain_b");
177  chain_b.append(std::make_unique<Reject>());
178  Chain chain_b_compare("test_chain_b");
179  chain_b_compare.append(std::make_unique<Reject>());
180  REQUIRE(chain_a == chain_a_compare);
181  chain_a = chain_b;
182  REQUIRE(chain_a == chain_b_compare);
183 }
184 
185 
186 TEST_CASE("Chain's are assignable (by move semantics).", "[Chain]")
187 {
188  Chain chain_a("test_chain_a");
189  chain_a.append(std::make_unique<Accept>(If().to("192.168")));
190  chain_a.append(std::make_unique<Reject>());
191  Chain chain_a_compare("test_chain_a");
192  chain_a_compare.append(std::make_unique<Accept>(If().to("192.168")));
193  chain_a_compare.append(std::make_unique<Reject>());
194  Chain chain_b("test_chain_b");
195  chain_b.append(std::make_unique<Reject>());
196  Chain chain_b_compare("test_chain_b");
197  chain_b_compare.append(std::make_unique<Reject>());
198  REQUIRE(chain_a == chain_a_compare);
199  chain_a = std::move(chain_b);
200  REQUIRE(chain_a == chain_b_compare);
201 }
202 
203 
204 TEST_CASE("Chain's 'action' method determines what to do with a packet with "
205  " respect to a destination address.", "[Rule]")
206 {
207  auto heartbeat = packet_v2::Packet(to_vector(HeartbeatV2()));
208  auto ping = packet_v1::Packet(to_vector(PingV1()));
209  auto set_mode = packet_v2::Packet(to_vector(SetModeV2()));
210  SECTION("When the chain is well formed.")
211  {
212  auto chain = std::make_shared<Chain>("main_chain");
213  auto subchain = std::make_shared<Chain>("sub_chain");
214  chain->append(std::make_unique<Accept>(If().to("192.168")));
215  chain->append(std::make_unique<Accept>(If().type("HEARTBEAT")));
216  chain->append(std::make_unique<Call>(subchain, If().to("172.0/8")));
217  chain->append(std::make_unique<Reject>(If().to("10.10")));
218  subchain->append(std::make_unique<Accept>(If().type("SET_MODE")));
219  subchain->append(std::make_unique<Accept>(If().to("172.16")));
220  REQUIRE(
221  chain->action(ping, MAVAddress("192.168")) ==
223  REQUIRE(
224  chain->action(ping, MAVAddress("192.168")) ==
226  REQUIRE(
227  chain->action(heartbeat, MAVAddress("192.0")) ==
229  REQUIRE(
230  chain->action(set_mode, MAVAddress("172.0")) ==
232  REQUIRE(
233  chain->action(ping, MAVAddress("172.16")) ==
235  REQUIRE(
236  chain->action(ping, MAVAddress("10.10")) ==
238  REQUIRE(
239  chain->action(ping, MAVAddress("172.0")) ==
241  }
242  SECTION("And throws an error when chain recursion is detected.")
243  {
244  auto chain = std::make_shared<Chain>("main_chain");
245  auto subchain = std::make_shared<Chain>("sub_chain");
246  chain->append(std::make_unique<Call>(subchain));
247  subchain->append(std::make_unique<Call>(chain));
248  REQUIRE_THROWS_AS(
249  chain->action(ping, MAVAddress("192.168")), RecursionError);
250  REQUIRE_THROWS_WITH(
251  chain->action(ping, MAVAddress("192.168")), "Recursion detected.");
252  }
253 }
254 
255 
256 TEST_CASE("Chain's are printable.", "[Chain]")
257 {
258  auto chain = std::make_shared<Chain>("default");
259  auto ap_in = std::make_shared<Chain>("ap-in");
260  auto ap_out = std::make_shared<Chain>("ap-out");
261  chain->append(
262  std::make_unique<Reject>(If().type("HEARTBEAT").from("10.10")));
263  chain->append(
264  std::make_unique<Accept>(-3, If().type("GPS_STATUS").to("172.0/8")));
265  chain->append(
266  std::make_unique<Accept>(
267  If().type("GLOBAL_POSITION_INT").to("172.0/8")));
268  chain->append(
269  std::make_unique<GoTo>(ap_in, 3, If().from("192.168")));
270  chain->append(std::make_unique<Call>(ap_out, If().to("192.168")));
271  chain->append(std::make_unique<Reject>());
272  REQUIRE(
273  str(*chain) ==
274  "chain default {\n"
275  " reject if HEARTBEAT from 10.10;\n"
276  " accept with priority -3 if GPS_STATUS to 172.0/8;\n"
277  " accept if GLOBAL_POSITION_INT to 172.0/8;\n"
278  " goto ap-in with priority 3 if from 192.168;\n"
279  " call ap-out if to 192.168;\n"
280  " reject;\n"
281  "}");
282 }
283 
284 
285 // Required for complete function coverage.
286 TEST_CASE("Run dynamic destructors (Chain).", "[Chain]")
287 {
288  Chain *chain = nullptr;
289  REQUIRE_NOTHROW(chain = new Chain("chain"));
290  REQUIRE_NOTHROW(delete chain);
291 }
static Action make_reject()
Definition: Action.cpp:113
std::string str(const T &object)
Definition: utility.hpp:128
Chain chain_a_compare("test_chain_a")
Definition: If.hpp:35
static Action make_accept(std::optional< int > priority={})
Definition: Action.cpp:100
TEST_CASE("Chain's are constructable.", "[Chain]")
Definition: test_Chain.cpp:41
static Action make_continue()
Definition: Action.cpp:126
Chain chain_b_compare("test_chain_b")
def heartbeat(mav)
Definition: logger.py:42
Definition: Chain.hpp:37
void append(std::unique_ptr< Rule > rule)
Definition: Chain.cpp:116
chain_a
Definition: test_Chain.cpp:199
auto ping
Definition: test_Call.cpp:229
Chain chain_b("test_chain_b")