text:
    [self replicate: REMOTE push: PUSH filter: FILTER docIDs: DOCIDS expectError: ERR]

#define replic8Continuous(DB, REMOTE, PUSH, FILTER, OPTIONS, ERR) \
    [self replicateContinuous: REMOTE push: PUSH filter: FILTER options: OPTIONS expectError: ERR]

@interface CBLManager (Seekrit)
- (CBLStatus) parseReplicatorProperties: (NSDictionary*)properties
                            toDatabase: (CBLDatabase**)outDatabase   // may be NULL
                                remote: (NSURL**)outRemote          // may be NULL
                                isPush: (BOOL*)outIsPush
                          createTarget: (BOOL*)outCreateTarget
                               headers: (NSDictionary**)outHeaders
                            authorizer: (id<CBLAuthorizer>*)outAuthorizer;
@end

@interface ReplicatorInternal_Tests : CBLTestCaseWithDB
@end

@implementation ReplicatorInternal_Tests

- (void) test_01_Pusher {
    RequireTestCase(CBLDatabase);
    __block int filterCalls = 0;
    __weak ReplicatorInternal_Tests* weakSelf = self;
    [db setFilterNamed: @"filter" asBlock: ^BOOL(CBLSavedRevision *revision, NSDictionary* params) {
        ReplicatorInternal_Tests* self = weakSelf;
        Log(@"Test filter called with params = %@", params);
        Log(@"Rev = %@, properties = %@", revision, revision.properties);
        Assert(revision.properties);
        ++filterCalls;
        return YES;
    }];

    // Create some documents:
    NSMutableDictionary* props = $mdict({@"_id", @"doc1"},
                                        {@"foo", @1}, {@"bar", $false});
    CBLStatus status;
    CBL_Revision* rev1 = [db putRevision: [CBL_MutableRevision revisionWithProperties: props]
                        prevRevisionID: nil allowConflict: NO status: &status];
    AssertEq(status, kCBLStatusCreated);

    props[@"_rev"] = rev1.revID;
    props[@"UPDATED"] = $true;
    CBL_Revision* rev2 = [db putRevision: [CBL_MutableRevision revisionWithProperties: props]
                        prevRevisionID: rev1.revID allowConflict: NO status: &status];
    AssertEq(status, kCBLStatusCreated);

    props = $mdict({@"_id", @"doc2"},
                   {@"baz", @(666)}, {@"fnord", $true});
    [db putRevision: [CBL_MutableRevision revisionWithProperties: props]
                        prevRevisionID: nil allowConflict: NO status: &status];
    AssertEq(status, kCBLStatusCreated);
#pragma unused(rev2)

    // Push them to the remote:
    NSURL* remoteDB = [self remoteTestDBURL: kScratchDBName];
    if (!remoteDB)
        return;
    [self eraseRemoteDB: remoteDB];
    id lastSeq = replic8(db, remoteDB, YES, @"filter", nil, nil);
    AssertEqual(lastSeq, @"3");
    AssertEq(filterCalls, 2);
}

- (void) test_02_Puller {
    RequireTestCase(01_Pusher);
    NSURL* remoteURL = [self remoteTestDBURL: kScratchDBName];
    if (!remoteURL)
        return;

    id lastSeq = replic8(db, remoteURL, NO, nil, nil, nil);
    AssertEqual(lastSeq, @3);

    AssertEq(db.documentCount, 2u);
    AssertEq(db.lastSequenceNumber, (self.isSQLiteDB ? 3 : 2));

    // Replicate again; should complete but add no revisions:
    Log(@"Second replication, should get no more revs:");
    replic8(db, ([self remoteTestDBURL: kScratchDBName]), NO, nil, nil, nil);
    AssertEq(db.lastSequenceNumber, (self.isSQLiteDB ? 3 : 2));

    CBL_Revision* doc = [db getDocumentWithID: @"doc1" revisionID: nil];
    Assert(doc);
    Assert([doc.revID hasPrefix: @"2-"]);
    AssertEqual(doc[@"foo"], @1);

    doc = [db getDocumentWithID: @"doc2" revisionID: nil];
    Assert(doc);
    Assert([doc.revID hasPrefix: @"1-"]);
    AssertEqual(doc[@"fnord"], $true);
}

- (void) test_03_Puller_Continuous {
    RequireTestCase(02_Puller);
    NSURL* remoteURL = [self remoteTestDBURL: kScratchDBName];
    if (!remoteURL)
        return;

    id lastSeq = replic8Continuous(db, remoteURL, NO, nil, nil, nil);
    AssertEqual(lastSeq, @3);

    AssertEq(db.documentCount, 2u);
    AssertEq(db.lastSequenceNumber, (self.isSQLiteDB ? 3 : 2));

    // Replicate again; should complete but add no revisions:
    Log(@"Second replication, should get no more revs:");
    replic8Continuous(db, remoteURL, NO, nil, nil, nil);
    AssertEq(db.lastSequenceNumber, (self.isSQLiteDB ? 3 : 2));

    CBL_Revision* doc = [db getDocumentWithID: @"doc1" revisionID: nil];
    Assert(doc);
    Assert([doc.revID hasPrefix: @"2-"]);
    AssertEqual(doc[@"foo"], @1);

    doc = [db getDocumentWithID: @"doc2" revisionID: nil];
    Assert(doc);
    Assert([doc.revID hasPrefix: @"1-"]);
    AssertEqual(doc[@"fnord"], $true);
}

- (void) test_04_Puller_Continuous_PermanentError {
    RequireTestCase(Puller);
    NSURL* remoteURL = [self remoteTestDBURL: @"non_existent_remote_db"];
    if (!remoteURL)
        return;

    NSError* error = CBLStatusToNSError(kCBLStatusNotFound, nil);
    replic8Continuous(db, remoteURL, NO, nil, nil, error);
}

