aboutsummaryrefslogtreecommitdiff
path: root/lldb/unittests/Core/PluginManagerTest.cpp
blob: c7a56b28a42fc67ccbf7c0ef15bfb52e5b0678e3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471

#include "lldb/Core/PluginManager.h"

#include "gtest/gtest.h"

using namespace lldb;
using namespace lldb_private;

// Mock system runtime plugin create functions.
// Make them all return different values to avoid the ICF optimization
// from combining them into the same function. The values returned
// are not valid SystemRuntime pointers, but they are unique and
// sufficient for testing.
SystemRuntime *CreateSystemRuntimePluginA(Process *process) {
  return (SystemRuntime *)0x1;
}

SystemRuntime *CreateSystemRuntimePluginB(Process *process) {
  return (SystemRuntime *)0x2;
}

SystemRuntime *CreateSystemRuntimePluginC(Process *process) {
  return (SystemRuntime *)0x3;
}

// Test class for testing the PluginManager.
// The PluginManager modifies global state when registering new plugins. This
// class is intended to undo those modifications in the destructor to give each
// test a clean slate with no registered plugins at the start of a test.
class PluginManagerTest : public testing::Test {
public:
  // Remove any pre-registered plugins so we have a known starting point.
  static void SetUpTestSuite() { RemoveAllRegisteredSystemRuntimePlugins(); }

  // Add mock system runtime plugins for testing.
  void RegisterMockSystemRuntimePlugins() {
    // Make sure the create functions all have different addresses.
    ASSERT_NE(CreateSystemRuntimePluginA, CreateSystemRuntimePluginB);
    ASSERT_NE(CreateSystemRuntimePluginB, CreateSystemRuntimePluginC);

    ASSERT_TRUE(PluginManager::RegisterPlugin("a", "test instance A",
                                              CreateSystemRuntimePluginA));
    ASSERT_TRUE(PluginManager::RegisterPlugin("b", "test instance B",
                                              CreateSystemRuntimePluginB));
    ASSERT_TRUE(PluginManager::RegisterPlugin("c", "test instance C",
                                              CreateSystemRuntimePluginC));
  }

  // Remove any plugins added during the tests.
  virtual ~PluginManagerTest() override {
    RemoveAllRegisteredSystemRuntimePlugins();
  }

protected:
  std::vector<SystemRuntimeCreateInstance> m_system_runtime_plugins;

  static void RemoveAllRegisteredSystemRuntimePlugins() {
    // Enable all currently registered plugins so we can get a handle to
    // their create callbacks in the loop below. Only enabled plugins
    // are returned from the PluginManager Get*CreateCallbackAtIndex apis.
    for (const RegisteredPluginInfo &PluginInfo :
         PluginManager::GetSystemRuntimePluginInfo()) {
      PluginManager::SetSystemRuntimePluginEnabled(PluginInfo.name, true);
    }

    // Get a handle to the create call backs for all the registered plugins.
    std::vector<SystemRuntimeCreateInstance> registered_plugin_callbacks;
    SystemRuntimeCreateInstance create_callback = nullptr;
    for (uint32_t idx = 0;
         (create_callback =
              PluginManager::GetSystemRuntimeCreateCallbackAtIndex(idx)) !=
         nullptr;
         ++idx) {
      registered_plugin_callbacks.push_back((create_callback));
    }

    // Remove all currently registered plugins.
    for (SystemRuntimeCreateInstance create_callback :
         registered_plugin_callbacks) {
      PluginManager::UnregisterPlugin(create_callback);
    }
  }
};

// Test basic register functionality.
TEST_F(PluginManagerTest, RegisterSystemRuntimePlugin) {
  RegisterMockSystemRuntimePlugins();

  ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(0),
            CreateSystemRuntimePluginA);

  ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(1),
            CreateSystemRuntimePluginB);

  ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(2),
            CreateSystemRuntimePluginC);

  ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(3), nullptr);
}

