Skip to content

Commit

Permalink
Introduce BPSwimlane holding LaneID that keeps the same when reused. (#…
Browse files Browse the repository at this point in the history
…487)

* Introduce BPSwimlane holding LaneID that keeps the same when reused.

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.

* Disable testTwoBPInstancesWithVideo for now
  • Loading branch information
YangSun33 authored Apr 28, 2021
1 parent fd40f2c commit 9c7d7af
Show file tree
Hide file tree
Showing 7 changed files with 249 additions and 111 deletions.
8 changes: 8 additions & 0 deletions bluepill/bluepill.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
0173520F23679E0A008BFA4E /* BPHTMLReportWriteTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 0173520E23679E0A008BFA4E /* BPHTMLReportWriteTests.m */; };
0173521323679E87008BFA4E /* TEST-FinalReport.xml in Resources */ = {isa = PBXBuildFile; fileRef = 0173521223679E87008BFA4E /* TEST-FinalReport.xml */; };
56B74BCA1E4C0A15004E6624 /* BPIntegrationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 56B74BC91E4C0A15004E6624 /* BPIntegrationTests.m */; };
8A3B01062637140D00211DAB /* BPSwimlane.m in Sources */ = {isa = PBXBuildFile; fileRef = 8AEAAC242604EF420084FB85 /* BPSwimlane.m */; };
8AEAAC252604EF420084FB85 /* BPSwimlane.m in Sources */ = {isa = PBXBuildFile; fileRef = 8AEAAC242604EF420084FB85 /* BPSwimlane.m */; };
B3109F792151F72F00B9309C /* CoreSimulator.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B3380AEE2150BD8700752E1B /* CoreSimulator.framework */; };
BA1809E91DBA8FC300D7D130 /* BPRunnerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BA1809E81DBA8FC300D7D130 /* BPRunnerTests.m */; };
BA1809EB1DBA910400D7D130 /* BPAppTests.m in Sources */ = {isa = PBXBuildFile; fileRef = BA1809EA1DBA910400D7D130 /* BPAppTests.m */; };
Expand Down Expand Up @@ -55,6 +57,8 @@
0173520E23679E0A008BFA4E /* BPHTMLReportWriteTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BPHTMLReportWriteTests.m; sourceTree = "<group>"; };
0173521223679E87008BFA4E /* TEST-FinalReport.xml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = "TEST-FinalReport.xml"; sourceTree = "<group>"; };
56B74BC91E4C0A15004E6624 /* BPIntegrationTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BPIntegrationTests.m; sourceTree = "<group>"; };
8AEAAC232604EF420084FB85 /* BPSwimlane.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BPSwimlane.h; sourceTree = "<group>"; };
8AEAAC242604EF420084FB85 /* BPSwimlane.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BPSwimlane.m; sourceTree = "<group>"; };
B3380AEE2150BD8700752E1B /* CoreSimulator.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreSimulator.framework; path = ../../../../../../../Library/Developer/PrivateFrameworks/CoreSimulator.framework; sourceTree = "<group>"; };
BA1809E01DBA8FB100D7D130 /* bluepill-tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "bluepill-tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
BA1809E41DBA8FB100D7D130 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
Expand Down Expand Up @@ -213,6 +217,8 @@
0173520D2366186A008BFA4E /* BPTestReportHTML.h */,
C41C41F71DB14B5F001F32A2 /* BPRunner.h */,
C41C41F81DB14B5F001F32A2 /* BPRunner.m */,
8AEAAC232604EF420084FB85 /* BPSwimlane.h */,
8AEAAC242604EF420084FB85 /* BPSwimlane.m */,
BAEF4B371DAC539400E68294 /* main.m */,
);
path = src;
Expand Down Expand Up @@ -351,6 +357,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
8A3B01062637140D00211DAB /* BPSwimlane.m in Sources */,
BA1809E91DBA8FC300D7D130 /* BPRunnerTests.m in Sources */,
BA1809FB1DBA949600D7D130 /* BPPacker.m in Sources */,
BA1809EB1DBA910400D7D130 /* BPAppTests.m in Sources */,
Expand All @@ -376,6 +383,7 @@
0173520C2366110D008BFA4E /* BPHTMLReportWriter.m in Sources */,
BAD8484A1DBC6A83007034CF /* BPReportCollector.m in Sources */,
C41C41F31DB04032001F32A2 /* BPApp.m in Sources */,
8AEAAC252604EF420084FB85 /* BPSwimlane.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
20 changes: 5 additions & 15 deletions bluepill/src/BPRunner.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

@property (nonatomic, strong) BPConfiguration *config;
@property (nonatomic, strong) NSString *bpExecutable;
@property (nonatomic, strong) NSMutableArray *nsTaskList;
@property (nonatomic, strong) NSMutableArray *swimlaneList;
@property (nonatomic, strong) NSDictionary *testHostSimTemplates;

/*!
Expand All @@ -27,24 +27,14 @@
+ (instancetype)BPRunnerWithConfig:(BPConfiguration *)config
withBpPath:(NSString *)bpPath;

/*!
* @discussion Create a new Simulator wrapped in a `bp` process. It will run the specified bundle and execute the block once it finishes.
* @param bundle The test bundle to execute.
* @param number The simulator number (will be printed in logs).
* @param block A completion block to execute when the NSTask has finished.
* @return An NSTask ready to be executed via [task launch] or nil in failure.
*
*/
- (NSTask *)newTaskWithBundle:(BPXCTestFile *)bundle
andNumber:(NSUInteger)number
andDevice:(NSString *)deviceID
andCompletionBlock:(void (^)(NSTask * ))block;