- (void) test_05_Puller_DatabaseValidation {
    RequireTestCase(Pusher);

    NSURL* remote = [self remoteTestDBURL: kScratchDBName];
    if (!remote)
        return;

    [db setValidationNamed:@"OnlyDoc1" asBlock:^(CBLRevision *newRevision, id<CBLValidationContext> context) {
        if (![newRevision.document.documentID isEqualToString:@"doc1"]) {
            [context reject];
        }
    }];

    // Start a named document pull replication.
    CBL_Replicator* repl = [[CBL_Replicator alloc] initWithDB: db remote: remote
                                                         push: NO continuous: NO];
    repl.authorizer = self.authorizer;
    [repl start];

    // Let the replicator run.
    Assert(repl.running);
    Log(@"Waiting for replicator to finish...");
    while (repl.running || repl.savingCheckpoint) {
        if (![[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode
                                      beforeDate: [NSDate dateWithTimeIntervalSinceNow: 0.5]])
            break;
    }
    Assert(!repl.running);
    Assert(!repl.savingCheckpoint);
    AssertNil(repl.error);
    Log(@"...replicator finished. lastSequence=%@", repl.lastSequence);
    id lastSeq = repl.lastSequence;
    AssertEqual(lastSeq, @3);

    AssertEq(db.documentCount, 1u);
    AssertEq(db.lastSequenceNumber, (self.isSQLiteDB ? 2 : 1));

    CBL_Revision* doc = [db getDocumentWithID: @"doc1" revisionID: nil];
    Assert(doc);
    Assert([doc.revID hasPrefix: @"2-"]);
    AssertEqual(doc[@"foo"], @1);
}

- (void) test_06_Puller_AuthFailure {
    RequireTestCase(Puller);
    NSURL* remoteURL = [self remoteTestDBURL: @"cbl_auth_test"];
    if (!remoteURL)
        return;
    // Add a bogus user to make auth fail:
    NSString* urlStr = remoteURL.absoluteString;
    urlStr = [urlStr stringByReplacingOccurrencesOfString: @"http://" withString: @"http://bogus@"];
    remoteURL = $url(urlStr);

    NSError* error = CBLStatusToNSError(kCBLStatusUnauthorized, nil);
    replic8Continuous(db, remoteURL, NO, nil, nil, error);
}

- (void) test_06_Puller_SSL {
    RequireTestCase(Pusher);
    NSURL* remoteURL = [self remoteSSLTestDBURL: @"public"];
    if (!remoteURL)
        return;

    Log(@"Replicating without root cert; should fail...");
    replic8(db, remoteURL, NO, nil, nil,
            ([NSError errorWithDomain: NSURLErrorDomain
                                code: NSURLErrorServerCertificateUntrusted userInfo: nil]));

    Log(@"Now replicating with root cert installed...");
    [CBL_Replicator setAnchorCerts: [self remoteTestDBAnchorCerts] onlyThese: NO];
    id lastSeq = replic8(db, remoteURL, NO, nil, nil, nil);
    [CBL_Replicator setAnchorCerts: nil onlyThese: NO];
    Assert([lastSeq intValue] >= 2);

    AssertEq(db.documentCount, 2u);
    AssertEq(db.lastSequenceNumber, 2);
}

- (void) test_07_Puller_SSL_Continuous {
    RequireTestCase(Pusher);
    NSURL* remoteURL = [self remoteSSLTestDBURL: @"public"];
    if (!remoteURL)
        return;

    Log(@"Replicating without root cert; should fail...");
    replic8Continuous(db, remoteURL, NO, nil, nil,
                      [NSError errorWithDomain: NSURLErrorDomain
                                          code: NSURLErrorServerCertificateUntrusted
                                      userInfo: nil]);

    Log(@"Now replicating with root cert installed...");
    [CBL_Replicator setAnchorCerts: [self remoteTestDBAnchorCerts] onlyThese: NO];
    id lastSeq = replic8Continuous(db, remoteURL, NO, nil, nil, nil);
    [CBL_Replicator setAnchorCerts: nil onlyThese: NO];
    Assert([lastSeq intValue] >= 2);

    AssertEq(db.documentCount, 2u);
    AssertEq(db.lastSequenceNumber, 2);
}

- (void) test_08_Puller_SSL_Pinned {
    RequireTestCase(Puller_SSL_Continuous);
    NSURL* remoteURL = [self remoteSSLTestDBURL: @"public"];
    if (!remoteURL)
        return;

    Log(@"Replicating with wrong pinned cert; should fail...");
    NSString* digest = @"123456789abcdef0123456789abcdef012345678";
    replic8Continuous(db, remoteURL, NO, nil, @{kCBLReplicatorOption_PinnedCert: digest},
                      [NSError errorWithDomain: NSURLErrorDomain
                                          code: NSURLErrorServerCertificateUntrusted
                                      userInfo: nil]);

    Log(@"Now replicating with correct pinned cert...");
    digest = @"c745fbfc03382125271daffc2e715a5b0172d1d8";
    id lastSeq = replic8Continuous(db, remoteURL, NO, nil,
                                   @{kCBLReplicatorOption_PinnedCert: digest},
                                   nil);
    [CBL_Replicator setAnchorCerts: nil onlyThese: NO];
    Assert([lastSeq intValue] >= 2);

    AssertEq(db.documentCount, 2u);
    AssertEq(db.lastSequenceNumber, 2);
}

- (void) test_09_Pusher_NonExistentServer {
    RequireTestCase(Pusher);
    NSURL* remoteURL = [NSURL URLWithString:@"http://mylocalhost/db"];
    if (!remoteURL) {
        Warn(@"Skipping test CBL_Pusher_NonExistentServer: invalid URL");
        return;
    }

    replic8(db, remoteURL, YES, nil, nil, [NSError errorWithDomain: NSURLErrorDomain
                                                              code: NSURLErrorCannotFindHost
                                                          userInfo: nil]);
}

- (void) test_10_Puller_NonExistentServer {
    RequireTestCase(Puller);
    NSURL* remoteURL = [NSURL URLWithString:@"http://mylocalhost/db"];
    if (!remoteURL) {
        Warn(@"Skipping test CBL_Puller_NonExistentServer: invalid URL");
        return;
    }

    replic8(db, remoteURL, NO, nil, nil, [NSError errorWithDomain: NSURLErrorDomain
                                                             code: NSURLErrorCannotFindHost
                                                         userInfo: nil]);
}

#if 0 // FIX: Sync Gateway doesn't support this yet!
- (void) test_11_Puller_DocIDs {
    RequireTestCase(Pusher); // CBL_Pusher populates the remote db that this test pulls from...

    NSURL* remote = [self remoteTestDBURL: kScratchDBName];
    if (!remote) {
        Warn(@"Skipping test: no remote URL");
        return;
    }

    // Start a named document pull replication.
    CBL_Replicator* repl = [[CBL_Replicator alloc] initWithDB: db remote: remote
                                                     push: NO continuous: NO];
    repl.docIDs = @[@"doc1"];
    repl.authorizer = self.authorizer;
    [repl start];

    // Let the replicator run.
    Assert(repl.running);
    Log(@"Waiting for replicator to finish...");
    while (repl.running || repl.savingCheckpoint) {
        if (![[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode
                                      beforeDate: [NSDate dateWithTimeIntervalSinceNow: 0.5]])
            break;
    }
    Assert(!repl.running);
    Assert(!repl.savingCheckpoint);
    AssertNil(repl.error);
    Log(@"...replicator finished. lastSequence=%@", repl.lastSequence);
    id lastSeq = repl.lastSequence;

    AssertEqual(lastSeq, @1);

    Log(@"GOT DOCS: %@", [db getAllDocs:nil]);

    AssertEq(db.documentCount, 1u);
    AssertEq(db.lastSequenceNumber, 2);

    // Replicate again; should complete but add no revisions:
    Log(@"Second replication, should get no more revs:");
    replic8(db, ([self remoteTestDBURL: kScratchDBName]), NO, nil, nil, nil);
    AssertEq(db.lastSequenceNumber, 3);

    CBL_Revision* doc = [db getDocumentWithID: @"doc1" revisionID: nil];
    Assert(doc);
    Assert([doc.revID hasPrefix: @"2-"]);
    AssertEqual(doc[@"foo"], @1);
}
#endif

- (void) test_12_Pusher_DocIDs {
    RequireTestCase(Puller_DocIDs);
    // Create some documents:
    for (int i = 1; i <= 10; i++) {
        NSDictionary* props = @{@"_id": $sprintf(@"doc%d", i)};
        CBLStatus status;
        [db putRevision: [CBL_MutableRevision revisionWithProperties: props]
             prevRevisionID: nil allowConflict: NO status: &status];
        AssertEq(status, kCBLStatusCreated);
    }

    // Push them to the remote:
    NSURL* remoteDB = [self remoteTestDBURL: kScratchDBName];
    if (!remoteDB)
        return;
    [self eraseRemoteDB: remoteDB];
    replic8(db, remoteDB, YES, nil, (@[@"doc4", @"doc7"]), nil);

    // Check _all_docs on the remote db and make sure only doc4 and doc7 were pushed:
    NSURL* allDocsURL = [remoteDB URLByAppendingPathComponent: @"_all_docs"];
    NSData* data = [NSData dataWithContentsOfURL: allDocsURL];
    Assert(data);
    NSDictionary* response = [CBLJSON JSONObjectWithData: data options: 0 error: NULL];
    NSArray* rows = response[@"rows"];
    AssertEq(rows.count, 2u);
    AssertEqual((rows[0])[@"id"], @"doc4");
    AssertEqual((rows[1])[@"id"], @"doc7");
}

- (void) test_15_ParseReplicatorProperties {
    CBLDatabase* parsedDB = nil;
    NSURL* remote = nil;
    BOOL isPush = NO, createTarget = NO;
    NSDictionary* headers = nil;

    NSDictionary* props;
    props = $dict({@"source", db.name},
                  {@"target", @"http://example.com"},
                  {@"create_target", $true});
    AssertEq(200, [dbmgr parseReplicatorProperties: props
                                        toDatabase: &parsedDB
                                            remote: &remote
                                            isPush: &isPush
                                      createTarget: &createTarget
                                           headers: &headers
                                        authorizer: NULL]);
    AssertEq(parsedDB, db);
    AssertEqual(remote, $url(@"http://example.com"));
    AssertEq(isPush, YES);
    AssertEq(createTarget, YES);
    AssertEqual(headers, nil);

    props = $dict({@"source", @"cbl:///foo"},
                  {@"target", db.name});
    AssertEq(200, [dbmgr parseReplicatorProperties: props
                                             toDatabase: &parsedDB
                                                 remote: &remote
                                                 isPush: &isPush
                                           createTarget: &createTarget
                                                headers: &headers
                                             authorizer: NULL]);
    AssertEq(parsedDB, db);
    AssertEqual(remote, $url(@"cbl:///foo"));
    AssertEq(isPush, NO);
    AssertEq(createTarget, NO);
    AssertEqual(headers, nil);

    if (NSClassFromString(@"CBLURLProtocol")) {
        // Local-to-local replication:
        props = $dict({@"source", @"foo"},
                      {@"target", @"bar"});
        AssertEq([dbmgr parseReplicatorProperties: props
                                            toDatabase: &parsedDB
                                                remote: &remote
                                                isPush: &isPush
                                          createTarget: &createTarget
                                               headers: &headers
                                            authorizer: NULL],
                  404);
        props = $dict({@"source", @"foo"},
                      {@"target", @"bar"}, {@"create_target", $true});
        AssertEq([dbmgr parseReplicatorProperties: props
                                            toDatabase: &parsedDB
                                                remote: &remote
                                                isPush: &isPush
                                          createTarget: &createTarget
                                               headers: &headers
                                            authorizer: NULL],
                  200);
        AssertEq(parsedDB, db);
        AssertEqual(remote, $url(@"http://lite.couchbase./bar/"));
        AssertEq(isPush, YES);
        AssertEq(createTarget, YES);
        AssertEqual(headers, nil);
    }

    NSDictionary* oauthDict = $dict({@"consumer_secret", @"consumer_secret"},
                                    {@"consumer_key", @"consumer_key"},
                                    {@"token_secret", @"token_secret"},
                                    {@"token", @"token"});
    props = $dict({@"source", $dict({@"url", @"http://example.com"},
                                    {@"headers", $dict({@"Excellence", @"Most"})},
                                    {@"auth", $dict({@"oauth", oauthDict})})},
                  {@"target", db.name});
    id<CBLAuthorizer> authorizer = nil;
    AssertEq(200, [dbmgr parseReplicatorProperties: props
                                             toDatabase: &parsedDB
                                                 remote: &remote
                                                 isPush: &isPush
                                           createTarget: &createTarget
                                                headers: &headers
                                             authorizer: &authorizer]);
    AssertEq(parsedDB, db);
    AssertEqual(remote, $url(@"http://example.com"));
    AssertEq(isPush, NO);
    AssertEq(createTarget, NO);
    AssertEqual(headers, $dict({@"Excellence", @"Most"}));
    Assert([authorizer isKindOfClass: [CBLOAuth1Authorizer class]]);
}

- (void) test_FindCommonAncestor {
    NSDictionary* revDict = $dict({@"ids", @[@"second", @"first"]}, {@"start", @2});
    CBL_Revision* rev = [CBL_Revision revisionWithProperties: $dict({@"_revisions", revDict})];
    AssertEq(CBLFindCommonAncestor(rev, @[]), 0);
    AssertEq(CBLFindCommonAncestor(rev, @[@"3-noway", @"1-nope"]), 0);
    AssertEq(CBLFindCommonAncestor(rev, @[@"3-noway", @"1-first"]), 1);
    AssertEq(CBLFindCommonAncestor(rev, @[@"3-noway", @"2-second", @"1-first"]), 2);
}

- (void) test_Reachability {
    NSArray* hostnames = @[@"couchbase.com", @"localhost", @"127.0.0.1", @"67.221.231.37",
                           @"fsdfsaf.fsdfdaf.fsfddf"];
    for (NSString* hostname in hostnames) {
        Log(@"Test reachability of %@ ...", hostname);
        CBLReachability* r = [[CBLReachability alloc] initWithHostName: hostname];
        Assert(r);
        Log(@"\tCBLReachability = %@", r);
        AssertEqual(r.hostName, hostname);
        __block BOOL resolved = NO;

        __weak CBLReachability *weakR = r;
        r.onChange = ^{
            CBLReachability *strongR = weakR;
            Log(@"\tonChange: known=%d, flags=%x --> reachable=%d",
                strongR.reachabilityKnown, strongR.reachabilityFlags, strongR.reachable);
            Log(@"\tCBLReachability = %@", strongR);
            if (strongR.reachabilityKnown)
                resolved = YES;
        };
        Assert([r start]);

        BOOL known = r.reachabilityKnown;
        Log(@"\tInitially: known=%d, flags=%x --> reachable=%d",
            known, r.reachabilityFlags, r.reachable);
        if (!known) {
            while (!resolved) {
                Log(@"\twaiting...");
                [[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode
                                         beforeDate: [NSDate dateWithTimeIntervalSinceNow: 0.5]];
            }
        }
        [r stop];
        Log(@"\t...done!");
    }
}

#pragma mark - UTILITY FUNCTIONS

- (NSString*) replicate: (NSURL*)remote
                   push: (BOOL)push
                 filter: (NSString*)filter
                 docIDs: (NSArray*)docIDs
            expectError: (NSError*) expectError
{
    CBL_Replicator* repl = [[CBL_Replicator alloc] initWithDB: db remote: remote
                                                        push: push continuous: NO];
    if (push)
        ((CBL_Pusher*)repl).createTarget = YES;
    repl.filterName = filter;
    repl.docIDs = docIDs;
    repl.authorizer = self.authorizer;
    [repl start];

    Assert(repl.running);
    Log(@"Waiting for replicator to finish...");
    NSDate* timeout = [NSDate dateWithTimeIntervalSinceNow: 10];
    while ((repl.running || repl.savingCheckpoint) && timeout.timeIntervalSinceNow > 0.0) {
        if (![[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode
                                      beforeDate: [NSDate dateWithTimeIntervalSinceNow: 0.5]]) {
            Warn(@"Runloop exiting unexpectedly!");
            break;
        }
    }
    Assert(!repl.running);
    Assert(!repl.savingCheckpoint);
    if (expectError) {
        Assert(!repl.running);
        AssertEqual(repl.error.domain, expectError.domain);
        AssertEq(repl.error.code, expectError.code);
        Log(@"...replicator got expected error %@", repl.error);
    } else {
        AssertNil(repl.error);
        Log(@"...replicator finished. lastSequence=%@", repl.lastSequence);
    }
    Assert(!repl.active);
    return repl.lastSequence;
}

- (NSString*) replicateContinuous: (NSURL*)remote
                             push: (BOOL)push
                           filter: (NSString*)filter
                          options: (NSDictionary*)options
                      expectError: (NSError*) expectError
{
    CBL_Replicator* repl = [[CBL_Replicator alloc] initWithDB: db remote: remote
                                                         push: push continuous: YES];
    if (push)
        ((CBL_Pusher*)repl).createTarget = YES;
    repl.filterName = filter;
    repl.authorizer = self.authorizer;
    repl.options = options;
    [repl start];

    // Start the replicator and wait for it to go active, then inactive:
    Assert(repl.running);
    Log(@"Waiting for replicator to go idle...");
    NSDate* timeout = [NSDate dateWithTimeIntervalSinceNow: 10];
    bool wasActive = repl.active;
    while ((repl.running || repl.savingCheckpoint) && timeout.timeIntervalSinceNow > 0.0) {
        if (![[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode
                                      beforeDate: [NSDate dateWithTimeIntervalSinceNow: 0.5]])
            break;
        if (!wasActive)
            wasActive = repl.active;
        else if (!repl.active)
            break;  // Went inactive, so it's done
    }
    Assert(wasActive && !repl.active);
    Assert(!repl.savingCheckpoint);

    if (expectError) {
        Assert(!repl.running);
        AssertEqual(repl.error.domain, expectError.domain);
        AssertEq(repl.error.code, expectError.code);
        Log(@"...replicator finished. error=%@", repl.error);
    } else {
        Assert(repl.running);
        AssertNil(repl.error);
        Log(@"...replicator finished. lastSequence=%@", repl.lastSequence);
    }
    NSString* result = repl.lastSequence;
    [repl stop];
    return result;
}

@end


text:
<?php
namespace PHPCrystal\PHPCrystalTest\Action;

use PHPCrystal\PHPCrystalTest\TestCase;
use PHPCrystal\PHPCrystalTest\Action\_Default\_Default\Index;
use PHPCrystal\PHPCrystalTest\_Trait\MakeRequest;
use PHPCrystal\PHPCrystal\Component\Exception as Exception;

class ActionTest extends TestCase
{
	use MakeRequest;

	public function testGetControllerName()
	{
		$this->assertEquals('_Default\_Default', Index::getControllerName());
	}

	public function testGetControllerClassName()
	{
		$this->assertEquals('PHPCrystal\PHPCrystalTest\Controller\_Default\_Default',
			Index::getControllerClassName());
	}

	public function testAccountEditAction()
	{
		$this->makeRequest('@app/fixture/http_request/post_account_edit.txt');

		$router = $this->appPkg->getActiveRouter();
		$action = $router->getAction();
		$this->assertInstanceOf('PHPCrystal\PHPCrystalTest\Action\_Default\Account\Edit', $action);

		$uriString = $action->getReverseURI(658263);
		$this->assertEquals('/account/658263/edit/', $uriString);

		$ctrlAnnot = $action->getExtendableInstance()->getControllerMethodAnnotation();
		$this->assertEquals('editUserProfileAction', $ctrlAnnot->getMethodName());
	}

	public function testDefaultRouteParam()
	{
		$this->makeRequest('@app/fixture/http_request/get_account');
		$currEvent = $this->appPkg->getCurrentEvent();
		$uriInput = $currEvent->getRequest()->getURIInput();
		$this->assertEquals('master', $uriInput->get('default_param'));
	}

	public function testDefaultRouteParam2()
	{
		$this->makeRequest('@app/fixture/http_request/get_account_8627');
		$currEvent = $this->appPkg->getCurrentEvent();
		$uriInput = $currEvent->getRequest()->getURIInput();
		$this->assertEquals(8627, $uriInput->get('default_param'));
	}

	/**
	 * @expectedException PHPCrystal\PHPCrystal\Component\Exception\Security\AuthRequired
	 */
	public function testAuthentication()
	{
		Exception\AbstractException::$nonMaskable = true;
		$this->makeRequest('@app/fixture/http_request/edit_account_1_no_sid');
	}

	public function testIndex()
	{
		$this->makeRequest('@app/fixture/http_request/get_index');
	}
}

redpajama_set_name: RedPajamaGithub


text:
class AddNameAndApprovedToFeeds < ActiveRecord::Migration
  def self.up
    add_column :feeds, :name, :string
    add_column :feeds, :approved, :integer, :default => 0

    Feed.all.each do |feed|
      feed.name = "<Pendiente de asignar un nombre>"
      feed.email = "planeta@cordobapedia.org"
      feed.approved = 1
      if feed.save(false)
        puts "."
      else
        puts "x"
        puts feed.errors.full_messages
      end
    end
  end

  def self.down
    remove_column :feeds, :name
    remove_column :feeds, :approved
  end
end


text:
import baseSlice from './_internals/baseSlice.js'

export function init(listOrString){
  if (typeof listOrString === 'string') return listOrString.slice(0, -1)

  return listOrString.length ?
    baseSlice(
      listOrString, 0, -1
    ) :
    []
}


text:
<!DOCTYPE html>

		<title>Jayrobin</title>
		<meta charset="utf-8">
		<link rel="shortcut icon" href="/img/favicon.ico">
		<link rel="preconnect" href="https://fonts.gstatic.com">
		<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300&display=swap" rel="stylesheet">
		<link rel="stylesheet" href="/css/normalize.css">
		<link rel="stylesheet" href="/css/styles.css">
		<meta name="viewport" content="width=device-width, initial-scale=1.0">

JAYROBIN

			Software Engineer. Mostly Front-end. I like to build stuff

Jurassic Park in Blender

When the COVID lockdown began I found myself with a lot of time for self-reflection. I realized that I judged most of my interests and hobbies by their immediate benefit, usually fitness or financial. I decided it had been far too long since I last created something "Just for the sake of it".

I've done a little 3D work in the past - just simple experiments in 3D Studio Max a decade or so ago. Not wanting to shell out for a license, I figured Blender would be a good choice. It's free after all.

I began like I always do, by making a plan: explore the surface area (i.e. see how newbies generally start), decide on success criteria (creating and sharing an animated scene), and set a time limit (one month).

So, I present to you: Jurassic Park on a budget...

			<video preload="auto" poster="https://external-preview.redd.it/hBnNYw5ZEgtP0Cbn5geQUHEXUavk3xuff79HLDgTtiE.png?width=960&crop=smart&format=pjpg&auto=webp&s=e3d4601608c507be1d242276233aeb81b2971e40" src="https://v.redd.it/wp18oql7l7361/DASH_720.mp4" width="100%" height="auto" controls poster="display-image.jpg">
			</video>

Resources

Blender

Blender Guru - Donut tutorial

Grant Abbitt - Sea Shack tutorial

ZiiRo - Simple animated plants

Hendy August - I Learn Blender From Scratch and Here's My 30 Day Progress (initial inspiration)

				Resume -
				LinkedIn -
				GitHub -
				email


text:
package immutable

import "fmt"

// ErrImmutable ...
type ErrImmutable struct {
	repo string
	tag  string
}

// Error ...
func (ei *ErrImmutable) Error() string {
	return fmt.Sprintf("Failed to process request due to '%s:%s' configured as immutable.", ei.repo, ei.tag)
}

// Unwrap ...
func (ei *ErrImmutable) Unwrap() error {
	return nil
}

// NewErrImmutable ...
func NewErrImmutable(msg, tag string) error {
	return &ErrImmutable{
		repo: msg,
		tag:  tag,
	}
}


text:
<?php

namespace Composer\Repository\Vcs;

use Composer\Config;
use Composer\Downloader\TransportException;
use Composer\Json\JsonFile;
use Composer\Cache;
use Composer\IO\IOInterface;
use Composer\Util\GitHub;

class GitHubDriver extends VcsDriver
{
protected $cache;
protected $owner;
protected $repository;
protected $tags;
protected $branches;
protected $rootIdentifier;
protected $repoData;
protected $hasIssues;
protected $infoCache = array();
protected $isPrivate = false;

protected $gitDriver;

public function initialize()
{
preg_match('#^(?:(?:https?|git)://([^/]+)/|git@([^:]+):)([^/]+)/(.+?)(?:\.git|/)?$#', $this->url, $match);
$this->owner = $match[3];
$this->repository = $match[4];
$this->originUrl = !empty($match[1]) ? $match[1] : $match[2];
if ($this->originUrl === 'www.github.com') {
$this->originUrl = 'github.com';
}
$this->cache = new Cache($this->io, $this->config->get('cache-repo-dir').'/'.$this->originUrl.'/'.$this->owner.'/'.$this->repository);

if (isset($this->repoConfig['no-api']) && $this->repoConfig['no-api']) {
$this->setupGitDriver($this->url);

return;
}

$this->fetchRootIdentifier();
}

public function getRepositoryUrl()
{
return 'https://'.$this->originUrl.'/'.$this->owner.'/'.$this->repository;
}

public function getRootIdentifier()
{
if ($this->gitDriver) {
return $this->gitDriver->getRootIdentifier();
}

return $this->rootIdentifier;
}

public function getUrl()
{
if ($this->gitDriver) {
return $this->gitDriver->getUrl();
}

return 'https://' . $this->originUrl . '/'.$this->owner.'/'.$this->repository.'.git';
}

protected function getApiUrl()
{
if ('github.com' === $this->originUrl) {
$apiUrl = 'api.github.com';
} else {
$apiUrl = $this->originUrl . '/api/v3';
}

return 'https://' . $apiUrl;
}

public function getSource($identifier)
{
if ($this->gitDriver) {
return $this->gitDriver->getSource($identifier);
}
if ($this->isPrivate) {

 $url = $this->generateSshUrl();
} else {
$url = $this->getUrl();
}

return array('type' => 'git', 'url' => $url, 'reference' => $identifier);
}

public function getDist($identifier)
{
$url = $this->getApiUrl() . '/repos/'.$this->owner.'/'.$this->repository.'/zipball/'.$identifier;

return array('type' => 'zip', 'url' => $url, 'reference' => $identifier, 'shasum' => '');
}

public function getComposerInformation($identifier)
{
if ($this->gitDriver) {
return $this->gitDriver->getComposerInformation($identifier);
}

if (!isset($this->infoCache[$identifier])) {
if ($this->shouldCache($identifier) && $res = $this->cache->read($identifier)) {
return $this->infoCache[$identifier] = JsonFile::parseJson($res);
}

$composer = $this->getBaseComposerInformation($identifier);
if ($composer) {

 if (!isset($composer['support']['source'])) {
$label = array_search($identifier, $this->getTags()) ?: array_search($identifier, $this->getBranches()) ?: $identifier;
$composer['support']['source'] = sprintf('https://%s/%s/%s/tree/%s', $this->originUrl, $this->owner, $this->repository, $label);
}
if (!isset($composer['support']['issues']) && $this->hasIssues) {
$composer['support']['issues'] = sprintf('https://%s/%s/%s/issues', $this->originUrl, $this->owner, $this->repository);
}
}

if ($this->shouldCache($identifier)) {
$this->cache->write($identifier, json_encode($composer));
}

$this->infoCache[$identifier] = $composer;
}

return $this->infoCache[$identifier];
}

public function getFileContent($file, $identifier)
{
if ($this->gitDriver) {
return $this->gitDriver->getFileContent($file, $identifier);
}

$notFoundRetries = 2;
while ($notFoundRetries) {
try {
$resource = $this->getApiUrl() . '/repos/'.$this->owner.'/'.$this->repository.'/contents/' . $file . '?ref='.urlencode($identifier);
$resource = JsonFile::parseJson($this->getContents($resource));
if (empty($resource['content']) || $resource['encoding'] !== 'base64' || !($content = base64_decode($resource['content']))) {
throw new \RuntimeException('Could not retrieve ' . $file . ' for '.$identifier);
}

return $content;
} catch (TransportException $e) {
if (404 !== $e->getCode()) {
throw $e;
}

 $notFoundRetries--;

return null;
}
}

return null;
}

public function getChangeDate($identifier)
{
if ($this->gitDriver) {
return $this->gitDriver->getChangeDate($identifier);
}

$resource = $this->getApiUrl() . '/repos/'.$this->owner.'/'.$this->repository.'/commits/'.urlencode($identifier);
$commit = JsonFile::parseJson($this->getContents($resource), $resource);

return new \DateTime($commit['commit']['committer']['date']);
}

public function getTags()
{
if ($this->gitDriver) {
return $this->gitDriver->getTags();
}
if (null === $this->tags) {
$this->tags = array();
$resource = $this->getApiUrl() . '/repos/'.$this->owner.'/'.$this->repository.'/tags?per_page=100';

do {
$tagsData = JsonFile::parseJson($this->getContents($resource), $resource);
foreach ($tagsData as $tag) {
$this->tags[$tag['name']] = $tag['commit']['sha'];
}

$resource = $this->getNextPage();
} while ($resource);
}

return $this->tags;
}

public function getBranches()
{
if ($this->gitDriver) {
return $this->gitDriver->getBranches();
}
if (null === $this->branches) {
$this->branches = array();
$resource = $this->getApiUrl() . '/repos/'.$this->owner.'/'.$this->repository.'/git/refs/heads?per_page=100';

$branchBlacklist = array('gh-pages');

do {
$branchData = JsonFile::parseJson($this->getContents($resource), $resource);
foreach ($branchData as $branch) {
$name = substr($branch['ref'], 11);
if (!in_array($name, $branchBlacklist)) {
$this->branches[$name] = $branch['object']['sha'];
}
}

$resource = $this->getNextPage();
} while ($resource);
}

return $this->branches;
}

public static function supports(IOInterface $io, Config $config, $url, $deep = false)
{
if (!preg_match('#^((?:https?|git)://([^/]+)/|git@([^:]+):)([^/]+)/(.+?)(?:\.git|/)?$#', $url, $matches)) {
return false;
}

$originUrl = !empty($matches[2]) ? $matches[2] : $matches[3];
if (!in_array(preg_replace('{^www\.}i', '', $originUrl), $config->get('github-domains'))) {
return false;
}

if (!extension_loaded('openssl')) {
$io->writeError('Skipping GitHub driver for '.$url.' because the OpenSSL PHP extension is missing.', true, IOInterface::VERBOSE);

return false;
}

return true;
}

public function getRepoData()
{
$this->fetchRootIdentifier();

return $this->repoData;
}

protected function generateSshUrl()
{
return 'git@' . $this->originUrl . ':'.$this->owner.'/'.$this->repository.'.git';
}

protected function getContents($url, $fetchingRepoData = false)
{
try {
return parent::getContents($url);
} catch (TransportException $e) {
$gitHubUtil = new GitHub($this->io, $this->config, $this->process, $this->remoteFilesystem);

switch ($e->getCode()) {
case 401:
case 404:

 if (!$fetchingRepoData) {
throw $e;
}

if ($gitHubUtil->authorizeOAuth($this->originUrl)) {
return parent::getContents($url);
}

if (!$this->io->isInteractive()) {
return $this->attemptCloneFallback();
}

$scopesIssued = array();
$scopesNeeded = array();
if ($headers = $e->getHeaders()) {
if ($scopes = $this->remoteFilesystem->findHeaderValue($headers, 'X-OAuth-Scopes')) {
$scopesIssued = explode(' ', $scopes);
}
if ($scopes = $this->remoteFilesystem->findHeaderValue($headers, 'X-Accepted-OAuth-Scopes')) {
$scopesNeeded = explode(' ', $scopes);
}
}
$scopesFailed = array_diff($scopesNeeded, $scopesIssued);

 if (!$headers || !count($scopesNeeded) || count($scopesFailed)) {
$gitHubUtil->authorizeOAuthInteractively($this->originUrl, 'Your GitHub credentials are required to fetch private repository metadata (<info>'.$this->url.'</info>)');
}

return parent::getContents($url);

case 403:
if (!$this->io->hasAuthentication($this->originUrl) && $gitHubUtil->authorizeOAuth($this->originUrl)) {
return parent::getContents($url);
}

if (!$this->io->isInteractive() && $fetchingRepoData) {
return $this->attemptCloneFallback();
}

$rateLimited = $githubUtil->isRateLimited($e->getHeaders());

if (!$this->io->hasAuthentication($this->originUrl)) {
if (!$this->io->isInteractive()) {
$this->io->writeError('<error>GitHub API limit exhausted. Failed to get metadata for the '.$this->url.' repository, try running in interactive mode so that you can enter your GitHub credentials to increase the API limit</error>');
throw $e;
}

$gitHubUtil->authorizeOAuthInteractively($this->originUrl, 'API limit exhausted. Enter your GitHub credentials to get a larger API limit (<info>'.$this->url.'</info>)');

return parent::getContents($url);
}

if ($rateLimited) {
$rateLimit = $githubUtil->getRateLimit($e->getHeaders());
$this->io->writeError(sprintf(
'<error>GitHub API limit (%d calls/hr) is exhausted. You are already authorized so you have to wait until %s before doing more requests</error>',
$rateLimit['limit'],
$rateLimit['reset']
));
}

throw $e;

default:
throw $e;
}
}
}

protected function fetchRootIdentifier()
{
if ($this->repoData) {
return;
}

$repoDataUrl = $this->getApiUrl() . '/repos/'.$this->owner.'/'.$this->repository;

$this->repoData = JsonFile::parseJson($this->getContents($repoDataUrl, true), $repoDataUrl);
if (null === $this->repoData && null !== $this->gitDriver) {
return;
}

$this->owner = $this->repoData['owner']['login'];
$this->repository = $this->repoData['name'];

$this->isPrivate = !empty($this->repoData['private']);
if (isset($this->repoData['default_branch'])) {
$this->rootIdentifier = $this->repoData['default_branch'];
} elseif (isset($this->repoData['master_branch'])) {
$this->rootIdentifier = $this->repoData['master_branch'];
} else {
$this->rootIdentifier = 'master';
}
$this->hasIssues = !empty($this->repoData['has_issues']);
}

protected function attemptCloneFallback()
{
$this->isPrivate = true;

try {

 $this->setupGitDriver($this->generateSshUrl());

return;
} catch (\RuntimeException $e) {
$this->gitDriver = null;

$this->io->writeError('<error>Failed to clone the '.$this->generateSshUrl().' repository, try running in interactive mode so that you can enter your GitHub credentials</error>');
throw $e;
}
}

protected function setupGitDriver($url)
{
$this->gitDriver = new GitDriver(
array('url' => $url),
$this->io,
$this->config,
$this->process,
$this->remoteFilesystem
);
$this->gitDriver->initialize();
}

protected function getNextPage()
{
$headers = $this->remoteFilesystem->getLastHeaders();
foreach ($headers as $header) {
if (preg_match('{^link:\s*(.+?)\s*$}i', $header, $match)) {
$links = explode(',', $match[1]);
foreach ($links as $link) {
if (preg_match('{<(.+?)>; *rel="next"}', $link, $match)) {
return $match[1];
}
}
}
}
}
}


text:
LevenbergMarquardt
==================


text:
CPPUNIT_TEST_SUITE_REGISTRATION(telnet_server_option_fixture);

using namespace std;
using namespace boost;
using namespace boost::assign;

namespace bll = boost::lambda;

typedef fake_datastream<odin::u8, odin::u8> fake_byte_stream;

class fake_server_option : public odin::telnet::server_option
{
public :
    fake_server_option(
        shared_ptr<odin::telnet::stream> const                &stream
      , shared_ptr<odin::telnet::negotiation_router> const    &negotiation_router
      , shared_ptr<odin::telnet::subnegotiation_router> const &subnegotiation_router)
        : odin::telnet::server_option(
            stream
          , negotiation_router
          , subnegotiation_router
          , odin::telnet::KERMIT)
    {
    }

    std::vector<odin::telnet::subnegotiation_type> subnegotiations_;

    void send_subnegotiation(
        odin::telnet::subnegotiation_content_type const &content)
    {
        odin::telnet::server_option::send_subnegotiation(content);
    }

private :
    virtual void on_subnegotiation(
        odin::telnet::subnegotiation_type const &subnegotiation)
    {
        subnegotiations_.push_back(subnegotiation);
    }
};

void telnet_server_option_fixture::test_constructor()
{
    boost::asio::io_service          io_service;
    shared_ptr<fake_byte_stream>     fake_stream(
        new fake_datastream<odin::u8, odin::u8>(io_service));
    shared_ptr<odin::telnet::stream> telnet_stream(
        new odin::telnet::stream(fake_stream, io_service));
    shared_ptr<odin::telnet::negotiation_router> telnet_negotiation_router(
        new odin::telnet::negotiation_router);
    shared_ptr<odin::telnet::subnegotiation_router> telnet_subnegotiation_router(
        new odin::telnet::subnegotiation_router);

    fake_server_option option(
        telnet_stream, telnet_negotiation_router, telnet_subnegotiation_router);

    CPPUNIT_ASSERT_EQUAL(odin::telnet::KERMIT, option.get_option_id());
    CPPUNIT_ASSERT_EQUAL(false, option.is_active());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_activation());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_deactivation());
}

void telnet_server_option_fixture::test_inactive_activate()
{
    boost::asio::io_service          io_service;
    shared_ptr<fake_byte_stream>     fake_stream(
        new fake_datastream<odin::u8, odin::u8>(io_service));
    shared_ptr<odin::telnet::stream> telnet_stream(
        new odin::telnet::stream(fake_stream, io_service));
    shared_ptr<odin::telnet::negotiation_router> telnet_negotiation_router(
        new odin::telnet::negotiation_router);
    shared_ptr<odin::telnet::subnegotiation_router> telnet_subnegotiation_router(
        new odin::telnet::subnegotiation_router);

    fake_server_option option(
        telnet_stream, telnet_negotiation_router, telnet_subnegotiation_router);

    CPPUNIT_ASSERT_EQUAL(false, option.is_active());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_activation());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_deactivation());

    odin::u32 request_completed = 0;
    function<void ()> request_complete_handler = (
        ++bll::var(request_completed)
    );

    option.on_request_complete(request_complete_handler);
    option.activate();

    CPPUNIT_ASSERT_EQUAL(false, option.is_active());
    CPPUNIT_ASSERT_EQUAL(true,  option.is_negotiating_activation());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_deactivation());

    fake_byte_stream::output_storage_type expected_data =
        list_of(odin::telnet::IAC)(odin::telnet::WILL)(odin::telnet::KERMIT);

    CPPUNIT_ASSERT(expected_data == fake_stream->read_data_written());

    CPPUNIT_ASSERT_EQUAL(odin::u32(0), request_completed);
}