// Test basic un-register functionality.
TEST_F(PluginManagerTest, UnRegisterSystemRuntimePlugin) {
  RegisterMockSystemRuntimePlugins();

  ASSERT_TRUE(PluginManager::UnregisterPlugin(CreateSystemRuntimePluginB));

  ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(0),
            CreateSystemRuntimePluginA);

  ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(1),
            CreateSystemRuntimePluginC);

  ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(2), nullptr);
}

// Test registered plugin info functionality.
TEST_F(PluginManagerTest, SystemRuntimePluginInfo) {
  RegisterMockSystemRuntimePlugins();

  std::vector<RegisteredPluginInfo> plugin_info =
      PluginManager::GetSystemRuntimePluginInfo();
  ASSERT_EQ(plugin_info.size(), 3u);
  ASSERT_EQ(plugin_info[0].name, "a");
  ASSERT_EQ(plugin_info[0].description, "test instance A");
  ASSERT_EQ(plugin_info[0].enabled, true);
  ASSERT_EQ(plugin_info[1].name, "b");
  ASSERT_EQ(plugin_info[1].description, "test instance B");
  ASSERT_EQ(plugin_info[1].enabled, true);
  ASSERT_EQ(plugin_info[2].name, "c");
  ASSERT_EQ(plugin_info[2].description, "test instance C");
  ASSERT_EQ(plugin_info[2].enabled, true);
}

// Test basic un-register functionality.
TEST_F(PluginManagerTest, UnRegisterSystemRuntimePluginInfo) {
  RegisterMockSystemRuntimePlugins();

  // Initial plugin info has all three registered plugins.
  std::vector<RegisteredPluginInfo> plugin_info =
      PluginManager::GetSystemRuntimePluginInfo();
  ASSERT_EQ(plugin_info.size(), 3u);

  ASSERT_TRUE(PluginManager::UnregisterPlugin(CreateSystemRuntimePluginB));

  // After un-registering a plugin it should be removed from plugin info.
  plugin_info = PluginManager::GetSystemRuntimePluginInfo();
  ASSERT_EQ(plugin_info.size(), 2u);
  ASSERT_EQ(plugin_info[0].name, "a");
  ASSERT_EQ(plugin_info[0].enabled, true);
  ASSERT_EQ(plugin_info[1].name, "c");
  ASSERT_EQ(plugin_info[1].enabled, true);
}

// Test plugin disable functionality.
TEST_F(PluginManagerTest, SystemRuntimePluginDisable) {
  RegisterMockSystemRuntimePlugins();

  // Disable plugin should succeed.
  ASSERT_TRUE(PluginManager::SetSystemRuntimePluginEnabled("b", false));

  // Disabling a plugin does not remove it from plugin info.
  std::vector<RegisteredPluginInfo> plugin_info =
      PluginManager::GetSystemRuntimePluginInfo();
  ASSERT_EQ(plugin_info.size(), 3u);
  ASSERT_EQ(plugin_info[0].name, "a");
  ASSERT_EQ(plugin_info[0].enabled, true);
  ASSERT_EQ(plugin_info[1].name, "b");
  ASSERT_EQ(plugin_info[1].enabled, false);
  ASSERT_EQ(plugin_info[2].name, "c");
  ASSERT_EQ(plugin_info[2].enabled, true);

  // Disabling a plugin does remove it from available plugins.
  ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(0),
            CreateSystemRuntimePluginA);
  ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(1),
            CreateSystemRuntimePluginC);
  ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(2), nullptr);
}

// Test plugin disable and enable functionality.
TEST_F(PluginManagerTest, SystemRuntimePluginDisableThenEnable) {
  RegisterMockSystemRuntimePlugins();

  // Initially plugin b is available in slot 1.
  ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(1),
            CreateSystemRuntimePluginB);

  // Disabling it will remove it from available plugins.
  ASSERT_TRUE(PluginManager::SetSystemRuntimePluginEnabled("b", false));
  ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(0),
            CreateSystemRuntimePluginA);
  ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(1),
            CreateSystemRuntimePluginC);

  // We can re-enable the plugin later and it should go back to the original
  // slot.
  ASSERT_TRUE(PluginManager::SetSystemRuntimePluginEnabled("b", true));
  ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(0),
            CreateSystemRuntimePluginA);
  ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(1),
            CreateSystemRuntimePluginB);
  ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(2),
            CreateSystemRuntimePluginC);

  // And show up in the plugin info correctly.
  std::vector<RegisteredPluginInfo> plugin_info =
      PluginManager::GetSystemRuntimePluginInfo();
  ASSERT_EQ(plugin_info.size(), 3u);
  ASSERT_EQ(plugin_info[0].name, "a");
  ASSERT_EQ(plugin_info[0].enabled, true);
  ASSERT_EQ(plugin_info[1].name, "b");
  ASSERT_EQ(plugin_info[1].enabled, true);
  ASSERT_EQ(plugin_info[2].name, "c");
  ASSERT_EQ(plugin_info[2].enabled, true);
}

