1212
1313 ==============================================================================*/
1414
15- #include < cstdlib >
15+ #include < map >
1616
1717struct CommandLineTests : public juce ::UnitTest
1818{
@@ -21,46 +21,32 @@ struct CommandLineTests : public juce::UnitTest
2121 {
2222 }
2323
24- static constexpr const char * knownEnvVars[] = {
25- " STRICTNESS_LEVEL" , " RANDOM_SEED" , " TIMEOUT_MS" , " VERBOSE" , " REPEAT" ,
26- " RANDOMISE" , " SKIP_GUI_TESTS" , " DATA_FILE" , " OUTPUT_DIR" , " OUTPUT_FILENAME" ,
27- " SAMPLE_RATES" , " BLOCK_SIZES" , " RTCHECK"
28- };
29-
30- static void setEnv (const char * name, const char * value)
24+ static settings_parser::EnvProvider emptyEnv ()
3125 {
32- #if JUCE_WINDOWS
33- _putenv_s (name, value);
34- #else
35- ::setenv (name, value, 1 );
36- #endif
26+ return [] (const juce::String&) { return juce::String (); };
3727 }
3828
39- static void unsetEnv ( const char * name )
29+ static settings_parser::EnvProvider envFrom (std::map<juce::String, juce::String> vars )
4030 {
41- # if JUCE_WINDOWS
42- _putenv_s (name, " " );
43- # else
44- ::unsetenv (name );
45- # endif
31+ return [vars = std::move (vars)] ( const juce::String& name)
32+ {
33+ const auto it = vars. find (name);
34+ return it != vars. end () ? it-> second : juce::String ( );
35+ };
4636 }
4737
48- static void clearKnownEnv ( )
38+ static PluginvalSettings parse ( const juce::String& cmd, settings_parser::EnvProvider env )
4939 {
50- for (auto * n : knownEnvVars)
51- unsetEnv (n);
40+ return settings_parser::parse (cmd, env);
5241 }
5342
5443 static PluginvalSettings parse (const juce::String& cmd)
5544 {
56- return settings_parser::parse (cmd);
45+ return settings_parser::parse (cmd, emptyEnv () );
5746 }
5847
5948 void runTest () override
6049 {
61- // Start from a clean environment so host env vars don't affect the deterministic tests.
62- clearKnownEnv ();
63-
6450 beginTest (" Command line defaults" );
6551 {
6652 const auto opts = parse (" " ).toPluginTestOptions ();
@@ -203,79 +189,93 @@ struct CommandLineTests : public juce::UnitTest
203189
204190 beginTest (" Environment variables" );
205191 {
206- setEnv ( " STRICTNESS_LEVEL " , " 7 " );
207- setEnv ( " RANDOM_SEED " , " 1234 " );
208- setEnv ( " TIMEOUT_MS " , " 20000 " );
209- setEnv ( " VERBOSE " , " 1 " );
210- setEnv ( " REPEAT " , " 11 " );
211- setEnv ( " RANDOMISE " , " 1 " );
212- setEnv ( " SKIP_GUI_TESTS " , " 1" );
213- setEnv ( " DATA_FILE " , " /path/to/file " );
214- setEnv ( " OUTPUT_DIR " , " /path/to/dir " );
215- setEnv ( " SAMPLE_RATES " , " 22050,44100 " );
216- setEnv ( " BLOCK_SIZES " , " 32,64 " );
217- setEnv ( " RTCHECK " , " relaxed " );
218-
219- const auto opts = parse ( " --validate x " ). toPluginTestOptions ( );
220-
221- clearKnownEnv ( );
222-
192+ const auto env = envFrom ({
193+ { " STRICTNESS_LEVEL " , " 7 " },
194+ { " RANDOM_SEED " , " 1234 " },
195+ { " TIMEOUT_MS " , " 20000 " },
196+ { " VERBOSE " , " 1 " },
197+ { " REPEAT " , " 11 " },
198+ { " RANDOMISE " , " 1" },
199+ { " SKIP_GUI_TESTS " , " 1 " },
200+ { " DATA_FILE " , " /path/to/file " },
201+ { " OUTPUT_DIR " , " /path/to/dir " },
202+ { " SAMPLE_RATES " , " 22050,44100 " },
203+ { " BLOCK_SIZES " , " 32,64 " },
204+ { " RTCHECK " , " relaxed " },
205+ } );
206+
207+ const auto settings = parse ( " --validate x " , env );
208+ const auto opts = settings. toPluginTestOptions ();
223209 expectEquals (opts.strictnessLevel , 7 );
224210 expectEquals (opts.randomSeed , (juce::int64) 1234 );
225211 expectEquals (opts.timeoutMs , (juce::int64) 20000 );
226212 expect (opts.verbose );
227213 expectEquals (opts.numRepeats , 11 );
228214 expect (opts.randomiseTestOrder );
229215 expect (opts.withGUI == false );
216+ expectEquals (juce::String (settings.dataFile ), juce::String (" /path/to/file" ));
230217 expect (opts.sampleRates == std::vector<double > ({ 22050.0 , 44100.0 }));
231218 expect (opts.blockSizes == std::vector<int > ({ 32 , 64 }));
232219 expect (opts.realtimeCheck == RealtimeCheck::relaxed);
233220 }
234221
235222 beginTest (" Command line overrides environment variables" );
236223 {
237- setEnv (" STRICTNESS_LEVEL" , " 3" );
238- const auto envOnly = parse (" --validate x" ).strictnessLevel ;
239- const auto cliWins = parse (" --strictness-level 9 --validate x" ).strictnessLevel ;
240- clearKnownEnv ();
241-
242- expectEquals (envOnly, 3 ); // env only
243- expectEquals (cliWins, 9 ); // CLI wins
224+ const auto env = envFrom ({ { " STRICTNESS_LEVEL" , " 3" } });
225+ expectEquals (parse (" --validate x" , env).strictnessLevel , 3 ); // env only
226+ expectEquals (parse (" --strictness-level 9 --validate x" , env).strictnessLevel , 9 ); // CLI wins
244227 }
245228
246- beginTest (" Config file and precedence ( CLI > env > config > defaults) " );
229+ beginTest (" Precedence: CLI > --config > env > defaults" );
247230 {
248231 juce::TemporaryFile configFile (" .json" );
249- configFile.getFile ().replaceWithText (R"( { "strictnessLevel": 2, "timeoutMs": 12345, "numRepeats": 4 })" );
232+ configFile.getFile ().replaceWithText (R"( { "strictnessLevel": 2, "timeoutMs": 12345 })" );
250233 const auto cfg = " --config " + configFile.getFile ().getFullPathName ().quoted ();
251234
235+ const auto env6 = envFrom ({ { " STRICTNESS_LEVEL" , " 6" } });
236+
237+ // env beats defaults
238+ expectEquals (parse (" --validate x" , env6).strictnessLevel , 6 );
239+
252240 // config alone
253241 {
254242 const auto s = parse (cfg + " --validate x" );
255243 expectEquals (s.strictnessLevel , 2 );
256244 expectEquals ((juce::int64) s.timeoutMs , (juce::int64) 12345 );
257- expectEquals (s.numRepeats , 4 );
258245 }
259246
260- // env overrides config
247+ // config beats env
261248 {
262- setEnv (" STRICTNESS_LEVEL" , " 6" );
263- const auto s = parse (cfg + " --validate x" );
264- clearKnownEnv ();
265- expectEquals (s.strictnessLevel , 6 ); // env beats config
266- expectEquals ((juce::int64) s.timeoutMs , (juce::int64) 12345 ); // still from config
249+ const auto s = parse (cfg + " --validate x" , env6);
250+ expectEquals (s.strictnessLevel , 2 ); // config wins over env
251+ expectEquals ((juce::int64) s.timeoutMs , (juce::int64) 12345 );
267252 }
268253
269- // CLI overrides env and config
254+ // CLI beats config and env
270255 {
271- setEnv (" STRICTNESS_LEVEL" , " 6" );
272- const auto s = parse (cfg + " --strictness-level 9 --validate x" );
273- clearKnownEnv ();
256+ const auto s = parse (cfg + " --strictness-level 9 --validate x" , env6);
274257 expectEquals (s.strictnessLevel , 9 );
275258 expectEquals ((juce::int64) s.timeoutMs , (juce::int64) 12345 );
276259 }
277260 }
278261
262+ beginTest (" Repeatable --config merges per key, last wins" );
263+ {
264+ juce::TemporaryFile baseFile (" .json" );
265+ baseFile.getFile ().replaceWithText (R"( { "strictnessLevel": 2, "timeoutMs": 11111 })" );
266+
267+ juce::TemporaryFile overrideFile (" .json" );
268+ overrideFile.getFile ().replaceWithText (R"( { "strictnessLevel": 8 })" );
269+
270+ const auto cmd = " --config " + baseFile.getFile ().getFullPathName ().quoted ()
271+ + " --config " + overrideFile.getFile ().getFullPathName ().quoted ()
272+ + " --validate x" ;
273+
274+ const auto s = parse (cmd);
275+ expectEquals (s.strictnessLevel , 8 ); // overridden by the second file
276+ expectEquals ((juce::int64) s.timeoutMs , (juce::int64) 11111 ); // untouched, kept from the first
277+ }
278+
279279 beginTest (" Child-process command line round-trip" );
280280 {
281281 PluginTests::Options opts;
0 commit comments