194 lines
5.9 KiB
Objective-C
194 lines
5.9 KiB
Objective-C
// Copyright 2004-present Facebook. All Rights Reserved.
|
|
|
|
#import <mach/mach_time.h>
|
|
|
|
#import <XCTest/XCTest.h>
|
|
|
|
#import "RCTContextExecutor.h"
|
|
#import "RCTUtils.h"
|
|
|
|
|
|
@interface RCTContextExecutorTests : XCTestCase
|
|
|
|
@end
|
|
|
|
@implementation RCTContextExecutorTests
|
|
{
|
|
RCTContextExecutor *_executor;
|
|
}
|
|
|
|
- (void)setUp
|
|
{
|
|
[super setUp];
|
|
_executor = [[RCTContextExecutor alloc] init];
|
|
RCTSetExecutorID(_executor);
|
|
[_executor setUp];
|
|
}
|
|
|
|
- (void)testNativeLoggingHookExceptionBehavior
|
|
{
|
|
dispatch_semaphore_t doneSem = dispatch_semaphore_create(0);
|
|
[_executor executeApplicationScript:@"var x = {toString: function() { throw 1; }}; nativeLoggingHook(x);"
|
|
sourceURL:[NSURL URLWithString:@"file://"]
|
|
onComplete:^(__unused id error){
|
|
dispatch_semaphore_signal(doneSem);
|
|
}];
|
|
dispatch_semaphore_wait(doneSem, DISPATCH_TIME_FOREVER);
|
|
[_executor invalidate];
|
|
}
|
|
|
|
static uint64_t _get_time_nanoseconds(void)
|
|
{
|
|
static struct mach_timebase_info tb_info = {0, 0};
|
|
static dispatch_once_t onceToken;
|
|
dispatch_once(&onceToken, ^{
|
|
int ret = mach_timebase_info(&tb_info);
|
|
assert(0 == ret);
|
|
});
|
|
|
|
return (mach_absolute_time() * tb_info.numer) / tb_info.denom;
|
|
}
|
|
|
|
- (void)testDeserializationPerf
|
|
{
|
|
// This test case checks the assumption that deserializing objects from JavaScript
|
|
// values one-by-one via ObjC JSC API is slower than using JSON string
|
|
// You might want to switch your tests schema to "Release" build configuration
|
|
|
|
JSContextGroupRef group = JSContextGroupCreate();
|
|
JSGlobalContextRef context = JSGlobalContextCreateInGroup(group, NULL);
|
|
id message = @[@[@1, @2, @3, @4], @[@{@"a": @1}, @{@"b": @2}], (id)kCFNull];
|
|
NSString *code = RCTJSONStringify(message, NULL);
|
|
JSStringRef script = JSStringCreateWithCFString((__bridge CFStringRef)code);
|
|
JSValueRef error = NULL;
|
|
JSValueRef value = JSEvaluateScript(context, script, NULL, NULL, 0, &error);
|
|
XCTAssertTrue(error == NULL);
|
|
|
|
id obj;
|
|
uint64_t start = _get_time_nanoseconds();
|
|
for (int i = 0; i < 10000; i++) {
|
|
JSStringRef jsonJSString = JSValueCreateJSONString(context, value, 0, nil);
|
|
NSString *jsonString = (__bridge NSString *)JSStringCopyCFString(kCFAllocatorDefault, jsonJSString);
|
|
JSStringRelease(jsonJSString);
|
|
|
|
obj = RCTJSONParse(jsonString, NULL);
|
|
}
|
|
NSLog(@"JSON Parse time: %.2fms", (_get_time_nanoseconds() - start) / 1000000.0);
|
|
|
|
JSStringRelease(script);
|
|
JSGlobalContextRelease(context);
|
|
JSContextGroupRelease(group);
|
|
}
|
|
|
|
- (void)MANUALLY_testJavaScriptCallSpeed
|
|
{
|
|
/**
|
|
* Since we almost don't change the RCTContextExecutor logic, and this test is
|
|
* very likely to become flaky (specially accross different devices) leave it
|
|
* to be run manually
|
|
*
|
|
* Previous Values: If you change the executor code, you should update this values
|
|
*/
|
|
|
|
int const runs = 4e5;
|
|
int const frequency = 10;
|
|
double const threshold = 0.1;
|
|
static double const expectedTimes[] = {
|
|
0x1.6199943826cf1p+13,
|
|
0x1.a3bc0a81551c3p+13,
|
|
0x1.d49fbb8602fe3p+13,
|
|
0x1.d1f64085ecb7bp+13,
|
|
};
|
|
|
|
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
|
|
NSString *script = @" \
|
|
var modules = { \
|
|
module: { \
|
|
method: function () { \
|
|
return true; \
|
|
} \
|
|
} \
|
|
}; \
|
|
function require(module) { \
|
|
return modules[module]; \
|
|
} \
|
|
";
|
|
|
|
[_executor executeApplicationScript:script sourceURL:[NSURL URLWithString:@"http://localhost:8081/"] onComplete:^(__unused NSError *error) {
|
|
NSMutableArray *params = [[NSMutableArray alloc] init];
|
|
id data = @1;
|
|
for (int i = 0; i < 4; i++) {
|
|
double samples[runs / frequency];
|
|
int size = runs / frequency;
|
|
double total = 0;
|
|
for (int j = 0; j < runs; j++) {
|
|
@autoreleasepool {
|
|
double start = _get_time_nanoseconds();
|
|
[_executor executeJSCall:@"module"
|
|
method:@"method"
|
|
arguments:params
|
|
context:RCTGetExecutorID(_executor)
|
|
callback:^(id json, __unused NSError *unused) {
|
|
XCTAssert([json isEqual:@YES], @"Invalid return");
|
|
}];
|
|
double run = _get_time_nanoseconds() - start;
|
|
if ((j % frequency) == frequency - 1) { // Warmup
|
|
total += run;
|
|
samples[j/frequency] = run;
|
|
}
|
|
}
|
|
}
|
|
|
|
double mean = total / size;
|
|
double variance = 0;
|
|
|
|
for (int j = 0; j < size; j++) {
|
|
variance += pow(samples[j] - mean, 2);
|
|
}
|
|
variance /= size;
|
|
|
|
double standardDeviation = sqrt(variance);
|
|
double marginOfError = standardDeviation * 1.645;
|
|
|
|
double lower = mean - marginOfError;
|
|
double upper = mean + marginOfError;
|
|
|
|
int s = 0;
|
|
total = 0;
|
|
for (int j = 0; j < size; j++) {
|
|
double v = samples[j];
|
|
if (v >= lower && v <= upper) {
|
|
samples[s++] = v;
|
|
total += v;
|
|
}
|
|
}
|
|
mean = total / s;
|
|
|
|
lower = mean * (1.0 - threshold);
|
|
upper = mean * (1.0 + threshold);
|
|
|
|
double expected = expectedTimes[i];
|
|
|
|
NSLog(@"Previous: %lf, New: %f -> %a", expected, mean, mean);
|
|
if (upper < expected) {
|
|
NSLog(@"You made JS calls with %d argument(s) %.2lf%% faster :) - Remember to update the tests with the new value: %a",
|
|
i, (1 - (double)mean / expected) * 100, mean);
|
|
}
|
|
|
|
|
|
XCTAssertTrue(lower < expected, @"You made JS calls with %d argument(s) %.2lf%% slower :( - If that's *really* necessary, update the tests with the new value: %a",
|
|
i, ((double)mean / expected - 1) * 100, mean);
|
|
|
|
[params addObject:data];
|
|
}
|
|
dispatch_semaphore_signal(semaphore);
|
|
}];
|
|
|
|
while (dispatch_semaphore_wait(semaphore, DISPATCH_TIME_NOW)) {
|
|
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
|
|
beforeDate:[NSDate dateWithTimeIntervalSinceNow:10]];
|
|
}
|
|
}
|
|
|
|
@end
|