// Test calling disable on an already disabled plugin is ok.
TEST_F(PluginManagerTest, SystemRuntimePluginDisableDisabled) {
  RegisterMockSystemRuntimePlugins();

  // Initial call to disable the plugin should succeed.
  ASSERT_TRUE(PluginManager::SetSystemRuntimePluginEnabled("b", false));

  // The second call should also succeed because the plugin is already disabled.
  ASSERT_TRUE(PluginManager::SetSystemRuntimePluginEnabled("b", false));

  // The call to re-enable the plugin should succeed.
  ASSERT_TRUE(PluginManager::SetSystemRuntimePluginEnabled("b", true));

  // The second call should also succeed since the plugin is already enabled.
  ASSERT_TRUE(PluginManager::SetSystemRuntimePluginEnabled("b", true));
}

// Test calling disable on an already disabled plugin is ok.
TEST_F(PluginManagerTest, SystemRuntimePluginDisableNonExistent) {
  RegisterMockSystemRuntimePlugins();

  // Both enable and disable should return false for a non-existent plugin.
  ASSERT_FALSE(
      PluginManager::SetSystemRuntimePluginEnabled("does_not_exist", true));
  ASSERT_FALSE(
      PluginManager::SetSystemRuntimePluginEnabled("does_not_exist", false));
}

// Test disabling all plugins and then re-enabling them in a different
// order will restore the original plugin order.
TEST_F(PluginManagerTest, SystemRuntimePluginDisableAll) {
  RegisterMockSystemRuntimePlugins();

  // Validate initial state of registered plugins.
  ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(0),
            CreateSystemRuntimePluginA);
  ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(1),
            CreateSystemRuntimePluginB);
  ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(2),
            CreateSystemRuntimePluginC);

  // Disable all the active plugins.
  ASSERT_TRUE(PluginManager::SetSystemRuntimePluginEnabled("a", false));
  ASSERT_TRUE(PluginManager::SetSystemRuntimePluginEnabled("b", false));
  ASSERT_TRUE(PluginManager::SetSystemRuntimePluginEnabled("c", false));

  // Should have no active plugins.
  ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(0), nullptr);
  ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(1), nullptr);
  ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(2), nullptr);

  // And show up in the plugin info correctly.
  std::vector<RegisteredPluginInfo> plugin_info =
      PluginManager::GetSystemRuntimePluginInfo();
  ASSERT_EQ(plugin_info.size(), 3u);
  ASSERT_EQ(plugin_info[0].name, "a");
  ASSERT_EQ(plugin_info[0].enabled, false);
  ASSERT_EQ(plugin_info[1].name, "b");
  ASSERT_EQ(plugin_info[1].enabled, false);
  ASSERT_EQ(plugin_info[2].name, "c");
  ASSERT_EQ(plugin_info[2].enabled, false);

  // Enable plugins in reverse order and validate expected indicies.
  // They should show up in the original plugin order.
  ASSERT_TRUE(PluginManager::SetSystemRuntimePluginEnabled("c", true));
  ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(0),
            CreateSystemRuntimePluginC);

  ASSERT_TRUE(PluginManager::SetSystemRuntimePluginEnabled("a", true));
  ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(0),
            CreateSystemRuntimePluginA);
  ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(1),
            CreateSystemRuntimePluginC);

  ASSERT_TRUE(PluginManager::SetSystemRuntimePluginEnabled("b", true));
  ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(0),
            CreateSystemRuntimePluginA);
  ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(1),
            CreateSystemRuntimePluginB);
  ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(2),
            CreateSystemRuntimePluginC);
}