void telnet_server_option_fixture::test_inactive_activate_deny()
{
    boost::asio::io_service          io_service;
    shared_ptr<fake_byte_stream>     fake_stream(
        new fake_datastream<odin::u8, odin::u8>(io_service));
    shared_ptr<odin::telnet::stream> telnet_stream(
        new odin::telnet::stream(fake_stream, io_service));
    shared_ptr<odin::telnet::negotiation_router> telnet_negotiation_router(
        new odin::telnet::negotiation_router);
    shared_ptr<odin::telnet::subnegotiation_router> telnet_subnegotiation_router(
        new odin::telnet::subnegotiation_router);

    odin::telnet::input_visitor visitor(
        shared_ptr<odin::telnet::command_router>()
      , telnet_negotiation_router
      , telnet_subnegotiation_router
      , NULL);

    fake_server_option option(
        telnet_stream, telnet_negotiation_router, telnet_subnegotiation_router);

    CPPUNIT_ASSERT_EQUAL(false, option.is_active());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_activation());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_deactivation());

    odin::u32 request_completed = 0;
    function<void ()> request_complete_handler = (
        ++bll::var(request_completed)
    );

    option.on_request_complete(request_complete_handler);
    option.activate();

    CPPUNIT_ASSERT_EQUAL(false, option.is_active());
    CPPUNIT_ASSERT_EQUAL(true,  option.is_negotiating_activation());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_deactivation());

    fake_byte_stream::output_storage_type expected_data =
        list_of(odin::telnet::IAC)(odin::telnet::WILL)(odin::telnet::KERMIT);

    CPPUNIT_ASSERT(expected_data == fake_stream->read_data_written());

    fake_stream->write_data_to_read(
        list_of(odin::telnet::IAC)(odin::telnet::DONT)(odin::telnet::KERMIT));

    odin::telnet::stream::input_callback_type callback =
    (
        bll::bind(&odin::telnet::apply_input_range, ref(visitor), bll::_1)
    );

    telnet_stream->async_read(1, callback);

    io_service.reset();
    io_service.run();

    CPPUNIT_ASSERT_EQUAL(false, option.is_active());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_activation());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_deactivation());
    CPPUNIT_ASSERT_EQUAL(odin::u32(1), request_completed);
}