/**
@discussion start running tests
@return 1: test failures 0: pass -1: failed to run tests
*/
- (int)runWithBPXCTestFiles:(NSArray<BPXCTestFile *>*)xcTestFiles;

- (void) interrupt;
- (void)interrupt;

- (NSUInteger)busySwimlaneCount;

@end
136 changes: 52 additions & 84 deletions bluepill/src/BPRunner.m
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#import "bp/src/SimulatorHelper.h"
#import "BPPacker.h"
#import "BPRunner.h"
#import "BPSwimlane.h"

#include <mach/mach.h>
#include <mach/mach_host.h>
Expand Down Expand Up @@ -69,66 +70,6 @@ + (instancetype)BPRunnerWithConfig:(BPConfiguration *)config
return runner;
}

- (NSTask *)newTaskWithBundle:(BPXCTestFile *)bundle
andNumber:(NSUInteger)number
andDevice:(NSString *)deviceID
andCompletionBlock:(void (^)(NSTask *))block {
BPConfiguration *cfg = [self.config mutableCopy];
assert(cfg);
cfg.appBundlePath = bundle.UITargetAppPath ?: bundle.testHostPath;
cfg.testBundlePath = bundle.testBundlePath;
cfg.testRunnerAppPath = bundle.UITargetAppPath ? bundle.testHostPath : nil;
cfg.testCasesToSkip = bundle.skipTestIdentifiers;
if (cfg.commandLineArguments) {
[cfg.commandLineArguments arrayByAddingObjectsFromArray:bundle.commandLineArguments];
} else {
cfg.commandLineArguments = bundle.commandLineArguments;
}
if (cfg.environmentVariables) {
NSMutableDictionary *newEnv = [[NSMutableDictionary alloc] initWithDictionary:cfg.environmentVariables];
for (NSString *key in bundle.environmentVariables) {
newEnv[key] = bundle.environmentVariables[key];
}
cfg.environmentVariables = (NSDictionary<NSString *, NSString *>*) newEnv;
} else {
cfg.environmentVariables = bundle.environmentVariables;
}
cfg.dependencies = bundle.dependencies;
if (self.config.cloneSimulator) {
cfg.templateSimUDID = self.testHostSimTemplates[bundle.testHostPath];
}
NSError *err;
NSString *tmpFileName = [NSString stringWithFormat:@"%@/bluepill-%u-config",
NSTemporaryDirectory(),
getpid()];

cfg.configOutputFile = [BPUtils mkstemp:tmpFileName withError:&err];
if (!cfg.configOutputFile) {
fprintf(stderr, "ERROR: %s\n", [[err localizedDescription] UTF8String]);
return nil;
}
cfg.outputDirectory = [self.config.outputDirectory
stringByAppendingPathComponent:
[NSString stringWithFormat:@"BP-%lu", (unsigned long)number]];
cfg.testTimeEstimatesJsonFile = self.config.testTimeEstimatesJsonFile;
[cfg printConfig];
NSTask *task = [[NSTask alloc] init];
[task setLaunchPath:self.bpExecutable];
[task setArguments:@[@"-c", cfg.configOutputFile]];
NSMutableDictionary *env = [[NSMutableDictionary alloc] init];
[env addEntriesFromDictionary:[[NSProcessInfo processInfo] environment]];
[env setObject:[NSString stringWithFormat:@"%lu", number] forKey:@"_BP_NUM"];
[task setEnvironment:env];
[task setTerminationHandler:^(NSTask *task) {
[[NSFileManager defaultManager] removeItemAtPath:cfg.configOutputFile
error:nil];
[BPUtils printInfo:INFO withString:@"BP-%lu (PID %u) has finished with exit code %d.",
number, [task processIdentifier], [task terminationStatus]];
block(task);
}];
return task;
}

