Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add wrappers for native XCTest video recorder #858

Merged
merged 5 commits into from
Mar 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions PrivateHeaders/XCTest/XCTRunnerDaemonSession.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,14 @@
@property(readonly) _Bool supportsLocationSimulation;
#endif

// Since Xcode 15.0-beta1
- (void)stopScreenRecordingWithUUID:(NSUUID *)arg1
withReply:(void (^)(NSError *))arg2;
- (void)startScreenRecordingWithRequest:(id/* XCTScreenRecordingRequest */)arg1
withReply:(void (^)(id/* XCTAttachmentFutureMetadata */, NSError *))arg2;
- (_Bool)supportsScreenRecording;
- (_Bool)preferScreenshotsOverScreenRecordings;

// Since Xcode 10.2
- (void)launchApplicationWithPath:(NSString *)arg1
bundleID:(NSString *)arg2
Expand Down
60 changes: 60 additions & 0 deletions WebDriverAgent.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

20 changes: 20 additions & 0 deletions WebDriverAgentLib/Commands/FBVideoCommands.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/

#import <Foundation/Foundation.h>

#import <WebDriverAgentLib/FBCommandHandler.h>

NS_ASSUME_NONNULL_BEGIN

@interface FBVideoCommands : NSObject <FBCommandHandler>

@end

NS_ASSUME_NONNULL_END
85 changes: 85 additions & 0 deletions WebDriverAgentLib/Commands/FBVideoCommands.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/

#import "FBVideoCommands.h"

#import "FBRouteRequest.h"
#import "FBScreenRecordingContainer.h"
#import "FBScreenRecordingPromise.h"
#import "FBScreenRecordingRequest.h"
#import "FBSession.h"
#import "FBXCTestDaemonsProxy.h"

const NSUInteger DEFAULT_FPS = 24;
const NSUInteger DEFAULT_CODEC = 0;

@implementation FBVideoCommands

+ (NSArray *)routes
{
return
@[
[[FBRoute POST:@"/wda/video/start"] respondWithTarget:self action:@selector(handleStartVideoRecording:)],
[[FBRoute POST:@"/wda/video/stop"] respondWithTarget:self action:@selector(handleStopVideoRecording:)],
[[FBRoute GET:@"/wda/video"] respondWithTarget:self action:@selector(handleGetVideoRecording:)],

[[FBRoute POST:@"/wda/video/start"].withoutSession respondWithTarget:self action:@selector(handleStartVideoRecording:)],
[[FBRoute POST:@"/wda/video/stop"].withoutSession respondWithTarget:self action:@selector(handleStopVideoRecording:)],
[[FBRoute GET:@"/wda/video"].withoutSession respondWithTarget:self action:@selector(handleGetVideoRecording:)],
KazuCocoa marked this conversation as resolved.
Show resolved Hide resolved
];
}

+ (id<FBResponsePayload>)handleStartVideoRecording:(FBRouteRequest *)request
{
FBScreenRecordingPromise *activeScreenRecording = FBScreenRecordingContainer.sharedInstance.screenRecordingPromise;
if (nil != activeScreenRecording) {
return FBResponseWithObject([FBScreenRecordingContainer.sharedInstance toDictionary] ?: [NSNull null]);
}

NSNumber *fps = (NSNumber *)request.arguments[@"fps"] ?: @(DEFAULT_FPS);
NSNumber *codec = (NSNumber *)request.arguments[@"codec"] ?: @(DEFAULT_CODEC);
FBScreenRecordingRequest *recordingRequest = [[FBScreenRecordingRequest alloc] initWithFps:fps.integerValue
codec:codec.longLongValue];
NSError *error;
FBScreenRecordingPromise* promise = [FBXCTestDaemonsProxy startScreenRecordingWithRequest:recordingRequest
error:&error];
if (nil == promise) {
[FBScreenRecordingContainer.sharedInstance reset];
return FBResponseWithUnknownError(error);
}
[FBScreenRecordingContainer.sharedInstance storeScreenRecordingPromise:promise
fps:fps.integerValue
codec:codec.longLongValue];
return FBResponseWithObject([FBScreenRecordingContainer.sharedInstance toDictionary]);
}

+ (id<FBResponsePayload>)handleStopVideoRecording:(FBRouteRequest *)request
{
FBScreenRecordingPromise *activeScreenRecording = FBScreenRecordingContainer.sharedInstance.screenRecordingPromise;
if (nil == activeScreenRecording) {
return FBResponseWithOK();
}

NSUUID *recordingId = activeScreenRecording.identifier;
NSDictionary *response = [FBScreenRecordingContainer.sharedInstance toDictionary];
NSError *error;
if (![FBXCTestDaemonsProxy stopScreenRecordingWithUUID:recordingId error:&error]) {
[FBScreenRecordingContainer.sharedInstance reset];
return FBResponseWithUnknownError(error);
}
[FBScreenRecordingContainer.sharedInstance reset];
return FBResponseWithObject(response);
}

+ (id<FBResponsePayload>)handleGetVideoRecording:(FBRouteRequest *)request
{
return FBResponseWithObject([FBScreenRecordingContainer.sharedInstance toDictionary] ?: [NSNull null]);
}