void telnet_server_option_fixture::test_inactive_activate_allow()
{
    boost::asio::io_service          io_service;
    shared_ptr<fake_byte_stream>     fake_stream(
        new fake_datastream<odin::u8, odin::u8>(io_service));
    shared_ptr<odin::telnet::stream> telnet_stream(
        new odin::telnet::stream(fake_stream, io_service));
    shared_ptr<odin::telnet::negotiation_router> telnet_negotiation_router(
        new odin::telnet::negotiation_router);
    shared_ptr<odin::telnet::subnegotiation_router> telnet_subnegotiation_router(
        new odin::telnet::subnegotiation_router);

    odin::telnet::input_visitor visitor(
        shared_ptr<odin::telnet::command_router>()
      , telnet_negotiation_router
      , telnet_subnegotiation_router
      , NULL);

    fake_server_option option(
        telnet_stream, telnet_negotiation_router, telnet_subnegotiation_router);

    CPPUNIT_ASSERT_EQUAL(false, option.is_active());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_activation());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_deactivation());

    odin::u32 request_completed = 0;
    function<void ()> request_complete_handler = (
        ++bll::var(request_completed)
    );

    option.on_request_complete(request_complete_handler);
    option.activate();

    CPPUNIT_ASSERT_EQUAL(false, option.is_active());
    CPPUNIT_ASSERT_EQUAL(true,  option.is_negotiating_activation());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_deactivation());

    fake_byte_stream::output_storage_type expected_data =
        list_of(odin::telnet::IAC)(odin::telnet::WILL)(odin::telnet::KERMIT);

    CPPUNIT_ASSERT(expected_data == fake_stream->read_data_written());

    fake_stream->write_data_to_read(
        list_of(odin::telnet::IAC)(odin::telnet::DO)(odin::telnet::KERMIT));

    odin::telnet::stream::input_callback_type callback =
    (
        bll::bind(&odin::telnet::apply_input_range, ref(visitor), bll::_1)
    );

    telnet_stream->async_read(1, callback);

    io_service.reset();
    io_service.run();

    CPPUNIT_ASSERT_EQUAL(true, option.is_active());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_activation());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_deactivation());
    CPPUNIT_ASSERT_EQUAL(odin::u32(1), request_completed);
}

