Skip to content

Commit

Permalink
Add Task from stdin data.
Browse files Browse the repository at this point in the history
Summary: Adds the ability to pass in some `NSData` to `stdin` directly rather than having to pump it manually from above. This makes the data passing asynchronous so the caller does not have to worry about blocking the `write` whilst pushing the data through the task. For processes that stay alive for the duration of their input, this is desirable as the process will terminate when the input ends and all the `NSData` has been written

Reviewed By: asm89

Differential Revision: D6772052

fbshipit-source-id: 1a5c0a1ae1dad53bc916e98238e8a55420eefa13
  • Loading branch information
lawrencelomax authored and facebook-github-bot committed Jan 24, 2018
1 parent 21735b2 commit 933a475
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 12 deletions.
17 changes: 8 additions & 9 deletions FBControlCore/Tasks/FBTask.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@

NS_ASSUME_NONNULL_BEGIN

@protocol FBFileConsumer;
@class FBTaskConfiguration;

/**
Expand Down Expand Up @@ -53,23 +52,23 @@ extern NSString *const FBTaskErrorDomain;
/**
Returns the stdout of the task.
May be called from any thread.
The types of these values are defined in FBTaskConfiguration.
The valid types for these values are the wrapped types in FBProcessOutput.
*/
@property (nonatomic, strong, nullable, readonly) id stdOut;

/**
Returns the stdout of the process:
The types of these values are defined in FBTaskConfiguration.
Returns the stdout of the task.
May be called from any thread.
The valid types for these values are the wrapped types in FBProcessOutput.
*/
@property (nonatomic, strong, nullable, readonly) id stdErr;

/**
Returns a consumer for the stdin.
This will only exist if:
- The Task is Configured to do so.
- The Task is running.
Returns the stdin of the task.
May be called from any thread.
The valid types for these values are the wrapped types in FBProcessInput.
*/
@property (nonatomic, strong, nullable, readonly) id<FBFileConsumer> stdIn;
@property (nonatomic, strong, nullable, readonly) id stdIn;

/**
Returns the Error associated with the task (if any). May be called from any thread.
Expand Down
8 changes: 8 additions & 0 deletions FBControlCore/Tasks/FBTaskBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,14 @@ NS_ASSUME_NONNULL_BEGIN
*/
- (instancetype)withStdInConnected;

/**
Creates a File Consumer for stdin.
@param data the data to send.
@return the reciver, for chaining.
*/
- (instancetype)withStdInFromData:(NSData *)data;

/**
The Set of Return Codes that are considered non-erroneous.
Expand Down
6 changes: 6 additions & 0 deletions FBControlCore/Tasks/FBTaskBuilder.m
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,12 @@ - (instancetype)withStdInConnected
return self;
}

- (instancetype)withStdInFromData:(NSData *)data
{
self.stdIn = [FBProcessInput inputFromData:data];
return self;
}

- (instancetype)withAcceptableTerminationStatusCodes:(NSSet<NSNumber *> *)statusCodes
{
NSParameterAssert(statusCodes);
Expand Down
8 changes: 8 additions & 0 deletions FBControlCore/Utility/FBProcessStream.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,14 @@ extern FBiOSTargetFutureType const FBiOSTargetFutureTypeProcessOutput;
*/
+ (FBProcessInput<id<FBFileConsumer>> *)inputProducingConsumer;

/**
An Input container that connects data to the iput.
@param data the data to send.
@return a Process Input instance.
*/
+ (FBProcessInput<NSData *> *)inputFromData:(NSData *)data;

#pragma mark Properties

/**
Expand Down
67 changes: 64 additions & 3 deletions FBControlCore/Utility/FBProcessStream.m
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,25 @@ @interface FBProcessOutput_String : FBProcessOutput_Data

@end

@interface FBProcessInput () <FBFileConsumer>
@interface FBProcessInput ()

@property (nonatomic, strong, nullable, readonly) NSPipe *pipe;
@property (nonatomic, strong, nullable, readonly) id<FBFileConsumer> writer;

@end

@interface FBProcessInput_Consumer : FBProcessInput <FBFileConsumer>

@end

@interface FBProcessInput_Data : FBProcessInput

- (instancetype)initWithData:(NSData *)data;

@property (nonatomic, strong, readonly) NSData *data;

@end

@implementation FBProcessOutput

#pragma mark Initializers
Expand Down Expand Up @@ -409,7 +421,12 @@ @implementation FBProcessInput

+ (FBProcessInput<id<FBFileConsumer>> *)inputProducingConsumer
{
return [[FBProcessInput alloc] init];
return [[FBProcessInput_Consumer alloc] init];
}

+ (FBProcessInput<NSData *> *)inputFromData:(NSData *)data
{
return [[FBProcessInput_Data alloc] initWithData:data];
}

#pragma mark FBStandardStream
Expand Down Expand Up @@ -462,9 +479,14 @@ @implementation FBProcessInput

- (id<FBFileConsumer>)contents
{
return self;
NSAssert(NO, @"-[%@ %@] is abstract and should be overridden", NSStringFromClass(self.class), NSStringFromSelector(_cmd));
return nil;
}

@end

@implementation FBProcessInput_Consumer

#pragma mark FBStandardStream

- (void)consumeData:(NSData *)data
Expand All @@ -478,4 +500,43 @@ - (void)consumeEndOfFile
[self detach];
}

- (id<FBFileConsumer>)contents
{
return self;
}

@end

@implementation FBProcessInput_Data

- (instancetype)initWithData:(NSData *)data
{
self = [super init];
if (!self) {
return nil;
}

_data = data;

return self;
}

#pragma mark FBStandardStream

- (FBFuture<NSPipe *> *)attachToPipeOrFileHandle
{
return [[super
attachToPipeOrFileHandle]
onQueue:FBProcessOutput.workQueue map:^(NSPipe *pipe) {
[self.writer consumeData:self.data];
[self.writer consumeEndOfFile];
return pipe;
}];
}

- (NSData *)contents
{
return self.data;
}

@end
19 changes: 19 additions & 0 deletions FBControlCoreTests/Tests/Integration/FBTaskTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -212,4 +212,23 @@ - (void)testInputReading
XCTAssertEqualObjects(expected, task.stdOut);
}

- (void)testInputFromData
{
NSData *expected = [@"FOO BAR BAZ" dataUsingEncoding:NSUTF8StringEncoding];

FBTask *task = [[[[[FBTaskBuilder
withLaunchPath:@"/bin/cat" arguments:@[]]
withStdInFromData:expected]
withStdOutInMemoryAsData]
withStdErrToDevNull]
startSynchronously];

NSError *error = nil;
BOOL waitSuccess = [task.completed awaitWithTimeout:2 error:&error] != nil;
XCTAssertNil(error);
XCTAssertTrue(waitSuccess);

XCTAssertEqualObjects(expected, task.stdOut);
}

@end

0 comments on commit 933a475

Please sign in to comment.