Start a few more helper apps as part of MTRCommissionableBrowserTests. (#35386)

Lets us test that we properly see more than one thing that's commissionable.
diff --git a/src/darwin/Framework/CHIPTests/MTRCommissionableBrowserTests.m b/src/darwin/Framework/CHIPTests/MTRCommissionableBrowserTests.m
index 6a85bfe..31ca37f 100644
--- a/src/darwin/Framework/CHIPTests/MTRCommissionableBrowserTests.m
+++ b/src/darwin/Framework/CHIPTests/MTRCommissionableBrowserTests.m
@@ -17,23 +17,21 @@
 
 #import <Matter/Matter.h>
 
-// system dependencies
-#import <XCTest/XCTest.h>
-
+#import "MTRTestCase.h"
 #import "MTRTestKeys.h"
+#import "MTRTestServerAppRunner.h"
 #import "MTRTestStorage.h"
 
 // Fixture 1: chip-all-clusters-app --KVS "$(mktemp -t chip-test-kvs)" --interface-id -1
-// Fixture 2: chip-all-clusters-app --KVS "$(mktemp -t chip-test-kvs)" --interface-id -1 \
-    --dac_provider credentials/development/commissioner_dut/struct_cd_origin_pid_vid_correct/test_case_vector.json \
-    --product-id 32768 --discriminator 3839
 
 static const uint16_t kLocalPort = 5541;
 static const uint16_t kTestVendorId = 0xFFF1u;
-static const __auto_type kTestProductIds = @[ @(0x8001u) ];
-static const __auto_type kTestDiscriminators = @[ @(3840u) ];
+static const __auto_type kTestProductIds = @[ @(0x8000u), @(0x8001u) ];
+static const __auto_type kTestDiscriminators = @[ @(2000), @(3839u), @(3840u) ];
 static const uint16_t kDiscoverDeviceTimeoutInSeconds = 10;
-static const uint16_t kExpectedDiscoveredDevicesCount = 1;
+static const uint16_t kExpectedDiscoveredDevicesCount = 3;
+
+static bool sHelperAppsStarted = false;
 
 // Singleton controller we use.
 static MTRDeviceController * sController = nil;
@@ -113,7 +111,7 @@
 }
 @end
 
-@interface MTRCommissionableBrowserTests : XCTestCase
+@interface MTRCommissionableBrowserTests : MTRTestCase
 @end
 
 @implementation MTRCommissionableBrowserTests
@@ -159,6 +157,21 @@
 - (void)setUp
 {
     [super setUp];
+
+    if (!sHelperAppsStarted) {
+        for (NSString * payload in @[
+                 @"MT:Y.K90SO527JA0648G00",
+                 @"MT:-24J0AFN00I40648G00",
+             ]) {
+            __auto_type * appRunner = [[MTRTestServerAppRunner alloc] initCrossTestWithAppName:@"all-clusters"
+                                                                                     arguments:@[]
+                                                                                       payload:payload
+                                                                                      testcase:self];
+            XCTAssertNotNil(appRunner);
+        }
+        sHelperAppsStarted = true;
+    }
+
     [self setContinueAfterFailure:NO];
 }
 
diff --git a/src/darwin/Framework/CHIPTests/MTRPairingTests.m b/src/darwin/Framework/CHIPTests/MTRPairingTests.m
index 125c5a8..38f7667 100644
--- a/src/darwin/Framework/CHIPTests/MTRPairingTests.m
+++ b/src/darwin/Framework/CHIPTests/MTRPairingTests.m
@@ -190,8 +190,6 @@
                                                                     arguments:@[
                                                                         @"--dac_provider",
                                                                         [self absolutePathFor:@"credentials/development/commissioner_dut/struct_cd_origin_pid_vid_correct/test_case_vector.json"],
-                                                                        @"--product-id",
-                                                                        @"32768",
                                                                     ]
                                                                       payload:kOnboardingPayload
                                                                      testcase:self];