// Test un-registering a disabled plugin works.
TEST_F(PluginManagerTest, UnRegisterDisabledSystemRuntimePlugin) {
  RegisterMockSystemRuntimePlugins();

  // Initial plugin info has all three registered plugins.
  std::vector<RegisteredPluginInfo> plugin_info =
      PluginManager::GetSystemRuntimePluginInfo();
  ASSERT_EQ(plugin_info.size(), 3u);

  // First disable a plugin, then unregister it. Both should succeed.
  ASSERT_TRUE(PluginManager::SetSystemRuntimePluginEnabled("b", false));
  ASSERT_TRUE(PluginManager::UnregisterPlugin(CreateSystemRuntimePluginB));

  // After un-registering a plugin it should be removed from plugin info.
  plugin_info = PluginManager::GetSystemRuntimePluginInfo();
  ASSERT_EQ(plugin_info.size(), 2u);
  ASSERT_EQ(plugin_info[0].name, "a");
  ASSERT_EQ(plugin_info[0].enabled, true);
  ASSERT_EQ(plugin_info[1].name, "c");
  ASSERT_EQ(plugin_info[1].enabled, true);
}

// Test un-registering and then re-registering a plugin will change the order of
// loaded plugins.
TEST_F(PluginManagerTest, UnRegisterSystemRuntimePluginChangesOrder) {
  RegisterMockSystemRuntimePlugins();

  std::vector<RegisteredPluginInfo> plugin_info =
      PluginManager::GetSystemRuntimePluginInfo();
  ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(0),
            CreateSystemRuntimePluginA);
  ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(1),
            CreateSystemRuntimePluginB);
  ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(2),
            CreateSystemRuntimePluginC);

  ASSERT_EQ(plugin_info.size(), 3u);
  ASSERT_EQ(plugin_info[0].name, "a");
  ASSERT_EQ(plugin_info[1].name, "b");
  ASSERT_EQ(plugin_info[2].name, "c");

  // Unregister and then registering a plugin puts it at the end of the order
  // list.
  ASSERT_TRUE(PluginManager::UnregisterPlugin(CreateSystemRuntimePluginB));
  ASSERT_TRUE(PluginManager::RegisterPlugin("b", "New test instance B",
                                            CreateSystemRuntimePluginB));

  // Check the callback indices match as expected.
  plugin_info = PluginManager::GetSystemRuntimePluginInfo();
  ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(0),
            CreateSystemRuntimePluginA);
  ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(1),
            CreateSystemRuntimePluginC);
  ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(2),
            CreateSystemRuntimePluginB);

  // And plugin info should match as well.
  ASSERT_EQ(plugin_info.size(), 3u);
  ASSERT_EQ(plugin_info[0].name, "a");
  ASSERT_EQ(plugin_info[1].name, "c");
  ASSERT_EQ(plugin_info[2].name, "b");
  ASSERT_EQ(plugin_info[2].description, "New test instance B");

  // Disabling and re-enabling the "c" plugin should slot it back
  // into the middle of the order. Originally it was last, but after
  // un-registering and re-registering "b" it should now stay in
  // the middle of the order.
  ASSERT_TRUE(PluginManager::SetSystemRuntimePluginEnabled("c", false));
  ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(0),
            CreateSystemRuntimePluginA);
  ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(1),
            CreateSystemRuntimePluginB);

  // And re-enabling
  ASSERT_TRUE(PluginManager::SetSystemRuntimePluginEnabled("c", true));
  ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(0),
            CreateSystemRuntimePluginA);
  ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(1),
            CreateSystemRuntimePluginC);
  ASSERT_EQ(PluginManager::GetSystemRuntimeCreateCallbackAtIndex(2),
            CreateSystemRuntimePluginB);
}

