Skip to content

Commit 2dfc0a9

Browse files
authored
Introduce BPSwimlane holding LaneID that keeps the same when reused. (#486)
Moved all NSTask-related code into BPSwimlane. BPSwimlane is in charge of launching tasks to run test bundles. BPSwimlane holds LaneID which keeps the same when reused, so the logs are in one lane in tracing-profile.
1 parent 76d758d commit 2dfc0a9

File tree

7 files changed

+246
-109
lines changed

7 files changed

+246
-109
lines changed

bluepill/bluepill.xcodeproj/project.pbxproj

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
0173520F23679E0A008BFA4E /* BPHTMLReportWriteTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 0173520E23679E0A008BFA4E /* BPHTMLReportWriteTests.m */; };
1313
0173521323679E87008BFA4E /* TEST-FinalReport.xml in Resources */ = {isa = PBXBuildFile; fileRef = 0173521223679E87008BFA4E /* TEST-FinalReport.xml */; };
1414
56B74BCA1E4C0A15004E6624 /* BPIntegrationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 56B74BC91E4C0A15004E6624 /* BPIntegrationTests.m */; };
15+
8A3B01062637140D00211DAB /* BPSwimlane.m in Sources */ = {isa = PBXBuildFile; fileRef = 8AEAAC242604EF420084FB85 /* BPSwimlane.m */; };
16+
8AEAAC252604EF420084FB85 /* BPSwimlane.m in Sources */ = {isa = PBXBuildFile; fileRef = 8AEAAC242604EF420084FB85 /* BPSwimlane.m */; };
1517
B3109F792151F72F00B9309C /* CoreSimulator.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B3380AEE2150BD8700752E1B /* CoreSimulator.framework */; };
1618
BA1809E91DBA8FC300D7D130 /* BPRunnerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BA1809E81DBA8FC300D7D130 /* BPRunnerTests.m */; };
1719
BA1809EB1DBA910400D7D130 /* BPAppTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BA1809EA1DBA910400D7D130 /* BPAppTests.m */; };
@@ -55,6 +57,8 @@
5557
0173520E23679E0A008BFA4E /* BPHTMLReportWriteTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BPHTMLReportWriteTests.m; sourceTree = "<group>"; };
5658
0173521223679E87008BFA4E /* TEST-FinalReport.xml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = "TEST-FinalReport.xml"; sourceTree = "<group>"; };
5759
56B74BC91E4C0A15004E6624 /* BPIntegrationTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BPIntegrationTests.m; sourceTree = "<group>"; };
60+
8AEAAC232604EF420084FB85 /* BPSwimlane.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BPSwimlane.h; sourceTree = "<group>"; };
61+
8AEAAC242604EF420084FB85 /* BPSwimlane.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BPSwimlane.m; sourceTree = "<group>"; };
5862
B3380AEE2150BD8700752E1B /* CoreSimulator.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreSimulator.framework; path = ../../../../../../../Library/Developer/PrivateFrameworks/CoreSimulator.framework; sourceTree = "<group>"; };
5963
BA1809E01DBA8FB100D7D130 /* bluepill-tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "bluepill-tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
6064
BA1809E41DBA8FB100D7D130 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@@ -213,6 +217,8 @@
213217
0173520D2366186A008BFA4E /* BPTestReportHTML.h */,
214218
C41C41F71DB14B5F001F32A2 /* BPRunner.h */,
215219
C41C41F81DB14B5F001F32A2 /* BPRunner.m */,
220+
8AEAAC232604EF420084FB85 /* BPSwimlane.h */,
221+
8AEAAC242604EF420084FB85 /* BPSwimlane.m */,
216222
BAEF4B371DAC539400E68294 /* main.m */,
217223
);
218224
path = src;
@@ -351,6 +357,7 @@
351357
isa = PBXSourcesBuildPhase;
352358
buildActionMask = 2147483647;
353359
files = (
360+
8A3B01062637140D00211DAB /* BPSwimlane.m in Sources */,
354361
BA1809E91DBA8FC300D7D130 /* BPRunnerTests.m in Sources */,
355362
BA1809FB1DBA949600D7D130 /* BPPacker.m in Sources */,
356363
BA1809EB1DBA910400D7D130 /* BPAppTests.m in Sources */,
@@ -376,6 +383,7 @@
376383
0173520C2366110D008BFA4E /* BPHTMLReportWriter.m in Sources */,
377384
BAD8484A1DBC6A83007034CF /* BPReportCollector.m in Sources */,
378385
C41C41F31DB04032001F32A2 /* BPApp.m in Sources */,
386+
8AEAAC252604EF420084FB85 /* BPSwimlane.m in Sources */,
379387
);
380388
runOnlyForDeploymentPostprocessing = 0;
381389
};