void telnet_server_option_fixture::test_inactive_deactivate()
{
    boost::asio::io_service          io_service;
    shared_ptr<fake_byte_stream>     fake_stream(
        new fake_datastream<odin::u8, odin::u8>(io_service));
    shared_ptr<odin::telnet::stream> telnet_stream(
        new odin::telnet::stream(fake_stream, io_service));
    shared_ptr<odin::telnet::negotiation_router> telnet_negotiation_router(
        new odin::telnet::negotiation_router);
    shared_ptr<odin::telnet::subnegotiation_router> telnet_subnegotiation_router(
        new odin::telnet::subnegotiation_router);

    odin::telnet::input_visitor visitor(
        shared_ptr<odin::telnet::command_router>()
      , telnet_negotiation_router
      , telnet_subnegotiation_router
      , NULL);

    fake_server_option option(
        telnet_stream, telnet_negotiation_router, telnet_subnegotiation_router);

    CPPUNIT_ASSERT_EQUAL(false, option.is_active());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_activation());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_deactivation());

    odin::u32 request_completed = 0;
    function<void ()> request_complete_handler = (++bll::var(request_completed));

    option.on_request_complete(request_complete_handler);
    option.deactivate();

    odin::telnet::stream::input_callback_type callback =
    (
        bll::bind(&odin::telnet::apply_input_range, ref(visitor), bll::_1)
    );

    telnet_stream->async_read(1, callback);

    io_service.reset();
    io_service.run();

    fake_byte_stream::input_storage_type expected_data;

    CPPUNIT_ASSERT(expected_data == fake_stream->read_data_written());
    CPPUNIT_ASSERT_EQUAL(false, option.is_active());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_activation());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_deactivation());
    CPPUNIT_ASSERT_EQUAL(odin::u32(1), request_completed);
}

void telnet_server_option_fixture::test_active_deactivate()
{
    boost::asio::io_service          io_service;
    shared_ptr<fake_byte_stream>     fake_stream(
        new fake_datastream<odin::u8, odin::u8>(io_service));
    shared_ptr<odin::telnet::stream> telnet_stream(
        new odin::telnet::stream(fake_stream, io_service));
    shared_ptr<odin::telnet::negotiation_router> telnet_negotiation_router(
        new odin::telnet::negotiation_router);
    shared_ptr<odin::telnet::subnegotiation_router> telnet_subnegotiation_router(
        new odin::telnet::subnegotiation_router);

    odin::telnet::input_visitor visitor(
        shared_ptr<odin::telnet::command_router>()
      , telnet_negotiation_router
      , telnet_subnegotiation_router
      , NULL);

    fake_server_option option(
        telnet_stream, telnet_negotiation_router, telnet_subnegotiation_router);

    CPPUNIT_ASSERT_EQUAL(false, option.is_active());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_activation());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_deactivation());

    odin::u32 request_completed = 0;
    function<void ()> request_complete_handler = (
        ++bll::var(request_completed)
    );

    option.on_request_complete(request_complete_handler);
    option.activate();

    CPPUNIT_ASSERT_EQUAL(false, option.is_active());
    CPPUNIT_ASSERT_EQUAL(true,  option.is_negotiating_activation());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_deactivation());

    fake_byte_stream::output_storage_type expected_data =
        list_of(odin::telnet::IAC)(odin::telnet::WILL)(odin::telnet::KERMIT);

    CPPUNIT_ASSERT(expected_data == fake_stream->read_data_written());

    fake_stream->write_data_to_read(
        list_of(odin::telnet::IAC)(odin::telnet::DO)(odin::telnet::KERMIT));

    odin::telnet::stream::input_callback_type callback =
    (
        bll::bind(&odin::telnet::apply_input_range, ref(visitor), bll::_1)
    );

    telnet_stream->async_read(1, callback);

    io_service.reset();
    io_service.run();

    CPPUNIT_ASSERT_EQUAL(true, option.is_active());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_activation());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_deactivation());
    CPPUNIT_ASSERT_EQUAL(odin::u32(1), request_completed);

    // Option is active HERE.

    option.deactivate();

    expected_data =
        list_of(odin::telnet::IAC)(odin::telnet::WONT)(odin::telnet::KERMIT);

    CPPUNIT_ASSERT(expected_data == fake_stream->read_data_written());

    CPPUNIT_ASSERT_EQUAL(true, option.is_active());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_activation());
    CPPUNIT_ASSERT_EQUAL(true, option.is_negotiating_deactivation());
    CPPUNIT_ASSERT_EQUAL(odin::u32(1), request_completed);
}

void telnet_server_option_fixture::test_active_deactivate_deny()
{
    boost::asio::io_service          io_service;
    shared_ptr<fake_byte_stream>     fake_stream(
        new fake_datastream<odin::u8, odin::u8>(io_service));
    shared_ptr<odin::telnet::stream> telnet_stream(
        new odin::telnet::stream(fake_stream, io_service));
    shared_ptr<odin::telnet::negotiation_router> telnet_negotiation_router(
        new odin::telnet::negotiation_router);
    shared_ptr<odin::telnet::subnegotiation_router> telnet_subnegotiation_router(
        new odin::telnet::subnegotiation_router);

    odin::telnet::input_visitor visitor(
        shared_ptr<odin::telnet::command_router>()
      , telnet_negotiation_router
      , telnet_subnegotiation_router
      , NULL);

    fake_server_option option(
        telnet_stream, telnet_negotiation_router, telnet_subnegotiation_router);

    CPPUNIT_ASSERT_EQUAL(false, option.is_active());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_activation());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_deactivation());

    odin::u32 request_completed = 0;
    function<void ()> request_complete_handler = (
        ++bll::var(request_completed)
    );

    option.on_request_complete(request_complete_handler);
    option.activate();

    CPPUNIT_ASSERT_EQUAL(false, option.is_active());
    CPPUNIT_ASSERT_EQUAL(true,  option.is_negotiating_activation());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_deactivation());

    fake_byte_stream::output_storage_type expected_data =
        list_of(odin::telnet::IAC)(odin::telnet::WILL)(odin::telnet::KERMIT);

    CPPUNIT_ASSERT(expected_data == fake_stream->read_data_written());

    fake_stream->write_data_to_read(
        list_of(odin::telnet::IAC)(odin::telnet::DO)(odin::telnet::KERMIT));

    odin::telnet::stream::input_callback_type callback =
    (
        bll::bind(&odin::telnet::apply_input_range, ref(visitor), bll::_1)
    );

    telnet_stream->async_read(1, callback);

    io_service.reset();
    io_service.run();

    CPPUNIT_ASSERT_EQUAL(true, option.is_active());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_activation());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_deactivation());
    CPPUNIT_ASSERT_EQUAL(odin::u32(1), request_completed);

    // Option is active HERE.

    option.deactivate();

    expected_data =
        list_of(odin::telnet::IAC)(odin::telnet::WONT)(odin::telnet::KERMIT);

    CPPUNIT_ASSERT(expected_data == fake_stream->read_data_written());

    CPPUNIT_ASSERT_EQUAL(true, option.is_active());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_activation());
    CPPUNIT_ASSERT_EQUAL(true, option.is_negotiating_deactivation());
    CPPUNIT_ASSERT_EQUAL(odin::u32(1), request_completed);

    fake_stream->write_data_to_read(
        list_of(odin::telnet::IAC)(odin::telnet::DO)(odin::telnet::KERMIT));

    telnet_stream->async_read(1, callback);

    io_service.reset();
    io_service.run();

    CPPUNIT_ASSERT_EQUAL(true, option.is_active());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_activation());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_deactivation());
    CPPUNIT_ASSERT_EQUAL(odin::u32(2), request_completed);
}