@end
57 changes: 57 additions & 0 deletions WebDriverAgentLib/Routing/FBScreenRecordingContainer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/**
*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@class FBScreenRecordingPromise;

@interface FBScreenRecordingContainer : NSObject

/** The amount of video FPS */
@property (readonly, nonatomic) NSUInteger fps;
/** Codec to use, where 0 is h264, 1 - HEVC */
@property (readonly, nonatomic) long long codec;
/** Keep the currently active screen resording promise. Equals to nil if no active screen recordings are running */
@property (readonly, nonatomic, nullable) FBScreenRecordingPromise* screenRecordingPromise;
/** The timestamp of the video startup as Unix float seconds */
@property (readonly, nonatomic, nullable) NSNumber *startedAt;

/**
@return singleton instance
*/
+ (instancetype)sharedInstance;

/**
Keeps current screen recording promise

@param screenRecordingPromise a promise to set
@param fps FPS value
@param codec Codec value
*/
- (void)storeScreenRecordingPromise:(FBScreenRecordingPromise *)screenRecordingPromise
fps:(NSUInteger)fps
codec:(long long)codec;
/**
Resets the current screen recording promise
*/
- (void)reset;

/**
Transforms the container content to a dictionary.

@return May return nil if no screen recording is currently running
*/
- (nullable NSDictionary *)toDictionary;

@end

NS_ASSUME_NONNULL_END
73 changes: 73 additions & 0 deletions WebDriverAgentLib/Routing/FBScreenRecordingContainer.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/**
*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/

#import "FBScreenRecordingContainer.h"

#import "FBScreenRecordingPromise.h"

@interface FBScreenRecordingContainer ()

@property (readwrite) NSUInteger fps;
@property (readwrite) long long codec;
@property (readwrite) FBScreenRecordingPromise* screenRecordingPromise;
@property (readwrite) NSNumber *startedAt;

@end

@implementation FBScreenRecordingContainer

+ (instancetype)sharedInstance
{
static FBScreenRecordingContainer *instance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[self alloc] init];
});
return instance;
}

- (void)storeScreenRecordingPromise:(FBScreenRecordingPromise *)screenRecordingPromise
fps:(NSUInteger)fps
codec:(long long)codec;
{
self.fps = fps;
self.codec = codec;
self.screenRecordingPromise = screenRecordingPromise;
self.startedAt = @([NSDate.date timeIntervalSince1970]);
}

- (void)reset;
{
self.fps = 0;
self.codec = 0;
if (nil != self.screenRecordingPromise) {
[XCTContext runActivityNamed:@"Video Cleanup" block:^(id<XCTActivity> activity){
[activity addAttachment:(XCTAttachment *)self.screenRecordingPromise.nativePromise];
}];
self.screenRecordingPromise = nil;
}
self.startedAt = nil;
}

- (nullable NSDictionary *)toDictionary
{
if (nil == self.screenRecordingPromise) {
return nil;
}

return @{
@"fps": @(self.fps),
@"codec": @(self.codec),
@"uuid": [self.screenRecordingPromise identifier].UUIDString ?: [NSNull null],
@"startedAt": self.startedAt ?: [NSNull null],
};
}

@end
30 changes: 30 additions & 0 deletions WebDriverAgentLib/Routing/FBScreenRecordingPromise.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/

#import <XCTest/XCTest.h>

NS_ASSUME_NONNULL_BEGIN

@interface FBScreenRecordingPromise : NSObject

/** Unique identiifier of the video recording, also used as the default file name */
@property (nonatomic, readonly) NSUUID *identifier;
/** Native screen recording promise */
@property (nonatomic, readonly) id nativePromise;

/**
Creates a wrapper object for a native screen recording promise

@param promise Native promise object to be wrapped
*/
- (instancetype)initWithNativePromise:(id)promise;

@end

NS_ASSUME_NONNULL_END
32 changes: 32 additions & 0 deletions WebDriverAgentLib/Routing/FBScreenRecordingPromise.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/**
*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/

#import "FBScreenRecordingPromise.h"

@interface FBScreenRecordingPromise ()
@property (readwrite) id nativePromise;
@end

@implementation FBScreenRecordingPromise

- (instancetype)initWithNativePromise:(id)promise
{
if ((self = [super init])) {
self.nativePromise = promise;
}
return self;
}

- (NSUUID *)identifier
{
return (NSUUID *)[self.nativePromise valueForKey:@"_UUID"];
}

@end
40 changes: 40 additions & 0 deletions WebDriverAgentLib/Routing/FBScreenRecordingRequest.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/

#import <XCTest/XCTest.h>

NS_ASSUME_NONNULL_BEGIN

@interface FBScreenRecordingRequest : NSObject

/** The amount of video FPS */
@property (readonly, nonatomic) NSUInteger fps;
/** Codec to use, where 0 is h264, 1 - HEVC */
@property (readonly, nonatomic) long long codec;

/**
Creates a custom wrapper for a screen recording reqeust

@param fps FPS value, see baove
@param codec Codex value, see above
*/
- (instancetype)initWithFps:(NSUInteger)fps codec:(long long)codec;

/**
Transforms the current wrapper instance to a native object,
which is ready to be passed to XCTest APIs

@param error If there was a failure converting the instance to a native object
@returns Native object instance
*/
- (nullable id)toNativeRequestWithError:(NSError **)error;

@end

NS_ASSUME_NONNULL_END
Loading
Loading