bluepill/src/BPRunner.h

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
@property (nonatomic, strong) BPConfiguration *config;
1717
@property (nonatomic, strong) NSString *bpExecutable;
18-
@property (nonatomic, strong) NSMutableArray *nsTaskList;
18+
@property (nonatomic, strong) NSMutableArray *swimlaneList;
1919
@property (nonatomic, strong) NSDictionary *testHostSimTemplates;
2020

2121
/*!
@@ -27,24 +27,14 @@
2727
+ (instancetype)BPRunnerWithConfig:(BPConfiguration *)config
2828
withBpPath:(NSString *)bpPath;
2929

30-
/*!
31-
* @discussion Create a new Simulator wrapped in a `bp` process. It will run the specified bundle and execute the block once it finishes.
32-
* @param bundle The test bundle to execute.
33-
* @param number The simulator number (will be printed in logs).
34-
* @param block A completion block to execute when the NSTask has finished.
35-
* @return An NSTask ready to be executed via [task launch] or nil in failure.
36-
*
37-
*/
38-
- (NSTask *)newTaskWithBundle:(BPXCTestFile *)bundle
39-
andNumber:(NSUInteger)number
40-
andDevice:(NSString *)deviceID
41-
andCompletionBlock:(void (^)(NSTask * ))block;
42-
4330
/**
4431
@discussion start running tests
4532
@return 1: test failures 0: pass -1: failed to run tests
4633
*/
4734
- (int)runWithBPXCTestFiles:(NSArray<BPXCTestFile *>*)xcTestFiles;
4835

49-
- (void) interrupt;
36+
- (void)interrupt;
37+
38+
- (NSUInteger)busySwimlaneCount;
39+
5040
@end

bluepill/src/BPRunner.m

Lines changed: 52 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#import "bp/src/SimulatorHelper.h"
1717
#import "BPPacker.h"
1818
#import "BPRunner.h"
19+
#import "BPSwimlane.h"
1920

2021
#include <mach/mach.h>
2122
#include <mach/mach_host.h>
@@ -69,66 +70,6 @@ + (instancetype)BPRunnerWithConfig:(BPConfiguration *)config
6970
return runner;
7071
}
7172