diff --git a/src/darwin/Framework/CHIPTests/TestHelpers/MTRTestCase.h b/src/darwin/Framework/CHIPTests/TestHelpers/MTRTestCase.h
index c9f20e1..e3668bb 100644
--- a/src/darwin/Framework/CHIPTests/TestHelpers/MTRTestCase.h
+++ b/src/darwin/Framework/CHIPTests/TestHelpers/MTRTestCase.h
@@ -47,6 +47,12 @@
  * tearDown happens.
  */
 - (void)launchTask:(NSTask *)task;
+
+/**
+ * Launch a cross-test task.  The task will be automatically terminated when the testsuite
+ * tearDown happens.
+ */
+- (void)launchCrossTestTask:(NSTask *)task;
 #endif // HAVE_NSTASK
 
 /**
diff --git a/src/darwin/Framework/CHIPTests/TestHelpers/MTRTestCase.mm b/src/darwin/Framework/CHIPTests/TestHelpers/MTRTestCase.mm
index e8f317e..fc615cc 100644
--- a/src/darwin/Framework/CHIPTests/TestHelpers/MTRTestCase.mm
+++ b/src/darwin/Framework/CHIPTests/TestHelpers/MTRTestCase.mm
@@ -19,12 +19,42 @@
 
 #import "MTRTestCase.h"
 
+#if HAVE_NSTASK
+// Tasks that are not scoped to a specific test, but rather to a specific test suite.
+static NSMutableSet<NSTask *> * runningCrossTestTasks;
+
+static void ClearTaskSet(NSMutableSet<NSTask *> * __strong & tasks)
+{
+    for (NSTask * task in tasks) {
+        NSLog(@"Terminating task %@", task);
+        [task terminate];
+    }
+    tasks = nil;
+}
+#endif // HAVE_NSTASK
+
 @implementation MTRTestCase {
 #if HAVE_NSTASK
     NSMutableSet<NSTask *> * _runningTasks;
 #endif // NSTask
 }
 
++ (void)setUp
+{
+    [super setUp];
+
+#if HAVE_NSTASK
+    runningCrossTestTasks = [[NSMutableSet alloc] init];
+#endif // HAVE_NSTASK
+}
+
++ (void)tearDown
+{
+#if HAVE_NSTASK
+    ClearTaskSet(runningCrossTestTasks);
+#endif // HAVE_NSTASK
+}
+
 - (void)setUp
 {
 #if HAVE_NSTASK
@@ -48,11 +78,7 @@
 #endif
 
 #if HAVE_NSTASK
-    for (NSTask * task in _runningTasks) {
-        NSLog(@"Terminating task %@", task);
-        [task terminate];
-    }
-    _runningTasks = nil;
+    ClearTaskSet(_runningTasks);
 #endif // HAVE_NSTASK
 
     [super tearDown];
@@ -76,14 +102,26 @@
     XCTAssertEqual([task terminationStatus], 0);
 }
 
-- (void)launchTask:(NSTask *)task
+- (void)doLaunchTask:(NSTask *)task
 {
     NSError * launchError;
     [task launchAndReturnError:&launchError];
     XCTAssertNil(launchError);
+}
+
+- (void)launchTask:(NSTask *)task
+{
+    [self doLaunchTask:task];
 
     [_runningTasks addObject:task];
 }
+
+- (void)launchCrossTestTask:(NSTask *)task
+{
+    [self doLaunchTask:task];
+
+    [runningCrossTestTasks addObject:task];
+}
 #endif // HAVE_NSTASK
 
 - (NSString *)absolutePathFor:(NSString *)matterRootRelativePath
diff --git a/src/darwin/Framework/CHIPTests/TestHelpers/MTRTestServerAppRunner.h b/src/darwin/Framework/CHIPTests/TestHelpers/MTRTestServerAppRunner.h
index 9297b76..b54b7db 100644
--- a/src/darwin/Framework/CHIPTests/TestHelpers/MTRTestServerAppRunner.h
+++ b/src/darwin/Framework/CHIPTests/TestHelpers/MTRTestServerAppRunner.h
@@ -46,6 +46,12 @@
 - (instancetype)initWithAppName:(NSString *)name arguments:(NSArray<NSString *> *)arguments payload:(NSString *)payload testcase:(MTRTestCase *)testcase;
 
 /**
+ * Same thing, but initialize as a "cross test" helper, which is not killed at
+ * the end of the current test (but is killed at the end of the current suite).
+ */
+- (instancetype)initCrossTestWithAppName:(NSString *)name arguments:(NSArray<NSString *> *)arguments payload:(NSString *)payload testcase:(MTRTestCase *)testcase;
+
+/**
  * Get the unique index that will be used for the next initialization.  This
  * allows including that index in the arguments provided.
  *
diff --git a/src/darwin/Framework/CHIPTests/TestHelpers/MTRTestServerAppRunner.m b/src/darwin/Framework/CHIPTests/TestHelpers/MTRTestServerAppRunner.m
index 9bc9e8e..cc64d62 100644
--- a/src/darwin/Framework/CHIPTests/TestHelpers/MTRTestServerAppRunner.m
+++ b/src/darwin/Framework/CHIPTests/TestHelpers/MTRTestServerAppRunner.m
@@ -36,7 +36,7 @@
 #endif
 }
 
-- (instancetype)initWithAppName:(NSString *)name arguments:(NSArray<NSString *> *)arguments payload:(NSString *)payload testcase:(MTRTestCase *)testcase
+- (instancetype)initInternalWithAppName:(NSString *)name arguments:(NSArray<NSString *> *)arguments payload:(NSString *)payload testcase:(MTRTestCase *)testcase isCrossTest:(BOOL)isCrossTest
 {
 #if !HAVE_NSTASK
     XCTFail("Unable to start server app when we do not have NSTask");
@@ -75,6 +75,8 @@
         [NSString stringWithFormat:@"%llu", passcode.unsignedLongLongValue],
         @"--KVS",
         [NSString stringWithFormat:@"/tmp/chip-%@-kvs%u", name, _uniqueIndex],
+        @"--product-id",
+        [NSString stringWithFormat:@"%u", parsedPayload.productID.unsignedShortValue],
     ];
 
     __auto_type * allArguments = [forcedArguments arrayByAddingObjectsFromArray:arguments];
@@ -90,7 +92,11 @@
     _appTask.standardOutput = [NSFileHandle fileHandleForWritingAtPath:outFile];
     _appTask.standardError = [NSFileHandle fileHandleForWritingAtPath:errorFile];
 
-    [testcase launchTask:_appTask];
+    if (isCrossTest) {
+        [testcase launchCrossTestTask:_appTask];
+    } else {
+        [testcase launchTask:_appTask];
+    }
 
     NSLog(@"Started chip-%@-app (%@) with arguments %@ stdout=%@ and stderr=%@", name, _appTask, allArguments, outFile, errorFile);
 
@@ -98,6 +104,16 @@
 #endif // HAVE_NSTASK
 }
 
+- (instancetype)initWithAppName:(NSString *)name arguments:(NSArray<NSString *> *)arguments payload:(NSString *)payload testcase:(MTRTestCase *)testcase
+{
+    return [self initInternalWithAppName:name arguments:arguments payload:payload testcase:testcase isCrossTest:NO];
+}
+
+- (instancetype)initCrossTestWithAppName:(NSString *)name arguments:(NSArray<NSString *> *)arguments payload:(NSString *)payload testcase:(MTRTestCase *)testcase
+{
+    return [self initInternalWithAppName:name arguments:arguments payload:payload testcase:testcase isCrossTest:YES];
+}
+
 + (unsigned)nextUniqueIndex
 {
     return sAppRunnerIndex;