void telnet_server_option_fixture::test_active_deactivate_allow()
{
    boost::asio::io_service          io_service;
    shared_ptr<fake_byte_stream>     fake_stream(
        new fake_datastream<odin::u8, odin::u8>(io_service));
    shared_ptr<odin::telnet::stream> telnet_stream(
        new odin::telnet::stream(fake_stream, io_service));
    shared_ptr<odin::telnet::negotiation_router> telnet_negotiation_router(
        new odin::telnet::negotiation_router);
    shared_ptr<odin::telnet::subnegotiation_router> telnet_subnegotiation_router(
        new odin::telnet::subnegotiation_router);

    odin::telnet::input_visitor visitor(
        shared_ptr<odin::telnet::command_router>()
      , telnet_negotiation_router
      , telnet_subnegotiation_router
      , NULL);

    fake_server_option option(
        telnet_stream, telnet_negotiation_router, telnet_subnegotiation_router);

    CPPUNIT_ASSERT_EQUAL(false, option.is_active());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_activation());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_deactivation());

    odin::u32 request_completed = 0;
    function<void ()> request_complete_handler = (
        ++bll::var(request_completed)
    );

    option.on_request_complete(request_complete_handler);
    option.activate();

    CPPUNIT_ASSERT_EQUAL(false, option.is_active());
    CPPUNIT_ASSERT_EQUAL(true,  option.is_negotiating_activation());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_deactivation());

    fake_byte_stream::output_storage_type expected_data =
        list_of(odin::telnet::IAC)(odin::telnet::WILL)(odin::telnet::KERMIT);

    CPPUNIT_ASSERT(expected_data == fake_stream->read_data_written());

    fake_stream->write_data_to_read(
        list_of(odin::telnet::IAC)(odin::telnet::DO)(odin::telnet::KERMIT));

    odin::telnet::stream::input_callback_type callback =
    (
        bll::bind(&odin::telnet::apply_input_range, ref(visitor), bll::_1)
    );

    telnet_stream->async_read(1, callback);

    io_service.reset();
    io_service.run();

    CPPUNIT_ASSERT_EQUAL(true, option.is_active());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_activation());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_deactivation());
    CPPUNIT_ASSERT_EQUAL(odin::u32(1), request_completed);

    // Option is active HERE.

    option.deactivate();

    expected_data =
        list_of(odin::telnet::IAC)(odin::telnet::WONT)(odin::telnet::KERMIT);

    CPPUNIT_ASSERT(expected_data == fake_stream->read_data_written());

    CPPUNIT_ASSERT_EQUAL(true, option.is_active());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_activation());
    CPPUNIT_ASSERT_EQUAL(true, option.is_negotiating_deactivation());
    CPPUNIT_ASSERT_EQUAL(odin::u32(1), request_completed);

    fake_stream->write_data_to_read(
        list_of(odin::telnet::IAC)(odin::telnet::DONT)(odin::telnet::KERMIT));

    telnet_stream->async_read(1, callback);

    io_service.reset();
    io_service.run();

    CPPUNIT_ASSERT_EQUAL(false, option.is_active());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_activation());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_deactivation());
    CPPUNIT_ASSERT_EQUAL(odin::u32(2), request_completed);
}

void telnet_server_option_fixture::test_non_activatable_inactive_activate()
{
    boost::asio::io_service          io_service;
    shared_ptr<fake_byte_stream>     fake_stream(
        new fake_datastream<odin::u8, odin::u8>(io_service));
    shared_ptr<odin::telnet::stream> telnet_stream(
        new odin::telnet::stream(fake_stream, io_service));
    shared_ptr<odin::telnet::negotiation_router> telnet_negotiation_router(
        new odin::telnet::negotiation_router);
    shared_ptr<odin::telnet::subnegotiation_router> telnet_subnegotiation_router(
        new odin::telnet::subnegotiation_router);

    odin::telnet::input_visitor visitor(
        shared_ptr<odin::telnet::command_router>()
      , telnet_negotiation_router
      , telnet_subnegotiation_router
      , NULL);

    fake_server_option option(
        telnet_stream, telnet_negotiation_router, telnet_subnegotiation_router);

    CPPUNIT_ASSERT_EQUAL(false, option.is_active());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_activation());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_deactivation());
    CPPUNIT_ASSERT_EQUAL(false, option.is_activatable());

    odin::u32 request_completed = 0;
    function<void ()> request_complete_handler = (
        ++bll::var(request_completed)
    );

    option.on_request_complete(request_complete_handler);

    fake_stream->write_data_to_read(
        list_of(odin::telnet::IAC)(odin::telnet::DO)(odin::telnet::KERMIT));

    odin::telnet::stream::input_callback_type callback =
    (
        bll::bind(&odin::telnet::apply_input_range, ref(visitor), bll::_1)
    );

    telnet_stream->async_read(1, callback);

    io_service.reset();
    io_service.run();

    fake_byte_stream::output_storage_type expected_data =
        list_of(odin::telnet::IAC)(odin::telnet::WONT)(odin::telnet::KERMIT);

    CPPUNIT_ASSERT(expected_data == fake_stream->read_data_written());

    CPPUNIT_ASSERT_EQUAL(odin::u32(0), request_completed);

    CPPUNIT_ASSERT_EQUAL(false, option.is_active());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_activation());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_deactivation());
    CPPUNIT_ASSERT_EQUAL(false, option.is_activatable());
}

void telnet_server_option_fixture::test_non_activatable_active_activate()
{
    boost::asio::io_service          io_service;
    shared_ptr<fake_byte_stream>     fake_stream(
        new fake_datastream<odin::u8, odin::u8>(io_service));
    shared_ptr<odin::telnet::stream> telnet_stream(
        new odin::telnet::stream(fake_stream, io_service));
    shared_ptr<odin::telnet::negotiation_router> telnet_negotiation_router(
        new odin::telnet::negotiation_router);
    shared_ptr<odin::telnet::subnegotiation_router> telnet_subnegotiation_router(
        new odin::telnet::subnegotiation_router);

    odin::telnet::input_visitor visitor(
        shared_ptr<odin::telnet::command_router>()
      , telnet_negotiation_router
      , telnet_subnegotiation_router
      , NULL);

    fake_server_option option(
        telnet_stream, telnet_negotiation_router, telnet_subnegotiation_router);

    CPPUNIT_ASSERT_EQUAL(false, option.is_active());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_activation());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_deactivation());
    CPPUNIT_ASSERT_EQUAL(false, option.is_activatable());

    odin::u32 request_completed = 0;
    function<void ()> request_complete_handler = (
        ++bll::var(request_completed)
    );

    option.on_request_complete(request_complete_handler);
    option.activate();

    odin::telnet::stream::input_callback_type callback =
    (
        bll::bind(&odin::telnet::apply_input_range, ref(visitor), bll::_1)
    );

    telnet_stream->async_read(1, callback);

    io_service.reset();
    io_service.run();

    fake_byte_stream::output_storage_type expected_data =
        list_of(odin::telnet::IAC)(odin::telnet::WILL)(odin::telnet::KERMIT);

    CPPUNIT_ASSERT(expected_data == fake_stream->read_data_written());

    CPPUNIT_ASSERT_EQUAL(false, option.is_active());
    CPPUNIT_ASSERT_EQUAL(true,  option.is_negotiating_activation());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_deactivation());
    CPPUNIT_ASSERT_EQUAL(false, option.is_activatable());
    CPPUNIT_ASSERT_EQUAL(odin::u32(0), request_completed);

    fake_stream->write_data_to_read(
        list_of(odin::telnet::IAC)(odin::telnet::DO)(odin::telnet::KERMIT));

    telnet_stream->async_read(1, callback);

    io_service.reset();
    io_service.run();

    CPPUNIT_ASSERT_EQUAL(true,  option.is_active());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_activation());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_deactivation());
    CPPUNIT_ASSERT_EQUAL(false, option.is_activatable());
    CPPUNIT_ASSERT_EQUAL(odin::u32(1), request_completed);

    fake_stream->write_data_to_read(
        list_of(odin::telnet::IAC)(odin::telnet::DO)(odin::telnet::KERMIT));

    telnet_stream->async_read(1, callback);

    io_service.reset();
    io_service.run();

    expected_data =
        list_of(odin::telnet::IAC)(odin::telnet::WILL)(odin::telnet::KERMIT);

    CPPUNIT_ASSERT(expected_data == fake_stream->read_data_written());

    CPPUNIT_ASSERT_EQUAL(true,  option.is_active());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_activation());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_deactivation());
    CPPUNIT_ASSERT_EQUAL(false, option.is_activatable());
    CPPUNIT_ASSERT_EQUAL(odin::u32(1), request_completed);
}

void telnet_server_option_fixture::test_non_activatable_inactive_deactivate()
{
    boost::asio::io_service          io_service;
    shared_ptr<fake_byte_stream>     fake_stream(
        new fake_datastream<odin::u8, odin::u8>(io_service));
    shared_ptr<odin::telnet::stream> telnet_stream(
        new odin::telnet::stream(fake_stream, io_service));
    shared_ptr<odin::telnet::negotiation_router> telnet_negotiation_router(
        new odin::telnet::negotiation_router);
    shared_ptr<odin::telnet::subnegotiation_router> telnet_subnegotiation_router(
        new odin::telnet::subnegotiation_router);

    odin::telnet::input_visitor visitor(
        shared_ptr<odin::telnet::command_router>()
      , telnet_negotiation_router
      , telnet_subnegotiation_router
      , NULL);

    fake_server_option option(
        telnet_stream, telnet_negotiation_router, telnet_subnegotiation_router);

    CPPUNIT_ASSERT_EQUAL(false, option.is_active());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_activation());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_deactivation());
    CPPUNIT_ASSERT_EQUAL(false, option.is_activatable());

    odin::u32 request_completed = 0;
    function<void ()> request_complete_handler = (
        ++bll::var(request_completed)
    );

    option.on_request_complete(request_complete_handler);

    fake_stream->write_data_to_read(
        list_of(odin::telnet::IAC)(odin::telnet::DONT)(odin::telnet::KERMIT));

    odin::telnet::stream::input_callback_type callback =
    (
        bll::bind(&odin::telnet::apply_input_range, ref(visitor), bll::_1)
    );

    telnet_stream->async_read(1, callback);

    io_service.reset();
    io_service.run();

    fake_byte_stream::output_storage_type expected_data =
        list_of(odin::telnet::IAC)(odin::telnet::WONT)(odin::telnet::KERMIT);

    CPPUNIT_ASSERT(expected_data == fake_stream->read_data_written());

    CPPUNIT_ASSERT_EQUAL(odin::u32(0), request_completed);

    CPPUNIT_ASSERT_EQUAL(false, option.is_active());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_activation());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_deactivation());
    CPPUNIT_ASSERT_EQUAL(false, option.is_activatable());
}