TEST_F(PluginManagerTest, MatchPluginName) {
  PluginNamespace Foo{"foo", nullptr, nullptr};
  RegisteredPluginInfo Bar{"bar", "bar plugin ", true};
  RegisteredPluginInfo Baz{"baz", "baz plugin ", true};

  // Empty pattern matches everything.
  ASSERT_TRUE(PluginManager::MatchPluginName("", Foo, Bar));

  // Plugin namespace matches all plugins in that namespace.
  ASSERT_TRUE(PluginManager::MatchPluginName("foo", Foo, Bar));
  ASSERT_TRUE(PluginManager::MatchPluginName("foo", Foo, Baz));

  // Fully qualified plugin name matches only that plugin.
  ASSERT_TRUE(PluginManager::MatchPluginName("foo.bar", Foo, Bar));
  ASSERT_FALSE(PluginManager::MatchPluginName("foo.baz", Foo, Bar));

  // Prefix match should not match.
  ASSERT_FALSE(PluginManager::MatchPluginName("f", Foo, Bar));
  ASSERT_FALSE(PluginManager::MatchPluginName("foo.", Foo, Bar));
  ASSERT_FALSE(PluginManager::MatchPluginName("foo.ba", Foo, Bar));
}

TEST_F(PluginManagerTest, JsonFormat) {
  RegisterMockSystemRuntimePlugins();

  // We expect the following JSON output:
  // {
  //   "system-runtime": [
  //     {
  //       "enabled": true,
  //       "name": "a"
  //     },
  //     {
  //       "enabled": true,
  //       "name": "b"
  //     },
  //     {
  //       "enabled": true,
  //       "name": "c"
  //     }
  //   ]
  // }
  llvm::json::Object obj = PluginManager::GetJSON();

  // We should have a "system-runtime" array in the top-level object.
  llvm::json::Array *maybe_array = obj.getArray("system-runtime");
  ASSERT_TRUE(maybe_array != nullptr);
  auto &array = *maybe_array;
  ASSERT_EQ(array.size(), 3u);

  // Check plugin "a" info.
  ASSERT_TRUE(array[0].getAsObject() != nullptr);
  ASSERT_TRUE(array[0].getAsObject()->getString("name") == "a");
  ASSERT_TRUE(array[0].getAsObject()->getBoolean("enabled") == true);

  // Check plugin "b" info.
  ASSERT_TRUE(array[1].getAsObject() != nullptr);
  ASSERT_TRUE(array[1].getAsObject()->getString("name") == "b");
  ASSERT_TRUE(array[1].getAsObject()->getBoolean("enabled") == true);

  // Check plugin "c" info.
  ASSERT_TRUE(array[2].getAsObject() != nullptr);
  ASSERT_TRUE(array[2].getAsObject()->getString("name") == "c");
  ASSERT_TRUE(array[2].getAsObject()->getBoolean("enabled") == true);

  // Disabling a plugin should be reflected in the JSON output.
  ASSERT_TRUE(PluginManager::SetSystemRuntimePluginEnabled("b", false));
  array = *PluginManager::GetJSON().getArray("system-runtime");
  ASSERT_TRUE(array[0].getAsObject()->getBoolean("enabled") == true);
  ASSERT_TRUE(array[1].getAsObject()->getBoolean("enabled") == false);
  ASSERT_TRUE(array[2].getAsObject()->getBoolean("enabled") == true);

  // Un-registering a plugin should be reflected in the JSON output.
  ASSERT_TRUE(PluginManager::UnregisterPlugin(CreateSystemRuntimePluginB));
  array = *PluginManager::GetJSON().getArray("system-runtime");
  ASSERT_EQ(array.size(), 2u);
  ASSERT_TRUE(array[0].getAsObject()->getString("name") == "a");
  ASSERT_TRUE(array[1].getAsObject()->getString("name") == "c");

  // Filtering the JSON output should only include the matching plugins.
  array =
      *PluginManager::GetJSON("system-runtime.c").getArray("system-runtime");
  ASSERT_EQ(array.size(), 1u);
  ASSERT_TRUE(array[0].getAsObject()->getString("name") == "c");

  // Empty JSON output is allowed if there are no matching plugins.
  obj = PluginManager::GetJSON("non-existent-plugin");
  ASSERT_TRUE(obj.empty());
}