72-
- (NSTask *)newTaskWithBundle:(BPXCTestFile *)bundle
73-
andNumber:(NSUInteger)number
74-
andDevice:(NSString *)deviceID
75-
andCompletionBlock:(void (^)(NSTask *))block {
76-
BPConfiguration *cfg = [self.config mutableCopy];
77-
assert(cfg);
78-
cfg.appBundlePath = bundle.UITargetAppPath ?: bundle.testHostPath;
79-
cfg.testBundlePath = bundle.testBundlePath;
80-
cfg.testRunnerAppPath = bundle.UITargetAppPath ? bundle.testHostPath : nil;
81-
cfg.testCasesToSkip = bundle.skipTestIdentifiers;
82-
if (cfg.commandLineArguments) {
83-
[cfg.commandLineArguments arrayByAddingObjectsFromArray:bundle.commandLineArguments];
84-
} else {
85-
cfg.commandLineArguments = bundle.commandLineArguments;
86-
}
87-
if (cfg.environmentVariables) {
88-
NSMutableDictionary *newEnv = [[NSMutableDictionary alloc] initWithDictionary:cfg.environmentVariables];
89-
for (NSString *key in bundle.environmentVariables) {
90-
newEnv[key] = bundle.environmentVariables[key];
91-
}
92-
cfg.environmentVariables = (NSDictionary<NSString *, NSString *>*) newEnv;
93-
} else {
94-
cfg.environmentVariables = bundle.environmentVariables;
95-
}
96-
cfg.dependencies = bundle.dependencies;
97-
if (self.config.cloneSimulator) {
98-
cfg.templateSimUDID = self.testHostSimTemplates[bundle.testHostPath];
99-
}
100-
NSError *err;
101-
NSString *tmpFileName = [NSString stringWithFormat:@"%@/bluepill-%u-config",
102-
NSTemporaryDirectory(),
103-
getpid()];
104-
105-
cfg.configOutputFile = [BPUtils mkstemp:tmpFileName withError:&err];
106-
if (!cfg.configOutputFile) {
107-
fprintf(stderr, "ERROR: %s\n", [[err localizedDescription] UTF8String]);
108-
return nil;
109-
}
110-
cfg.outputDirectory = [self.config.outputDirectory
111-
stringByAppendingPathComponent:
112-
[NSString stringWithFormat:@"BP-%lu", (unsigned long)number]];
113-
cfg.testTimeEstimatesJsonFile = self.config.testTimeEstimatesJsonFile;
114-
[cfg printConfig];
115-
NSTask *task = [[NSTask alloc] init];
116-
[task setLaunchPath:self.bpExecutable];
117-
[task setArguments:@[@"-c", cfg.configOutputFile]];
118-
NSMutableDictionary *env = [[NSMutableDictionary alloc] init];
119-
[env addEntriesFromDictionary:[[NSProcessInfo processInfo] environment]];
120-
[env setObject:[NSString stringWithFormat:@"%lu", number] forKey:@"_BP_NUM"];
121-
[task setEnvironment:env];
122-
[task setTerminationHandler:^(NSTask *task) {
123-
[[NSFileManager defaultManager] removeItemAtPath:cfg.configOutputFile
124-
error:nil];
125-
[BPUtils printInfo:INFO withString:@"BP-%lu (PID %u) has finished with exit code %d.",
126-
number, [task processIdentifier], [task terminationStatus]];
127-
block(task);
128-
}];
129-
return task;
130-
}
131-
13273
- (NSTask *)newTaskToDeleteDevice:(NSString *)deviceID andNumber:(NSUInteger)number {
13374
NSTask *task = [[NSTask alloc] init];
13475
[task setLaunchPath:self.bpExecutable];
@@ -210,15 +151,18 @@ - (int)runWithBPXCTestFiles:(NSArray<BPXCTestFile *> *)xcTestFiles {
210151
[bundles addObjectsFromArray:copyBundles];
211152
}
212153
[BPUtils printInfo:INFO withString:@"Packed tests into %lu bundles", (unsigned long)[bundles count]];
213-
__block NSUInteger launchedTasks = 0;
214154
NSUInteger taskNumber = 0;
215155
__block int rc = 0;
216156

157+
self.swimlaneList = [[NSMutableArray alloc] initWithCapacity:numSims];
158+
for (NSUInteger i = 1; i <= numSims; i++) {
159+
BPSwimlane *swimlane = [BPSwimlane BPSwimlaneWithLaneID:i];
160+
[self.swimlaneList addObject:swimlane];
161+
}
162+
217163
int maxProcs = maxprocs();
218164
int seconds = 0;
219-
__block NSMutableArray *taskList = [[NSMutableArray alloc] init];
220165
__block NSMutableArray *deviceList = [[NSMutableArray alloc] init];
221-
self.nsTaskList = [[NSMutableArray alloc] init];
222166
int old_interrupted = interrupted;
223167
NSRunningApplication *app;
224168
if (_config.headlessMode == NO) {
@@ -244,46 +188,53 @@ - (int)runWithBPXCTestFiles:(NSArray<BPXCTestFile *> *)xcTestFiles {
244188
int noLaunchedTasks;
245189
int canLaunchTask;
246190
@synchronized (self) {
247-
noLaunchedTasks = (launchedTasks == 0);
248-
canLaunchTask = (launchedTasks < numSims);
191+
NSUInteger busySwimlaneCount = [self busySwimlaneCount];
192+
noLaunchedTasks = (busySwimlaneCount == 0);
193+
canLaunchTask = (busySwimlaneCount < numSims);
249194
}
250195
if (noLaunchedTasks && (bundles.count == 0 || interrupted)) break;
251196
if (bundles.count > 0 && canLaunchTask && !interrupted) {
252197
NSString *deviceID = nil;
198+
BPSwimlane *swimlane = nil;
253199
@synchronized(self) {
254200
if ([deviceList count] > 0) {
255201
deviceID = [deviceList objectAtIndex:0];
256202
[deviceList removeObjectAtIndex:0];
257203
}
204+
swimlane = [self firstIdleSwimlane];
205+
swimlane.isBusy = YES;
258206
}
259-
NSTask *task = [self newTaskWithBundle:[bundles objectAtIndex:0] andNumber:++taskNumber andDevice:deviceID andCompletionBlock:^(NSTask * _Nonnull task) {
207+
BPXCTestFile *bundle = [bundles objectAtIndex:0];
208+
[swimlane launchTaskWithBundle:bundle
209+
andConfig:self.config
210+
andLaunchPath:self.bpExecutable
211+
andNumber:++taskNumber
212+
andDevice:deviceID
213+
andTemplateSimUDID:self.testHostSimTemplates[bundle.testHostPath]
214+
andCompletionBlock:^(NSTask * _Nonnull task) {
260215
@synchronized (self) {
261-
launchedTasks--;
262-
[taskList removeObject:[NSString stringWithFormat:@"%lu", taskNumber]];
263-
[self.nsTaskList removeObject:task];
264216
rc = (rc || [task terminationStatus]);
265217
};
266218
[BPUtils printInfo:INFO withString:@"PID %d exited %d.", [task processIdentifier], [task terminationStatus]];
267219
rc = (rc || [task terminationStatus]);
268220
}];
269-
if (!task) {
270-
[BPUtils printInfo:ERROR withString:@"task is nil!"];
271-
exit(1);
272-
}
273-
[task launch];
274221
@synchronized(self) {
275-
[taskList addObject:[NSString stringWithFormat:@"%lu", taskNumber]];
276-
[self.nsTaskList addObject:task];
277222
[bundles removeObjectAtIndex:0];
278-
[BPUtils printInfo:INFO withString:@"Started BP-%lu (PID %d).", taskNumber, [task processIdentifier]];
279-
launchedTasks++;
280223
}
281224
}
282225
sleep(1);
283226
if (seconds % 30 == 0) {
284227
NSString *listString;
228+
NSUInteger launchedTasks = 0;
285229
@synchronized (self) {
286-
listString = [taskList componentsJoinedByString:@", "];
230+
NSMutableArray *taskNumberList = [[NSMutableArray alloc] init];
231+
for (BPSwimlane *swimlane in self.swimlaneList) {
232+
if (swimlane.isBusy) {
233+
launchedTasks++;
234+
[taskNumberList addObject:[NSString stringWithFormat:@"%lu", swimlane.taskNumber]];
235+
}
236+
}
237+
listString = [taskNumberList componentsJoinedByString:@", "];
287238
}
288239
[BPUtils printInfo:INFO withString:@"%lu BP(s) still running. [%@]", launchedTasks, listString];
289240
[BPUtils printInfo:INFO withString:@"Using %d of %d processes.", numprocs(), maxProcs];
@@ -319,13 +270,11 @@ - (int)runWithBPXCTestFiles:(NSArray<BPXCTestFile *> *)xcTestFiles {
319270
}
320271

321272
- (void)interrupt {
322-
if (self.nsTaskList == nil) return;
273+
if (self.swimlaneList == nil) return;
323274

324-
for (int i = 0; i < [self.nsTaskList count]; i++) {
325-
[((NSTask *)[self.nsTaskList objectAtIndex:i]) interrupt];
275+
for (BPSwimlane *swimlane in self.swimlaneList) {
276+
[swimlane interrupt];
326277
}
327-
328-
[self.nsTaskList removeAllObjects];
329278
}
330279

331280
- (void)addCounters {
@@ -390,4 +339,23 @@ - (void)addCounters {
390339
}
391340
}
392341

342+
- (NSUInteger)busySwimlaneCount {
343+
NSUInteger count = 0;
344+
for (BPSwimlane *swimlane in self.swimlaneList) {
345+
if (swimlane.isBusy) {
346+
count++;
347+
}
348+
}
349+
return count;
350+
}
351+
352+
- (BPSwimlane *)firstIdleSwimlane {
353+
for (BPSwimlane *swimlane in self.swimlaneList) {
354+
if (!swimlane.isBusy) {
355+
return swimlane;
356+
}
357+
}
358+
return nil;
359+
}
360+
393361
@end

bluepill/src/BPSwimlane.h

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Copyright 2016 LinkedIn Corporation
2+
// Licensed under the BSD 2-Clause License (the "License");
3+
// you may not use this file except in compliance with the License.
4+
// You may obtain a copy of the License at https://opensource.org/licenses/BSD-2-Clause
5+
//
6+
// Unless required by applicable law or agreed to in writing, software
7+
// distributed under the License is distributed on an "AS IS" BASIS,
8+
// WITHOUT WARRANTIES OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
9+
10+
#import <Foundation/Foundation.h>
11+
#import "bp/src/BPXCTestFile.h"
12+
#import "bp/src/BPConfiguration.h"
13+
14+
@interface BPSwimlane : NSObject
15+
16+
@property (nonatomic, assign) BOOL isBusy;
17+
@property (nonatomic, assign) NSUInteger taskNumber;
18+
19+
/*!
20+
* @discussion get a BPSwimlane to execute `bp`.
21+
* @param laneID the Lane ID that keeps the same for `bp` tasks.
22+
* @return return the BPSwimlane.
23+
*/
24+
+ (instancetype)BPSwimlaneWithLaneID:(NSUInteger)laneID;
25+
26+
/*!
27+
* @discussion Launch a NSTask to create a new Simulator wrapped in a `bp` process. It will run the specified bundle and execute the block once it finishes.
28+
* @param bundle The test bundle to execute.
29+
* @param config The BPConfiguration of the BPRunner.
30+
* @param number The simulator number (will be printed in logs). *
31+
* @param block A completion block to execute when the NSTask has finished.
32+
*
33+
*/
34+
- (void)launchTaskWithBundle:(BPXCTestFile *)bundle
35+
andConfig:(BPConfiguration *)config
36+
andLaunchPath:(NSString *)launchPath
37+
andNumber:(NSUInteger)number
38+
andDevice:(NSString *)deviceID
39+
andTemplateSimUDID:(NSString *)templateSimUDID
40+
andCompletionBlock:(void (^)(NSTask *))block;
41+
42+
- (void)interrupt;
43+
44+
@end

0 commit comments

Comments
 (0)