void telnet_server_option_fixture::test_non_activatable_active_deactivate()
{
    boost::asio::io_service          io_service;
    shared_ptr<fake_byte_stream>     fake_stream(
        new fake_datastream<odin::u8, odin::u8>(io_service));
    shared_ptr<odin::telnet::stream> telnet_stream(
        new odin::telnet::stream(fake_stream, io_service));
    shared_ptr<odin::telnet::negotiation_router> telnet_negotiation_router(
        new odin::telnet::negotiation_router);
    shared_ptr<odin::telnet::subnegotiation_router> telnet_subnegotiation_router(
        new odin::telnet::subnegotiation_router);

    odin::telnet::input_visitor visitor(
        shared_ptr<odin::telnet::command_router>()
      , telnet_negotiation_router
      , telnet_subnegotiation_router
      , NULL);

    fake_server_option option(
        telnet_stream, telnet_negotiation_router, telnet_subnegotiation_router);

    CPPUNIT_ASSERT_EQUAL(false, option.is_active());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_activation());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_deactivation());
    CPPUNIT_ASSERT_EQUAL(false, option.is_activatable());

    odin::u32 request_completed = 0;
    function<void ()> request_complete_handler = (
        ++bll::var(request_completed)
    );

    option.on_request_complete(request_complete_handler);
    option.activate();

    odin::telnet::stream::input_callback_type callback =
    (
        bll::bind(&odin::telnet::apply_input_range, ref(visitor), bll::_1)
    );

    telnet_stream->async_read(1, callback);

    io_service.reset();
    io_service.run();

    fake_byte_stream::output_storage_type expected_data =
        list_of(odin::telnet::IAC)(odin::telnet::WILL)(odin::telnet::KERMIT);

    CPPUNIT_ASSERT(expected_data == fake_stream->read_data_written());

    CPPUNIT_ASSERT_EQUAL(false, option.is_active());
    CPPUNIT_ASSERT_EQUAL(true,  option.is_negotiating_activation());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_deactivation());
    CPPUNIT_ASSERT_EQUAL(false, option.is_activatable());
    CPPUNIT_ASSERT_EQUAL(odin::u32(0), request_completed);

    fake_stream->write_data_to_read(
        list_of(odin::telnet::IAC)(odin::telnet::DO)(odin::telnet::KERMIT));

    telnet_stream->async_read(1, callback);

    io_service.reset();
    io_service.run();

    CPPUNIT_ASSERT_EQUAL(true,  option.is_active());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_activation());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_deactivation());
    CPPUNIT_ASSERT_EQUAL(false, option.is_activatable());
    CPPUNIT_ASSERT_EQUAL(odin::u32(1), request_completed);

    fake_stream->write_data_to_read(
        list_of(odin::telnet::IAC)(odin::telnet::DONT)(odin::telnet::KERMIT));

    telnet_stream->async_read(1, callback);

    io_service.reset();
    io_service.run();

    expected_data =
        list_of(odin::telnet::IAC)(odin::telnet::WONT)(odin::telnet::KERMIT);

    CPPUNIT_ASSERT(expected_data == fake_stream->read_data_written());

    CPPUNIT_ASSERT_EQUAL(false, option.is_active());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_activation());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_deactivation());
    CPPUNIT_ASSERT_EQUAL(false, option.is_activatable());
    CPPUNIT_ASSERT_EQUAL(odin::u32(2), request_completed);
}

void telnet_server_option_fixture::test_activatable_inactive_activate()
{
    boost::asio::io_service          io_service;
    shared_ptr<fake_byte_stream>     fake_stream(
        new fake_datastream<odin::u8, odin::u8>(io_service));
    shared_ptr<odin::telnet::stream> telnet_stream(
        new odin::telnet::stream(fake_stream, io_service));
    shared_ptr<odin::telnet::negotiation_router> telnet_negotiation_router(
        new odin::telnet::negotiation_router);
    shared_ptr<odin::telnet::subnegotiation_router> telnet_subnegotiation_router(
        new odin::telnet::subnegotiation_router);

    odin::telnet::input_visitor visitor(
        shared_ptr<odin::telnet::command_router>()
      , telnet_negotiation_router
      , telnet_subnegotiation_router
      , NULL);

    fake_server_option option(
        telnet_stream, telnet_negotiation_router, telnet_subnegotiation_router);

    CPPUNIT_ASSERT_EQUAL(false, option.is_active());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_activation());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_deactivation());
    CPPUNIT_ASSERT_EQUAL(false, option.is_activatable());

    odin::u32 request_completed = 0;
    function<void ()> request_complete_handler = (
        ++bll::var(request_completed)
    );

    option.on_request_complete(request_complete_handler);
    option.set_activatable(true);

    CPPUNIT_ASSERT_EQUAL(true, option.is_activatable());

    fake_stream->write_data_to_read(
        list_of(odin::telnet::IAC)(odin::telnet::DO)(odin::telnet::KERMIT));

    odin::telnet::stream::input_callback_type callback =
    (
        bll::bind(&odin::telnet::apply_input_range, ref(visitor), bll::_1)
    );

    telnet_stream->async_read(1, callback);

    io_service.reset();
    io_service.run();

    fake_byte_stream::output_storage_type expected_data =
        list_of(odin::telnet::IAC)(odin::telnet::WILL)(odin::telnet::KERMIT);

    CPPUNIT_ASSERT(expected_data == fake_stream->read_data_written());

    CPPUNIT_ASSERT_EQUAL(odin::u32(1), request_completed);

    CPPUNIT_ASSERT_EQUAL(true,  option.is_active());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_activation());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_deactivation());
    CPPUNIT_ASSERT_EQUAL(true,  option.is_activatable());
}

void telnet_server_option_fixture::test_activatable_active_activate()
{
    boost::asio::io_service          io_service;
    shared_ptr<fake_byte_stream>     fake_stream(
        new fake_datastream<odin::u8, odin::u8>(io_service));
    shared_ptr<odin::telnet::stream> telnet_stream(
        new odin::telnet::stream(fake_stream, io_service));
    shared_ptr<odin::telnet::negotiation_router> telnet_negotiation_router(
        new odin::telnet::negotiation_router);
    shared_ptr<odin::telnet::subnegotiation_router> telnet_subnegotiation_router(
        new odin::telnet::subnegotiation_router);

    odin::telnet::input_visitor visitor(
        shared_ptr<odin::telnet::command_router>()
      , telnet_negotiation_router
      , telnet_subnegotiation_router
      , NULL);

    fake_server_option option(
        telnet_stream, telnet_negotiation_router, telnet_subnegotiation_router);

    CPPUNIT_ASSERT_EQUAL(false, option.is_active());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_activation());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_deactivation());
    CPPUNIT_ASSERT_EQUAL(false, option.is_activatable());

    odin::u32 request_completed = 0;
    function<void ()> request_complete_handler = (
        ++bll::var(request_completed)
    );

    option.on_request_complete(request_complete_handler);
    option.set_activatable(true);

    CPPUNIT_ASSERT_EQUAL(true, option.is_activatable());

    odin::telnet::stream::input_callback_type callback =
    (
        bll::bind(&odin::telnet::apply_input_range, ref(visitor), bll::_1)
    );

    telnet_stream->async_read(1, callback);

    option.activate();

    io_service.reset();
    io_service.run();

    fake_byte_stream::output_storage_type expected_data =
        list_of(odin::telnet::IAC)(odin::telnet::WILL)(odin::telnet::KERMIT);

    CPPUNIT_ASSERT(expected_data == fake_stream->read_data_written());

    CPPUNIT_ASSERT_EQUAL(odin::u32(0), request_completed);

    CPPUNIT_ASSERT_EQUAL(false,  option.is_active());
    CPPUNIT_ASSERT_EQUAL(true,  option.is_negotiating_activation());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_deactivation());
    CPPUNIT_ASSERT_EQUAL(true,  option.is_activatable());

    fake_stream->write_data_to_read(
        list_of(odin::telnet::IAC)(odin::telnet::DO)(odin::telnet::KERMIT));

    telnet_stream->async_read(1, callback);

    io_service.reset();
    io_service.run();

    CPPUNIT_ASSERT_EQUAL(odin::u32(1), request_completed);

    CPPUNIT_ASSERT_EQUAL(true,  option.is_active());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_activation());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_deactivation());
    CPPUNIT_ASSERT_EQUAL(true,  option.is_activatable());

    fake_stream->write_data_to_read(
        list_of(odin::telnet::IAC)(odin::telnet::DO)(odin::telnet::KERMIT));

    telnet_stream->async_read(1, callback);

    io_service.reset();
    io_service.run();

    expected_data =
        list_of(odin::telnet::IAC)(odin::telnet::WILL)(odin::telnet::KERMIT);

    CPPUNIT_ASSERT(expected_data == fake_stream->read_data_written());

    CPPUNIT_ASSERT_EQUAL(odin::u32(1), request_completed);

    CPPUNIT_ASSERT_EQUAL(true,  option.is_active());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_activation());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_deactivation());
    CPPUNIT_ASSERT_EQUAL(true,  option.is_activatable());
}

void telnet_server_option_fixture::test_activatable_inactive_deactivate()
{
    boost::asio::io_service          io_service;
    shared_ptr<fake_byte_stream>     fake_stream(
        new fake_datastream<odin::u8, odin::u8>(io_service));
    shared_ptr<odin::telnet::stream> telnet_stream(
        new odin::telnet::stream(fake_stream, io_service));
    shared_ptr<odin::telnet::negotiation_router> telnet_negotiation_router(
        new odin::telnet::negotiation_router);
    shared_ptr<odin::telnet::subnegotiation_router> telnet_subnegotiation_router(
        new odin::telnet::subnegotiation_router);

    odin::telnet::input_visitor visitor(
        shared_ptr<odin::telnet::command_router>()
      , telnet_negotiation_router
      , telnet_subnegotiation_router
      , NULL);

    fake_server_option option(
        telnet_stream, telnet_negotiation_router, telnet_subnegotiation_router);

    CPPUNIT_ASSERT_EQUAL(false, option.is_active());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_activation());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_deactivation());
    CPPUNIT_ASSERT_EQUAL(false, option.is_activatable());

    odin::u32 request_completed = 0;
    function<void ()> request_complete_handler = (
        ++bll::var(request_completed)
    );

    option.on_request_complete(request_complete_handler);
    option.set_activatable(true);

    CPPUNIT_ASSERT_EQUAL(true, option.is_activatable());

    fake_stream->write_data_to_read(
        list_of(odin::telnet::IAC)(odin::telnet::DONT)(odin::telnet::KERMIT));

    odin::telnet::stream::input_callback_type callback =
    (
        bll::bind(&odin::telnet::apply_input_range, ref(visitor), bll::_1)
    );

    telnet_stream->async_read(1, callback);

    io_service.reset();
    io_service.run();

    fake_byte_stream::output_storage_type expected_data =
        list_of(odin::telnet::IAC)(odin::telnet::WONT)(odin::telnet::KERMIT);

    CPPUNIT_ASSERT(expected_data == fake_stream->read_data_written());

    CPPUNIT_ASSERT_EQUAL(odin::u32(0), request_completed);

    CPPUNIT_ASSERT_EQUAL(false, option.is_active());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_activation());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_deactivation());
    CPPUNIT_ASSERT_EQUAL(true,  option.is_activatable());
}