- (NSTask *)newTaskToDeleteDevice:(NSString *)deviceID andNumber:(NSUInteger)number {
NSTask *task = [[NSTask alloc] init];
[task setLaunchPath:self.bpExecutable];
Expand Down Expand Up @@ -210,15 +151,18 @@ - (int)runWithBPXCTestFiles:(NSArray<BPXCTestFile *> *)xcTestFiles {
[bundles addObjectsFromArray:copyBundles];
}
[BPUtils printInfo:INFO withString:@"Packed tests into %lu bundles", (unsigned long)[bundles count]];
__block NSUInteger launchedTasks = 0;
NSUInteger taskNumber = 0;
__block int rc = 0;

self.swimlaneList = [[NSMutableArray alloc] initWithCapacity:numSims];
for (NSUInteger i = 1; i <= numSims; i++) {
BPSwimlane *swimlane = [BPSwimlane BPSwimlaneWithLaneID:i];
[self.swimlaneList addObject:swimlane];
}

int maxProcs = maxprocs();
int seconds = 0;
__block NSMutableArray *taskList = [[NSMutableArray alloc] init];
__block NSMutableArray *deviceList = [[NSMutableArray alloc] init];
self.nsTaskList = [[NSMutableArray alloc] init];
int old_interrupted = interrupted;
NSRunningApplication *app;
if (_config.headlessMode == NO) {
Expand All @@ -244,46 +188,53 @@ - (int)runWithBPXCTestFiles:(NSArray<BPXCTestFile *> *)xcTestFiles {
int noLaunchedTasks;
int canLaunchTask;
@synchronized (self) {
noLaunchedTasks = (launchedTasks == 0);
canLaunchTask = (launchedTasks < numSims);
NSUInteger busySwimlaneCount = [self busySwimlaneCount];
noLaunchedTasks = (busySwimlaneCount == 0);
canLaunchTask = (busySwimlaneCount < numSims);
}
if (noLaunchedTasks && (bundles.count == 0 || interrupted)) break;
if (bundles.count > 0 && canLaunchTask && !interrupted) {
NSString *deviceID = nil;
BPSwimlane *swimlane = nil;
@synchronized(self) {
if ([deviceList count] > 0) {
deviceID = [deviceList objectAtIndex:0];
[deviceList removeObjectAtIndex:0];
}
swimlane = [self firstIdleSwimlane];
swimlane.isBusy = YES;
}
NSTask *task = [self newTaskWithBundle:[bundles objectAtIndex:0] andNumber:++taskNumber andDevice:deviceID andCompletionBlock:^(NSTask * _Nonnull task) {
BPXCTestFile *bundle = [bundles objectAtIndex:0];
[swimlane launchTaskWithBundle:bundle
andConfig:self.config
andLaunchPath:self.bpExecutable
andNumber:++taskNumber
andDevice:deviceID
andTemplateSimUDID:self.testHostSimTemplates[bundle.testHostPath]
andCompletionBlock:^(NSTask * _Nonnull task) {
@synchronized (self) {
launchedTasks--;
[taskList removeObject:[NSString stringWithFormat:@"%lu", taskNumber]];
[self.nsTaskList removeObject:task];
rc = (rc || [task terminationStatus]);
};
[BPUtils printInfo:INFO withString:@"PID %d exited %d.", [task processIdentifier], [task terminationStatus]];
rc = (rc || [task terminationStatus]);
}];
if (!task) {
[BPUtils printInfo:ERROR withString:@"task is nil!"];
exit(1);
}
[task launch];
@synchronized(self) {
[taskList addObject:[NSString stringWithFormat:@"%lu", taskNumber]];
[self.nsTaskList addObject:task];
[bundles removeObjectAtIndex:0];
[BPUtils printInfo:INFO withString:@"Started BP-%lu (PID %d).", taskNumber, [task processIdentifier]];
launchedTasks++;
}
}
sleep(1);
if (seconds % 30 == 0) {
NSString *listString;
NSUInteger launchedTasks = 0;
@synchronized (self) {
listString = [taskList componentsJoinedByString:@", "];
NSMutableArray *taskNumberList = [[NSMutableArray alloc] init];
for (BPSwimlane *swimlane in self.swimlaneList) {
if (swimlane.isBusy) {
launchedTasks++;
[taskNumberList addObject:[NSString stringWithFormat:@"%lu", swimlane.taskNumber]];
}
}
listString = [taskNumberList componentsJoinedByString:@", "];
}
[BPUtils printInfo:INFO withString:@"%lu BP(s) still running. [%@]", launchedTasks, listString];
[BPUtils printInfo:INFO withString:@"Using %d of %d processes.", numprocs(), maxProcs];
Expand Down Expand Up @@ -319,13 +270,11 @@ - (int)runWithBPXCTestFiles:(NSArray<BPXCTestFile *> *)xcTestFiles {
}

- (void)interrupt {
if (self.nsTaskList == nil) return;
if (self.swimlaneList == nil) return;

for (int i = 0; i < [self.nsTaskList count]; i++) {
[((NSTask *)[self.nsTaskList objectAtIndex:i]) interrupt];
for (BPSwimlane *swimlane in self.swimlaneList) {
[swimlane interrupt];
}

[self.nsTaskList removeAllObjects];
}

- (void)addCounters {
Expand Down Expand Up @@ -390,4 +339,23 @@ - (void)addCounters {
}
}

- (NSUInteger)busySwimlaneCount {
NSUInteger count = 0;
for (BPSwimlane *swimlane in self.swimlaneList) {
if (swimlane.isBusy) {
count++;
}
}
return count;
}

- (BPSwimlane *)firstIdleSwimlane {
for (BPSwimlane *swimlane in self.swimlaneList) {
if (!swimlane.isBusy) {
return swimlane;
}
}
return nil;
}

@end
44 changes: 44 additions & 0 deletions bluepill/src/BPSwimlane.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright 2016 LinkedIn Corporation
// Licensed under the BSD 2-Clause License (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at https://opensource.org/licenses/BSD-2-Clause
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

#import <Foundation/Foundation.h>
#import "bp/src/BPXCTestFile.h"
#import "bp/src/BPConfiguration.h"

@interface BPSwimlane : NSObject

@property (nonatomic, assign) BOOL isBusy;
@property (nonatomic, assign) NSUInteger taskNumber;

/*!
* @discussion get a BPSwimlane to execute `bp`.
* @param laneID the Lane ID that keeps the same for `bp` tasks.
* @return return the BPSwimlane.
*/
+ (instancetype)BPSwimlaneWithLaneID:(NSUInteger)laneID;

/*!
* @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.
* @param bundle The test bundle to execute.
* @param config The BPConfiguration of the BPRunner.
* @param number The simulator number (will be printed in logs). *
* @param block A completion block to execute when the NSTask has finished.
*
*/
- (void)launchTaskWithBundle:(BPXCTestFile *)bundle
andConfig:(BPConfiguration *)config
andLaunchPath:(NSString *)launchPath
andNumber:(NSUInteger)number
andDevice:(NSString *)deviceID
andTemplateSimUDID:(NSString *)templateSimUDID
andCompletionBlock:(void (^)(NSTask *))block;

- (void)interrupt;

@end
Loading

0 comments on commit 9c7d7af

Please sign in to comment.