blob: 556fea9fa4c9de3531b0429ce685fb947465fe95 [file] [log] [blame]
Austin Schuhcb108412019-10-13 16:09:54 -07001#include "aos/configuration.h"
2
Austin Schuh99f7c6a2024-06-25 22:07:44 -07003#include "absl/log/check.h"
4#include "absl/log/log.h"
Austin Schuhcb108412019-10-13 16:09:54 -07005#include "absl/strings/strip.h"
Philipp Schrader790cb542023-07-05 21:06:52 -07006#include "flatbuffers/reflection.h"
Philipp Schrader790cb542023-07-05 21:06:52 -07007#include "gmock/gmock.h"
8#include "gtest/gtest.h"
9
Nathan Leong307c9692022-10-08 15:25:03 -070010#include "aos/events/ping_generated.h"
Austin Schuhcb108412019-10-13 16:09:54 -070011#include "aos/json_to_flatbuffer.h"
Austin Schuh1ef01ef2021-02-07 20:40:36 -080012#include "aos/testing/flatbuffer_eq.h"
Austin Schuh373f1762021-06-02 21:07:09 -070013#include "aos/testing/path.h"
Austin Schuhcb108412019-10-13 16:09:54 -070014#include "aos/testing/test_logging.h"
15#include "aos/util/file.h"
Austin Schuhcb108412019-10-13 16:09:54 -070016
Stephan Pleinesf63bde82024-01-13 15:59:33 -080017namespace aos::configuration::testing {
Austin Schuhcb108412019-10-13 16:09:54 -070018
Austin Schuh373f1762021-06-02 21:07:09 -070019using aos::testing::ArtifactPath;
Austin Schuhfb37c612022-08-11 15:24:51 -070020namespace chrono = std::chrono;
Austin Schuh66602132020-02-28 13:38:37 -080021
Austin Schuhcb108412019-10-13 16:09:54 -070022class ConfigurationTest : public ::testing::Test {
23 public:
24 ConfigurationTest() { ::aos::testing::EnableTestLogging(); }
25};
26
27typedef ConfigurationTest ConfigurationDeathTest;
28
29// *the* expected location for all working tests.
Austin Schuh1ef01ef2021-02-07 20:40:36 -080030aos::FlatbufferDetachedBuffer<Channel> ExpectedLocation() {
31 return JsonToFlatbuffer<Channel>(
32 "{ \"name\": \"/foo\", \"type\": \".aos.bar\", \"max_size\": 5 }");
33}
34
Austin Schuhbca6cf02019-12-22 17:28:34 -080035// And for multinode setups
Austin Schuh1ef01ef2021-02-07 20:40:36 -080036aos::FlatbufferDetachedBuffer<Channel> ExpectedMultinodeLocation() {
37 return JsonToFlatbuffer<Channel>(
38 "{ \"name\": \"/foo\", \"type\": \".aos.bar\", \"max_size\": 5, "
39 "\"source_node\": \"pi1\" }");
40}
Austin Schuhcb108412019-10-13 16:09:54 -070041
42// Tests that we can read and merge a configuration.
43TEST_F(ConfigurationTest, ConfigMerge) {
Austin Schuh40485ed2019-10-26 21:51:44 -070044 FlatbufferDetachedBuffer<Configuration> config =
Austin Schuh373f1762021-06-02 21:07:09 -070045 ReadConfig(ArtifactPath("aos/testdata/config1.json"));
Ravago Jonescf453ab2020-05-06 21:14:53 -070046 LOG(INFO) << "Read: " << FlatbufferToJson(config, {.multi_line = true});
Austin Schuhcb108412019-10-13 16:09:54 -070047
Austin Schuh373f1762021-06-02 21:07:09 -070048 EXPECT_EQ(absl::StripSuffix(util::ReadFileToStringOrDie(
49 ArtifactPath("aos/testdata/expected.json")),
50 "\n"),
51 FlatbufferToJson(config, {.multi_line = true}));
Austin Schuhcb108412019-10-13 16:09:54 -070052}
53
Austin Schuhc9e10ec2020-01-26 16:08:28 -080054// Tests that we can get back a ChannelIndex.
55TEST_F(ConfigurationTest, ChannelIndex) {
56 FlatbufferDetachedBuffer<Configuration> config =
Austin Schuh373f1762021-06-02 21:07:09 -070057 ReadConfig(ArtifactPath("aos/testdata/config1.json"));
Austin Schuhc9e10ec2020-01-26 16:08:28 -080058
59 EXPECT_EQ(
60 ChannelIndex(&config.message(), config.message().channels()->Get(1u)),
61 1u);
62}
63
Austin Schuh217a9782019-12-21 23:02:50 -080064// Tests that we can read and merge a multinode configuration.
65TEST_F(ConfigurationTest, ConfigMergeMultinode) {
66 FlatbufferDetachedBuffer<Configuration> config =
Austin Schuh373f1762021-06-02 21:07:09 -070067 ReadConfig(ArtifactPath("aos/testdata/config1_multinode.json"));
Ravago Jonescf453ab2020-05-06 21:14:53 -070068 LOG(INFO) << "Read: " << FlatbufferToJson(config, {.multi_line = true});
Austin Schuh217a9782019-12-21 23:02:50 -080069
Ravago Jonescf453ab2020-05-06 21:14:53 -070070 EXPECT_EQ(std::string(absl::StripSuffix(
Austin Schuh373f1762021-06-02 21:07:09 -070071 util::ReadFileToStringOrDie(
72 ArtifactPath("aos/testdata/expected_multinode.json")),
Ravago Jonescf453ab2020-05-06 21:14:53 -070073 "\n")),
74 FlatbufferToJson(config, {.multi_line = true}));
Austin Schuh217a9782019-12-21 23:02:50 -080075}
76
Alex Perrycb7da4b2019-08-28 19:35:56 -070077// Tests that we sort the entries in a config so we can look entries up.
78TEST_F(ConfigurationTest, UnsortedConfig) {
79 FlatbufferDetachedBuffer<Configuration> config =
Austin Schuh373f1762021-06-02 21:07:09 -070080 ReadConfig(ArtifactPath("aos/testdata/backwards.json"));
Alex Perrycb7da4b2019-08-28 19:35:56 -070081
Ravago Jonescf453ab2020-05-06 21:14:53 -070082 LOG(INFO) << "Read: " << FlatbufferToJson(config, {.multi_line = true});
Alex Perrycb7da4b2019-08-28 19:35:56 -070083
Austin Schuhf1fff282020-03-28 16:57:32 -070084 EXPECT_EQ(FlatbufferToJson(GetChannel(config, "/aos/robot_state",
Austin Schuhbca6cf02019-12-22 17:28:34 -080085 "aos.RobotState", "app1", nullptr)),
Austin Schuhf1fff282020-03-28 16:57:32 -070086 "{ \"name\": \"/aos/robot_state\", \"type\": \"aos.RobotState\", "
Alex Perrycb7da4b2019-08-28 19:35:56 -070087 "\"max_size\": 5 }");
88}
89
Austin Schuhcb108412019-10-13 16:09:54 -070090// Tests that we die when a file is imported twice.
91TEST_F(ConfigurationDeathTest, DuplicateFile) {
92 EXPECT_DEATH(
93 {
Austin Schuh40485ed2019-10-26 21:51:44 -070094 FlatbufferDetachedBuffer<Configuration> config =
Austin Schuh373f1762021-06-02 21:07:09 -070095 ReadConfig(ArtifactPath("aos/testdata/config1_bad.json"));
Austin Schuhcb108412019-10-13 16:09:54 -070096 },
Austin Schuh373f1762021-06-02 21:07:09 -070097 "aos/testdata/config1_bad.json");
Austin Schuhcb108412019-10-13 16:09:54 -070098}
99
Milind Upadhyay17098ba2022-04-15 22:18:50 -0700100// Tests that we die when we give an invalid path.
101TEST_F(ConfigurationDeathTest, NonexistentFile) {
102 EXPECT_DEATH(
103 {
104 FlatbufferDetachedBuffer<Configuration> config =
105 ReadConfig("nonexistent/config.json");
106 },
107 "above error");
108}
109
110// Tests that we return std::nullopt when we give an invalid path.
111TEST_F(ConfigurationTest, NonexistentFileOptional) {
112 std::optional<FlatbufferDetachedBuffer<Configuration>> config =
113 MaybeReadConfig("nonexistent/config.json");
114 EXPECT_FALSE(config.has_value());
115}
116
Austin Schuhf1fff282020-03-28 16:57:32 -0700117// Tests that we reject invalid channel names. This means any channels with //
118// in their name, a trailing /, or regex characters.
119TEST_F(ConfigurationDeathTest, InvalidChannelName) {
120 EXPECT_DEATH(
121 {
122 FlatbufferDetachedBuffer<Configuration> config =
Austin Schuh373f1762021-06-02 21:07:09 -0700123 ReadConfig(ArtifactPath("aos/testdata/invalid_channel_name1.json"));
Austin Schuhf1fff282020-03-28 16:57:32 -0700124 },
125 "Channel names can't end with '/'");
126 EXPECT_DEATH(
127 {
128 FlatbufferDetachedBuffer<Configuration> config =
Austin Schuh373f1762021-06-02 21:07:09 -0700129 ReadConfig(ArtifactPath("aos/testdata/invalid_channel_name2.json"));
Austin Schuhf1fff282020-03-28 16:57:32 -0700130 },
131 "Invalid channel name");
132 EXPECT_DEATH(
133 {
134 FlatbufferDetachedBuffer<Configuration> config =
Austin Schuh373f1762021-06-02 21:07:09 -0700135 ReadConfig(ArtifactPath("aos/testdata/invalid_channel_name3.json"));
Austin Schuhf1fff282020-03-28 16:57:32 -0700136 LOG(FATAL) << "Foo";
137 },
138 "Invalid channel name");
Austin Schuh47e382e2023-05-28 11:20:56 -0700139 EXPECT_DEATH(
140 {
141 FlatbufferDetachedBuffer<Configuration> config =
142 ReadConfig(ArtifactPath("aos/testdata/invalid_channel_name4.json"));
143 LOG(FATAL) << "Foo";
144 },
145 "Channel names must start with '/'");
Austin Schuhf1fff282020-03-28 16:57:32 -0700146}
147
Austin Schuh8d6cea82020-02-28 12:17:16 -0800148// Tests that we can modify a config with a json snippet.
149TEST_F(ConfigurationTest, MergeWithConfig) {
150 FlatbufferDetachedBuffer<Configuration> config =
Austin Schuh373f1762021-06-02 21:07:09 -0700151 ReadConfig(ArtifactPath("aos/testdata/config1.json"));
Ravago Jonescf453ab2020-05-06 21:14:53 -0700152 LOG(INFO) << "Read: " << FlatbufferToJson(config, {.multi_line = true});
Austin Schuh8d6cea82020-02-28 12:17:16 -0800153
154 FlatbufferDetachedBuffer<Configuration> updated_config =
155 MergeWithConfig(&config.message(),
156 R"channel({
157 "channels": [
158 {
159 "name": "/foo",
160 "type": ".aos.bar",
161 "max_size": 100
162 }
163 ]
164})channel");
165
Austin Schuh373f1762021-06-02 21:07:09 -0700166 EXPECT_EQ(absl::StripSuffix(util::ReadFileToStringOrDie(ArtifactPath(
167 "aos/testdata/expected_merge_with.json")),
Ravago Jonescf453ab2020-05-06 21:14:53 -0700168 "\n"),
169 FlatbufferToJson(updated_config, {.multi_line = true}));
Austin Schuh8d6cea82020-02-28 12:17:16 -0800170}
171
Austin Schuhcb108412019-10-13 16:09:54 -0700172// Tests that we can lookup a location, complete with maps, from a merged
173// config.
Austin Schuh40485ed2019-10-26 21:51:44 -0700174TEST_F(ConfigurationTest, GetChannel) {
175 FlatbufferDetachedBuffer<Configuration> config =
Austin Schuh373f1762021-06-02 21:07:09 -0700176 ReadConfig(ArtifactPath("aos/testdata/config1.json"));
Austin Schuhcb108412019-10-13 16:09:54 -0700177
178 // Test a basic lookup first.
Austin Schuh1ef01ef2021-02-07 20:40:36 -0800179 EXPECT_THAT(GetChannel(config, "/foo", ".aos.bar", "app1", nullptr),
180 aos::testing::FlatbufferEq(ExpectedLocation()));
Austin Schuhcb108412019-10-13 16:09:54 -0700181
182 // Test that an invalid name results in nullptr back.
Austin Schuhbca6cf02019-12-22 17:28:34 -0800183 EXPECT_EQ(GetChannel(config, "/invalid_name", ".aos.bar", "app1", nullptr),
184 nullptr);
Austin Schuhcb108412019-10-13 16:09:54 -0700185
186 // Tests that a root map/rename works. And that they get processed from the
187 // bottom up.
Austin Schuh1ef01ef2021-02-07 20:40:36 -0800188 EXPECT_THAT(GetChannel(config, "/batman", ".aos.bar", "app1", nullptr),
189 aos::testing::FlatbufferEq(ExpectedLocation()));
Austin Schuhcb108412019-10-13 16:09:54 -0700190
191 // And then test that an application specific map/rename works.
Austin Schuh1ef01ef2021-02-07 20:40:36 -0800192 EXPECT_THAT(GetChannel(config, "/bar", ".aos.bar", "app1", nullptr),
193 aos::testing::FlatbufferEq(ExpectedLocation()));
194 EXPECT_THAT(GetChannel(config, "/baz", ".aos.bar", "app2", nullptr),
195 aos::testing::FlatbufferEq(ExpectedLocation()));
Austin Schuhcb108412019-10-13 16:09:54 -0700196
197 // And then test that an invalid application name gets properly ignored.
Austin Schuh1ef01ef2021-02-07 20:40:36 -0800198 EXPECT_THAT(GetChannel(config, "/foo", ".aos.bar", "app3", nullptr),
199 aos::testing::FlatbufferEq(ExpectedLocation()));
Austin Schuhbca6cf02019-12-22 17:28:34 -0800200}
201
James Kuszmaulc8503f32022-06-25 16:17:12 -0700202// Tests that we can do reverse-lookups of channel names.
203TEST_F(ConfigurationTest, GetChannelAliases) {
204 FlatbufferDetachedBuffer<Configuration> config =
205 ReadConfig(ArtifactPath("aos/testdata/config1.json"));
206
207 // Test a basic lookup first.
208 EXPECT_THAT(
209 GetChannelAliases(&config.message(), "/foo", ".aos.bar", "app1", nullptr),
210 ::testing::UnorderedElementsAre("/foo", "/batman", "/bar"));
211 EXPECT_THAT(
212 GetChannelAliases(&config.message(), "/bar", ".aos.bar", "app1", nullptr),
213 ::testing::UnorderedElementsAre("/batman", "/bar"));
214 EXPECT_THAT(GetChannelAliases(&config.message(), "/batman", ".aos.bar",
215 "app1", nullptr),
216 ::testing::UnorderedElementsAre("/batman"));
217 // /bar (deliberately) does not get included because of the ordering in the
218 // map.
219 EXPECT_THAT(
220 GetChannelAliases(&config.message(), "/foo", ".aos.bar", "", nullptr),
221 ::testing::UnorderedElementsAre("/foo", "/batman"));
222 EXPECT_THAT(
223 GetChannelAliases(&config.message(), "/foo", ".aos.bar", "app2", nullptr),
224 ::testing::UnorderedElementsAre("/foo", "/batman", "/baz"));
225}
226
Austin Schuhbca6cf02019-12-22 17:28:34 -0800227// Tests that we can lookup a location with node specific maps.
228TEST_F(ConfigurationTest, GetChannelMultinode) {
229 FlatbufferDetachedBuffer<Configuration> config =
Austin Schuh373f1762021-06-02 21:07:09 -0700230 ReadConfig(ArtifactPath("aos/testdata/good_multinode.json"));
Austin Schuhbca6cf02019-12-22 17:28:34 -0800231 const Node *pi1 = GetNode(&config.message(), "pi1");
232 const Node *pi2 = GetNode(&config.message(), "pi2");
233
234 // Test a basic lookup first.
Austin Schuh1ef01ef2021-02-07 20:40:36 -0800235 EXPECT_THAT(GetChannel(config, "/foo", ".aos.bar", "app1", pi1),
236 aos::testing::FlatbufferEq(ExpectedMultinodeLocation()));
237 EXPECT_THAT(GetChannel(config, "/foo", ".aos.bar", "app1", pi2),
238 aos::testing::FlatbufferEq(ExpectedMultinodeLocation()));
Austin Schuhbca6cf02019-12-22 17:28:34 -0800239
240 // Tests that a root map/rename works with a node specific map.
Austin Schuh1ef01ef2021-02-07 20:40:36 -0800241 EXPECT_THAT(GetChannel(config, "/batman", ".aos.bar", "app1", pi1),
242 aos::testing::FlatbufferEq(ExpectedMultinodeLocation()));
Austin Schuhbca6cf02019-12-22 17:28:34 -0800243
Austin Schuha22ee352024-05-08 16:24:48 -0700244 // Tests that node specific maps get ignored
245 EXPECT_EQ(GetChannel(config, "/batman", ".aos.bar", "", nullptr), nullptr);
246
Austin Schuhbca6cf02019-12-22 17:28:34 -0800247 // Tests that a root map/rename fails with a node specific map for the wrong
248 // node.
249 EXPECT_EQ(GetChannel(config, "/batman", ".aos.bar", "app1", pi2), nullptr);
250
251 // And then test that an application specific map/rename works.
Austin Schuh1ef01ef2021-02-07 20:40:36 -0800252 EXPECT_THAT(GetChannel(config, "/batman2", ".aos.bar", "app1", pi1),
253 aos::testing::FlatbufferEq(ExpectedMultinodeLocation()));
254 EXPECT_THAT(GetChannel(config, "/batman3", ".aos.bar", "app1", pi1),
255 aos::testing::FlatbufferEq(ExpectedMultinodeLocation()));
Austin Schuhbca6cf02019-12-22 17:28:34 -0800256
257 // And then that it fails when the node changes.
258 EXPECT_EQ(GetChannel(config, "/batman3", ".aos.bar", "app1", pi2), nullptr);
259}
260
James Kuszmaulc8503f32022-06-25 16:17:12 -0700261// Tests that reverse channel lookup on a multi-node config (including with
262// wildcards) works.
263TEST_F(ConfigurationTest, GetChannelAliasesMultinode) {
264 FlatbufferDetachedBuffer<Configuration> config =
265 ReadConfig(ArtifactPath("aos/testdata/good_multinode.json"));
266
267 const Node *pi1 = GetNode(&config.message(), "pi1");
268 const Node *pi2 = GetNode(&config.message(), "pi2");
269
270 EXPECT_THAT(
271 GetChannelAliases(&config.message(), "/foo", ".aos.bar", "app1", pi1),
272 ::testing::UnorderedElementsAre("/foo", "/batman", "/batman2", "/batman3",
273 "/magic/string"));
274
275 EXPECT_THAT(
276 GetChannelAliases(&config.message(), "/foo", ".aos.baz", "app1", pi1),
277 ::testing::UnorderedElementsAre("/foo", "/batman3", "/magic/string"));
278
279 EXPECT_THAT(
280 GetChannelAliases(&config.message(), "/foo/testing", ".aos.bar", "", pi1),
281 ::testing::UnorderedElementsAre("/foo/testing", "/magic/string/testing"));
282
283 EXPECT_THAT(
284 GetChannelAliases(&config.message(), "/foo/testing", ".aos.bar", "app1",
285 pi2),
286 ::testing::UnorderedElementsAre("/foo/testing", "/magic/string/testing"));
287}
288
Austin Schuhbca6cf02019-12-22 17:28:34 -0800289// Tests that we can lookup a location with type specific maps.
290TEST_F(ConfigurationTest, GetChannelTypedMultinode) {
291 FlatbufferDetachedBuffer<Configuration> config =
Austin Schuh373f1762021-06-02 21:07:09 -0700292 ReadConfig(ArtifactPath("aos/testdata/good_multinode.json"));
Austin Schuhbca6cf02019-12-22 17:28:34 -0800293 const Node *pi1 = GetNode(&config.message(), "pi1");
294
295 // Test a basic lookup first.
Austin Schuh1ef01ef2021-02-07 20:40:36 -0800296 EXPECT_THAT(GetChannel(config, "/batman", ".aos.bar", "app1", pi1),
297 aos::testing::FlatbufferEq(ExpectedMultinodeLocation()));
Austin Schuhbca6cf02019-12-22 17:28:34 -0800298
299 // Now confirm that a second message on the same name doesn't get remapped.
300 const char *kExpectedBazMultinodeLocation =
301 "{ \"name\": \"/batman\", \"type\": \".aos.baz\", \"max_size\": 5, "
302 "\"source_node\": \"pi1\" }";
303 EXPECT_EQ(
304 FlatbufferToJson(GetChannel(config, "/batman", ".aos.baz", "app1", pi1)),
305 kExpectedBazMultinodeLocation);
Austin Schuhcb108412019-10-13 16:09:54 -0700306}
307
Austin Schuhf1fff282020-03-28 16:57:32 -0700308// Tests that we can lookup a location with a glob
309TEST_F(ConfigurationTest, GetChannelGlob) {
310 FlatbufferDetachedBuffer<Configuration> config =
Austin Schuh373f1762021-06-02 21:07:09 -0700311 ReadConfig(ArtifactPath("aos/testdata/good_multinode.json"));
Austin Schuhf1fff282020-03-28 16:57:32 -0700312 const Node *pi1 = GetNode(&config.message(), "pi1");
313
314 // Confirm that a glob with nothing after it matches.
Austin Schuh1ef01ef2021-02-07 20:40:36 -0800315 EXPECT_THAT(GetChannel(config, "/magic/string", ".aos.bar", "app7", pi1),
316 aos::testing::FlatbufferEq(ExpectedMultinodeLocation()));
Austin Schuhf1fff282020-03-28 16:57:32 -0700317
318 // Now confirm that glob with something following it matches and renames
319 // correctly.
320 const char *kExpectedSubfolderMultinodeLocation =
321 "{ \"name\": \"/foo/subfolder\", \"type\": \".aos.bar\", \"max_size\": "
322 "5, \"source_node\": \"pi1\" }";
323 EXPECT_EQ(FlatbufferToJson(GetChannel(config, "/magic/string/subfolder",
324 ".aos.bar", "app7", pi1)),
325 kExpectedSubfolderMultinodeLocation);
326}
327
Austin Schuh217a9782019-12-21 23:02:50 -0800328// Tests that we reject a configuration which has a nodes list, but has channels
329// withoout source_node filled out.
330TEST_F(ConfigurationDeathTest, InvalidSourceNode) {
331 EXPECT_DEATH(
332 {
333 FlatbufferDetachedBuffer<Configuration> config =
Austin Schuh373f1762021-06-02 21:07:09 -0700334 ReadConfig(ArtifactPath("aos/testdata/invalid_nodes.json"));
Austin Schuh217a9782019-12-21 23:02:50 -0800335 },
336 "source_node");
337
338 EXPECT_DEATH(
339 {
340 FlatbufferDetachedBuffer<Configuration> config =
Austin Schuh373f1762021-06-02 21:07:09 -0700341 ReadConfig(ArtifactPath("aos/testdata/invalid_source_node.json"));
Austin Schuh217a9782019-12-21 23:02:50 -0800342 },
343 "source_node");
344
345 EXPECT_DEATH(
346 {
Austin Schuh373f1762021-06-02 21:07:09 -0700347 FlatbufferDetachedBuffer<Configuration> config = ReadConfig(
348 ArtifactPath("aos/testdata/invalid_destination_node.json"));
Austin Schuh217a9782019-12-21 23:02:50 -0800349 },
350 "destination_nodes");
351
352 EXPECT_DEATH(
353 {
354 FlatbufferDetachedBuffer<Configuration> config =
Austin Schuh373f1762021-06-02 21:07:09 -0700355 ReadConfig(ArtifactPath("aos/testdata/self_forward.json"));
Austin Schuh217a9782019-12-21 23:02:50 -0800356 },
357 "forwarding data to itself");
358}
359
360// Tests that our node writeable helpers work as intended.
361TEST_F(ConfigurationTest, ChannelIsSendableOnNode) {
362 FlatbufferDetachedBuffer<Channel> good_channel(JsonToFlatbuffer(
Austin Schuh719946b2019-12-28 14:51:01 -0800363 R"channel({
Austin Schuh217a9782019-12-21 23:02:50 -0800364 "name": "/test",
365 "type": "aos.examples.Ping",
366 "source_node": "foo"
367})channel",
368 Channel::MiniReflectTypeTable()));
369
370 FlatbufferDetachedBuffer<Channel> bad_channel(JsonToFlatbuffer(
Austin Schuh719946b2019-12-28 14:51:01 -0800371 R"channel({
Austin Schuh217a9782019-12-21 23:02:50 -0800372 "name": "/test",
373 "type": "aos.examples.Ping",
374 "source_node": "bar"
375})channel",
376 Channel::MiniReflectTypeTable()));
377
378 FlatbufferDetachedBuffer<Node> node(JsonToFlatbuffer(
Austin Schuh719946b2019-12-28 14:51:01 -0800379 R"node({
Austin Schuh217a9782019-12-21 23:02:50 -0800380 "name": "foo"
381})node",
382 Node::MiniReflectTypeTable()));
383
384 EXPECT_TRUE(
385 ChannelIsSendableOnNode(&good_channel.message(), &node.message()));
386 EXPECT_FALSE(
387 ChannelIsSendableOnNode(&bad_channel.message(), &node.message()));
388}
389
390// Tests that our node readable and writeable helpers work as intended.
391TEST_F(ConfigurationTest, ChannelIsReadableOnNode) {
392 FlatbufferDetachedBuffer<Channel> good_channel(JsonToFlatbuffer(
Austin Schuh719946b2019-12-28 14:51:01 -0800393 R"channel({
Austin Schuh217a9782019-12-21 23:02:50 -0800394 "name": "/test",
395 "type": "aos.examples.Ping",
396 "source_node": "bar",
397 "destination_nodes": [
Austin Schuh719946b2019-12-28 14:51:01 -0800398 {
399 "name": "baz"
400 },
401 {
402 "name": "foo"
403 }
Austin Schuh217a9782019-12-21 23:02:50 -0800404 ]
405})channel",
406 Channel::MiniReflectTypeTable()));
407
408 FlatbufferDetachedBuffer<Channel> bad_channel1(JsonToFlatbuffer(
Austin Schuh719946b2019-12-28 14:51:01 -0800409 R"channel({
Austin Schuh217a9782019-12-21 23:02:50 -0800410 "name": "/test",
411 "type": "aos.examples.Ping",
412 "source_node": "bar"
413})channel",
414 Channel::MiniReflectTypeTable()));
415
416 FlatbufferDetachedBuffer<Channel> bad_channel2(JsonToFlatbuffer(
Austin Schuh719946b2019-12-28 14:51:01 -0800417 R"channel({
Austin Schuh217a9782019-12-21 23:02:50 -0800418 "name": "/test",
419 "type": "aos.examples.Ping",
420 "source_node": "bar",
421 "destination_nodes": [
Austin Schuh719946b2019-12-28 14:51:01 -0800422 {
423 "name": "baz"
424 }
Austin Schuh217a9782019-12-21 23:02:50 -0800425 ]
426})channel",
427 Channel::MiniReflectTypeTable()));
428
429 FlatbufferDetachedBuffer<Node> node(JsonToFlatbuffer(
Austin Schuh719946b2019-12-28 14:51:01 -0800430 R"node({
Austin Schuh217a9782019-12-21 23:02:50 -0800431 "name": "foo"
432})node",
433 Node::MiniReflectTypeTable()));
434
435 EXPECT_TRUE(
436 ChannelIsReadableOnNode(&good_channel.message(), &node.message()));
437 EXPECT_FALSE(
438 ChannelIsReadableOnNode(&bad_channel1.message(), &node.message()));
439 EXPECT_FALSE(
440 ChannelIsReadableOnNode(&bad_channel2.message(), &node.message()));
441}
442
James Kuszmaul24db2d32023-05-26 11:40:12 -0700443// Tests that our channel is forwarded helpers work as intended.
444TEST_F(ConfigurationTest, ChannelIsForwardedFromNode) {
445 FlatbufferDetachedBuffer<Channel> forwarded_channel(JsonToFlatbuffer(
446 R"channel({
447 "name": "/test",
448 "type": "aos.examples.Ping",
449 "source_node": "bar",
450 "destination_nodes": [
451 {
452 "name": "baz"
453 },
454 {
455 "name": "foo"
456 }
457 ]
458})channel",
459 Channel::MiniReflectTypeTable()));
460
461 FlatbufferDetachedBuffer<Channel> single_node_channel(JsonToFlatbuffer(
462 R"channel({
463 "name": "/test",
464 "type": "aos.examples.Ping"
465})channel",
466 Channel::MiniReflectTypeTable()));
467
468 FlatbufferDetachedBuffer<Channel> zero_length_vector_channel(JsonToFlatbuffer(
469 R"channel({
470 "name": "/test",
471 "type": "aos.examples.Ping",
472 "source_node": "bar",
473 "destination_nodes": [
474 ]
475})channel",
476 Channel::MiniReflectTypeTable()));
477
478 FlatbufferDetachedBuffer<Node> node(JsonToFlatbuffer(
479 R"node({
480 "name": "bar"
481})node",
482 Node::MiniReflectTypeTable()));
483
484 FlatbufferDetachedBuffer<Node> readable_node(JsonToFlatbuffer(
485 R"node({
486 "name": "foo"
487})node",
488 Node::MiniReflectTypeTable()));
489
490 EXPECT_TRUE(ChannelIsForwardedFromNode(&forwarded_channel.message(),
491 &node.message()));
492 EXPECT_FALSE(ChannelIsForwardedFromNode(&forwarded_channel.message(),
493 &readable_node.message()));
494 EXPECT_FALSE(
495 ChannelIsForwardedFromNode(&single_node_channel.message(), nullptr));
496 EXPECT_FALSE(ChannelIsForwardedFromNode(&zero_length_vector_channel.message(),
497 &node.message()));
498}
499
Austin Schuh719946b2019-12-28 14:51:01 -0800500// Tests that our node message is logged helpers work as intended.
501TEST_F(ConfigurationTest, ChannelMessageIsLoggedOnNode) {
502 FlatbufferDetachedBuffer<Channel> logged_on_self_channel(JsonToFlatbuffer(
503 R"channel({
504 "name": "/test",
505 "type": "aos.examples.Ping",
506 "source_node": "bar",
507 "destination_nodes": [
508 {
509 "name": "baz"
510 }
511 ]
512})channel",
513 Channel::MiniReflectTypeTable()));
Austin Schuh217a9782019-12-21 23:02:50 -0800514
Austin Schuh719946b2019-12-28 14:51:01 -0800515 FlatbufferDetachedBuffer<Channel> not_logged_channel(JsonToFlatbuffer(
516 R"channel({
517 "name": "/test",
518 "type": "aos.examples.Ping",
519 "source_node": "bar",
520 "logger": "NOT_LOGGED",
521 "destination_nodes": [
522 {
523 "name": "baz",
524 "timestamp_logger": "LOCAL_LOGGER"
525 }
526 ]
527})channel",
528 Channel::MiniReflectTypeTable()));
529
530 FlatbufferDetachedBuffer<Channel> logged_on_remote_channel(JsonToFlatbuffer(
531 R"channel({
532 "name": "/test",
533 "type": "aos.examples.Ping",
534 "source_node": "bar",
535 "logger": "REMOTE_LOGGER",
Austin Schuhda40e472020-03-28 15:15:29 -0700536 "logger_nodes": ["baz"],
Austin Schuh719946b2019-12-28 14:51:01 -0800537 "destination_nodes": [
538 {
539 "name": "baz"
540 }
541 ]
542})channel",
543 Channel::MiniReflectTypeTable()));
544
545 FlatbufferDetachedBuffer<Channel> logged_on_separate_logger_node_channel(
546 JsonToFlatbuffer(
547 R"channel({
548 "name": "/test",
549 "type": "aos.examples.Ping",
550 "source_node": "bar",
551 "logger": "REMOTE_LOGGER",
Austin Schuhda40e472020-03-28 15:15:29 -0700552 "logger_nodes": ["foo"],
Austin Schuh719946b2019-12-28 14:51:01 -0800553 "destination_nodes": [
554 {
555 "name": "baz"
556 }
557 ]
558})channel",
559 Channel::MiniReflectTypeTable()));
560
Ravago Jonescf453ab2020-05-06 21:14:53 -0700561 FlatbufferDetachedBuffer<Channel> logged_on_both_channel(JsonToFlatbuffer(
562 R"channel({
Austin Schuh719946b2019-12-28 14:51:01 -0800563 "name": "/test",
564 "type": "aos.examples.Ping",
565 "source_node": "bar",
566 "logger": "LOCAL_AND_REMOTE_LOGGER",
Austin Schuhda40e472020-03-28 15:15:29 -0700567 "logger_nodes": ["baz"],
Austin Schuh719946b2019-12-28 14:51:01 -0800568 "destination_nodes": [
569 {
570 "name": "baz"
571 }
572 ]
573})channel",
Ravago Jonescf453ab2020-05-06 21:14:53 -0700574 Channel::MiniReflectTypeTable()));
Austin Schuh719946b2019-12-28 14:51:01 -0800575
576 FlatbufferDetachedBuffer<Node> foo_node(JsonToFlatbuffer(
577 R"node({
578 "name": "foo"
579})node",
580 Node::MiniReflectTypeTable()));
581
582 FlatbufferDetachedBuffer<Node> bar_node(JsonToFlatbuffer(
583 R"node({
584 "name": "bar"
585})node",
586 Node::MiniReflectTypeTable()));
587
588 FlatbufferDetachedBuffer<Node> baz_node(JsonToFlatbuffer(
589 R"node({
590 "name": "baz"
591})node",
592 Node::MiniReflectTypeTable()));
593
594 // Local logger.
595 EXPECT_FALSE(ChannelMessageIsLoggedOnNode(&logged_on_self_channel.message(),
596 &foo_node.message()));
597 EXPECT_TRUE(ChannelMessageIsLoggedOnNode(&logged_on_self_channel.message(),
598 &bar_node.message()));
599 EXPECT_FALSE(ChannelMessageIsLoggedOnNode(&logged_on_self_channel.message(),
600 &baz_node.message()));
Austin Schuh48e94502021-06-18 18:35:53 -0700601 EXPECT_TRUE(
602 ChannelMessageIsLoggedOnNode(&logged_on_self_channel.message(), nullptr));
Austin Schuh719946b2019-12-28 14:51:01 -0800603
604 // No logger.
605 EXPECT_FALSE(ChannelMessageIsLoggedOnNode(&not_logged_channel.message(),
606 &foo_node.message()));
607 EXPECT_FALSE(ChannelMessageIsLoggedOnNode(&not_logged_channel.message(),
Ravago Jonescf453ab2020-05-06 21:14:53 -0700608 &bar_node.message()));
Austin Schuh719946b2019-12-28 14:51:01 -0800609 EXPECT_FALSE(ChannelMessageIsLoggedOnNode(&not_logged_channel.message(),
610 &baz_node.message()));
Austin Schuh48e94502021-06-18 18:35:53 -0700611 EXPECT_FALSE(
612 ChannelMessageIsLoggedOnNode(&not_logged_channel.message(), nullptr));
Austin Schuh719946b2019-12-28 14:51:01 -0800613
614 // Remote logger.
615 EXPECT_FALSE(ChannelMessageIsLoggedOnNode(&logged_on_remote_channel.message(),
616 &foo_node.message()));
617 EXPECT_FALSE(ChannelMessageIsLoggedOnNode(&logged_on_remote_channel.message(),
618 &bar_node.message()));
619 EXPECT_TRUE(ChannelMessageIsLoggedOnNode(&logged_on_remote_channel.message(),
620 &baz_node.message()));
621
622 // Separate logger.
623 EXPECT_TRUE(ChannelMessageIsLoggedOnNode(
624 &logged_on_separate_logger_node_channel.message(), &foo_node.message()));
625 EXPECT_FALSE(ChannelMessageIsLoggedOnNode(
626 &logged_on_separate_logger_node_channel.message(), &bar_node.message()));
627 EXPECT_FALSE(ChannelMessageIsLoggedOnNode(
628 &logged_on_separate_logger_node_channel.message(), &baz_node.message()));
629
630 // Logged in multiple places.
631 EXPECT_FALSE(ChannelMessageIsLoggedOnNode(&logged_on_both_channel.message(),
632 &foo_node.message()));
633 EXPECT_TRUE(ChannelMessageIsLoggedOnNode(&logged_on_both_channel.message(),
634 &bar_node.message()));
635 EXPECT_TRUE(ChannelMessageIsLoggedOnNode(&logged_on_both_channel.message(),
636 &baz_node.message()));
637}
638
Austin Schuh48e94502021-06-18 18:35:53 -0700639// Tests that our node message is logged helpers work as intended.
640TEST_F(ConfigurationDeathTest, ChannelMessageIsLoggedOnNode) {
641 FlatbufferDetachedBuffer<Channel> logged_on_both_channel(JsonToFlatbuffer(
642 R"channel({
643 "name": "/test",
644 "type": "aos.examples.Ping",
645 "source_node": "bar",
646 "logger": "LOCAL_AND_REMOTE_LOGGER",
647 "logger_nodes": ["baz"],
648 "destination_nodes": [
649 {
650 "name": "baz"
651 }
652 ]
653})channel",
654 Channel::MiniReflectTypeTable()));
655
656 FlatbufferDetachedBuffer<Channel> logged_on_separate_logger_node_channel(
657 JsonToFlatbuffer(
658 R"channel({
659 "name": "/test",
660 "type": "aos.examples.Ping",
661 "source_node": "bar",
662 "logger": "REMOTE_LOGGER",
663 "logger_nodes": ["foo"],
664 "destination_nodes": [
665 {
666 "name": "baz"
667 }
668 ]
669})channel",
670 Channel::MiniReflectTypeTable()));
671
672 EXPECT_DEATH(
673 {
674 ChannelMessageIsLoggedOnNode(&logged_on_both_channel.message(),
675 nullptr);
676 },
677 "Unsupported logging configuration in a single node world");
678 EXPECT_DEATH(
679 {
680 ChannelMessageIsLoggedOnNode(
681 &logged_on_separate_logger_node_channel.message(), nullptr);
682 },
683 "Unsupported logging configuration in a single node world");
684}
685
Austin Schuh719946b2019-12-28 14:51:01 -0800686// Tests that our forwarding timestamps are logged helpers work as intended.
687TEST_F(ConfigurationTest, ConnectionDeliveryTimeIsLoggedOnNode) {
688 FlatbufferDetachedBuffer<Channel> logged_on_self_channel(JsonToFlatbuffer(
689 R"channel({
690 "name": "/test",
691 "type": "aos.examples.Ping",
692 "source_node": "bar",
693 "logger": "REMOTE_LOGGER",
Austin Schuhda40e472020-03-28 15:15:29 -0700694 "logger_nodes": ["baz"],
Austin Schuh719946b2019-12-28 14:51:01 -0800695 "destination_nodes": [
696 {
697 "name": "baz"
698 }
699 ]
700})channel",
701 Channel::MiniReflectTypeTable()));
702
703 FlatbufferDetachedBuffer<Channel> not_logged_channel(JsonToFlatbuffer(
704 R"channel({
705 "name": "/test",
706 "type": "aos.examples.Ping",
707 "source_node": "bar",
708 "logger": "NOT_LOGGED",
709 "destination_nodes": [
710 {
711 "name": "baz",
712 "timestamp_logger": "NOT_LOGGED"
713 }
714 ]
715})channel",
716 Channel::MiniReflectTypeTable()));
717
718 FlatbufferDetachedBuffer<Channel> logged_on_remote_channel(JsonToFlatbuffer(
719 R"channel({
720 "name": "/test",
721 "type": "aos.examples.Ping",
722 "source_node": "bar",
723 "destination_nodes": [
724 {
725 "name": "baz",
726 "timestamp_logger": "REMOTE_LOGGER",
Austin Schuhda40e472020-03-28 15:15:29 -0700727 "timestamp_logger_nodes": ["bar"]
Austin Schuh719946b2019-12-28 14:51:01 -0800728 }
729 ]
730})channel",
731 Channel::MiniReflectTypeTable()));
732
733 FlatbufferDetachedBuffer<Channel> logged_on_separate_logger_node_channel(
734 JsonToFlatbuffer(
735 R"channel({
736 "name": "/test",
737 "type": "aos.examples.Ping",
738 "source_node": "bar",
739 "logger": "REMOTE_LOGGER",
Austin Schuhda40e472020-03-28 15:15:29 -0700740 "logger_nodes": ["foo"],
Austin Schuh719946b2019-12-28 14:51:01 -0800741 "destination_nodes": [
742 {
743 "name": "baz",
744 "timestamp_logger": "REMOTE_LOGGER",
Austin Schuhda40e472020-03-28 15:15:29 -0700745 "timestamp_logger_nodes": ["foo"]
Austin Schuh719946b2019-12-28 14:51:01 -0800746 }
747 ]
748})channel",
749 Channel::MiniReflectTypeTable()));
750
Ravago Jonescf453ab2020-05-06 21:14:53 -0700751 FlatbufferDetachedBuffer<Channel> logged_on_both_channel(JsonToFlatbuffer(
752 R"channel({
Austin Schuh719946b2019-12-28 14:51:01 -0800753 "name": "/test",
754 "type": "aos.examples.Ping",
755 "source_node": "bar",
756 "destination_nodes": [
757 {
758 "name": "baz",
759 "timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
Austin Schuhda40e472020-03-28 15:15:29 -0700760 "timestamp_logger_nodes": ["bar"]
Austin Schuh719946b2019-12-28 14:51:01 -0800761 }
762 ]
763})channel",
Ravago Jonescf453ab2020-05-06 21:14:53 -0700764 Channel::MiniReflectTypeTable()));
Austin Schuh719946b2019-12-28 14:51:01 -0800765
766 FlatbufferDetachedBuffer<Node> foo_node(JsonToFlatbuffer(
767 R"node({
768 "name": "foo"
769})node",
770 Node::MiniReflectTypeTable()));
771
772 FlatbufferDetachedBuffer<Node> bar_node(JsonToFlatbuffer(
773 R"node({
774 "name": "bar"
775})node",
776 Node::MiniReflectTypeTable()));
777
778 FlatbufferDetachedBuffer<Node> baz_node(JsonToFlatbuffer(
779 R"node({
780 "name": "baz"
781})node",
782 Node::MiniReflectTypeTable()));
783
784 // Local logger.
785 EXPECT_FALSE(ConnectionDeliveryTimeIsLoggedOnNode(
786 &logged_on_self_channel.message(), &baz_node.message(),
787 &foo_node.message()));
788 EXPECT_FALSE(ConnectionDeliveryTimeIsLoggedOnNode(
789 &logged_on_self_channel.message(), &baz_node.message(),
790 &bar_node.message()));
791 EXPECT_TRUE(ConnectionDeliveryTimeIsLoggedOnNode(
792 &logged_on_self_channel.message(), &baz_node.message(),
793 &baz_node.message()));
794
795 // No logger means.
796 EXPECT_FALSE(ConnectionDeliveryTimeIsLoggedOnNode(
797 &not_logged_channel.message(), &baz_node.message(), &foo_node.message()));
798 EXPECT_FALSE(ConnectionDeliveryTimeIsLoggedOnNode(
799 &not_logged_channel.message(), &baz_node.message(), &bar_node.message()));
800 EXPECT_FALSE(ConnectionDeliveryTimeIsLoggedOnNode(
801 &not_logged_channel.message(), &baz_node.message(), &baz_node.message()));
802
803 // Remote logger.
804 EXPECT_FALSE(ConnectionDeliveryTimeIsLoggedOnNode(
805 &logged_on_remote_channel.message(), &baz_node.message(),
806 &foo_node.message()));
807 EXPECT_TRUE(ConnectionDeliveryTimeIsLoggedOnNode(
808 &logged_on_remote_channel.message(), &baz_node.message(),
809 &bar_node.message()));
810 EXPECT_FALSE(ConnectionDeliveryTimeIsLoggedOnNode(
811 &logged_on_remote_channel.message(), &baz_node.message(),
812 &baz_node.message()));
813
814 // Separate logger.
815 EXPECT_TRUE(ConnectionDeliveryTimeIsLoggedOnNode(
816 &logged_on_separate_logger_node_channel.message(), &baz_node.message(),
817 &foo_node.message()));
818 EXPECT_FALSE(ConnectionDeliveryTimeIsLoggedOnNode(
819 &logged_on_separate_logger_node_channel.message(), &baz_node.message(),
820 &bar_node.message()));
821 EXPECT_FALSE(ConnectionDeliveryTimeIsLoggedOnNode(
822 &logged_on_separate_logger_node_channel.message(), &baz_node.message(),
823 &baz_node.message()));
824
825 // Logged on both the node and a remote node.
826 EXPECT_FALSE(ConnectionDeliveryTimeIsLoggedOnNode(
827 &logged_on_both_channel.message(), &baz_node.message(),
828 &foo_node.message()));
829 EXPECT_TRUE(ConnectionDeliveryTimeIsLoggedOnNode(
830 &logged_on_both_channel.message(), &baz_node.message(),
831 &bar_node.message()));
832 EXPECT_TRUE(ConnectionDeliveryTimeIsLoggedOnNode(
833 &logged_on_both_channel.message(), &baz_node.message(),
834 &baz_node.message()));
835}
Austin Schuhe84c3ed2019-12-14 15:29:48 -0800836
837// Tests that we can deduce source nodes from a multinode config.
838TEST_F(ConfigurationTest, SourceNodeNames) {
839 FlatbufferDetachedBuffer<Configuration> config =
Austin Schuh373f1762021-06-02 21:07:09 -0700840 ReadConfig(ArtifactPath("aos/testdata/config1_multinode.json"));
Austin Schuhe84c3ed2019-12-14 15:29:48 -0800841
842 // This is a bit simplistic in that it doesn't test deduplication, but it does
843 // exercise a lot of the logic.
844 EXPECT_THAT(
845 SourceNodeNames(&config.message(), config.message().nodes()->Get(0)),
846 ::testing::ElementsAreArray({"pi2"}));
847 EXPECT_THAT(
848 SourceNodeNames(&config.message(), config.message().nodes()->Get(1)),
849 ::testing::ElementsAreArray({"pi1"}));
850}
851
852// Tests that we can deduce destination nodes from a multinode config.
853TEST_F(ConfigurationTest, DestinationNodeNames) {
854 FlatbufferDetachedBuffer<Configuration> config =
Austin Schuh373f1762021-06-02 21:07:09 -0700855 ReadConfig(ArtifactPath("aos/testdata/config1_multinode.json"));
Austin Schuhe84c3ed2019-12-14 15:29:48 -0800856
857 // This is a bit simplistic in that it doesn't test deduplication, but it does
858 // exercise a lot of the logic.
859 EXPECT_THAT(
860 DestinationNodeNames(&config.message(), config.message().nodes()->Get(0)),
861 ::testing::ElementsAreArray({"pi2"}));
862 EXPECT_THAT(
863 DestinationNodeNames(&config.message(), config.message().nodes()->Get(1)),
864 ::testing::ElementsAreArray({"pi1"}));
865}
866
Austin Schuhc9e10ec2020-01-26 16:08:28 -0800867// Tests that we can pull out all the nodes.
868TEST_F(ConfigurationTest, GetNodes) {
869 {
870 FlatbufferDetachedBuffer<Configuration> config =
Austin Schuh373f1762021-06-02 21:07:09 -0700871 ReadConfig(ArtifactPath("aos/testdata/good_multinode.json"));
Austin Schuhc9e10ec2020-01-26 16:08:28 -0800872 const Node *pi1 = GetNode(&config.message(), "pi1");
873 const Node *pi2 = GetNode(&config.message(), "pi2");
874
875 EXPECT_THAT(GetNodes(&config.message()), ::testing::ElementsAre(pi1, pi2));
876 }
877
878 {
879 FlatbufferDetachedBuffer<Configuration> config =
Austin Schuh373f1762021-06-02 21:07:09 -0700880 ReadConfig(ArtifactPath("aos/testdata/config1.json"));
Austin Schuhc9e10ec2020-01-26 16:08:28 -0800881 EXPECT_THAT(GetNodes(&config.message()), ::testing::ElementsAre(nullptr));
882 }
883}
884
Austin Schuh65465332020-11-05 17:36:53 -0800885// Tests that we can pull out all the nodes with a tag.
886TEST_F(ConfigurationTest, GetNodesWithTag) {
887 {
888 FlatbufferDetachedBuffer<Configuration> config =
Austin Schuh373f1762021-06-02 21:07:09 -0700889 ReadConfig(ArtifactPath("aos/testdata/good_multinode.json"));
Austin Schuh65465332020-11-05 17:36:53 -0800890 const Node *pi1 = GetNode(&config.message(), "pi1");
891 const Node *pi2 = GetNode(&config.message(), "pi2");
892
893 EXPECT_THAT(GetNodesWithTag(&config.message(), "a"),
894 ::testing::ElementsAre(pi1));
895 EXPECT_THAT(GetNodesWithTag(&config.message(), "b"),
896 ::testing::ElementsAre(pi2));
897 EXPECT_THAT(GetNodesWithTag(&config.message(), "c"),
898 ::testing::ElementsAre(pi1, pi2));
899 }
900
901 {
902 FlatbufferDetachedBuffer<Configuration> config =
Austin Schuh373f1762021-06-02 21:07:09 -0700903 ReadConfig(ArtifactPath("aos/testdata/config1.json"));
Austin Schuh65465332020-11-05 17:36:53 -0800904 EXPECT_THAT(GetNodesWithTag(&config.message(), "arglfish"),
905 ::testing::ElementsAre(nullptr));
906 }
907}
908
Brian Silverman631b6262021-11-10 12:25:08 -0800909// Tests that we can check if a node has a tag.
910TEST_F(ConfigurationTest, NodeHasTag) {
911 {
912 FlatbufferDetachedBuffer<Configuration> config =
913 ReadConfig(ArtifactPath("aos/testdata/good_multinode.json"));
914 const Node *pi1 = GetNode(&config.message(), "pi1");
915 const Node *pi2 = GetNode(&config.message(), "pi2");
916
917 EXPECT_TRUE(NodeHasTag(pi1, "a"));
918 EXPECT_FALSE(NodeHasTag(pi2, "a"));
919 EXPECT_FALSE(NodeHasTag(pi1, "b"));
920 EXPECT_TRUE(NodeHasTag(pi2, "b"));
921 EXPECT_TRUE(NodeHasTag(pi1, "c"));
922 EXPECT_TRUE(NodeHasTag(pi2, "c"));
923 EXPECT_FALSE(NodeHasTag(pi1, "nope"));
924 EXPECT_FALSE(NodeHasTag(pi2, "nope"));
925 }
926
927 EXPECT_TRUE(NodeHasTag(nullptr, "arglfish"));
928}
929
Austin Schuhc9e10ec2020-01-26 16:08:28 -0800930// Tests that we can extract a node index from a config.
931TEST_F(ConfigurationTest, GetNodeIndex) {
932 FlatbufferDetachedBuffer<Configuration> config =
Austin Schuh373f1762021-06-02 21:07:09 -0700933 ReadConfig(ArtifactPath("aos/testdata/good_multinode.json"));
Austin Schuh04408fc2020-02-16 21:48:54 -0800934 FlatbufferDetachedBuffer<Configuration> config2 =
Austin Schuh373f1762021-06-02 21:07:09 -0700935 ReadConfig(ArtifactPath("aos/testdata/good_multinode.json"));
Austin Schuhc9e10ec2020-01-26 16:08:28 -0800936 const Node *pi1 = GetNode(&config.message(), "pi1");
937 const Node *pi2 = GetNode(&config.message(), "pi2");
938
Austin Schuh04408fc2020-02-16 21:48:54 -0800939 // Try the normal case.
Austin Schuhc9e10ec2020-01-26 16:08:28 -0800940 EXPECT_EQ(GetNodeIndex(&config.message(), pi1), 0);
941 EXPECT_EQ(GetNodeIndex(&config.message(), pi2), 1);
Austin Schuh04408fc2020-02-16 21:48:54 -0800942
943 // Now try if we have node pointers from a different message.
944 EXPECT_EQ(GetNodeIndex(&config2.message(), pi1), 0);
945 EXPECT_EQ(GetNodeIndex(&config2.message(), pi2), 1);
946
947 // And now try string names.
948 EXPECT_EQ(GetNodeIndex(&config2.message(), pi1->name()->string_view()), 0);
949 EXPECT_EQ(GetNodeIndex(&config2.message(), pi2->name()->string_view()), 1);
Austin Schuhc9e10ec2020-01-26 16:08:28 -0800950}
951
952// Tests that GetNodeOrDie handles both single and multi-node worlds and returns
953// valid nodes.
954TEST_F(ConfigurationDeathTest, GetNodeOrDie) {
955 FlatbufferDetachedBuffer<Configuration> config =
Austin Schuh373f1762021-06-02 21:07:09 -0700956 ReadConfig(ArtifactPath("aos/testdata/good_multinode.json"));
Austin Schuhc9e10ec2020-01-26 16:08:28 -0800957 FlatbufferDetachedBuffer<Configuration> config2 =
Austin Schuh373f1762021-06-02 21:07:09 -0700958 ReadConfig(ArtifactPath("aos/testdata/good_multinode.json"));
Austin Schuhc9e10ec2020-01-26 16:08:28 -0800959 {
960 // Simple case, nullptr -> nullptr
961 FlatbufferDetachedBuffer<Configuration> single_node_config =
Austin Schuh373f1762021-06-02 21:07:09 -0700962 ReadConfig(ArtifactPath("aos/testdata/config1.json"));
Austin Schuhc9e10ec2020-01-26 16:08:28 -0800963 EXPECT_EQ(nullptr, GetNodeOrDie(&single_node_config.message(), nullptr));
964
965 // Confirm that we die when a node is passed in.
966 EXPECT_DEATH(
967 {
968 GetNodeOrDie(&single_node_config.message(),
969 config.message().nodes()->Get(0));
970 },
971 "Provided a node in a single node world.");
972 }
973
974 const Node *pi1 = GetNode(&config.message(), "pi1");
975 // Now try a lookup using a node from a different instance of the config.
976 EXPECT_EQ(pi1,
977 GetNodeOrDie(&config.message(), config2.message().nodes()->Get(0)));
978}
979
Brian Silvermanaa2633f2020-02-17 21:04:14 -0800980TEST_F(ConfigurationTest, GetNodeFromHostname) {
981 FlatbufferDetachedBuffer<Configuration> config =
Austin Schuh373f1762021-06-02 21:07:09 -0700982 ReadConfig(ArtifactPath("aos/testdata/good_multinode.json"));
Austin Schuh6bdcc372024-06-27 14:49:11 -0700983 {
984 const Node *pi1 = GetNodeFromHostname(&config.message(), "raspberrypi");
985 ASSERT_TRUE(pi1 != nullptr);
986 EXPECT_EQ("pi1", pi1->name()->string_view());
987 }
988 {
989 const Node *pi2 = GetNodeFromHostname(&config.message(), "raspberrypi2");
990 ASSERT_TRUE(pi2 != nullptr);
991 EXPECT_EQ("pi2", pi2->name()->string_view());
992 }
Brian Silvermanaa2633f2020-02-17 21:04:14 -0800993 EXPECT_EQ(nullptr, GetNodeFromHostname(&config.message(), "raspberrypi3"));
994 EXPECT_EQ(nullptr, GetNodeFromHostname(&config.message(), "localhost"));
995 EXPECT_EQ(nullptr, GetNodeFromHostname(&config.message(), "3"));
996}
997
998TEST_F(ConfigurationTest, GetNodeFromHostnames) {
999 FlatbufferDetachedBuffer<Configuration> config =
Austin Schuh373f1762021-06-02 21:07:09 -07001000 ReadConfig(ArtifactPath("aos/testdata/good_multinode_hostnames.json"));
Austin Schuh6bdcc372024-06-27 14:49:11 -07001001 {
1002 const Node *pi1 = GetNodeFromHostname(&config.message(), "raspberrypi");
1003 ASSERT_TRUE(pi1 != nullptr);
1004 EXPECT_EQ("pi1", pi1->name()->string_view());
1005 }
1006 {
1007 const Node *pi2 = GetNodeFromHostname(&config.message(), "raspberrypi2");
1008 ASSERT_TRUE(pi2 != nullptr);
1009 EXPECT_EQ("pi2", pi2->name()->string_view());
1010 }
1011 {
1012 const Node *pi2 = GetNodeFromHostname(&config.message(), "raspberrypi3");
1013 ASSERT_TRUE(pi2 != nullptr);
1014 EXPECT_EQ("pi2", pi2->name()->string_view());
1015 }
1016 {
1017 const Node *pi2 = GetNodeFromHostname(&config.message(), "other");
1018 ASSERT_TRUE(pi2 != nullptr);
1019 EXPECT_EQ("pi2", pi2->name()->string_view());
1020 }
Brian Silvermanaa2633f2020-02-17 21:04:14 -08001021 EXPECT_EQ(nullptr, GetNodeFromHostname(&config.message(), "raspberrypi4"));
1022 EXPECT_EQ(nullptr, GetNodeFromHostname(&config.message(), "localhost"));
1023 EXPECT_EQ(nullptr, GetNodeFromHostname(&config.message(), "3"));
1024}
1025
Austin Schuhfc7b6a02021-07-12 21:19:07 -07001026// Tests that SourceNodeIndex reasonably handles a multi-node log file.
1027TEST_F(ConfigurationTest, SourceNodeIndex) {
1028 FlatbufferDetachedBuffer<Configuration> config =
1029 ReadConfig(ArtifactPath("aos/testdata/good_multinode.json"));
1030 std::vector<size_t> result = SourceNodeIndex(&config.message());
1031
1032 EXPECT_THAT(result, ::testing::ElementsAreArray({0, 1, 0, 0}));
1033}
1034
Austin Schuh1ccc3a12024-04-30 17:46:29 -07001035// Tests that SourceNode reasonably handles both single and multi-node configs.
1036TEST_F(ConfigurationTest, SourceNode) {
1037 {
1038 FlatbufferDetachedBuffer<Configuration> config_single_node =
1039 ReadConfig(ArtifactPath("aos/testdata/config1.json"));
1040 const Node *result =
1041 SourceNode(&config_single_node.message(),
1042 config_single_node.message().channels()->Get(0));
1043 EXPECT_EQ(result, nullptr);
1044 }
1045
1046 {
1047 FlatbufferDetachedBuffer<Configuration> config_multi_node =
1048 ReadConfig(ArtifactPath("aos/testdata/good_multinode.json"));
1049 size_t pi1_channels = 0;
1050 size_t pi2_channels = 0;
1051 for (const aos::Channel *channel :
1052 *config_multi_node.message().channels()) {
1053 const Node *result = SourceNode(&config_multi_node.message(), channel);
1054 if (channel->source_node()->string_view() == "pi1") {
1055 ++pi1_channels;
1056 EXPECT_EQ(result, config_multi_node.message().nodes()->Get(0));
1057 } else {
1058 ++pi2_channels;
1059 EXPECT_EQ(result, config_multi_node.message().nodes()->Get(1));
1060 }
1061 }
1062 EXPECT_GT(pi1_channels, 0u);
1063 EXPECT_GT(pi2_channels, 0u);
1064 }
1065}
1066
Austin Schuh5e95bd62021-10-11 18:40:22 -07001067// Tests that we reject invalid logging configurations.
1068TEST_F(ConfigurationDeathTest, InvalidLoggerConfig) {
1069 EXPECT_DEATH(
1070 {
Milind Upadhyay17098ba2022-04-15 22:18:50 -07001071 FlatbufferDetachedBuffer<Configuration> config = ReadConfig(
1072 ArtifactPath("aos/testdata/invalid_logging_configuration.json"));
Austin Schuh5e95bd62021-10-11 18:40:22 -07001073 },
1074 "Logging timestamps without data");
1075}
1076
Austin Schuha156fb22021-10-11 19:23:21 -07001077// Tests that we reject duplicate timestamp destination node configurations.
1078TEST_F(ConfigurationDeathTest, DuplicateTimestampDestinationNodes) {
1079 EXPECT_DEATH(
1080 {
1081 FlatbufferDetachedBuffer<Configuration> config = ReadConfig(
1082 ArtifactPath("aos/testdata/duplicate_destination_nodes.json"));
1083 },
1084 "Found duplicate timestamp_logger_nodes in");
1085}
1086
1087// Tests that we reject duplicate logger node configurations for a channel's
1088// data.
1089TEST_F(ConfigurationDeathTest, DuplicateLoggerNodes) {
1090 EXPECT_DEATH(
1091 {
1092 FlatbufferDetachedBuffer<Configuration> config = ReadConfig(
1093 ArtifactPath("aos/testdata/duplicate_logger_nodes.json"));
1094 },
1095 "Found duplicate logger_nodes in");
1096}
1097
Austin Schuhfb37c612022-08-11 15:24:51 -07001098// Tests that we properly compute the queue size for the provided duration.
1099TEST_F(ConfigurationTest, QueueSize) {
1100 EXPECT_EQ(QueueSize(100, chrono::seconds(2)), 200);
1101 EXPECT_EQ(QueueSize(200, chrono::seconds(2)), 400);
1102 EXPECT_EQ(QueueSize(100, chrono::seconds(6)), 600);
1103 EXPECT_EQ(QueueSize(100, chrono::milliseconds(10)), 1);
1104 EXPECT_EQ(QueueSize(100, chrono::milliseconds(10) - chrono::nanoseconds(1)),
1105 1);
1106 EXPECT_EQ(QueueSize(100, chrono::milliseconds(10) - chrono::nanoseconds(2)),
1107 1);
1108}
1109
1110// Tests that we compute scratch buffer size correctly too.
1111TEST_F(ConfigurationTest, QueueScratchBufferSize) {
1112 const aos::FlatbufferDetachedBuffer<Channel> channel =
1113 JsonToFlatbuffer<Channel>(
1114 "{ \"name\": \"/foo\", \"type\": \".aos.bar\", \"num_readers\": 5, "
1115 "\"num_senders\": 10 }");
Austin Schuhfb37c612022-08-11 15:24:51 -07001116 EXPECT_EQ(QueueScratchBufferSize(&channel.message()), 15);
1117}
1118
Nathan Leong307c9692022-10-08 15:25:03 -07001119// Tests that GetSchema returns schema of specified type
1120TEST_F(ConfigurationTest, GetSchema) {
1121 FlatbufferDetachedBuffer<Configuration> config =
1122 ReadConfig(ArtifactPath("aos/events/pingpong_config.json"));
1123 FlatbufferVector<reflection::Schema> expected_schema =
1124 FileToFlatbuffer<reflection::Schema>(
1125 ArtifactPath("aos/events/ping.bfbs"));
1126 EXPECT_EQ(FlatbufferToJson(GetSchema(&config.message(), "aos.examples.Ping")),
1127 FlatbufferToJson(expected_schema));
1128 EXPECT_EQ(GetSchema(&config.message(), "invalid_name"), nullptr);
1129}
1130
1131// Tests that GetSchema template returns schema of specified type
1132TEST_F(ConfigurationTest, GetSchemaTemplate) {
1133 FlatbufferDetachedBuffer<Configuration> config =
1134 ReadConfig(ArtifactPath("aos/events/pingpong_config.json"));
1135 FlatbufferVector<reflection::Schema> expected_schema =
1136 FileToFlatbuffer<reflection::Schema>(
1137 ArtifactPath("aos/events/ping.bfbs"));
1138 EXPECT_EQ(FlatbufferToJson(GetSchema<aos::examples::Ping>(&config.message())),
1139 FlatbufferToJson(expected_schema));
1140}
1141
1142// Tests that GetSchemaDetachedBuffer returns detached buffer of specified type
1143TEST_F(ConfigurationTest, GetSchemaDetachedBuffer) {
1144 FlatbufferDetachedBuffer<Configuration> config =
1145 ReadConfig(ArtifactPath("aos/events/pingpong_config.json"));
1146 FlatbufferVector<reflection::Schema> expected_schema =
1147 FileToFlatbuffer<reflection::Schema>(
1148 ArtifactPath("aos/events/ping.bfbs"));
1149 EXPECT_EQ(FlatbufferToJson(
1150 GetSchemaDetachedBuffer(&config.message(), "aos.examples.Ping")
1151 .value()),
1152 FlatbufferToJson(expected_schema));
1153 EXPECT_EQ(GetSchemaDetachedBuffer(&config.message(), "invalid_name"),
1154 std::nullopt);
1155}
1156
James Kuszmaul741a4d02023-01-05 14:59:21 -08001157// Tests that we can use a utility to add individual channels to a single-node
1158// config.
1159TEST_F(ConfigurationTest, AddChannelToConfigSingleNode) {
1160 FlatbufferDetachedBuffer<Configuration> base_config =
1161 ReadConfig(ArtifactPath("aos/testdata/config1.json"));
1162
1163 FlatbufferVector<reflection::Schema> schema =
1164 FileToFlatbuffer<reflection::Schema>(
1165 ArtifactPath("aos/events/ping.bfbs"));
1166
1167 FlatbufferDetachedBuffer<Configuration> new_config =
1168 AddChannelToConfiguration(&base_config.message(), "/new", schema);
1169
1170 ASSERT_EQ(new_config.message().channels()->size(),
1171 base_config.message().channels()->size() + 1);
1172
1173 const Channel *channel =
1174 GetChannel(new_config, "/new", "aos.examples.Ping", "", nullptr);
1175 ASSERT_TRUE(channel != nullptr);
1176 ASSERT_TRUE(channel->has_schema());
1177 // Check that we don't populate channel settings that we don't override the
1178 // defaults of.
1179 ASSERT_FALSE(channel->has_frequency());
1180}
1181
1182// Tests that we can use a utility to add individual channels to a multi-node
1183// config.
1184TEST_F(ConfigurationTest, AddChannelToConfigMultiNode) {
1185 FlatbufferDetachedBuffer<Configuration> base_config =
1186 ReadConfig(ArtifactPath("aos/testdata/good_multinode.json"));
1187
1188 FlatbufferVector<reflection::Schema> schema =
1189 FileToFlatbuffer<reflection::Schema>(
1190 ArtifactPath("aos/events/ping.bfbs"));
1191
1192 aos::ChannelT channel_overrides;
1193 channel_overrides.frequency = 971;
1194 FlatbufferDetachedBuffer<Configuration> new_config =
1195 AddChannelToConfiguration(&base_config.message(), "/new", schema,
1196 GetNode(&base_config.message(), "pi1"),
1197 channel_overrides);
1198
1199 ASSERT_EQ(new_config.message().channels()->size(),
1200 base_config.message().channels()->size() + 1);
1201
1202 const Channel *channel =
1203 GetChannel(new_config, "/new", "aos.examples.Ping", "", nullptr);
1204 ASSERT_TRUE(channel != nullptr);
1205 ASSERT_TRUE(channel->has_schema());
1206 ASSERT_TRUE(channel->has_source_node());
1207 ASSERT_EQ("pi1", channel->source_node()->string_view());
1208 ASSERT_EQ(971, channel->frequency());
1209}
1210
Maxwell Gumley8c1b87f2024-02-13 17:54:52 -07001211// Create a new configuration with the specified channel removed.
1212// Initially there must be exactly one channel in the base_config that matches
1213// the criteria. Check to make sure the new configuration has one less channel,
1214// and that channel is the specified channel.
1215void TestGetPartialConfiguration(const Configuration &base_config,
1216 std::string_view test_channel_name,
1217 std::string_view test_channel_type) {
1218 const Channel *channel_from_base_config = GetChannel(
1219 &base_config, test_channel_name, test_channel_type, "", nullptr);
1220 ASSERT_TRUE(channel_from_base_config != nullptr);
1221
1222 const FlatbufferDetachedBuffer<Configuration> new_config =
1223 configuration::GetPartialConfiguration(
1224 base_config,
1225 // should_include_channel function
1226 [test_channel_name, test_channel_type](const Channel &channel) {
1227 if (channel.name()->string_view() == test_channel_name &&
1228 channel.type()->string_view() == test_channel_type) {
1229 LOG(INFO) << "Omitting channel from save_log, channel: "
1230 << channel.name()->string_view() << ", "
1231 << channel.type()->string_view();
1232 return false;
1233 }
1234 return true;
1235 });
1236
1237 EXPECT_EQ(new_config.message().channels()->size(),
1238 base_config.channels()->size() - 1);
1239
1240 channel_from_base_config = GetChannel(&base_config, test_channel_name,
1241 test_channel_type, "", nullptr);
1242 EXPECT_TRUE(channel_from_base_config != nullptr);
1243
1244 const Channel *channel_from_new_config =
1245 GetChannel(new_config, test_channel_name, test_channel_type, "", nullptr);
1246 EXPECT_TRUE(channel_from_new_config == nullptr);
1247}
1248
1249// Tests that we can use a utility to remove individual channels from a
1250// single-node config.
1251TEST_F(ConfigurationTest, RemoveChannelsFromConfigSingleNode) {
1252 FlatbufferDetachedBuffer<Configuration> base_config =
1253 ReadConfig(ArtifactPath("aos/testdata/config1.json"));
1254
1255 constexpr std::string_view test_channel_name = "/foo2";
1256 constexpr std::string_view test_channel_type = ".aos.bar";
1257
1258 TestGetPartialConfiguration(base_config.message(), test_channel_name,
1259 test_channel_type);
1260}
1261
1262// Tests that we can use a utility to remove individual channels from a
1263// multi-node config.
1264TEST_F(ConfigurationTest, RemoveChannelsFromConfigMultiNode) {
1265 FlatbufferDetachedBuffer<Configuration> base_config =
1266 ReadConfig(ArtifactPath("aos/testdata/good_multinode.json"));
1267
1268 constexpr std::string_view test_channel_name = "/batman";
1269 constexpr std::string_view test_channel_type = ".aos.baz";
1270
1271 TestGetPartialConfiguration(base_config.message(), test_channel_name,
1272 test_channel_type);
1273}
1274
Maxwell Gumleyf84aca32024-05-10 13:51:59 -06001275// Test fixture for testing IsNodeFromConfiguration.
1276// Initializes multiple configurations which share the same node names.
1277// Use IsNodeFromConfiguration to check if a node is in a configuration.
1278class IsNodeFromConfigurationFixtureTest : public ConfigurationTest {
1279 protected:
1280 // Use unique_ptr for deferred initialization
1281 std::unique_ptr<FlatbufferDetachedBuffer<Configuration>> config1;
1282 std::unique_ptr<FlatbufferDetachedBuffer<Configuration>> config2;
1283 const Node *node1_config1;
1284 const Node *node2_config1;
1285 const Node *node1_config2;
1286 const Node *node2_config2;
1287
1288 IsNodeFromConfigurationFixtureTest() {
1289 // Initialize configurations here
1290 config1 = std::make_unique<FlatbufferDetachedBuffer<Configuration>>(
1291 JsonToFlatbuffer(R"config({
1292 "nodes": [
1293 {"name": "node1"},
1294 {"name": "node2"}
1295 ]
1296 })config",
1297 Configuration::MiniReflectTypeTable()));
1298
1299 config2 = std::make_unique<FlatbufferDetachedBuffer<Configuration>>(
1300 JsonToFlatbuffer(R"config({
1301 "nodes": [
1302 {"name": "node1"},
1303 {"name": "node2"}
1304 ]
1305 })config",
1306 Configuration::MiniReflectTypeTable()));
1307
1308 // Initialize nodes after configuration initialization
1309 node1_config1 = config1->message().nodes()->Get(0);
1310 node2_config1 = config1->message().nodes()->Get(1);
1311 node1_config2 = config2->message().nodes()->Get(0);
1312 node2_config2 = config2->message().nodes()->Get(1);
1313 }
1314
1315 void SetUp() override {
1316 ConfigurationTest::SetUp();
1317 // Any additional setup can go here.
1318 }
1319};
1320
1321// Test case when node exists in the configuration.
1322TEST_F(IsNodeFromConfigurationFixtureTest, NodeExistsInConfiguration) {
1323 EXPECT_TRUE(IsNodeFromConfiguration(&config1->message(), node1_config1));
1324 EXPECT_TRUE(IsNodeFromConfiguration(&config1->message(), node2_config1));
1325}
1326
1327// Test case when node does not exist in the configuration.
1328TEST_F(IsNodeFromConfigurationFixtureTest, NodeDoesNotExistsInConfiguration) {
1329 EXPECT_FALSE(IsNodeFromConfiguration(&config1->message(), node1_config2));
1330 EXPECT_FALSE(IsNodeFromConfiguration(&config1->message(), node2_config2));
1331}
1332
1333// Test case for nodes with same names but from different configurations.
1334TEST_F(IsNodeFromConfigurationFixtureTest, SameNameDifferentConfiguration) {
1335 EXPECT_FALSE(IsNodeFromConfiguration(&config1->message(), node1_config2));
1336 EXPECT_FALSE(IsNodeFromConfiguration(&config1->message(), node2_config2));
1337 EXPECT_FALSE(IsNodeFromConfiguration(&config2->message(), node1_config1));
1338 EXPECT_FALSE(IsNodeFromConfiguration(&config2->message(), node2_config1));
1339}
1340
1341// Test case for null pointers.
1342TEST_F(IsNodeFromConfigurationFixtureTest, NullPointers) {
1343 EXPECT_FALSE(IsNodeFromConfiguration(nullptr, nullptr));
1344 EXPECT_FALSE(IsNodeFromConfiguration(&config1->message(), nullptr));
1345 EXPECT_FALSE(IsNodeFromConfiguration(nullptr, node1_config1));
1346}
1347
1348// Tests that SourceNode reasonably handles both single and multi-node configs.
1349TEST(IsNodeFromConfigurationTest, SingleNode) {
1350 FlatbufferDetachedBuffer<Configuration> config_single_node =
1351 ReadConfig(ArtifactPath("aos/testdata/config1.json"));
1352 EXPECT_TRUE(IsNodeFromConfiguration(&config_single_node.message(), nullptr));
1353}
1354
Austin Schuh605d5ff2024-05-10 15:59:54 -07001355// Tests that we can use a utility to remove individual channels from a
1356// multi-node config.
1357TEST_F(ConfigurationTest, MultinodeMerge) {
1358 FlatbufferDetachedBuffer<Configuration> config =
1359 ReadConfig(ArtifactPath("aos/testdata/multinode_merge.json"));
1360
1361 EXPECT_EQ(
1362 absl::StripSuffix(util::ReadFileToStringOrDie(ArtifactPath(
1363 "aos/testdata/multinode_merge_expected.json")),
1364 "\n"),
1365 FlatbufferToJson(config, {.multi_line = true}));
1366}
1367
Stephan Pleinesf63bde82024-01-13 15:59:33 -08001368} // namespace aos::configuration::testing