void telnet_server_option_fixture::test_activatable_active_deactivate()
{
    boost::asio::io_service          io_service;
    shared_ptr<fake_byte_stream>     fake_stream(
        new fake_datastream<odin::u8, odin::u8>(io_service));
    shared_ptr<odin::telnet::stream> telnet_stream(
        new odin::telnet::stream(fake_stream, io_service));
    shared_ptr<odin::telnet::negotiation_router> telnet_negotiation_router(
        new odin::telnet::negotiation_router);
    shared_ptr<odin::telnet::subnegotiation_router> telnet_subnegotiation_router(
        new odin::telnet::subnegotiation_router);

    odin::telnet::input_visitor visitor(
        shared_ptr<odin::telnet::command_router>()
      , telnet_negotiation_router
      , telnet_subnegotiation_router
      , NULL);

    fake_server_option option(
        telnet_stream, telnet_negotiation_router, telnet_subnegotiation_router);

    CPPUNIT_ASSERT_EQUAL(false, option.is_active());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_activation());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_deactivation());
    CPPUNIT_ASSERT_EQUAL(false, option.is_activatable());

    odin::u32 request_completed = 0;
    function<void ()> request_complete_handler = (
        ++bll::var(request_completed)
    );

    option.on_request_complete(request_complete_handler);
    option.set_activatable(true);

    CPPUNIT_ASSERT_EQUAL(true, option.is_activatable());

    odin::telnet::stream::input_callback_type callback =
    (
        bll::bind(&odin::telnet::apply_input_range, ref(visitor), bll::_1)
    );

    telnet_stream->async_read(1, callback);

    option.activate();

    io_service.reset();
    io_service.run();

    fake_byte_stream::output_storage_type expected_data =
        list_of(odin::telnet::IAC)(odin::telnet::WILL)(odin::telnet::KERMIT);

    CPPUNIT_ASSERT(expected_data == fake_stream->read_data_written());

    CPPUNIT_ASSERT_EQUAL(odin::u32(0), request_completed);

    CPPUNIT_ASSERT_EQUAL(false,  option.is_active());
    CPPUNIT_ASSERT_EQUAL(true,  option.is_negotiating_activation());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_deactivation());
    CPPUNIT_ASSERT_EQUAL(true,  option.is_activatable());

    fake_stream->write_data_to_read(
        list_of(odin::telnet::IAC)(odin::telnet::DO)(odin::telnet::KERMIT));

    telnet_stream->async_read(1, callback);

    io_service.reset();
    io_service.run();

    CPPUNIT_ASSERT_EQUAL(odin::u32(1), request_completed);

    CPPUNIT_ASSERT_EQUAL(true,  option.is_active());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_activation());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_deactivation());
    CPPUNIT_ASSERT_EQUAL(true,  option.is_activatable());

    fake_stream->write_data_to_read(
        list_of(odin::telnet::IAC)(odin::telnet::DONT)(odin::telnet::KERMIT));

    telnet_stream->async_read(1, callback);

    io_service.reset();
    io_service.run();

    expected_data =
        list_of(odin::telnet::IAC)(odin::telnet::WONT)(odin::telnet::KERMIT);

    CPPUNIT_ASSERT(expected_data == fake_stream->read_data_written());

    CPPUNIT_ASSERT_EQUAL(odin::u32(2), request_completed);

    CPPUNIT_ASSERT_EQUAL(false, option.is_active());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_activation());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_deactivation());
    CPPUNIT_ASSERT_EQUAL(true,  option.is_activatable());
}

void telnet_server_option_fixture::test_inactive_subnegotiation()
{
    boost::asio::io_service          io_service;
    shared_ptr<fake_byte_stream>     fake_stream(
        new fake_datastream<odin::u8, odin::u8>(io_service));
    shared_ptr<odin::telnet::stream> telnet_stream(
        new odin::telnet::stream(fake_stream, io_service));
    shared_ptr<odin::telnet::negotiation_router> telnet_negotiation_router(
        new odin::telnet::negotiation_router);
    shared_ptr<odin::telnet::subnegotiation_router> telnet_subnegotiation_router(
        new odin::telnet::subnegotiation_router);

    odin::telnet::input_visitor visitor(
        shared_ptr<odin::telnet::command_router>()
      , telnet_negotiation_router
      , telnet_subnegotiation_router
      , NULL);

    fake_server_option option(
        telnet_stream, telnet_negotiation_router, telnet_subnegotiation_router);

    fake_byte_stream::input_storage_type subnegotiation =
        list_of(odin::telnet::IAC)(odin::telnet::SB)(odin::telnet::KERMIT)
               ('a')('b')
               (odin::telnet::IAC)(odin::telnet::SE);

    fake_stream->write_data_to_read(subnegotiation);

    odin::telnet::stream::input_callback_type callback =
    (
        bll::bind(&odin::telnet::apply_input_range, ref(visitor), bll::_1)
    );

    telnet_stream->async_read(1, callback);

    io_service.reset();
    io_service.run();

    CPPUNIT_ASSERT_EQUAL(true, option.subnegotiations_.empty());
}

void telnet_server_option_fixture::test_active_subnegotiation()
{
    boost::asio::io_service          io_service;
    shared_ptr<fake_byte_stream>     fake_stream(
        new fake_datastream<odin::u8, odin::u8>(io_service));
    shared_ptr<odin::telnet::stream> telnet_stream(
        new odin::telnet::stream(fake_stream, io_service));
    shared_ptr<odin::telnet::negotiation_router> telnet_negotiation_router(
        new odin::telnet::negotiation_router);
    shared_ptr<odin::telnet::subnegotiation_router> telnet_subnegotiation_router(
        new odin::telnet::subnegotiation_router);

    odin::telnet::input_visitor visitor(
        shared_ptr<odin::telnet::command_router>()
      , telnet_negotiation_router
      , telnet_subnegotiation_router
      , NULL);

    fake_server_option option(
        telnet_stream, telnet_negotiation_router, telnet_subnegotiation_router);

    CPPUNIT_ASSERT_EQUAL(false, option.is_active());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_activation());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_deactivation());

    odin::u32 request_completed = 0;
    function<void ()> request_complete_handler = (
        ++bll::var(request_completed)
    );

    option.on_request_complete(request_complete_handler);
    option.activate();

    CPPUNIT_ASSERT_EQUAL(false, option.is_active());
    CPPUNIT_ASSERT_EQUAL(true,  option.is_negotiating_activation());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_deactivation());

    fake_byte_stream::output_storage_type expected_data =
        list_of(odin::telnet::IAC)(odin::telnet::WILL)(odin::telnet::KERMIT);

    CPPUNIT_ASSERT(expected_data == fake_stream->read_data_written());

    fake_stream->write_data_to_read(
        list_of(odin::telnet::IAC)(odin::telnet::DO)(odin::telnet::KERMIT));

    odin::telnet::stream::input_callback_type callback =
    (
        bll::bind(&odin::telnet::apply_input_range, ref(visitor), bll::_1)
    );

    telnet_stream->async_read(1, callback);

    io_service.reset();
    io_service.run();

    CPPUNIT_ASSERT_EQUAL(true, option.is_active());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_activation());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_deactivation());
    CPPUNIT_ASSERT_EQUAL(odin::u32(1), request_completed);

    fake_byte_stream::input_storage_type subnegotiation =
        list_of(odin::telnet::IAC)(odin::telnet::SB)(odin::telnet::KERMIT)
               ('a')('b')
               (odin::telnet::IAC)(odin::telnet::SE);

    fake_stream->write_data_to_read(subnegotiation);

    telnet_stream->async_read(1, callback);

    io_service.reset();
    io_service.run();

    odin::telnet::subnegotiation_type expected_subnegotiation;
    expected_subnegotiation.option_id_ = odin::telnet::KERMIT;
    expected_subnegotiation.content_   = list_of('a')('b');

    CPPUNIT_ASSERT_EQUAL(std::size_t(1), option.subnegotiations_.size());
    CPPUNIT_ASSERT_EQUAL(
        expected_subnegotiation.option_id_
      , option.subnegotiations_[0].option_id_);
    CPPUNIT_ASSERT(
        expected_subnegotiation.content_
     == option.subnegotiations_[0].content_);
}

void telnet_server_option_fixture::test_send_subnegotiation()
{
    boost::asio::io_service          io_service;
    shared_ptr<fake_byte_stream>     fake_stream(
        new fake_datastream<odin::u8, odin::u8>(io_service));
    shared_ptr<odin::telnet::stream> telnet_stream(
        new odin::telnet::stream(fake_stream, io_service));
    shared_ptr<odin::telnet::negotiation_router> telnet_negotiation_router(
        new odin::telnet::negotiation_router);
    shared_ptr<odin::telnet::subnegotiation_router> telnet_subnegotiation_router(
        new odin::telnet::subnegotiation_router);

    odin::telnet::input_visitor visitor(
        shared_ptr<odin::telnet::command_router>()
      , telnet_negotiation_router
      , telnet_subnegotiation_router
      , NULL);

    fake_server_option option(
        telnet_stream, telnet_negotiation_router, telnet_subnegotiation_router);

    CPPUNIT_ASSERT_EQUAL(false, option.is_active());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_activation());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_deactivation());

    odin::u32 request_completed = 0;
    function<void ()> request_complete_handler = (
        ++bll::var(request_completed)
    );

    option.on_request_complete(request_complete_handler);
    option.activate();

    CPPUNIT_ASSERT_EQUAL(false, option.is_active());
    CPPUNIT_ASSERT_EQUAL(true,  option.is_negotiating_activation());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_deactivation());

    fake_byte_stream::output_storage_type expected_data =
        list_of(odin::telnet::IAC)(odin::telnet::WILL)(odin::telnet::KERMIT);

    CPPUNIT_ASSERT(expected_data == fake_stream->read_data_written());

    fake_stream->write_data_to_read(
        list_of(odin::telnet::IAC)(odin::telnet::DO)(odin::telnet::KERMIT));

    odin::telnet::stream::input_callback_type callback =
    (
        bll::bind(&odin::telnet::apply_input_range, ref(visitor), bll::_1)
    );

    telnet_stream->async_read(1, callback);

    io_service.reset();
    io_service.run();

    CPPUNIT_ASSERT_EQUAL(true, option.is_active());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_activation());
    CPPUNIT_ASSERT_EQUAL(false, option.is_negotiating_deactivation());
    CPPUNIT_ASSERT_EQUAL(odin::u32(1), request_completed);

    // Now test that sending the subnegotiation actually works.
    odin::telnet::subnegotiation_content_type content = list_of('a')('b')('c');

    option.send_subnegotiation(content);

    io_service.reset();
    io_service.run();

    fake_byte_stream::output_storage_type expected_subnegotiation_data =
        list_of(odin::telnet::IAC)(odin::telnet::SB)(odin::telnet::KERMIT)
               ('a')('b')('c')
               (odin::telnet::IAC)(odin::telnet::SE);

    CPPUNIT_ASSERT(
        expected_subnegotiation_data == fake_stream->read_data_written());
}


text:
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <RelativeLayout
        tools:context=".MainActivity"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="@dimen/abc_action_bar_default_height_material"
            android:background="?attr/colorPrimary"
            android:theme="@style/ThemeOverlay.AppCompat.Dark" />

        <FrameLayout
            android:id="@+id/container"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_below="@id/toolbar"
            android:focusable="true"
            android:focusableInTouchMode="true"
            android:clickable="true" />

    </RelativeLayout>

    <android.support.design.widget.NavigationView
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:background="@android:color/white">

        <include layout="@layout/main_drawer_layout" />

    </android.support.design.widget.NavigationView>

</android.support.v4.widget.DrawerLayout>