blob: d980dad757764362d8c8d96457bc731543fedac4 [file] [log] [blame]
Austin Schuhcb108412019-10-13 16:09:54 -07001#include "aos/configuration.h"
2
3#include "absl/strings/strip.h"
Philipp Schrader790cb542023-07-05 21:06:52 -07004#include "flatbuffers/reflection.h"
5#include "glog/logging.h"
6#include "gmock/gmock.h"
7#include "gtest/gtest.h"
8
Nathan Leong307c9692022-10-08 15:25:03 -07009#include "aos/events/ping_generated.h"
Austin Schuhcb108412019-10-13 16:09:54 -070010#include "aos/json_to_flatbuffer.h"
Austin Schuh1ef01ef2021-02-07 20:40:36 -080011#include "aos/testing/flatbuffer_eq.h"
Austin Schuh373f1762021-06-02 21:07:09 -070012#include "aos/testing/path.h"
Austin Schuhcb108412019-10-13 16:09:54 -070013#include "aos/testing/test_logging.h"
14#include "aos/util/file.h"
Austin Schuhcb108412019-10-13 16:09:54 -070015
Stephan Pleinesf63bde82024-01-13 15:59:33 -080016namespace aos::configuration::testing {
Austin Schuhcb108412019-10-13 16:09:54 -070017
Austin Schuh373f1762021-06-02 21:07:09 -070018using aos::testing::ArtifactPath;
Austin Schuhfb37c612022-08-11 15:24:51 -070019namespace chrono = std::chrono;
Austin Schuh66602132020-02-28 13:38:37 -080020
Austin Schuhcb108412019-10-13 16:09:54 -070021class ConfigurationTest : public ::testing::Test {
22 public:
23 ConfigurationTest() { ::aos::testing::EnableTestLogging(); }
24};
25
26typedef ConfigurationTest ConfigurationDeathTest;
27
28// *the* expected location for all working tests.
Austin Schuh1ef01ef2021-02-07 20:40:36 -080029aos::FlatbufferDetachedBuffer<Channel> ExpectedLocation() {
30 return JsonToFlatbuffer<Channel>(
31 "{ \"name\": \"/foo\", \"type\": \".aos.bar\", \"max_size\": 5 }");
32}
33
Austin Schuhbca6cf02019-12-22 17:28:34 -080034// And for multinode setups
Austin Schuh1ef01ef2021-02-07 20:40:36 -080035aos::FlatbufferDetachedBuffer<Channel> ExpectedMultinodeLocation() {
36 return JsonToFlatbuffer<Channel>(
37 "{ \"name\": \"/foo\", \"type\": \".aos.bar\", \"max_size\": 5, "
38 "\"source_node\": \"pi1\" }");
39}
Austin Schuhcb108412019-10-13 16:09:54 -070040
41// Tests that we can read and merge a configuration.
42TEST_F(ConfigurationTest, ConfigMerge) {
Austin Schuh40485ed2019-10-26 21:51:44 -070043 FlatbufferDetachedBuffer<Configuration> config =
Austin Schuh373f1762021-06-02 21:07:09 -070044 ReadConfig(ArtifactPath("aos/testdata/config1.json"));
Ravago Jonescf453ab2020-05-06 21:14:53 -070045 LOG(INFO) << "Read: " << FlatbufferToJson(config, {.multi_line = true});
Austin Schuhcb108412019-10-13 16:09:54 -070046
Austin Schuh373f1762021-06-02 21:07:09 -070047 EXPECT_EQ(absl::StripSuffix(util::ReadFileToStringOrDie(
48 ArtifactPath("aos/testdata/expected.json")),
49 "\n"),
50 FlatbufferToJson(config, {.multi_line = true}));
Austin Schuhcb108412019-10-13 16:09:54 -070051}
52
Austin Schuhc9e10ec2020-01-26 16:08:28 -080053// Tests that we can get back a ChannelIndex.
54TEST_F(ConfigurationTest, ChannelIndex) {
55 FlatbufferDetachedBuffer<Configuration> config =
Austin Schuh373f1762021-06-02 21:07:09 -070056 ReadConfig(ArtifactPath("aos/testdata/config1.json"));
Austin Schuhc9e10ec2020-01-26 16:08:28 -080057
58 EXPECT_EQ(
59 ChannelIndex(&config.message(), config.message().channels()->Get(1u)),
60 1u);
61}
62
Austin Schuh217a9782019-12-21 23:02:50 -080063// Tests that we can read and merge a multinode configuration.
64TEST_F(ConfigurationTest, ConfigMergeMultinode) {
65 FlatbufferDetachedBuffer<Configuration> config =
Austin Schuh373f1762021-06-02 21:07:09 -070066 ReadConfig(ArtifactPath("aos/testdata/config1_multinode.json"));
Ravago Jonescf453ab2020-05-06 21:14:53 -070067 LOG(INFO) << "Read: " << FlatbufferToJson(config, {.multi_line = true});
Austin Schuh217a9782019-12-21 23:02:50 -080068
Ravago Jonescf453ab2020-05-06 21:14:53 -070069 EXPECT_EQ(std::string(absl::StripSuffix(
Austin Schuh373f1762021-06-02 21:07:09 -070070 util::ReadFileToStringOrDie(
71 ArtifactPath("aos/testdata/expected_multinode.json")),
Ravago Jonescf453ab2020-05-06 21:14:53 -070072 "\n")),
73 FlatbufferToJson(config, {.multi_line = true}));
Austin Schuh217a9782019-12-21 23:02:50 -080074}
75
Alex Perrycb7da4b2019-08-28 19:35:56 -070076// Tests that we sort the entries in a config so we can look entries up.
77TEST_F(ConfigurationTest, UnsortedConfig) {
78 FlatbufferDetachedBuffer<Configuration> config =
Austin Schuh373f1762021-06-02 21:07:09 -070079 ReadConfig(ArtifactPath("aos/testdata/backwards.json"));
Alex Perrycb7da4b2019-08-28 19:35:56 -070080
Ravago Jonescf453ab2020-05-06 21:14:53 -070081 LOG(INFO) << "Read: " << FlatbufferToJson(config, {.multi_line = true});
Alex Perrycb7da4b2019-08-28 19:35:56 -070082
Austin Schuhf1fff282020-03-28 16:57:32 -070083 EXPECT_EQ(FlatbufferToJson(GetChannel(config, "/aos/robot_state",
Austin Schuhbca6cf02019-12-22 17:28:34 -080084 "aos.RobotState", "app1", nullptr)),
Austin Schuhf1fff282020-03-28 16:57:32 -070085 "{ \"name\": \"/aos/robot_state\", \"type\": \"aos.RobotState\", "
Alex Perrycb7da4b2019-08-28 19:35:56 -070086 "\"max_size\": 5 }");
87}
88
Austin Schuhcb108412019-10-13 16:09:54 -070089// Tests that we die when a file is imported twice.
90TEST_F(ConfigurationDeathTest, DuplicateFile) {
91 EXPECT_DEATH(
92 {
Austin Schuh40485ed2019-10-26 21:51:44 -070093 FlatbufferDetachedBuffer<Configuration> config =
Austin Schuh373f1762021-06-02 21:07:09 -070094 ReadConfig(ArtifactPath("aos/testdata/config1_bad.json"));
Austin Schuhcb108412019-10-13 16:09:54 -070095 },
Austin Schuh373f1762021-06-02 21:07:09 -070096 "aos/testdata/config1_bad.json");
Austin Schuhcb108412019-10-13 16:09:54 -070097}
98
Milind Upadhyay17098ba2022-04-15 22:18:50 -070099// Tests that we die when we give an invalid path.
100TEST_F(ConfigurationDeathTest, NonexistentFile) {
101 EXPECT_DEATH(
102 {
103 FlatbufferDetachedBuffer<Configuration> config =
104 ReadConfig("nonexistent/config.json");
105 },
106 "above error");
107}
108
109// Tests that we return std::nullopt when we give an invalid path.
110TEST_F(ConfigurationTest, NonexistentFileOptional) {
111 std::optional<FlatbufferDetachedBuffer<Configuration>> config =
112 MaybeReadConfig("nonexistent/config.json");
113 EXPECT_FALSE(config.has_value());
114}
115
Austin Schuhf1fff282020-03-28 16:57:32 -0700116// Tests that we reject invalid channel names. This means any channels with //
117// in their name, a trailing /, or regex characters.
118TEST_F(ConfigurationDeathTest, InvalidChannelName) {
119 EXPECT_DEATH(
120 {
121 FlatbufferDetachedBuffer<Configuration> config =
Austin Schuh373f1762021-06-02 21:07:09 -0700122 ReadConfig(ArtifactPath("aos/testdata/invalid_channel_name1.json"));
Austin Schuhf1fff282020-03-28 16:57:32 -0700123 },
124 "Channel names can't end with '/'");
125 EXPECT_DEATH(
126 {
127 FlatbufferDetachedBuffer<Configuration> config =
Austin Schuh373f1762021-06-02 21:07:09 -0700128 ReadConfig(ArtifactPath("aos/testdata/invalid_channel_name2.json"));
Austin Schuhf1fff282020-03-28 16:57:32 -0700129 },
130 "Invalid channel name");
131 EXPECT_DEATH(
132 {
133 FlatbufferDetachedBuffer<Configuration> config =
Austin Schuh373f1762021-06-02 21:07:09 -0700134 ReadConfig(ArtifactPath("aos/testdata/invalid_channel_name3.json"));
Austin Schuhf1fff282020-03-28 16:57:32 -0700135 LOG(FATAL) << "Foo";
136 },
137 "Invalid channel name");
Austin Schuh47e382e2023-05-28 11:20:56 -0700138 EXPECT_DEATH(
139 {
140 FlatbufferDetachedBuffer<Configuration> config =
141 ReadConfig(ArtifactPath("aos/testdata/invalid_channel_name4.json"));
142 LOG(FATAL) << "Foo";
143 },
144 "Channel names must start with '/'");
Austin Schuhf1fff282020-03-28 16:57:32 -0700145}
146
Austin Schuh8d6cea82020-02-28 12:17:16 -0800147// Tests that we can modify a config with a json snippet.
148TEST_F(ConfigurationTest, MergeWithConfig) {
149 FlatbufferDetachedBuffer<Configuration> config =
Austin Schuh373f1762021-06-02 21:07:09 -0700150 ReadConfig(ArtifactPath("aos/testdata/config1.json"));
Ravago Jonescf453ab2020-05-06 21:14:53 -0700151 LOG(INFO) << "Read: " << FlatbufferToJson(config, {.multi_line = true});
Austin Schuh8d6cea82020-02-28 12:17:16 -0800152
153 FlatbufferDetachedBuffer<Configuration> updated_config =
154 MergeWithConfig(&config.message(),
155 R"channel({
156 "channels": [
157 {
158 "name": "/foo",
159 "type": ".aos.bar",
160 "max_size": 100
161 }
162 ]
163})channel");
164
Austin Schuh373f1762021-06-02 21:07:09 -0700165 EXPECT_EQ(absl::StripSuffix(util::ReadFileToStringOrDie(ArtifactPath(
166 "aos/testdata/expected_merge_with.json")),
Ravago Jonescf453ab2020-05-06 21:14:53 -0700167 "\n"),
168 FlatbufferToJson(updated_config, {.multi_line = true}));
Austin Schuh8d6cea82020-02-28 12:17:16 -0800169}
170
Austin Schuhcb108412019-10-13 16:09:54 -0700171// Tests that we can lookup a location, complete with maps, from a merged
172// config.
Austin Schuh40485ed2019-10-26 21:51:44 -0700173TEST_F(ConfigurationTest, GetChannel) {
174 FlatbufferDetachedBuffer<Configuration> config =
Austin Schuh373f1762021-06-02 21:07:09 -0700175 ReadConfig(ArtifactPath("aos/testdata/config1.json"));
Austin Schuhcb108412019-10-13 16:09:54 -0700176
177 // Test a basic lookup first.
Austin Schuh1ef01ef2021-02-07 20:40:36 -0800178 EXPECT_THAT(GetChannel(config, "/foo", ".aos.bar", "app1", nullptr),
179 aos::testing::FlatbufferEq(ExpectedLocation()));
Austin Schuhcb108412019-10-13 16:09:54 -0700180
181 // Test that an invalid name results in nullptr back.
Austin Schuhbca6cf02019-12-22 17:28:34 -0800182 EXPECT_EQ(GetChannel(config, "/invalid_name", ".aos.bar", "app1", nullptr),
183 nullptr);
Austin Schuhcb108412019-10-13 16:09:54 -0700184
185 // Tests that a root map/rename works. And that they get processed from the
186 // bottom up.
Austin Schuh1ef01ef2021-02-07 20:40:36 -0800187 EXPECT_THAT(GetChannel(config, "/batman", ".aos.bar", "app1", nullptr),
188 aos::testing::FlatbufferEq(ExpectedLocation()));
Austin Schuhcb108412019-10-13 16:09:54 -0700189
190 // And then test that an application specific map/rename works.
Austin Schuh1ef01ef2021-02-07 20:40:36 -0800191 EXPECT_THAT(GetChannel(config, "/bar", ".aos.bar", "app1", nullptr),
192 aos::testing::FlatbufferEq(ExpectedLocation()));
193 EXPECT_THAT(GetChannel(config, "/baz", ".aos.bar", "app2", nullptr),
194 aos::testing::FlatbufferEq(ExpectedLocation()));
Austin Schuhcb108412019-10-13 16:09:54 -0700195
196 // And then test that an invalid application name gets properly ignored.
Austin Schuh1ef01ef2021-02-07 20:40:36 -0800197 EXPECT_THAT(GetChannel(config, "/foo", ".aos.bar", "app3", nullptr),
198 aos::testing::FlatbufferEq(ExpectedLocation()));
Austin Schuhbca6cf02019-12-22 17:28:34 -0800199}
200
James Kuszmaulc8503f32022-06-25 16:17:12 -0700201// Tests that we can do reverse-lookups of channel names.
202TEST_F(ConfigurationTest, GetChannelAliases) {
203 FlatbufferDetachedBuffer<Configuration> config =
204 ReadConfig(ArtifactPath("aos/testdata/config1.json"));
205
206 // Test a basic lookup first.
207 EXPECT_THAT(
208 GetChannelAliases(&config.message(), "/foo", ".aos.bar", "app1", nullptr),
209 ::testing::UnorderedElementsAre("/foo", "/batman", "/bar"));
210 EXPECT_THAT(
211 GetChannelAliases(&config.message(), "/bar", ".aos.bar", "app1", nullptr),
212 ::testing::UnorderedElementsAre("/batman", "/bar"));
213 EXPECT_THAT(GetChannelAliases(&config.message(), "/batman", ".aos.bar",
214 "app1", nullptr),
215 ::testing::UnorderedElementsAre("/batman"));
216 // /bar (deliberately) does not get included because of the ordering in the
217 // map.
218 EXPECT_THAT(
219 GetChannelAliases(&config.message(), "/foo", ".aos.bar", "", nullptr),
220 ::testing::UnorderedElementsAre("/foo", "/batman"));
221 EXPECT_THAT(
222 GetChannelAliases(&config.message(), "/foo", ".aos.bar", "app2", nullptr),
223 ::testing::UnorderedElementsAre("/foo", "/batman", "/baz"));
224}
225
Austin Schuhbca6cf02019-12-22 17:28:34 -0800226// Tests that we can lookup a location with node specific maps.
227TEST_F(ConfigurationTest, GetChannelMultinode) {
228 FlatbufferDetachedBuffer<Configuration> config =
Austin Schuh373f1762021-06-02 21:07:09 -0700229 ReadConfig(ArtifactPath("aos/testdata/good_multinode.json"));
Austin Schuhbca6cf02019-12-22 17:28:34 -0800230 const Node *pi1 = GetNode(&config.message(), "pi1");
231 const Node *pi2 = GetNode(&config.message(), "pi2");
232
233 // Test a basic lookup first.
Austin Schuh1ef01ef2021-02-07 20:40:36 -0800234 EXPECT_THAT(GetChannel(config, "/foo", ".aos.bar", "app1", pi1),
235 aos::testing::FlatbufferEq(ExpectedMultinodeLocation()));
236 EXPECT_THAT(GetChannel(config, "/foo", ".aos.bar", "app1", pi2),
237 aos::testing::FlatbufferEq(ExpectedMultinodeLocation()));
Austin Schuhbca6cf02019-12-22 17:28:34 -0800238
239 // Tests that a root map/rename works with a node specific map.
Austin Schuh1ef01ef2021-02-07 20:40:36 -0800240 EXPECT_THAT(GetChannel(config, "/batman", ".aos.bar", "app1", pi1),
241 aos::testing::FlatbufferEq(ExpectedMultinodeLocation()));
Austin Schuhbca6cf02019-12-22 17:28:34 -0800242
Austin Schuha22ee352024-05-08 16:24:48 -0700243 // Tests that node specific maps get ignored
244 EXPECT_EQ(GetChannel(config, "/batman", ".aos.bar", "", nullptr), nullptr);
245
Austin Schuhbca6cf02019-12-22 17:28:34 -0800246 // Tests that a root map/rename fails with a node specific map for the wrong
247 // node.
248 EXPECT_EQ(GetChannel(config, "/batman", ".aos.bar", "app1", pi2), nullptr);
249
250 // And then test that an application specific map/rename works.
Austin Schuh1ef01ef2021-02-07 20:40:36 -0800251 EXPECT_THAT(GetChannel(config, "/batman2", ".aos.bar", "app1", pi1),
252 aos::testing::FlatbufferEq(ExpectedMultinodeLocation()));
253 EXPECT_THAT(GetChannel(config, "/batman3", ".aos.bar", "app1", pi1),
254 aos::testing::FlatbufferEq(ExpectedMultinodeLocation()));
Austin Schuhbca6cf02019-12-22 17:28:34 -0800255
256 // And then that it fails when the node changes.
257 EXPECT_EQ(GetChannel(config, "/batman3", ".aos.bar", "app1", pi2), nullptr);
258}
259
James Kuszmaulc8503f32022-06-25 16:17:12 -0700260// Tests that reverse channel lookup on a multi-node config (including with
261// wildcards) works.
262TEST_F(ConfigurationTest, GetChannelAliasesMultinode) {
263 FlatbufferDetachedBuffer<Configuration> config =
264 ReadConfig(ArtifactPath("aos/testdata/good_multinode.json"));
265
266 const Node *pi1 = GetNode(&config.message(), "pi1");
267 const Node *pi2 = GetNode(&config.message(), "pi2");
268
269 EXPECT_THAT(
270 GetChannelAliases(&config.message(), "/foo", ".aos.bar", "app1", pi1),
271 ::testing::UnorderedElementsAre("/foo", "/batman", "/batman2", "/batman3",
272 "/magic/string"));
273
274 EXPECT_THAT(
275 GetChannelAliases(&config.message(), "/foo", ".aos.baz", "app1", pi1),
276 ::testing::UnorderedElementsAre("/foo", "/batman3", "/magic/string"));
277
278 EXPECT_THAT(
279 GetChannelAliases(&config.message(), "/foo/testing", ".aos.bar", "", pi1),
280 ::testing::UnorderedElementsAre("/foo/testing", "/magic/string/testing"));
281
282 EXPECT_THAT(
283 GetChannelAliases(&config.message(), "/foo/testing", ".aos.bar", "app1",
284 pi2),
285 ::testing::UnorderedElementsAre("/foo/testing", "/magic/string/testing"));
286}
287
Austin Schuhbca6cf02019-12-22 17:28:34 -0800288// Tests that we can lookup a location with type specific maps.
289TEST_F(ConfigurationTest, GetChannelTypedMultinode) {
290 FlatbufferDetachedBuffer<Configuration> config =
Austin Schuh373f1762021-06-02 21:07:09 -0700291 ReadConfig(ArtifactPath("aos/testdata/good_multinode.json"));
Austin Schuhbca6cf02019-12-22 17:28:34 -0800292 const Node *pi1 = GetNode(&config.message(), "pi1");
293
294 // Test a basic lookup first.
Austin Schuh1ef01ef2021-02-07 20:40:36 -0800295 EXPECT_THAT(GetChannel(config, "/batman", ".aos.bar", "app1", pi1),
296 aos::testing::FlatbufferEq(ExpectedMultinodeLocation()));
Austin Schuhbca6cf02019-12-22 17:28:34 -0800297
298 // Now confirm that a second message on the same name doesn't get remapped.
299 const char *kExpectedBazMultinodeLocation =
300 "{ \"name\": \"/batman\", \"type\": \".aos.baz\", \"max_size\": 5, "
301 "\"source_node\": \"pi1\" }";
302 EXPECT_EQ(
303 FlatbufferToJson(GetChannel(config, "/batman", ".aos.baz", "app1", pi1)),
304 kExpectedBazMultinodeLocation);
Austin Schuhcb108412019-10-13 16:09:54 -0700305}
306
Austin Schuhf1fff282020-03-28 16:57:32 -0700307// Tests that we can lookup a location with a glob
308TEST_F(ConfigurationTest, GetChannelGlob) {
309 FlatbufferDetachedBuffer<Configuration> config =
Austin Schuh373f1762021-06-02 21:07:09 -0700310 ReadConfig(ArtifactPath("aos/testdata/good_multinode.json"));
Austin Schuhf1fff282020-03-28 16:57:32 -0700311 const Node *pi1 = GetNode(&config.message(), "pi1");
312
313 // Confirm that a glob with nothing after it matches.
Austin Schuh1ef01ef2021-02-07 20:40:36 -0800314 EXPECT_THAT(GetChannel(config, "/magic/string", ".aos.bar", "app7", pi1),
315 aos::testing::FlatbufferEq(ExpectedMultinodeLocation()));
Austin Schuhf1fff282020-03-28 16:57:32 -0700316
317 // Now confirm that glob with something following it matches and renames
318 // correctly.
319 const char *kExpectedSubfolderMultinodeLocation =
320 "{ \"name\": \"/foo/subfolder\", \"type\": \".aos.bar\", \"max_size\": "
321 "5, \"source_node\": \"pi1\" }";
322 EXPECT_EQ(FlatbufferToJson(GetChannel(config, "/magic/string/subfolder",
323 ".aos.bar", "app7", pi1)),
324 kExpectedSubfolderMultinodeLocation);
325}
326
Austin Schuh217a9782019-12-21 23:02:50 -0800327// Tests that we reject a configuration which has a nodes list, but has channels
328// withoout source_node filled out.
329TEST_F(ConfigurationDeathTest, InvalidSourceNode) {
330 EXPECT_DEATH(
331 {
332 FlatbufferDetachedBuffer<Configuration> config =
Austin Schuh373f1762021-06-02 21:07:09 -0700333 ReadConfig(ArtifactPath("aos/testdata/invalid_nodes.json"));
Austin Schuh217a9782019-12-21 23:02:50 -0800334 },
335 "source_node");
336
337 EXPECT_DEATH(
338 {
339 FlatbufferDetachedBuffer<Configuration> config =
Austin Schuh373f1762021-06-02 21:07:09 -0700340 ReadConfig(ArtifactPath("aos/testdata/invalid_source_node.json"));
Austin Schuh217a9782019-12-21 23:02:50 -0800341 },
342 "source_node");
343
344 EXPECT_DEATH(
345 {
Austin Schuh373f1762021-06-02 21:07:09 -0700346 FlatbufferDetachedBuffer<Configuration> config = ReadConfig(
347 ArtifactPath("aos/testdata/invalid_destination_node.json"));
Austin Schuh217a9782019-12-21 23:02:50 -0800348 },
349 "destination_nodes");
350
351 EXPECT_DEATH(
352 {
353 FlatbufferDetachedBuffer<Configuration> config =
Austin Schuh373f1762021-06-02 21:07:09 -0700354 ReadConfig(ArtifactPath("aos/testdata/self_forward.json"));
Austin Schuh217a9782019-12-21 23:02:50 -0800355 },
356 "forwarding data to itself");
357}
358
359// Tests that our node writeable helpers work as intended.
360TEST_F(ConfigurationTest, ChannelIsSendableOnNode) {
361 FlatbufferDetachedBuffer<Channel> good_channel(JsonToFlatbuffer(
Austin Schuh719946b2019-12-28 14:51:01 -0800362 R"channel({
Austin Schuh217a9782019-12-21 23:02:50 -0800363 "name": "/test",
364 "type": "aos.examples.Ping",
365 "source_node": "foo"
366})channel",
367 Channel::MiniReflectTypeTable()));
368
369 FlatbufferDetachedBuffer<Channel> bad_channel(JsonToFlatbuffer(
Austin Schuh719946b2019-12-28 14:51:01 -0800370 R"channel({
Austin Schuh217a9782019-12-21 23:02:50 -0800371 "name": "/test",
372 "type": "aos.examples.Ping",
373 "source_node": "bar"
374})channel",
375 Channel::MiniReflectTypeTable()));
376
377 FlatbufferDetachedBuffer<Node> node(JsonToFlatbuffer(
Austin Schuh719946b2019-12-28 14:51:01 -0800378 R"node({
Austin Schuh217a9782019-12-21 23:02:50 -0800379 "name": "foo"
380})node",
381 Node::MiniReflectTypeTable()));
382
383 EXPECT_TRUE(
384 ChannelIsSendableOnNode(&good_channel.message(), &node.message()));
385 EXPECT_FALSE(
386 ChannelIsSendableOnNode(&bad_channel.message(), &node.message()));
387}
388
389// Tests that our node readable and writeable helpers work as intended.
390TEST_F(ConfigurationTest, ChannelIsReadableOnNode) {
391 FlatbufferDetachedBuffer<Channel> good_channel(JsonToFlatbuffer(
Austin Schuh719946b2019-12-28 14:51:01 -0800392 R"channel({
Austin Schuh217a9782019-12-21 23:02:50 -0800393 "name": "/test",
394 "type": "aos.examples.Ping",
395 "source_node": "bar",
396 "destination_nodes": [
Austin Schuh719946b2019-12-28 14:51:01 -0800397 {
398 "name": "baz"
399 },
400 {
401 "name": "foo"
402 }
Austin Schuh217a9782019-12-21 23:02:50 -0800403 ]
404})channel",
405 Channel::MiniReflectTypeTable()));
406
407 FlatbufferDetachedBuffer<Channel> bad_channel1(JsonToFlatbuffer(
Austin Schuh719946b2019-12-28 14:51:01 -0800408 R"channel({
Austin Schuh217a9782019-12-21 23:02:50 -0800409 "name": "/test",
410 "type": "aos.examples.Ping",
411 "source_node": "bar"
412})channel",
413 Channel::MiniReflectTypeTable()));
414
415 FlatbufferDetachedBuffer<Channel> bad_channel2(JsonToFlatbuffer(
Austin Schuh719946b2019-12-28 14:51:01 -0800416 R"channel({
Austin Schuh217a9782019-12-21 23:02:50 -0800417 "name": "/test",
418 "type": "aos.examples.Ping",
419 "source_node": "bar",
420 "destination_nodes": [
Austin Schuh719946b2019-12-28 14:51:01 -0800421 {
422 "name": "baz"
423 }
Austin Schuh217a9782019-12-21 23:02:50 -0800424 ]
425})channel",
426 Channel::MiniReflectTypeTable()));
427
428 FlatbufferDetachedBuffer<Node> node(JsonToFlatbuffer(
Austin Schuh719946b2019-12-28 14:51:01 -0800429 R"node({
Austin Schuh217a9782019-12-21 23:02:50 -0800430 "name": "foo"
431})node",
432 Node::MiniReflectTypeTable()));
433
434 EXPECT_TRUE(
435 ChannelIsReadableOnNode(&good_channel.message(), &node.message()));
436 EXPECT_FALSE(
437 ChannelIsReadableOnNode(&bad_channel1.message(), &node.message()));
438 EXPECT_FALSE(
439 ChannelIsReadableOnNode(&bad_channel2.message(), &node.message()));
440}
441
James Kuszmaul24db2d32023-05-26 11:40:12 -0700442// Tests that our channel is forwarded helpers work as intended.
443TEST_F(ConfigurationTest, ChannelIsForwardedFromNode) {
444 FlatbufferDetachedBuffer<Channel> forwarded_channel(JsonToFlatbuffer(
445 R"channel({
446 "name": "/test",
447 "type": "aos.examples.Ping",
448 "source_node": "bar",
449 "destination_nodes": [
450 {
451 "name": "baz"
452 },
453 {
454 "name": "foo"
455 }
456 ]
457})channel",
458 Channel::MiniReflectTypeTable()));
459
460 FlatbufferDetachedBuffer<Channel> single_node_channel(JsonToFlatbuffer(
461 R"channel({
462 "name": "/test",
463 "type": "aos.examples.Ping"
464})channel",
465 Channel::MiniReflectTypeTable()));
466
467 FlatbufferDetachedBuffer<Channel> zero_length_vector_channel(JsonToFlatbuffer(
468 R"channel({
469 "name": "/test",
470 "type": "aos.examples.Ping",
471 "source_node": "bar",
472 "destination_nodes": [
473 ]
474})channel",
475 Channel::MiniReflectTypeTable()));
476
477 FlatbufferDetachedBuffer<Node> node(JsonToFlatbuffer(
478 R"node({
479 "name": "bar"
480})node",
481 Node::MiniReflectTypeTable()));
482
483 FlatbufferDetachedBuffer<Node> readable_node(JsonToFlatbuffer(
484 R"node({
485 "name": "foo"
486})node",
487 Node::MiniReflectTypeTable()));
488
489 EXPECT_TRUE(ChannelIsForwardedFromNode(&forwarded_channel.message(),
490 &node.message()));
491 EXPECT_FALSE(ChannelIsForwardedFromNode(&forwarded_channel.message(),
492 &readable_node.message()));
493 EXPECT_FALSE(
494 ChannelIsForwardedFromNode(&single_node_channel.message(), nullptr));
495 EXPECT_FALSE(ChannelIsForwardedFromNode(&zero_length_vector_channel.message(),
496 &node.message()));
497}
498
Austin Schuh719946b2019-12-28 14:51:01 -0800499// Tests that our node message is logged helpers work as intended.
500TEST_F(ConfigurationTest, ChannelMessageIsLoggedOnNode) {
501 FlatbufferDetachedBuffer<Channel> logged_on_self_channel(JsonToFlatbuffer(
502 R"channel({
503 "name": "/test",
504 "type": "aos.examples.Ping",
505 "source_node": "bar",
506 "destination_nodes": [
507 {
508 "name": "baz"
509 }
510 ]
511})channel",
512 Channel::MiniReflectTypeTable()));
Austin Schuh217a9782019-12-21 23:02:50 -0800513
Austin Schuh719946b2019-12-28 14:51:01 -0800514 FlatbufferDetachedBuffer<Channel> not_logged_channel(JsonToFlatbuffer(
515 R"channel({
516 "name": "/test",
517 "type": "aos.examples.Ping",
518 "source_node": "bar",
519 "logger": "NOT_LOGGED",
520 "destination_nodes": [
521 {
522 "name": "baz",
523 "timestamp_logger": "LOCAL_LOGGER"
524 }
525 ]
526})channel",
527 Channel::MiniReflectTypeTable()));
528
529 FlatbufferDetachedBuffer<Channel> logged_on_remote_channel(JsonToFlatbuffer(
530 R"channel({
531 "name": "/test",
532 "type": "aos.examples.Ping",
533 "source_node": "bar",
534 "logger": "REMOTE_LOGGER",
Austin Schuhda40e472020-03-28 15:15:29 -0700535 "logger_nodes": ["baz"],
Austin Schuh719946b2019-12-28 14:51:01 -0800536 "destination_nodes": [
537 {
538 "name": "baz"
539 }
540 ]
541})channel",
542 Channel::MiniReflectTypeTable()));
543
544 FlatbufferDetachedBuffer<Channel> logged_on_separate_logger_node_channel(
545 JsonToFlatbuffer(
546 R"channel({
547 "name": "/test",
548 "type": "aos.examples.Ping",
549 "source_node": "bar",
550 "logger": "REMOTE_LOGGER",
Austin Schuhda40e472020-03-28 15:15:29 -0700551 "logger_nodes": ["foo"],
Austin Schuh719946b2019-12-28 14:51:01 -0800552 "destination_nodes": [
553 {
554 "name": "baz"
555 }
556 ]
557})channel",
558 Channel::MiniReflectTypeTable()));
559
Ravago Jonescf453ab2020-05-06 21:14:53 -0700560 FlatbufferDetachedBuffer<Channel> logged_on_both_channel(JsonToFlatbuffer(
561 R"channel({
Austin Schuh719946b2019-12-28 14:51:01 -0800562 "name": "/test",
563 "type": "aos.examples.Ping",
564 "source_node": "bar",
565 "logger": "LOCAL_AND_REMOTE_LOGGER",
Austin Schuhda40e472020-03-28 15:15:29 -0700566 "logger_nodes": ["baz"],
Austin Schuh719946b2019-12-28 14:51:01 -0800567 "destination_nodes": [
568 {
569 "name": "baz"
570 }
571 ]
572})channel",
Ravago Jonescf453ab2020-05-06 21:14:53 -0700573 Channel::MiniReflectTypeTable()));
Austin Schuh719946b2019-12-28 14:51:01 -0800574
575 FlatbufferDetachedBuffer<Node> foo_node(JsonToFlatbuffer(
576 R"node({
577 "name": "foo"
578})node",
579 Node::MiniReflectTypeTable()));
580
581 FlatbufferDetachedBuffer<Node> bar_node(JsonToFlatbuffer(
582 R"node({
583 "name": "bar"
584})node",
585 Node::MiniReflectTypeTable()));
586
587 FlatbufferDetachedBuffer<Node> baz_node(JsonToFlatbuffer(
588 R"node({
589 "name": "baz"
590})node",
591 Node::MiniReflectTypeTable()));
592
593 // Local logger.
594 EXPECT_FALSE(ChannelMessageIsLoggedOnNode(&logged_on_self_channel.message(),
595 &foo_node.message()));
596 EXPECT_TRUE(ChannelMessageIsLoggedOnNode(&logged_on_self_channel.message(),
597 &bar_node.message()));
598 EXPECT_FALSE(ChannelMessageIsLoggedOnNode(&logged_on_self_channel.message(),
599 &baz_node.message()));
Austin Schuh48e94502021-06-18 18:35:53 -0700600 EXPECT_TRUE(
601 ChannelMessageIsLoggedOnNode(&logged_on_self_channel.message(), nullptr));
Austin Schuh719946b2019-12-28 14:51:01 -0800602
603 // No logger.
604 EXPECT_FALSE(ChannelMessageIsLoggedOnNode(&not_logged_channel.message(),
605 &foo_node.message()));
606 EXPECT_FALSE(ChannelMessageIsLoggedOnNode(&not_logged_channel.message(),
Ravago Jonescf453ab2020-05-06 21:14:53 -0700607 &bar_node.message()));
Austin Schuh719946b2019-12-28 14:51:01 -0800608 EXPECT_FALSE(ChannelMessageIsLoggedOnNode(&not_logged_channel.message(),
609 &baz_node.message()));
Austin Schuh48e94502021-06-18 18:35:53 -0700610 EXPECT_FALSE(
611 ChannelMessageIsLoggedOnNode(&not_logged_channel.message(), nullptr));
Austin Schuh719946b2019-12-28 14:51:01 -0800612
613 // Remote logger.
614 EXPECT_FALSE(ChannelMessageIsLoggedOnNode(&logged_on_remote_channel.message(),
615 &foo_node.message()));
616 EXPECT_FALSE(ChannelMessageIsLoggedOnNode(&logged_on_remote_channel.message(),
617 &bar_node.message()));
618 EXPECT_TRUE(ChannelMessageIsLoggedOnNode(&logged_on_remote_channel.message(),
619 &baz_node.message()));
620
621 // Separate logger.
622 EXPECT_TRUE(ChannelMessageIsLoggedOnNode(
623 &logged_on_separate_logger_node_channel.message(), &foo_node.message()));
624 EXPECT_FALSE(ChannelMessageIsLoggedOnNode(
625 &logged_on_separate_logger_node_channel.message(), &bar_node.message()));
626 EXPECT_FALSE(ChannelMessageIsLoggedOnNode(
627 &logged_on_separate_logger_node_channel.message(), &baz_node.message()));
628
629 // Logged in multiple places.
630 EXPECT_FALSE(ChannelMessageIsLoggedOnNode(&logged_on_both_channel.message(),
631 &foo_node.message()));
632 EXPECT_TRUE(ChannelMessageIsLoggedOnNode(&logged_on_both_channel.message(),
633 &bar_node.message()));
634 EXPECT_TRUE(ChannelMessageIsLoggedOnNode(&logged_on_both_channel.message(),
635 &baz_node.message()));
636}
637
Austin Schuh48e94502021-06-18 18:35:53 -0700638// Tests that our node message is logged helpers work as intended.
639TEST_F(ConfigurationDeathTest, ChannelMessageIsLoggedOnNode) {
640 FlatbufferDetachedBuffer<Channel> logged_on_both_channel(JsonToFlatbuffer(
641 R"channel({
642 "name": "/test",
643 "type": "aos.examples.Ping",
644 "source_node": "bar",
645 "logger": "LOCAL_AND_REMOTE_LOGGER",
646 "logger_nodes": ["baz"],
647 "destination_nodes": [
648 {
649 "name": "baz"
650 }
651 ]
652})channel",
653 Channel::MiniReflectTypeTable()));
654
655 FlatbufferDetachedBuffer<Channel> logged_on_separate_logger_node_channel(
656 JsonToFlatbuffer(
657 R"channel({
658 "name": "/test",
659 "type": "aos.examples.Ping",
660 "source_node": "bar",
661 "logger": "REMOTE_LOGGER",
662 "logger_nodes": ["foo"],
663 "destination_nodes": [
664 {
665 "name": "baz"
666 }
667 ]
668})channel",
669 Channel::MiniReflectTypeTable()));
670
671 EXPECT_DEATH(
672 {
673 ChannelMessageIsLoggedOnNode(&logged_on_both_channel.message(),
674 nullptr);
675 },
676 "Unsupported logging configuration in a single node world");
677 EXPECT_DEATH(
678 {
679 ChannelMessageIsLoggedOnNode(
680 &logged_on_separate_logger_node_channel.message(), nullptr);
681 },
682 "Unsupported logging configuration in a single node world");
683}
684
Austin Schuh719946b2019-12-28 14:51:01 -0800685// Tests that our forwarding timestamps are logged helpers work as intended.
686TEST_F(ConfigurationTest, ConnectionDeliveryTimeIsLoggedOnNode) {
687 FlatbufferDetachedBuffer<Channel> logged_on_self_channel(JsonToFlatbuffer(
688 R"channel({
689 "name": "/test",
690 "type": "aos.examples.Ping",
691 "source_node": "bar",
692 "logger": "REMOTE_LOGGER",
Austin Schuhda40e472020-03-28 15:15:29 -0700693 "logger_nodes": ["baz"],
Austin Schuh719946b2019-12-28 14:51:01 -0800694 "destination_nodes": [
695 {
696 "name": "baz"
697 }
698 ]
699})channel",
700 Channel::MiniReflectTypeTable()));
701
702 FlatbufferDetachedBuffer<Channel> not_logged_channel(JsonToFlatbuffer(
703 R"channel({
704 "name": "/test",
705 "type": "aos.examples.Ping",
706 "source_node": "bar",
707 "logger": "NOT_LOGGED",
708 "destination_nodes": [
709 {
710 "name": "baz",
711 "timestamp_logger": "NOT_LOGGED"
712 }
713 ]
714})channel",
715 Channel::MiniReflectTypeTable()));
716
717 FlatbufferDetachedBuffer<Channel> logged_on_remote_channel(JsonToFlatbuffer(
718 R"channel({
719 "name": "/test",
720 "type": "aos.examples.Ping",
721 "source_node": "bar",
722 "destination_nodes": [
723 {
724 "name": "baz",
725 "timestamp_logger": "REMOTE_LOGGER",
Austin Schuhda40e472020-03-28 15:15:29 -0700726 "timestamp_logger_nodes": ["bar"]
Austin Schuh719946b2019-12-28 14:51:01 -0800727 }
728 ]
729})channel",
730 Channel::MiniReflectTypeTable()));
731
732 FlatbufferDetachedBuffer<Channel> logged_on_separate_logger_node_channel(
733 JsonToFlatbuffer(
734 R"channel({
735 "name": "/test",
736 "type": "aos.examples.Ping",
737 "source_node": "bar",
738 "logger": "REMOTE_LOGGER",
Austin Schuhda40e472020-03-28 15:15:29 -0700739 "logger_nodes": ["foo"],
Austin Schuh719946b2019-12-28 14:51:01 -0800740 "destination_nodes": [
741 {
742 "name": "baz",
743 "timestamp_logger": "REMOTE_LOGGER",
Austin Schuhda40e472020-03-28 15:15:29 -0700744 "timestamp_logger_nodes": ["foo"]
Austin Schuh719946b2019-12-28 14:51:01 -0800745 }
746 ]
747})channel",
748 Channel::MiniReflectTypeTable()));
749
Ravago Jonescf453ab2020-05-06 21:14:53 -0700750 FlatbufferDetachedBuffer<Channel> logged_on_both_channel(JsonToFlatbuffer(
751 R"channel({
Austin Schuh719946b2019-12-28 14:51:01 -0800752 "name": "/test",
753 "type": "aos.examples.Ping",
754 "source_node": "bar",
755 "destination_nodes": [
756 {
757 "name": "baz",
758 "timestamp_logger": "LOCAL_AND_REMOTE_LOGGER",
Austin Schuhda40e472020-03-28 15:15:29 -0700759 "timestamp_logger_nodes": ["bar"]
Austin Schuh719946b2019-12-28 14:51:01 -0800760 }
761 ]
762})channel",
Ravago Jonescf453ab2020-05-06 21:14:53 -0700763 Channel::MiniReflectTypeTable()));
Austin Schuh719946b2019-12-28 14:51:01 -0800764
765 FlatbufferDetachedBuffer<Node> foo_node(JsonToFlatbuffer(
766 R"node({
767 "name": "foo"
768})node",
769 Node::MiniReflectTypeTable()));
770
771 FlatbufferDetachedBuffer<Node> bar_node(JsonToFlatbuffer(
772 R"node({
773 "name": "bar"
774})node",
775 Node::MiniReflectTypeTable()));
776
777 FlatbufferDetachedBuffer<Node> baz_node(JsonToFlatbuffer(
778 R"node({
779 "name": "baz"
780})node",
781 Node::MiniReflectTypeTable()));
782
783 // Local logger.
784 EXPECT_FALSE(ConnectionDeliveryTimeIsLoggedOnNode(
785 &logged_on_self_channel.message(), &baz_node.message(),
786 &foo_node.message()));
787 EXPECT_FALSE(ConnectionDeliveryTimeIsLoggedOnNode(
788 &logged_on_self_channel.message(), &baz_node.message(),
789 &bar_node.message()));
790 EXPECT_TRUE(ConnectionDeliveryTimeIsLoggedOnNode(
791 &logged_on_self_channel.message(), &baz_node.message(),
792 &baz_node.message()));
793
794 // No logger means.
795 EXPECT_FALSE(ConnectionDeliveryTimeIsLoggedOnNode(
796 &not_logged_channel.message(), &baz_node.message(), &foo_node.message()));
797 EXPECT_FALSE(ConnectionDeliveryTimeIsLoggedOnNode(
798 &not_logged_channel.message(), &baz_node.message(), &bar_node.message()));
799 EXPECT_FALSE(ConnectionDeliveryTimeIsLoggedOnNode(
800 &not_logged_channel.message(), &baz_node.message(), &baz_node.message()));
801
802 // Remote logger.
803 EXPECT_FALSE(ConnectionDeliveryTimeIsLoggedOnNode(
804 &logged_on_remote_channel.message(), &baz_node.message(),
805 &foo_node.message()));
806 EXPECT_TRUE(ConnectionDeliveryTimeIsLoggedOnNode(
807 &logged_on_remote_channel.message(), &baz_node.message(),
808 &bar_node.message()));
809 EXPECT_FALSE(ConnectionDeliveryTimeIsLoggedOnNode(
810 &logged_on_remote_channel.message(), &baz_node.message(),
811 &baz_node.message()));
812
813 // Separate logger.
814 EXPECT_TRUE(ConnectionDeliveryTimeIsLoggedOnNode(
815 &logged_on_separate_logger_node_channel.message(), &baz_node.message(),
816 &foo_node.message()));
817 EXPECT_FALSE(ConnectionDeliveryTimeIsLoggedOnNode(
818 &logged_on_separate_logger_node_channel.message(), &baz_node.message(),
819 &bar_node.message()));
820 EXPECT_FALSE(ConnectionDeliveryTimeIsLoggedOnNode(
821 &logged_on_separate_logger_node_channel.message(), &baz_node.message(),
822 &baz_node.message()));
823
824 // Logged on both the node and a remote node.
825 EXPECT_FALSE(ConnectionDeliveryTimeIsLoggedOnNode(
826 &logged_on_both_channel.message(), &baz_node.message(),
827 &foo_node.message()));
828 EXPECT_TRUE(ConnectionDeliveryTimeIsLoggedOnNode(
829 &logged_on_both_channel.message(), &baz_node.message(),
830 &bar_node.message()));
831 EXPECT_TRUE(ConnectionDeliveryTimeIsLoggedOnNode(
832 &logged_on_both_channel.message(), &baz_node.message(),
833 &baz_node.message()));
834}
Austin Schuhe84c3ed2019-12-14 15:29:48 -0800835
836// Tests that we can deduce source nodes from a multinode config.
837TEST_F(ConfigurationTest, SourceNodeNames) {
838 FlatbufferDetachedBuffer<Configuration> config =
Austin Schuh373f1762021-06-02 21:07:09 -0700839 ReadConfig(ArtifactPath("aos/testdata/config1_multinode.json"));
Austin Schuhe84c3ed2019-12-14 15:29:48 -0800840
841 // This is a bit simplistic in that it doesn't test deduplication, but it does
842 // exercise a lot of the logic.
843 EXPECT_THAT(
844 SourceNodeNames(&config.message(), config.message().nodes()->Get(0)),
845 ::testing::ElementsAreArray({"pi2"}));
846 EXPECT_THAT(
847 SourceNodeNames(&config.message(), config.message().nodes()->Get(1)),
848 ::testing::ElementsAreArray({"pi1"}));
849}
850
851// Tests that we can deduce destination nodes from a multinode config.
852TEST_F(ConfigurationTest, DestinationNodeNames) {
853 FlatbufferDetachedBuffer<Configuration> config =
Austin Schuh373f1762021-06-02 21:07:09 -0700854 ReadConfig(ArtifactPath("aos/testdata/config1_multinode.json"));
Austin Schuhe84c3ed2019-12-14 15:29:48 -0800855
856 // This is a bit simplistic in that it doesn't test deduplication, but it does
857 // exercise a lot of the logic.
858 EXPECT_THAT(
859 DestinationNodeNames(&config.message(), config.message().nodes()->Get(0)),
860 ::testing::ElementsAreArray({"pi2"}));
861 EXPECT_THAT(
862 DestinationNodeNames(&config.message(), config.message().nodes()->Get(1)),
863 ::testing::ElementsAreArray({"pi1"}));
864}
865
Austin Schuhc9e10ec2020-01-26 16:08:28 -0800866// Tests that we can pull out all the nodes.
867TEST_F(ConfigurationTest, GetNodes) {
868 {
869 FlatbufferDetachedBuffer<Configuration> config =
Austin Schuh373f1762021-06-02 21:07:09 -0700870 ReadConfig(ArtifactPath("aos/testdata/good_multinode.json"));
Austin Schuhc9e10ec2020-01-26 16:08:28 -0800871 const Node *pi1 = GetNode(&config.message(), "pi1");
872 const Node *pi2 = GetNode(&config.message(), "pi2");
873
874 EXPECT_THAT(GetNodes(&config.message()), ::testing::ElementsAre(pi1, pi2));
875 }
876
877 {
878 FlatbufferDetachedBuffer<Configuration> config =
Austin Schuh373f1762021-06-02 21:07:09 -0700879 ReadConfig(ArtifactPath("aos/testdata/config1.json"));
Austin Schuhc9e10ec2020-01-26 16:08:28 -0800880 EXPECT_THAT(GetNodes(&config.message()), ::testing::ElementsAre(nullptr));
881 }
882}
883
Austin Schuh65465332020-11-05 17:36:53 -0800884// Tests that we can pull out all the nodes with a tag.
885TEST_F(ConfigurationTest, GetNodesWithTag) {
886 {
887 FlatbufferDetachedBuffer<Configuration> config =
Austin Schuh373f1762021-06-02 21:07:09 -0700888 ReadConfig(ArtifactPath("aos/testdata/good_multinode.json"));
Austin Schuh65465332020-11-05 17:36:53 -0800889 const Node *pi1 = GetNode(&config.message(), "pi1");
890 const Node *pi2 = GetNode(&config.message(), "pi2");
891
892 EXPECT_THAT(GetNodesWithTag(&config.message(), "a"),
893 ::testing::ElementsAre(pi1));
894 EXPECT_THAT(GetNodesWithTag(&config.message(), "b"),
895 ::testing::ElementsAre(pi2));
896 EXPECT_THAT(GetNodesWithTag(&config.message(), "c"),
897 ::testing::ElementsAre(pi1, pi2));
898 }
899
900 {
901 FlatbufferDetachedBuffer<Configuration> config =
Austin Schuh373f1762021-06-02 21:07:09 -0700902 ReadConfig(ArtifactPath("aos/testdata/config1.json"));
Austin Schuh65465332020-11-05 17:36:53 -0800903 EXPECT_THAT(GetNodesWithTag(&config.message(), "arglfish"),
904 ::testing::ElementsAre(nullptr));
905 }
906}
907
Brian Silverman631b6262021-11-10 12:25:08 -0800908// Tests that we can check if a node has a tag.
909TEST_F(ConfigurationTest, NodeHasTag) {
910 {
911 FlatbufferDetachedBuffer<Configuration> config =
912 ReadConfig(ArtifactPath("aos/testdata/good_multinode.json"));
913 const Node *pi1 = GetNode(&config.message(), "pi1");
914 const Node *pi2 = GetNode(&config.message(), "pi2");
915
916 EXPECT_TRUE(NodeHasTag(pi1, "a"));
917 EXPECT_FALSE(NodeHasTag(pi2, "a"));
918 EXPECT_FALSE(NodeHasTag(pi1, "b"));
919 EXPECT_TRUE(NodeHasTag(pi2, "b"));
920 EXPECT_TRUE(NodeHasTag(pi1, "c"));
921 EXPECT_TRUE(NodeHasTag(pi2, "c"));
922 EXPECT_FALSE(NodeHasTag(pi1, "nope"));
923 EXPECT_FALSE(NodeHasTag(pi2, "nope"));
924 }
925
926 EXPECT_TRUE(NodeHasTag(nullptr, "arglfish"));
927}
928
Austin Schuhc9e10ec2020-01-26 16:08:28 -0800929// Tests that we can extract a node index from a config.
930TEST_F(ConfigurationTest, GetNodeIndex) {
931 FlatbufferDetachedBuffer<Configuration> config =
Austin Schuh373f1762021-06-02 21:07:09 -0700932 ReadConfig(ArtifactPath("aos/testdata/good_multinode.json"));
Austin Schuh04408fc2020-02-16 21:48:54 -0800933 FlatbufferDetachedBuffer<Configuration> config2 =
Austin Schuh373f1762021-06-02 21:07:09 -0700934 ReadConfig(ArtifactPath("aos/testdata/good_multinode.json"));
Austin Schuhc9e10ec2020-01-26 16:08:28 -0800935 const Node *pi1 = GetNode(&config.message(), "pi1");
936 const Node *pi2 = GetNode(&config.message(), "pi2");
937
Austin Schuh04408fc2020-02-16 21:48:54 -0800938 // Try the normal case.
Austin Schuhc9e10ec2020-01-26 16:08:28 -0800939 EXPECT_EQ(GetNodeIndex(&config.message(), pi1), 0);
940 EXPECT_EQ(GetNodeIndex(&config.message(), pi2), 1);
Austin Schuh04408fc2020-02-16 21:48:54 -0800941
942 // Now try if we have node pointers from a different message.
943 EXPECT_EQ(GetNodeIndex(&config2.message(), pi1), 0);
944 EXPECT_EQ(GetNodeIndex(&config2.message(), pi2), 1);
945
946 // And now try string names.
947 EXPECT_EQ(GetNodeIndex(&config2.message(), pi1->name()->string_view()), 0);
948 EXPECT_EQ(GetNodeIndex(&config2.message(), pi2->name()->string_view()), 1);
Austin Schuhc9e10ec2020-01-26 16:08:28 -0800949}
950
951// Tests that GetNodeOrDie handles both single and multi-node worlds and returns
952// valid nodes.
953TEST_F(ConfigurationDeathTest, GetNodeOrDie) {
954 FlatbufferDetachedBuffer<Configuration> config =
Austin Schuh373f1762021-06-02 21:07:09 -0700955 ReadConfig(ArtifactPath("aos/testdata/good_multinode.json"));
Austin Schuhc9e10ec2020-01-26 16:08:28 -0800956 FlatbufferDetachedBuffer<Configuration> config2 =
Austin Schuh373f1762021-06-02 21:07:09 -0700957 ReadConfig(ArtifactPath("aos/testdata/good_multinode.json"));
Austin Schuhc9e10ec2020-01-26 16:08:28 -0800958 {
959 // Simple case, nullptr -> nullptr
960 FlatbufferDetachedBuffer<Configuration> single_node_config =
Austin Schuh373f1762021-06-02 21:07:09 -0700961 ReadConfig(ArtifactPath("aos/testdata/config1.json"));
Austin Schuhc9e10ec2020-01-26 16:08:28 -0800962 EXPECT_EQ(nullptr, GetNodeOrDie(&single_node_config.message(), nullptr));
963
964 // Confirm that we die when a node is passed in.
965 EXPECT_DEATH(
966 {
967 GetNodeOrDie(&single_node_config.message(),
968 config.message().nodes()->Get(0));
969 },
970 "Provided a node in a single node world.");
971 }
972
973 const Node *pi1 = GetNode(&config.message(), "pi1");
974 // Now try a lookup using a node from a different instance of the config.
975 EXPECT_EQ(pi1,
976 GetNodeOrDie(&config.message(), config2.message().nodes()->Get(0)));
977}
978
Brian Silvermanaa2633f2020-02-17 21:04:14 -0800979TEST_F(ConfigurationTest, GetNodeFromHostname) {
980 FlatbufferDetachedBuffer<Configuration> config =
Austin Schuh373f1762021-06-02 21:07:09 -0700981 ReadConfig(ArtifactPath("aos/testdata/good_multinode.json"));
Austin Schuh6bdcc372024-06-27 14:49:11 -0700982 {
983 const Node *pi1 = GetNodeFromHostname(&config.message(), "raspberrypi");
984 ASSERT_TRUE(pi1 != nullptr);
985 EXPECT_EQ("pi1", pi1->name()->string_view());
986 }
987 {
988 const Node *pi2 = GetNodeFromHostname(&config.message(), "raspberrypi2");
989 ASSERT_TRUE(pi2 != nullptr);
990 EXPECT_EQ("pi2", pi2->name()->string_view());
991 }
Brian Silvermanaa2633f2020-02-17 21:04:14 -0800992 EXPECT_EQ(nullptr, GetNodeFromHostname(&config.message(), "raspberrypi3"));
993 EXPECT_EQ(nullptr, GetNodeFromHostname(&config.message(), "localhost"));
994 EXPECT_EQ(nullptr, GetNodeFromHostname(&config.message(), "3"));
995}
996
997TEST_F(ConfigurationTest, GetNodeFromHostnames) {
998 FlatbufferDetachedBuffer<Configuration> config =
Austin Schuh373f1762021-06-02 21:07:09 -0700999 ReadConfig(ArtifactPath("aos/testdata/good_multinode_hostnames.json"));
Austin Schuh6bdcc372024-06-27 14:49:11 -07001000 {
1001 const Node *pi1 = GetNodeFromHostname(&config.message(), "raspberrypi");
1002 ASSERT_TRUE(pi1 != nullptr);
1003 EXPECT_EQ("pi1", pi1->name()->string_view());
1004 }
1005 {
1006 const Node *pi2 = GetNodeFromHostname(&config.message(), "raspberrypi2");
1007 ASSERT_TRUE(pi2 != nullptr);
1008 EXPECT_EQ("pi2", pi2->name()->string_view());
1009 }
1010 {
1011 const Node *pi2 = GetNodeFromHostname(&config.message(), "raspberrypi3");
1012 ASSERT_TRUE(pi2 != nullptr);
1013 EXPECT_EQ("pi2", pi2->name()->string_view());
1014 }
1015 {
1016 const Node *pi2 = GetNodeFromHostname(&config.message(), "other");
1017 ASSERT_TRUE(pi2 != nullptr);
1018 EXPECT_EQ("pi2", pi2->name()->string_view());
1019 }
Brian Silvermanaa2633f2020-02-17 21:04:14 -08001020 EXPECT_EQ(nullptr, GetNodeFromHostname(&config.message(), "raspberrypi4"));
1021 EXPECT_EQ(nullptr, GetNodeFromHostname(&config.message(), "localhost"));
1022 EXPECT_EQ(nullptr, GetNodeFromHostname(&config.message(), "3"));
1023}
1024
Austin Schuhfc7b6a02021-07-12 21:19:07 -07001025// Tests that SourceNodeIndex reasonably handles a multi-node log file.
1026TEST_F(ConfigurationTest, SourceNodeIndex) {
1027 FlatbufferDetachedBuffer<Configuration> config =
1028 ReadConfig(ArtifactPath("aos/testdata/good_multinode.json"));
1029 std::vector<size_t> result = SourceNodeIndex(&config.message());
1030
1031 EXPECT_THAT(result, ::testing::ElementsAreArray({0, 1, 0, 0}));
1032}
1033
Austin Schuh1ccc3a12024-04-30 17:46:29 -07001034// Tests that SourceNode reasonably handles both single and multi-node configs.
1035TEST_F(ConfigurationTest, SourceNode) {
1036 {
1037 FlatbufferDetachedBuffer<Configuration> config_single_node =
1038 ReadConfig(ArtifactPath("aos/testdata/config1.json"));
1039 const Node *result =
1040 SourceNode(&config_single_node.message(),
1041 config_single_node.message().channels()->Get(0));
1042 EXPECT_EQ(result, nullptr);
1043 }
1044
1045 {
1046 FlatbufferDetachedBuffer<Configuration> config_multi_node =
1047 ReadConfig(ArtifactPath("aos/testdata/good_multinode.json"));
1048 size_t pi1_channels = 0;
1049 size_t pi2_channels = 0;
1050 for (const aos::Channel *channel :
1051 *config_multi_node.message().channels()) {
1052 const Node *result = SourceNode(&config_multi_node.message(), channel);
1053 if (channel->source_node()->string_view() == "pi1") {
1054 ++pi1_channels;
1055 EXPECT_EQ(result, config_multi_node.message().nodes()->Get(0));
1056 } else {
1057 ++pi2_channels;
1058 EXPECT_EQ(result, config_multi_node.message().nodes()->Get(1));
1059 }
1060 }
1061 EXPECT_GT(pi1_channels, 0u);
1062 EXPECT_GT(pi2_channels, 0u);
1063 }
1064}
1065
Austin Schuh5e95bd62021-10-11 18:40:22 -07001066// Tests that we reject invalid logging configurations.
1067TEST_F(ConfigurationDeathTest, InvalidLoggerConfig) {
1068 EXPECT_DEATH(
1069 {
Milind Upadhyay17098ba2022-04-15 22:18:50 -07001070 FlatbufferDetachedBuffer<Configuration> config = ReadConfig(
1071 ArtifactPath("aos/testdata/invalid_logging_configuration.json"));
Austin Schuh5e95bd62021-10-11 18:40:22 -07001072 },
1073 "Logging timestamps without data");
1074}
1075
Austin Schuha156fb22021-10-11 19:23:21 -07001076// Tests that we reject duplicate timestamp destination node configurations.
1077TEST_F(ConfigurationDeathTest, DuplicateTimestampDestinationNodes) {
1078 EXPECT_DEATH(
1079 {
1080 FlatbufferDetachedBuffer<Configuration> config = ReadConfig(
1081 ArtifactPath("aos/testdata/duplicate_destination_nodes.json"));
1082 },
1083 "Found duplicate timestamp_logger_nodes in");
1084}
1085
1086// Tests that we reject duplicate logger node configurations for a channel's
1087// data.
1088TEST_F(ConfigurationDeathTest, DuplicateLoggerNodes) {
1089 EXPECT_DEATH(
1090 {
1091 FlatbufferDetachedBuffer<Configuration> config = ReadConfig(
1092 ArtifactPath("aos/testdata/duplicate_logger_nodes.json"));
1093 },
1094 "Found duplicate logger_nodes in");
1095}
1096
Austin Schuhfb37c612022-08-11 15:24:51 -07001097// Tests that we properly compute the queue size for the provided duration.
1098TEST_F(ConfigurationTest, QueueSize) {
1099 EXPECT_EQ(QueueSize(100, chrono::seconds(2)), 200);
1100 EXPECT_EQ(QueueSize(200, chrono::seconds(2)), 400);
1101 EXPECT_EQ(QueueSize(100, chrono::seconds(6)), 600);
1102 EXPECT_EQ(QueueSize(100, chrono::milliseconds(10)), 1);
1103 EXPECT_EQ(QueueSize(100, chrono::milliseconds(10) - chrono::nanoseconds(1)),
1104 1);
1105 EXPECT_EQ(QueueSize(100, chrono::milliseconds(10) - chrono::nanoseconds(2)),
1106 1);
1107}
1108
1109// Tests that we compute scratch buffer size correctly too.
1110TEST_F(ConfigurationTest, QueueScratchBufferSize) {
1111 const aos::FlatbufferDetachedBuffer<Channel> channel =
1112 JsonToFlatbuffer<Channel>(
1113 "{ \"name\": \"/foo\", \"type\": \".aos.bar\", \"num_readers\": 5, "
1114 "\"num_senders\": 10 }");
Austin Schuhfb37c612022-08-11 15:24:51 -07001115 EXPECT_EQ(QueueScratchBufferSize(&channel.message()), 15);
1116}
1117
Nathan Leong307c9692022-10-08 15:25:03 -07001118// Tests that GetSchema returns schema of specified type
1119TEST_F(ConfigurationTest, GetSchema) {
1120 FlatbufferDetachedBuffer<Configuration> config =
1121 ReadConfig(ArtifactPath("aos/events/pingpong_config.json"));
1122 FlatbufferVector<reflection::Schema> expected_schema =
1123 FileToFlatbuffer<reflection::Schema>(
1124 ArtifactPath("aos/events/ping.bfbs"));
1125 EXPECT_EQ(FlatbufferToJson(GetSchema(&config.message(), "aos.examples.Ping")),
1126 FlatbufferToJson(expected_schema));
1127 EXPECT_EQ(GetSchema(&config.message(), "invalid_name"), nullptr);
1128}
1129
1130// Tests that GetSchema template returns schema of specified type
1131TEST_F(ConfigurationTest, GetSchemaTemplate) {
1132 FlatbufferDetachedBuffer<Configuration> config =
1133 ReadConfig(ArtifactPath("aos/events/pingpong_config.json"));
1134 FlatbufferVector<reflection::Schema> expected_schema =
1135 FileToFlatbuffer<reflection::Schema>(
1136 ArtifactPath("aos/events/ping.bfbs"));
1137 EXPECT_EQ(FlatbufferToJson(GetSchema<aos::examples::Ping>(&config.message())),
1138 FlatbufferToJson(expected_schema));
1139}
1140
1141// Tests that GetSchemaDetachedBuffer returns detached buffer of specified type
1142TEST_F(ConfigurationTest, GetSchemaDetachedBuffer) {
1143 FlatbufferDetachedBuffer<Configuration> config =
1144 ReadConfig(ArtifactPath("aos/events/pingpong_config.json"));
1145 FlatbufferVector<reflection::Schema> expected_schema =
1146 FileToFlatbuffer<reflection::Schema>(
1147 ArtifactPath("aos/events/ping.bfbs"));
1148 EXPECT_EQ(FlatbufferToJson(
1149 GetSchemaDetachedBuffer(&config.message(), "aos.examples.Ping")
1150 .value()),
1151 FlatbufferToJson(expected_schema));
1152 EXPECT_EQ(GetSchemaDetachedBuffer(&config.message(), "invalid_name"),
1153 std::nullopt);
1154}
1155
James Kuszmaul741a4d02023-01-05 14:59:21 -08001156// Tests that we can use a utility to add individual channels to a single-node
1157// config.
1158TEST_F(ConfigurationTest, AddChannelToConfigSingleNode) {
1159 FlatbufferDetachedBuffer<Configuration> base_config =
1160 ReadConfig(ArtifactPath("aos/testdata/config1.json"));
1161
1162 FlatbufferVector<reflection::Schema> schema =
1163 FileToFlatbuffer<reflection::Schema>(
1164 ArtifactPath("aos/events/ping.bfbs"));
1165
1166 FlatbufferDetachedBuffer<Configuration> new_config =
1167 AddChannelToConfiguration(&base_config.message(), "/new", schema);
1168
1169 ASSERT_EQ(new_config.message().channels()->size(),
1170 base_config.message().channels()->size() + 1);
1171
1172 const Channel *channel =
1173 GetChannel(new_config, "/new", "aos.examples.Ping", "", nullptr);
1174 ASSERT_TRUE(channel != nullptr);
1175 ASSERT_TRUE(channel->has_schema());
1176 // Check that we don't populate channel settings that we don't override the
1177 // defaults of.
1178 ASSERT_FALSE(channel->has_frequency());
1179}
1180
1181// Tests that we can use a utility to add individual channels to a multi-node
1182// config.
1183TEST_F(ConfigurationTest, AddChannelToConfigMultiNode) {
1184 FlatbufferDetachedBuffer<Configuration> base_config =
1185 ReadConfig(ArtifactPath("aos/testdata/good_multinode.json"));
1186
1187 FlatbufferVector<reflection::Schema> schema =
1188 FileToFlatbuffer<reflection::Schema>(
1189 ArtifactPath("aos/events/ping.bfbs"));
1190
1191 aos::ChannelT channel_overrides;
1192 channel_overrides.frequency = 971;
1193 FlatbufferDetachedBuffer<Configuration> new_config =
1194 AddChannelToConfiguration(&base_config.message(), "/new", schema,
1195 GetNode(&base_config.message(), "pi1"),
1196 channel_overrides);
1197
1198 ASSERT_EQ(new_config.message().channels()->size(),
1199 base_config.message().channels()->size() + 1);
1200
1201 const Channel *channel =
1202 GetChannel(new_config, "/new", "aos.examples.Ping", "", nullptr);
1203 ASSERT_TRUE(channel != nullptr);
1204 ASSERT_TRUE(channel->has_schema());
1205 ASSERT_TRUE(channel->has_source_node());
1206 ASSERT_EQ("pi1", channel->source_node()->string_view());
1207 ASSERT_EQ(971, channel->frequency());
1208}
1209
Maxwell Gumley8c1b87f2024-02-13 17:54:52 -07001210// Create a new configuration with the specified channel removed.
1211// Initially there must be exactly one channel in the base_config that matches
1212// the criteria. Check to make sure the new configuration has one less channel,
1213// and that channel is the specified channel.
1214void TestGetPartialConfiguration(const Configuration &base_config,
1215 std::string_view test_channel_name,
1216 std::string_view test_channel_type) {
1217 const Channel *channel_from_base_config = GetChannel(
1218 &base_config, test_channel_name, test_channel_type, "", nullptr);
1219 ASSERT_TRUE(channel_from_base_config != nullptr);
1220
1221 const FlatbufferDetachedBuffer<Configuration> new_config =
1222 configuration::GetPartialConfiguration(
1223 base_config,
1224 // should_include_channel function
1225 [test_channel_name, test_channel_type](const Channel &channel) {
1226 if (channel.name()->string_view() == test_channel_name &&
1227 channel.type()->string_view() == test_channel_type) {
1228 LOG(INFO) << "Omitting channel from save_log, channel: "
1229 << channel.name()->string_view() << ", "
1230 << channel.type()->string_view();
1231 return false;
1232 }
1233 return true;
1234 });
1235
1236 EXPECT_EQ(new_config.message().channels()->size(),
1237 base_config.channels()->size() - 1);
1238
1239 channel_from_base_config = GetChannel(&base_config, test_channel_name,
1240 test_channel_type, "", nullptr);
1241 EXPECT_TRUE(channel_from_base_config != nullptr);
1242
1243 const Channel *channel_from_new_config =
1244 GetChannel(new_config, test_channel_name, test_channel_type, "", nullptr);
1245 EXPECT_TRUE(channel_from_new_config == nullptr);
1246}
1247
1248// Tests that we can use a utility to remove individual channels from a
1249// single-node config.
1250TEST_F(ConfigurationTest, RemoveChannelsFromConfigSingleNode) {
1251 FlatbufferDetachedBuffer<Configuration> base_config =
1252 ReadConfig(ArtifactPath("aos/testdata/config1.json"));
1253
1254 constexpr std::string_view test_channel_name = "/foo2";
1255 constexpr std::string_view test_channel_type = ".aos.bar";
1256
1257 TestGetPartialConfiguration(base_config.message(), test_channel_name,
1258 test_channel_type);
1259}
1260
1261// Tests that we can use a utility to remove individual channels from a
1262// multi-node config.
1263TEST_F(ConfigurationTest, RemoveChannelsFromConfigMultiNode) {
1264 FlatbufferDetachedBuffer<Configuration> base_config =
1265 ReadConfig(ArtifactPath("aos/testdata/good_multinode.json"));
1266
1267 constexpr std::string_view test_channel_name = "/batman";
1268 constexpr std::string_view test_channel_type = ".aos.baz";
1269
1270 TestGetPartialConfiguration(base_config.message(), test_channel_name,
1271 test_channel_type);
1272}
1273
Maxwell Gumleyf84aca32024-05-10 13:51:59 -06001274// Test fixture for testing IsNodeFromConfiguration.
1275// Initializes multiple configurations which share the same node names.
1276// Use IsNodeFromConfiguration to check if a node is in a configuration.
1277class IsNodeFromConfigurationFixtureTest : public ConfigurationTest {
1278 protected:
1279 // Use unique_ptr for deferred initialization
1280 std::unique_ptr<FlatbufferDetachedBuffer<Configuration>> config1;
1281 std::unique_ptr<FlatbufferDetachedBuffer<Configuration>> config2;
1282 const Node *node1_config1;
1283 const Node *node2_config1;
1284 const Node *node1_config2;
1285 const Node *node2_config2;
1286
1287 IsNodeFromConfigurationFixtureTest() {
1288 // Initialize configurations here
1289 config1 = std::make_unique<FlatbufferDetachedBuffer<Configuration>>(
1290 JsonToFlatbuffer(R"config({
1291 "nodes": [
1292 {"name": "node1"},
1293 {"name": "node2"}
1294 ]
1295 })config",
1296 Configuration::MiniReflectTypeTable()));
1297
1298 config2 = std::make_unique<FlatbufferDetachedBuffer<Configuration>>(
1299 JsonToFlatbuffer(R"config({
1300 "nodes": [
1301 {"name": "node1"},
1302 {"name": "node2"}
1303 ]
1304 })config",
1305 Configuration::MiniReflectTypeTable()));
1306
1307 // Initialize nodes after configuration initialization
1308 node1_config1 = config1->message().nodes()->Get(0);
1309 node2_config1 = config1->message().nodes()->Get(1);
1310 node1_config2 = config2->message().nodes()->Get(0);
1311 node2_config2 = config2->message().nodes()->Get(1);
1312 }
1313
1314 void SetUp() override {
1315 ConfigurationTest::SetUp();
1316 // Any additional setup can go here.
1317 }
1318};
1319
1320// Test case when node exists in the configuration.
1321TEST_F(IsNodeFromConfigurationFixtureTest, NodeExistsInConfiguration) {
1322 EXPECT_TRUE(IsNodeFromConfiguration(&config1->message(), node1_config1));
1323 EXPECT_TRUE(IsNodeFromConfiguration(&config1->message(), node2_config1));
1324}
1325
1326// Test case when node does not exist in the configuration.
1327TEST_F(IsNodeFromConfigurationFixtureTest, NodeDoesNotExistsInConfiguration) {
1328 EXPECT_FALSE(IsNodeFromConfiguration(&config1->message(), node1_config2));
1329 EXPECT_FALSE(IsNodeFromConfiguration(&config1->message(), node2_config2));
1330}
1331
1332// Test case for nodes with same names but from different configurations.
1333TEST_F(IsNodeFromConfigurationFixtureTest, SameNameDifferentConfiguration) {
1334 EXPECT_FALSE(IsNodeFromConfiguration(&config1->message(), node1_config2));
1335 EXPECT_FALSE(IsNodeFromConfiguration(&config1->message(), node2_config2));
1336 EXPECT_FALSE(IsNodeFromConfiguration(&config2->message(), node1_config1));
1337 EXPECT_FALSE(IsNodeFromConfiguration(&config2->message(), node2_config1));
1338}
1339
1340// Test case for null pointers.
1341TEST_F(IsNodeFromConfigurationFixtureTest, NullPointers) {
1342 EXPECT_FALSE(IsNodeFromConfiguration(nullptr, nullptr));
1343 EXPECT_FALSE(IsNodeFromConfiguration(&config1->message(), nullptr));
1344 EXPECT_FALSE(IsNodeFromConfiguration(nullptr, node1_config1));
1345}
1346
1347// Tests that SourceNode reasonably handles both single and multi-node configs.
1348TEST(IsNodeFromConfigurationTest, SingleNode) {
1349 FlatbufferDetachedBuffer<Configuration> config_single_node =
1350 ReadConfig(ArtifactPath("aos/testdata/config1.json"));
1351 EXPECT_TRUE(IsNodeFromConfiguration(&config_single_node.message(), nullptr));
1352}
1353
Austin Schuh605d5ff2024-05-10 15:59:54 -07001354// Tests that we can use a utility to remove individual channels from a
1355// multi-node config.
1356TEST_F(ConfigurationTest, MultinodeMerge) {
1357 FlatbufferDetachedBuffer<Configuration> config =
1358 ReadConfig(ArtifactPath("aos/testdata/multinode_merge.json"));
1359
1360 EXPECT_EQ(
1361 absl::StripSuffix(util::ReadFileToStringOrDie(ArtifactPath(
1362 "aos/testdata/multinode_merge_expected.json")),
1363 "\n"),
1364 FlatbufferToJson(config, {.multi_line = true}));
1365}
1366
Stephan Pleinesf63bde82024-01-13 15:59:33 -08001367} // namespace aos::configuration::testing