/* * * Copyright 2015 gRPC authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ #import <UIKit/UIKit.h> #import <XCTest/XCTest.h> #import <RxLibrary/GRXBufferedPipe.h> #import <RxLibrary/GRXWriteable.h> #import <RxLibrary/GRXWriter.h> #define TEST_TIMEOUT 1 // A mock of a GRXSingleValueHandler block that can be queried for how many times it was called and // what were the last values passed to it. // // TODO(jcanizales): Move this to a test util library, and add tests for it. @interface CapturingSingleValueHandler : NSObject @property(nonatomic, readonly) void (^block)(id value, NSError *errorOrNil); @property(nonatomic, readonly) NSUInteger timesCalled; @property(nonatomic, readonly) id value; @property(nonatomic, readonly) NSError *errorOrNil; + (instancetype)handler; @end @implementation CapturingSingleValueHandler + (instancetype)handler { return [[self alloc] init]; } - (GRXSingleHandler)block { return ^(id value, NSError *errorOrNil) { ++_timesCalled; _value = value; _errorOrNil = errorOrNil; }; } @end // TODO(jcanizales): Split into one file per tested class. @interface RxLibraryUnitTests : XCTestCase @end @implementation RxLibraryUnitTests + (void)setUp { NSLog(@"GRPCClientTests Started"); } #pragma mark Writeable - (void)testWriteableSingleHandlerIsCalledForValue { // Given: CapturingSingleValueHandler *handler = [CapturingSingleValueHandler handler]; id anyValue = @7; // If: id<GRXWriteable> writeable = [GRXWriteable writeableWithSingleHandler:handler.block]; [writeable writeValue:anyValue]; [writeable writesFinishedWithError:nil]; // Then: XCTAssertEqual(handler.timesCalled, 1); XCTAssertEqualObjects(handler.value, anyValue); XCTAssertEqualObjects(handler.errorOrNil, nil); } - (void)testWriteableSingleHandlerIsCalledForError { // Given: CapturingSingleValueHandler *handler = [CapturingSingleValueHandler handler]; NSError *anyError = [NSError errorWithDomain:@"domain" code:7 userInfo:nil]; // If: id<GRXWriteable> writeable = [GRXWriteable writeableWithSingleHandler:handler.block]; [writeable writesFinishedWithError:anyError]; // Then: XCTAssertEqual(handler.timesCalled, 1); XCTAssertEqualObjects(handler.value, nil); XCTAssertEqualObjects(handler.errorOrNil, anyError); } - (void)testWriteableSingleHandlerIsCalledOnlyOnce_ValueThenError { // Given: CapturingSingleValueHandler *handler = [CapturingSingleValueHandler handler]; id anyValue = @7; NSError *anyError = [NSError errorWithDomain:@"domain" code:7 userInfo:nil]; // If: id<GRXWriteable> writeable = [GRXWriteable writeableWithSingleHandler:handler.block]; [writeable writeValue:anyValue]; [writeable writesFinishedWithError:anyError]; // Then: XCTAssertEqual(handler.timesCalled, 1); XCTAssertEqualObjects(handler.value, anyValue); XCTAssertEqualObjects(handler.errorOrNil, nil); } - (void)testWriteableSingleHandlerIsCalledOnlyOnce_ValueThenValue { // Given: CapturingSingleValueHandler *handler = [CapturingSingleValueHandler handler]; id anyValue = @7; // If: id<GRXWriteable> writeable = [GRXWriteable writeableWithSingleHandler:handler.block]; [writeable writeValue:anyValue]; [writeable writeValue:anyValue]; [writeable writesFinishedWithError:nil]; // Then: XCTAssertEqual(handler.timesCalled, 1); XCTAssertEqualObjects(handler.value, anyValue); XCTAssertEqualObjects(handler.errorOrNil, nil); } - (void)testWriteableSingleHandlerFailsOnEmptyWriter { // Given: CapturingSingleValueHandler *handler = [CapturingSingleValueHandler handler]; // If: id<GRXWriteable> writeable = [GRXWriteable writeableWithSingleHandler:handler.block]; [writeable writesFinishedWithError:nil]; // Then: XCTAssertEqual(handler.timesCalled, 1); XCTAssertEqualObjects(handler.value, nil); XCTAssertNotNil(handler.errorOrNil); } #pragma mark BufferedPipe - (void)testBufferedPipePropagatesValue { __weak XCTestExpectation *expectation = [self expectationWithDescription:@"Response received"]; // Given: CapturingSingleValueHandler *handler = [CapturingSingleValueHandler handler]; id<GRXWriteable> writeable = [GRXWriteable writeableWithSingleHandler:^(id value, NSError *errorOrNil) { handler.block(value, errorOrNil); [expectation fulfill]; }]; id anyValue = @7; // If: GRXBufferedPipe *pipe = [GRXBufferedPipe pipe]; [pipe startWithWriteable:writeable]; [pipe writeValue:anyValue]; [pipe writesFinishedWithError:nil]; // Then: [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil]; XCTAssertEqual(handler.timesCalled, 1); XCTAssertEqualObjects(handler.value, anyValue); XCTAssertEqualObjects(handler.errorOrNil, nil); } - (void)testBufferedPipePropagatesError { __weak XCTestExpectation *expectation = [self expectationWithDescription:@"Response received"]; // Given: CapturingSingleValueHandler *handler = [CapturingSingleValueHandler handler]; id<GRXWriteable> writeable = [GRXWriteable writeableWithSingleHandler:^(id value, NSError *errorOrNil) { handler.block(value, errorOrNil); [expectation fulfill]; }]; NSError *anyError = [NSError errorWithDomain:@"domain" code:7 userInfo:nil]; // If: GRXBufferedPipe *pipe = [GRXBufferedPipe pipe]; [pipe startWithWriteable:writeable]; [pipe writesFinishedWithError:anyError]; // Then: [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil]; XCTAssertEqual(handler.timesCalled, 1); XCTAssertEqualObjects(handler.value, nil); XCTAssertEqualObjects(handler.errorOrNil, anyError); } - (void)testBufferedPipeFinishWriteWhilePaused { __weak XCTestExpectation *expectation = [self expectationWithDescription:@"Response received"]; // Given: CapturingSingleValueHandler *handler = [CapturingSingleValueHandler handler]; id<GRXWriteable> writeable = [GRXWriteable writeableWithSingleHandler:^(id value, NSError *errorOrNil) { handler.block(value, errorOrNil); [expectation fulfill]; }]; id anyValue = @7; // If: GRXBufferedPipe *pipe = [GRXBufferedPipe pipe]; // Write something, then finish [pipe writeValue:anyValue]; [pipe writesFinishedWithError:nil]; // then start the writeable [pipe startWithWriteable:writeable]; // Then: [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil]; XCTAssertEqual(handler.timesCalled, 1); XCTAssertEqualObjects(handler.value, anyValue); XCTAssertEqualObjects(handler.errorOrNil, nil); } #define WRITE_ROUNDS (1000) - (void)testBufferedPipeResumeWhenDealloc { id anyValue = @7; id<GRXWriteable> writeable = [GRXWriteable writeableWithSingleHandler:^(id value, NSError *errorOrNil){ }]; // Release after alloc; GRXBufferedPipe *pipe = [GRXBufferedPipe pipe]; pipe = nil; // Release after write but before start pipe = [GRXBufferedPipe pipe]; for (int i = 0; i < WRITE_ROUNDS; i++) { [pipe writeValue:anyValue]; } pipe = nil; // Release after start but not write pipe = [GRXBufferedPipe pipe]; [pipe startWithWriteable:writeable]; pipe = nil; // Release after start and write pipe = [GRXBufferedPipe pipe]; for (int i = 0; i < WRITE_ROUNDS; i++) { [pipe writeValue:anyValue]; } [pipe startWithWriteable:writeable]; pipe = nil; // Release after start, write and pause pipe = [GRXBufferedPipe pipe]; [pipe startWithWriteable:writeable]; for (int i = 0; i < WRITE_ROUNDS; i++) { [pipe writeValue:anyValue]; } pipe.state = GRXWriterStatePaused; for (int i = 0; i < WRITE_ROUNDS; i++) { [pipe writeValue:anyValue]; } pipe = nil; // Release after start, write, pause and finish pipe = [GRXBufferedPipe pipe]; [pipe startWithWriteable:writeable]; for (int i = 0; i < WRITE_ROUNDS; i++) { [pipe writeValue:anyValue]; } pipe.state = GRXWriterStatePaused; for (int i = 0; i < WRITE_ROUNDS; i++) { [pipe writeValue:anyValue]; } [pipe finishWithError:nil]; pipe = nil; // Release after start, write, pause, finish and resume pipe = [GRXBufferedPipe pipe]; [pipe startWithWriteable:writeable]; for (int i = 0; i < WRITE_ROUNDS; i++) { [pipe writeValue:anyValue]; } pipe.state = GRXWriterStatePaused; for (int i = 0; i < WRITE_ROUNDS; i++) { [pipe writeValue:anyValue]; } [pipe finishWithError:nil]; pipe.state = GRXWriterStateStarted; pipe = nil; } @end