GCC Code Coverage Report


Directory: ./
File: channeltester/ChannelTester.cpp
Date: 2026-10-29 08:11:58
Exec Total Coverage
Lines: 371 409 90.7%
Functions: 21 22 95.5%
Branches: 660 1300 50.8%

Line Branch Exec Source
1 /*
2 * This file is part of the Falcon Player (FPP) and is Copyright (C)
3 * 2013-2022 by the Falcon Player Developers.
4 *
5 * The Falcon Player (FPP) is free software, and is covered under
6 * multiple Open Source licenses. Please see the included 'LICENSES'
7 * file for descriptions of what files are covered by each license.
8 *
9 * This source file is covered under the LGPL v2.1 as described in the
10 * included LICENSE.LGPL file.
11 */
12
13 #include "fpp-pch.h"
14
15 #include "fpp-json.h"
16 #include "fpphttp.h" // drogon/HTTP helpers used here; no longer pulled transitively (see fpphttp_types.h)
17 #include "fpphttp_clientip.h" // getEffectiveClientIP() - FPP-internal, not in the plugin API
18
19 #include "../common.h"
20 #include "../log.h"
21 #include <unistd.h> // usleep -- needed directly for NOPCH builds
22
23 #include "ChannelTester.h"
24 #include "channeloutput/ChannelOutputSetup.h"
25 #include "channeloutput/channeloutputthread.h"
26 #include "commands/Commands.h"
27
28 // Test Patterns
29 #include "OutputTester.h"
30 #include "RGBChase.h"
31 #include "RGBCycle.h"
32 #include "RGBFill.h"
33 #include "SingleChase.h"
34
35 ChannelTester ChannelTester::INSTANCE;
36
37 /*
38 *
39 */
40 2346 ChannelTester::ChannelTester() :
41 2346 m_testPattern(NULL) {
42
1/2
✓ Branch 0 taken 2346 times.
✗ Branch 1 not taken.
2346 LogExcess(VB_CHANNELOUT, "ChannelTester::ChannelTester()\n");
43 2346 }
44
45 /*
46 *
47 */
48 916 ChannelTester::~ChannelTester() {
49 916 LogExcess(VB_CHANNELOUT, "ChannelTester::~ChannelTester()\n");
50
51 916 std::lock_guard<std::mutex> lock(m_testLock);
52
53
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 458 times.
916 if (m_testPattern) {
54 ✗ delete m_testPattern;
55 ✗ m_testPattern = NULL;
56 }
57 916 }
58
59 class StopTestingCommand : public Command {
60 public:
61 1887 StopTestingCommand() :
62
2/4
✓ Branch 0 taken 1887 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1887 times.
✗ Branch 3 not taken.
3774 Command("Test Stop") {
63 1887 }
64 7468 virtual ~StopTestingCommand() {}
65
66 119 virtual std::unique_ptr<Result> run(const std::vector<std::string>& args) {
67
1/2
✓ Branch 0 taken 119 times.
✗ Branch 1 not taken.
119 Json::Value config;
68
2/4
✓ Branch 0 taken 119 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 119 times.
✗ Branch 3 not taken.
119 config["enabled"] = 0;
69
1/2
✓ Branch 0 taken 119 times.
✗ Branch 1 not taken.
119 ChannelTester::INSTANCE.SetupTest(config);
70
1/2
✓ Branch 0 taken 119 times.
✗ Branch 1 not taken.
238 return std::make_unique<Result>("Stopped");
71 119 }
72 };
73 class StartTestingCommand : public Command {
74 public:
75 1887 StartTestingCommand() :
76
2/4
✓ Branch 0 taken 1887 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1887 times.
✗ Branch 3 not taken.
3774 Command("Test Start") {
77
6/12
✓ Branch 0 taken 1887 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1887 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1887 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1887 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 1887 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 1887 times.
✗ Branch 11 not taken.
11322 args.push_back(CommandArg("UpdateInterval", "int", "Update Interval (ms)").setDefaultValue("1000").setRange(100, 10000));
78
7/14
✓ Branch 0 taken 1887 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1887 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1887 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1887 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 1887 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 1887 times.
✗ Branch 11 not taken.
✓ Branch 12 taken 1887 times.
✗ Branch 13 not taken.
15096 args.push_back(CommandArg("TestPattern", "subcommand", "Test Pattern").setContentListUrl("api/fppd/testing/tests/"));
79 1887 }
80 7468 virtual ~StartTestingCommand() {}
81
82 96 std::string mapSubMode(const std::string& m) {
83
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 96 times.
96 if (m == "R-G-B-All") {
84 ✗ return "RGBA";
85 }
86
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 96 times.
96 if (m == "R-G-B-None") {
87 ✗ return "RGBN";
88 }
89
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 96 times.
96 if (m == "R-G-B-All-None") {
90 ✗ return "RGBAN";
91 }
92
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 94 times.
96 if (m == "R-G-B-W") {
93
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
4 return "RGBW";
94 }
95
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 94 times.
94 if (m == "R-G-B-W-All") {
96 ✗ return "RGBWA";
97 }
98
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 94 times.
94 if (m == "R-G-B-W-None") {
99 ✗ return "RGBWN";
100 }
101
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 94 times.
94 if (m == "R-G-B-W-All-None") {
102 ✗ return "RGBWAN";
103 }
104
1/2
✓ Branch 0 taken 94 times.
✗ Branch 1 not taken.
188 return "RGB";
105 }
106 96 std::string mapPattern(const std::string& m) {
107
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 96 times.
96 if (m == "R-G-B-All") {
108 ✗ return "FF000000FF000000FFFFFFFF";
109 }
110
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 96 times.
96 if (m == "R-G-B-None") {
111 ✗ return "FF000000FF000000FF000000";
112 }
113
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 96 times.
96 if (m == "R-G-B-All-None") {
114 ✗ return "FF000000FF000000FFFFFFFF000000";
115 }
116 // RGBW patterns use 4-channel (8 hex digit) groups: R, G, B, W, All, None
117
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 94 times.
96 if (m == "R-G-B-W") {
118
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
4 return "FF00000000FF00000000FF00000000FF";
119 }
120
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 94 times.
94 if (m == "R-G-B-W-All") {
121 ✗ return "FF00000000FF00000000FF00000000FFFFFFFFFF";
122 }
123
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 94 times.
94 if (m == "R-G-B-W-None") {
124 ✗ return "FF00000000FF00000000FF00000000FF00000000";
125 }
126
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 94 times.
94 if (m == "R-G-B-W-All-None") {
127 ✗ return "FF00000000FF00000000FF00000000FFFFFFFFFF00000000";
128 }
129
1/2
✓ Branch 0 taken 94 times.
✗ Branch 1 not taken.
188 return "FF000000FF000000FF";
130 }
131 // Number of channels per pixel implied by the chase/cycle type (4 for RGBW patterns, 3 otherwise)
132 96 int mapChannelsPerNode(const std::string& m) {
133
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 94 times.
96 return (m.find("W") != std::string::npos) ? 4 : 3;
134 }
135
136 188 virtual std::unique_ptr<Result> run(const std::vector<std::string>& args) {
137 // printf("Run test:\n");
138 // for (auto& a : args) {
139 // printf("%s\n", a.c_str());
140 // }
141 // The two CommandArgs declared above are only the fixed head of this
142 // command's argument list; the rest come from the "subcommand" arg,
143 // whose shape render_GET() publishes per test pattern (a channel
144 // range plus at least one pattern-specific value, so four in total,
145 // five for "Single Channel Chase"). Nothing between here and the
146 // caller knows that, so an under-supplied call - "Test Start" with
147 // just an interval and a pattern name, as arrived from an API client -
148 // used to index args[3] off the end of the vector and read a
149 // std::string out of unrelated memory.
150 //
151 // The interval is parsed with atoi() rather than std::stoi() for the
152 // same reason: an empty or non-numeric value is reachable from the
153 // API, and stoi() throws where nothing catches it. The remaining
154 // std::stoi/stoul/substr reads below can still throw on a malformed
155 // argument value - that is a separate defect from this one.
156
4/6
✓ Branch 0 taken 188 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 188 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 183 times.
188 size_t needed = (args.size() > 1 && args[1] == "Single Channel Chase") ? 5 : 4;
157
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 187 times.
188 if (args.size() < needed) {
158 // Built by appending rather than as one operator+ chain: GCC 12-14
159 // cannot bound std::to_string() in a chain and mis-reports the
160 // temporary's small-string buffer as overflowed (-Wstringop-overflow).
161
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 std::string msg = "Test Start needs ";
162
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 msg += std::to_string(needed);
163
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 msg += " arguments, found ";
164
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 msg += std::to_string(args.size());
165
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 return std::make_unique<ErrorResult>(msg);
166 1 }
167
1/2
✓ Branch 0 taken 187 times.
✗ Branch 1 not taken.
187 Json::Value config;
168
2/4
✓ Branch 0 taken 187 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 187 times.
✗ Branch 3 not taken.
187 config["enabled"] = 1;
169
2/4
✓ Branch 0 taken 187 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 187 times.
✗ Branch 3 not taken.
187 config["cycleMS"] = atoi(args[0].c_str());
170
1/2
✓ Branch 0 taken 187 times.
✗ Branch 1 not taken.
187 std::string effect = args[1];
171
2/4
✓ Branch 0 taken 187 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 187 times.
✗ Branch 3 not taken.
187 config["colorPattern"] = "";
172
3/4
✓ Branch 0 taken 187 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 29 times.
✓ Branch 3 taken 158 times.
187 if (effect == "RGB Chase") {
173
2/4
✓ Branch 0 taken 29 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 29 times.
✗ Branch 3 not taken.
29 config["mode"] = "RGBChase";
174
5/10
✓ Branch 0 taken 29 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 29 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 29 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 29 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 29 times.
✗ Branch 9 not taken.
49 config["subMode"] = std::string("RGBChase-") + mapSubMode(args[3]);
175
3/6
✓ Branch 0 taken 29 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 29 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 29 times.
✗ Branch 5 not taken.
29 config["colorPattern"] = mapPattern(args[3]);
176
2/4
✓ Branch 0 taken 29 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 29 times.
✗ Branch 3 not taken.
29 config["channelsPerNode"] = mapChannelsPerNode(args[3]);
177
3/4
✓ Branch 0 taken 158 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 67 times.
✓ Branch 3 taken 91 times.
158 } else if (effect == "RGB Cycle") {
178
2/4
✓ Branch 0 taken 67 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 67 times.
✗ Branch 3 not taken.
67 config["mode"] = "RGBCycle";
179
5/10
✓ Branch 0 taken 67 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 67 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 67 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 67 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 67 times.
✗ Branch 9 not taken.
191 config["subMode"] = std::string("RGBCycle-") + mapSubMode(args[3]);
180
3/6
✓ Branch 0 taken 67 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 67 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 67 times.
✗ Branch 5 not taken.
67 config["colorPattern"] = mapPattern(args[3]);
181
2/4
✓ Branch 0 taken 67 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 67 times.
✗ Branch 3 not taken.
67 config["channelsPerNode"] = mapChannelsPerNode(args[3]);
182
3/4
✓ Branch 0 taken 91 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 89 times.
91 } else if (effect == "Custom Chase") {
183
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 config["mode"] = "RGBChase";
184
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 config["subMode"] = "RGBChase-RGBCustom";
185
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 config["colorPattern"] = args[3];
186
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (args.size() > 4) {
187
3/6
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
2 config["channelsPerNode"] = std::stoi(args[4], nullptr, 10);
188 }
189
2/4
✓ Branch 0 taken 89 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 89 times.
89 } else if (effect == "Custom Cycle") {
190 ✗ config["mode"] = "RGBCycle";
191 ✗ config["subMode"] = "RGBCycle-RGBCustom";
192 ✗ config["colorPattern"] = args[3];
193 ✗ if (args.size() > 4) {
194 ✗ config["channelsPerNode"] = std::stoi(args[4], nullptr, 10);
195 }
196
3/4
✓ Branch 0 taken 89 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 49 times.
✓ Branch 3 taken 40 times.
89 } else if (effect == "RGB Single Color") {
197
2/4
✓ Branch 0 taken 49 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 49 times.
✗ Branch 3 not taken.
49 config["mode"] = "RGBFill";
198
2/2
✓ Branch 0 taken 48 times.
✓ Branch 1 taken 1 times.
49 std::string hexStr = args[3].substr(1); // Remove # prefix
199 // 8 hex digits (RGBW) can exceed INT_MAX, so parse as unsigned
200
2/2
✓ Branch 0 taken 47 times.
✓ Branch 1 taken 1 times.
48 uint32_t v = (uint32_t)std::stoul(hexStr, nullptr, 16);
201
202
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 46 times.
47 if (hexStr.length() == 8) {
203 // RGBW format (8 hex digits) - 4 channels per pixel
204
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 config["channelsPerNode"] = 4;
205
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 config["color4"] = (int)(v & 0xFF); // W
206
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 config["color3"] = (int)((v >> 8) & 0xFF); // B
207
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 config["color2"] = (int)((v >> 16) & 0xFF); // G
208
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 config["color1"] = (int)((v >> 24) & 0xFF); // R
209 } else {
210 // RGB format (6 hex digits) - 3 channels per pixel
211
2/4
✓ Branch 0 taken 46 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 46 times.
✗ Branch 3 not taken.
46 config["channelsPerNode"] = 3;
212
2/4
✓ Branch 0 taken 46 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 46 times.
✗ Branch 3 not taken.
46 config["color3"] = (int)(v & 0xFF); // B
213
2/4
✓ Branch 0 taken 46 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 46 times.
✗ Branch 3 not taken.
46 config["color2"] = (int)((v >> 8) & 0xFF); // G
214
2/4
✓ Branch 0 taken 46 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 46 times.
✗ Branch 3 not taken.
46 config["color1"] = (int)((v >> 16) & 0xFF); // R
215 }
216
3/4
✓ Branch 0 taken 40 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 35 times.
88 } else if (effect == "Single Channel Chase") {
217
2/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
5 config["mode"] = "SingleChase";
218
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 int v = std::stoi(args[3], nullptr, 10);
219
2/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
5 config["chaseValue"] = v;
220
4/6
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
5 config["chaseSize"] = std::stoi(args[4], nullptr, 10);
221
3/4
✓ Branch 0 taken 35 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 32 times.
35 } else if (effect == "Single Channel Fill") {
222
2/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
3 config["mode"] = "RGBFill";
223
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
3 int v = std::stoi(args[3], nullptr, 10);
224
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 config["color1"] = v;
225
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 config["color2"] = v;
226
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 config["color3"] = v;
227
3/4
✓ Branch 0 taken 32 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 31 times.
✓ Branch 3 taken 1 times.
32 } else if (effect == "Output Specific") {
228
2/4
✓ Branch 0 taken 31 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 31 times.
✗ Branch 3 not taken.
31 config["mode"] = "Outputs";
229
2/4
✓ Branch 0 taken 31 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 31 times.
✗ Branch 3 not taken.
31 config["outputs"] = args[2];
230
1/2
✓ Branch 0 taken 31 times.
✗ Branch 1 not taken.
31 int v = std::stoi(args[3], nullptr, 10);
231
2/4
✓ Branch 0 taken 31 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 31 times.
✗ Branch 3 not taken.
31 config["type"] = v;
232
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 30 times.
31 if (args.size() > 4) {
233
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 config["config"] = LoadJsonFromString(args[4]);
234 }
235 }
236
3/4
✓ Branch 0 taken 183 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 152 times.
✓ Branch 3 taken 31 times.
183 if (effect != "Output Specific") {
237
7/10
✓ Branch 0 taken 152 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 152 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 152 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 17 times.
✓ Branch 7 taken 135 times.
✓ Branch 8 taken 17 times.
✓ Branch 9 taken 135 times.
152 if (args[2] == "" || args[2] == "*") {
238
3/6
✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 17 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 17 times.
✗ Branch 5 not taken.
17 config["channelSet"] = "1-" + std::to_string(FPPD_MAX_CHANNELS);
239 } else {
240
2/4
✓ Branch 0 taken 135 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 135 times.
✗ Branch 3 not taken.
135 config["channelSet"] = args[2];
241 }
242 }
243
2/4
✓ Branch 0 taken 183 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 183 times.
✗ Branch 3 not taken.
183 config["channelSetType"] = "channelRange";
244
1/2
✓ Branch 0 taken 183 times.
✗ Branch 1 not taken.
183 ChannelTester::INSTANCE.SetupTest(config);
245
1/2
✓ Branch 0 taken 183 times.
✗ Branch 1 not taken.
183 return std::make_unique<Result>("Started");
246 191 }
247 };
248 ✗ void ChannelTester::StopTest() {
249 ✗ Json::Value config;
250 ✗ config["enabled"] = 0;
251 ✗ ChannelTester::INSTANCE.SetupTest(config);
252 ✗ }
253
254 1887 void ChannelTester::RegisterCommands() {
255
6/12
✓ Branch 0 taken 1887 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1887 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1887 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1727 times.
✓ Branch 7 taken 160 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 1727 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
3934 CommandManager::INSTANCE.addCategorizedCommand(new StartTestingCommand(), "Outputs", 1);
256
6/12
✓ Branch 0 taken 1887 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1887 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1887 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1727 times.
✓ Branch 7 taken 160 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 1727 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
2207 CommandManager::INSTANCE.addCategorizedCommand(new StopTestingCommand(), "Outputs", 1);
257 1887 }
258 // --------------------------------------------------------------------------
259 // OpenAPI docs for the /fppd/testing/* endpoints handled below.
260 // --------------------------------------------------------------------------
261
262 /**
263 * Get the current test-mode configuration.
264 *
265 * @route GET /api/fppd/testing
266 * @response 200 Object with the current test `config`.
267 */
268
269 /**
270 * List the available test pattern names.
271 *
272 * @route GET /api/fppd/testing/tests
273 * @response 200 Array of test pattern names.
274 * ```json
275 * ["RGB Chase", "RGB Cycle", "Custom Chase", "Custom Cycle", "RGB Single Color", "Single Channel Chase", "Single Channel Fill", "Output Specific"]
276 * ```
277 */
278
279 /**
280 * Get the argument definitions for a specific test pattern.
281 *
282 * @route GET /api/fppd/testing/tests/{pattern}
283 * @response 200 Object with an `args` array describing the pattern's inputs.
284 * @response 400 The named test pattern does not exist.
285 */
286 114 HttpResponsePtr ChannelTester::render_GET(const HttpRequestPtr& req) {
287
1/2
✓ Branch 0 taken 114 times.
✗ Branch 1 not taken.
114 Json::Value result;
288
2/4
✓ Branch 0 taken 114 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 114 times.
✗ Branch 3 not taken.
114 auto parts = getPathPieces(req->path());
289 114 int plen = parts.size();
290 // ex: /fppd/testing/tests/RGB Chase
291
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 106 times.
114 if (plen == 2) {
292
4/8
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 8 times.
✗ Branch 7 not taken.
8 result["config"] = LoadJsonFromString(GetConfig().c_str());
293
2/4
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
8 result["Status"] = "OK";
294
2/4
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
8 result["respCode"] = 200;
295
2/4
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
8 result["Message"] = "";
296
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 95 times.
106 } else if (plen == 3) {
297
2/4
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 11 times.
✗ Branch 3 not taken.
11 result.append("RGB Chase");
298
2/4
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 11 times.
✗ Branch 3 not taken.
11 result.append("RGB Cycle");
299
2/4
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 11 times.
✗ Branch 3 not taken.
11 result.append("Custom Chase");
300
2/4
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 11 times.
✗ Branch 3 not taken.
11 result.append("Custom Cycle");
301
2/4
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 11 times.
✗ Branch 3 not taken.
11 result.append("RGB Single Color");
302
2/4
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 11 times.
✗ Branch 3 not taken.
11 result.append("Single Channel Chase");
303
2/4
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 11 times.
✗ Branch 3 not taken.
11 result.append("Single Channel Fill");
304
2/4
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 11 times.
✗ Branch 3 not taken.
11 result.append("Output Specific");
305
2/2
✓ Branch 0 taken 85 times.
✓ Branch 1 taken 10 times.
95 } else if (plen == 4) {
306
1/2
✓ Branch 0 taken 85 times.
✗ Branch 1 not taken.
85 std::string effect = parts[3];
307
3/4
✓ Branch 0 taken 85 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 84 times.
✓ Branch 3 taken 1 times.
85 if (effect != "Output Specific") {
308
1/2
✓ Branch 0 taken 84 times.
✗ Branch 1 not taken.
84 Json::Value vcr;
309
2/4
✓ Branch 0 taken 84 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 84 times.
✗ Branch 3 not taken.
84 vcr["allowBlanks"] = false;
310
2/4
✓ Branch 0 taken 84 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 84 times.
✗ Branch 3 not taken.
84 vcr["contentListUrl"] = "api/models?simple=true";
311
1/2
✓ Branch 0 taken 84 times.
✗ Branch 1 not taken.
84 std::string rng = GetOutputRangesAsString(false, true);
312
3/6
✓ Branch 0 taken 84 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 84 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 84 times.
✗ Branch 5 not taken.
84 vcr["contents"].append(rng.c_str());
313
2/4
✓ Branch 0 taken 84 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 84 times.
✗ Branch 3 not taken.
84 vcr["description"] = "Channel Range/Model";
314
2/4
✓ Branch 0 taken 84 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 84 times.
✗ Branch 3 not taken.
84 vcr["name"] = "ChannelRange";
315
2/4
✓ Branch 0 taken 84 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 84 times.
✗ Branch 3 not taken.
84 vcr["optional"] = false;
316
2/4
✓ Branch 0 taken 84 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 84 times.
✗ Branch 3 not taken.
84 vcr["type"] = "datalist";
317
2/4
✓ Branch 0 taken 84 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 84 times.
✗ Branch 3 not taken.
84 result["args"].append(vcr);
318 84 }
319
3/4
✓ Branch 0 taken 85 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 82 times.
85 if (effect == "RGB Chase") {
320
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 Json::Value v;
321
2/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
3 v["description"] = "Chase Type";
322
2/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
3 v["name"] = "ChaseType";
323
2/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
3 v["optional"] = false;
324
2/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
3 v["type"] = "string";
325
3/6
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
3 v["contents"].append("R-G-B");
326
3/6
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
3 v["contents"].append("R-G-B-W");
327
3/6
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
3 v["contents"].append("R-G-B-All");
328
3/6
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
3 v["contents"].append("R-G-B-W-All");
329
3/6
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
3 v["contents"].append("R-G-B-None");
330
3/6
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
3 v["contents"].append("R-G-B-W-None");
331
3/6
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
3 v["contents"].append("R-G-B-All-None");
332
3/6
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
3 v["contents"].append("R-G-B-W-All-None");
333
2/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
3 result["args"].append(v);
334
3/4
✓ Branch 0 taken 82 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 81 times.
85 } else if (effect == "RGB Cycle") {
335
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 Json::Value v;
336
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 v["description"] = "Cycle Type";
337
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 v["name"] = "CycleType";
338
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 v["optional"] = false;
339
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 v["type"] = "string";
340
3/6
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
1 v["contents"].append("R-G-B");
341
3/6
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
1 v["contents"].append("R-G-B-W");
342
3/6
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
1 v["contents"].append("R-G-B-All");
343
3/6
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
1 v["contents"].append("R-G-B-W-All");
344
3/6
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
1 v["contents"].append("R-G-B-None");
345
3/6
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
1 v["contents"].append("R-G-B-W-None");
346
3/6
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
1 v["contents"].append("R-G-B-All-None");
347
3/6
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
1 v["contents"].append("R-G-B-W-All-None");
348
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 result["args"].append(v);
349
3/4
✓ Branch 0 taken 81 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 80 times.
82 } else if (effect == "Custom Chase") {
350
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 Json::Value v;
351
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 v["description"] = "Pattern";
352
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 v["name"] = "Pattern";
353
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 v["optional"] = false;
354
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 v["type"] = "string";
355
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 v["default"] = "FF000000FF000000FF";
356
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 result["args"].append(v);
357
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 Json::Value cpn;
358
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 cpn["description"] = "Channels Per Node";
359
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 cpn["name"] = "ChannelsPerNode";
360
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 cpn["optional"] = true;
361
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 cpn["type"] = "string";
362
3/6
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
1 cpn["contents"].append("3");
363
3/6
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
1 cpn["contents"].append("4");
364
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 cpn["default"] = "3";
365
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 result["args"].append(cpn);
366
3/4
✓ Branch 0 taken 80 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 79 times.
81 } else if (effect == "Custom Cycle") {
367
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 Json::Value v;
368
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 v["description"] = "Pattern";
369
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 v["name"] = "Pattern";
370
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 v["optional"] = false;
371
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 v["type"] = "string";
372
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 v["default"] = "FF000000FF000000FF";
373
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 result["args"].append(v);
374
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 Json::Value cpn;
375
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 cpn["description"] = "Channels Per Node";
376
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 cpn["name"] = "ChannelsPerNode";
377
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 cpn["optional"] = true;
378
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 cpn["type"] = "string";
379
3/6
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
1 cpn["contents"].append("3");
380
3/6
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
1 cpn["contents"].append("4");
381
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 cpn["default"] = "3";
382
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 result["args"].append(cpn);
383
3/4
✓ Branch 0 taken 79 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 78 times.
80 } else if (effect == "RGB Single Color") {
384
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 Json::Value v;
385
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 v["description"] = "Color";
386
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 v["name"] = "Color";
387
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 v["optional"] = false;
388
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 v["type"] = "color";
389
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 v["default"] = "#FFFFFF";
390
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 result["args"].append(v);
391
3/4
✓ Branch 0 taken 78 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 77 times.
79 } else if (effect == "Single Channel Chase") {
392
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 Json::Value v;
393
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 v["description"] = "Value";
394
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 v["name"] = "Value";
395
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 v["optional"] = false;
396
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 v["type"] = "range";
397
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 v["default"] = "255";
398
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 v["min"] = 0;
399
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 v["max"] = 255;
400
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 result["args"].append(v);
401
402
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 Json::Value v2;
403
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 v2["description"] = "Chase Size";
404
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 v2["name"] = "ChaseSize";
405
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 v2["optional"] = false;
406
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 v2["type"] = "int";
407
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 v2["default"] = "2";
408
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 v2["min"] = 2;
409
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 v2["max"] = 10;
410
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 result["args"].append(v2);
411
3/4
✓ Branch 0 taken 77 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 76 times.
78 } else if (effect == "Single Channel Fill") {
412
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 Json::Value v;
413
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 v["description"] = "Value";
414
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 v["name"] = "Value";
415
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 v["optional"] = false;
416
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 v["type"] = "range";
417
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 v["default"] = "255";
418
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 v["min"] = 0;
419
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 v["max"] = 255;
420
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 result["args"].append(v);
421
3/4
✓ Branch 0 taken 76 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 75 times.
77 } else if (effect == "Output Specific") {
422
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 Json::Value vcr;
423
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 vcr["allowBlanks"] = false;
424
3/6
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
1 vcr["contents"].append("--ALL--");
425
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
1 for (auto& a : GetOutputTypes()) {
426 ✗ vcr["contents"].append(a);
427 1 }
428
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 vcr["default"] = "--ALL--";
429
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 vcr["description"] = "Output Types";
430
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 vcr["name"] = "OutputTypes";
431
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 vcr["optional"] = false;
432
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 vcr["type"] = "string";
433
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 result["args"].append(vcr);
434
435
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 Json::Value v;
436
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 v["description"] = "Test Type";
437
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 v["name"] = "TestType";
438
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 v["optional"] = false;
439
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 v["type"] = "range";
440
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 v["default"] = "1";
441
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 v["min"] = 1;
442
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 v["max"] = 99;
443
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
1 result["args"].append(v);
444 1 } else {
445
4/8
✓ Branch 0 taken 75 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 75 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 75 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 75 times.
✗ Branch 7 not taken.
150 return makeStringResponse("Test Pattern " + effect + " not found", 400, "text/plain");
446 }
447
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 75 times.
85 }
448
2/4
✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 39 times.
✗ Branch 3 not taken.
78 std::string resultStr = SaveJsonToString(result);
449
2/4
✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 39 times.
✗ Branch 3 not taken.
39 return makeStringResponse(resultStr, 200, "application/json");
450 114 }
451 /**
452 * Activate or deactivate test mode. POST the test configuration JSON (with an
453 * `enabled` flag); an empty/disabled config turns test mode off.
454 *
455 * @route POST /api/fppd/testing
456 * @response 200 Test mode activated or deactivated.
457 */
458 26 HttpResponsePtr ChannelTester::render_POST(const HttpRequestPtr& req) {
459
1/2
✓ Branch 0 taken 26 times.
✗ Branch 1 not taken.
26 Json::Value result;
460
1/2
✓ Branch 0 taken 26 times.
✗ Branch 1 not taken.
26 std::string content = std::string{ getRequestContent(req) };
461 // Default 200 rather than the 2000 the Debug-level JSON dumps use: this is
462 // the one Info-level line that could emit a multi-KB entry, and a real test
463 // spec is only a few hundred bytes. TruncateForLog appends the true size,
464 // so nothing is hidden - and the crash ring caps a line at 240 bytes anyway.
465
3/6
✓ Branch 0 taken 26 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 26 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 26 times.
✗ Branch 5 not taken.
26 LogDebug(VB_COMMAND, "POST /api/fppd/testing from %s: \"%s\"\n", getEffectiveClientIP(req).c_str(), TruncateForLog(content).c_str());
466
5/6
✓ Branch 0 taken 26 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 25 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 8 times.
✓ Branch 5 taken 17 times.
27 if (ChannelTester::INSTANCE.SetupTest(content)) {
467
2/4
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
8 result["Status"] = "OK";
468
2/4
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
8 result["respCode"] = 200;
469
2/4
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
8 result["Message"] = "Test Mode Activated";
470 } else {
471
2/4
✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 17 times.
✗ Branch 3 not taken.
17 result["Status"] = "OK";
472
2/4
✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 17 times.
✗ Branch 3 not taken.
17 result["respCode"] = 200;
473
2/4
✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 17 times.
✗ Branch 3 not taken.
17 result["Message"] = "Test Mode Deactivated";
474 }
475
2/4
✓ Branch 0 taken 25 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 25 times.
✗ Branch 3 not taken.
50 std::string resultStr = SaveJsonToString(result);
476
2/4
✓ Branch 0 taken 25 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 25 times.
✗ Branch 3 not taken.
50 return makeStringResponse(resultStr, 200, "application/json");
477 27 }
478 /*
479 *
480 */
481 39 int ChannelTester::SetupTest(const std::string configStr) {
482
1/2
✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
39 LogDebug(VB_CHANNELOUT, "ChannelTester::SetupTest()\n");
483
1/2
✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
39 LogDebug(VB_CHANNELOUT, " %s\n", configStr.c_str());
484
485
1/2
✓ Branch 0 taken 39 times.
✗ Branch 1 not taken.
39 Json::Value config;
486
487
4/4
✓ Branch 0 taken 38 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 11 times.
✓ Branch 3 taken 27 times.
39 if (!LoadJsonFromString(configStr, config)) {
488
1/2
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
11 LogErr(VB_CHANNELOUT,
489 "Error parsing Test Pattern config string: '%s'\n",
490 configStr.c_str());
491
492 11 return 0;
493 }
494
1/2
✓ Branch 0 taken 27 times.
✗ Branch 1 not taken.
27 return SetupTest(config);
495 39 }
496 329 int ChannelTester::SetupTest(const Json::Value& config) {
497 // The config reaches here straight from the API and the command socket, so
498 // it can be any shape at all. The test patterns read it with jsoncpp
499 // accessors, which throw on a type mismatch (a color sent as "#202020"
500 // rather than an int, say), and the command dispatch above has no handler
501 // for that, so a single malformed request used to abort fppd.
502 try {
503
1/2
✓ Branch 0 taken 329 times.
✗ Branch 1 not taken.
329 return SetupTestInternal(config);
504 ✗ } catch (const Json::Exception& e) {
505 ✗ LogErr(VB_CHANNELOUT, "Ignoring malformed Test Pattern config: %s\n", e.what());
506 // A pattern that threw part way through Init() must not be left
507 // behind: Testing() only checks that the pointer is set, so it would
508 // be handed to OverlayTestData with its channel data unallocated.
509 // This mirrors the cleanup the Init() failure path below does.
510 ✗ std::unique_lock<std::mutex> lock(m_testLock);
511 ✗ if (m_testPattern) {
512 ✗ delete m_testPattern;
513 ✗ m_testPattern = nullptr;
514 }
515 ✗ return 0;
516 ✗ }
517 }
518
519 329 int ChannelTester::SetupTestInternal(const Json::Value& config) {
520 // unique_lock (not lock_guard) because this function unlocks and
521 // re-locks mid-function below while waiting for the channel output
522 // loop to clear test data.
523
1/2
✓ Branch 0 taken 329 times.
✗ Branch 1 not taken.
329 std::unique_lock<std::mutex> lock(m_testLock);
524 329 int result = 0;
525
526
4/6
✓ Branch 0 taken 329 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 329 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 195 times.
✓ Branch 5 taken 134 times.
658 if (JsonInt(config, "enabled")) {
527
3/6
✓ Branch 0 taken 195 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 195 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 195 times.
✗ Branch 5 not taken.
585 std::string patternName = JsonString(config, "mode");
528
529
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 191 times.
195 if (m_testPattern) {
530
3/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 3 times.
4 if (patternName != m_testPattern.load()->Name()) {
531
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 delete m_testPattern;
532 1 m_testPattern = NULL;
533 }
534 }
535
536
2/2
✓ Branch 0 taken 192 times.
✓ Branch 1 taken 3 times.
195 if (!m_testPattern) {
537
3/4
✓ Branch 0 taken 192 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 186 times.
192 if (patternName == "SingleChase")
538
3/8
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 6 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
6 m_testPattern = new TestPatternSingleChase();
539
3/4
✓ Branch 0 taken 186 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 32 times.
✓ Branch 3 taken 154 times.
186 else if (patternName == "RGBChase")
540
3/8
✓ Branch 0 taken 32 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 32 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 32 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
32 m_testPattern = new TestPatternRGBChase();
541
3/4
✓ Branch 0 taken 154 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 55 times.
✓ Branch 3 taken 99 times.
154 else if (patternName == "RGBFill")
542
3/8
✓ Branch 0 taken 55 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 55 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 55 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
55 m_testPattern = new TestPatternRGBFill();
543
3/4
✓ Branch 0 taken 99 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 67 times.
✓ Branch 3 taken 32 times.
99 else if (patternName == "RGBCycle")
544
3/8
✓ Branch 0 taken 67 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 67 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 67 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
67 m_testPattern = new TestPatternRGBCycle();
545
3/4
✓ Branch 0 taken 32 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 31 times.
✓ Branch 3 taken 1 times.
32 else if (patternName == "Outputs")
546
3/8
✓ Branch 0 taken 31 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 31 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 31 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
31 m_testPattern = new OutputTester();
547 }
548
549
2/2
✓ Branch 0 taken 194 times.
✓ Branch 1 taken 1 times.
195 if (m_testPattern) {
550
2/4
✓ Branch 0 taken 194 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 194 times.
✗ Branch 3 not taken.
194 result = m_testPattern.load()->Init(config);
551
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 194 times.
194 if (!result) {
552 ✗ delete m_testPattern;
553 ✗ m_testPattern = NULL;
554 }
555 }
556 195 } else {
557
2/2
✓ Branch 0 taken 120 times.
✓ Branch 1 taken 14 times.
134 if (m_testPattern) {
558 120 m_testPattern.load()->DisableTest();
559
1/2
✓ Branch 0 taken 120 times.
✗ Branch 1 not taken.
120 lock.unlock();
560
561 // Give the channel output loop time to clear test data
562
1/2
✓ Branch 0 taken 120 times.
✗ Branch 1 not taken.
120 usleep(150000);
563
564
1/2
✓ Branch 0 taken 120 times.
✗ Branch 1 not taken.
120 lock.lock();
565
566
1/2
✓ Branch 0 taken 120 times.
✗ Branch 1 not taken.
120 delete m_testPattern;
567 120 m_testPattern = NULL;
568 }
569 }
570
571 // Matches the coverage of the original pthread_mutex_unlock() here:
572 // m_configStr is intentionally set outside m_testLock.
573
1/2
✓ Branch 0 taken 329 times.
✗ Branch 1 not taken.
329 lock.unlock();
574
575 {
576
1/2
✓ Branch 0 taken 329 times.
✗ Branch 1 not taken.
329 std::lock_guard<std::mutex> cl(m_configLock);
577
2/4
✓ Branch 0 taken 329 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 329 times.
✗ Branch 3 not taken.
329 m_configStr = SaveJsonToString(config);
578 329 }
579
580 // Ask for output, as an effect or an overlay does when it starts. The
581 // main loop starts the output thread for a test only when it is not
582 // running, and a thread that has just gone idle -- after a Test Stop, say
583 // -- still counts as running for the 2.5 s it waits to be asked, so a
584 // test started then put nothing out until that wait ran out. Outside
585 // m_testLock: the output thread takes it every frame.
586
2/2
✓ Branch 0 taken 194 times.
✓ Branch 1 taken 135 times.
329 if (result) {
587
1/2
✓ Branch 0 taken 194 times.
✗ Branch 1 not taken.
194 StartChannelOutputThread();
588 }
589 329 return result;
590 329 }
591
592 /*
593 *
594 */
595 30015 void ChannelTester::OverlayTestData(char* channelData) {
596
1/2
✓ Branch 0 taken 30015 times.
✗ Branch 1 not taken.
30015 LogExcess(VB_CHANNELOUT, "ChannelTester::OverlayTestData()\n");
597
598 // lock_guard releases the lock on every return path (including the
599 // early return below), matching the coverage of the previous
600 // pthread_mutex_lock()/unlock() pairs.
601
1/2
✓ Branch 0 taken 30015 times.
✗ Branch 1 not taken.
30015 std::lock_guard<std::mutex> lock(m_testLock);
602
603
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30015 times.
30015 if (!m_testPattern) {
604 ✗ return;
605 }
606
607
1/2
✓ Branch 0 taken 30015 times.
✗ Branch 1 not taken.
30015 m_testPattern.load()->OverlayTestData(channelData);
608
1/2
✓ Branch 0 taken 30015 times.
✗ Branch 1 not taken.
30015 }
609
610 /*
611 *
612 */
613 535833 int ChannelTester::Testing(void) {
614 // Only whether it is set: the pattern itself is used under m_testLock.
615
2/2
✓ Branch 0 taken 60079 times.
✓ Branch 1 taken 475754 times.
535833 return m_testPattern.load(std::memory_order_relaxed) ? 1 : 0;
616 }
617
618 /*
619 *
620 */
621 11 std::string ChannelTester::GetConfig(void) {
622
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 5 times.
11 if (!m_testPattern)
623
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
12 return std::string("{ \"enabled\": 0 }");
624
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 std::lock_guard<std::mutex> cl(m_configLock);
625
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 return m_configStr;
626 5 }
627
628 500 Json::Value ChannelTester::GetStatusJson(void) {
629
1/2
✓ Branch 0 taken 500 times.
✗ Branch 1 not taken.
500 Json::Value result;
630
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 500 times.
500 if (!m_testPattern)
631 ✗ return result;
632 500 std::string cfg;
633 {
634
1/2
✓ Branch 0 taken 500 times.
✗ Branch 1 not taken.
500 std::lock_guard<std::mutex> cl(m_configLock);
635
1/2
✓ Branch 0 taken 500 times.
✗ Branch 1 not taken.
500 cfg = m_configStr;
636 500 }
637
5/10
✓ Branch 0 taken 500 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 500 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 500 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 500 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 500 times.
500 if (!LoadJsonFromString(cfg, result) || !result.isObject())
638 ✗ return Json::Value();
639
1/2
✓ Branch 0 taken 500 times.
✗ Branch 1 not taken.
500 result.removeMember("config");
640 500 return result;
641 500 }
642