refactor(WebWorker): Abstract message passing and serialization to UIMessageBroker

closes #3703

Closes #3815
This commit is contained in:
Jason Teplitz
2015-08-21 16:55:50 -07:00
parent aeef19e2a6
commit 21f60c5dce
22 changed files with 319 additions and 248 deletions
@@ -0,0 +1,21 @@
library angular2.test.web_workers.worker.mock_event_emitter;
import 'dart:core';
import 'dart:async';
import "package:angular2/src/facade/async.dart";
class MockEventEmitter extends EventEmitter {
List<Function> _nextFns = new List();
@override
StreamSubscription listen(void onData(dynamic line),
{void onError(Error error), void onDone(), bool cancelOnError}) {
_nextFns.add(onData);
return null;
}
@override
void add(value) {
_nextFns.forEach((fn) => fn(value));
}
}
@@ -0,0 +1,18 @@
import {EventEmitter} from 'angular2/src/facade/async';
import * as Rx from 'rx';
import {ListWrapper} from 'angular2/src/facade/collection';
export class MockEventEmitter extends EventEmitter {
private _nextFns: List<Function> = [];
constructor() { super(); }
observer(generator: any): Rx.IDisposable {
this._nextFns.push(generator.next);
return null;
}
next(value: any) {
ListWrapper.forEach(this._nextFns, (fn) => { fn(value); });
}
}
@@ -0,0 +1,69 @@
import {
AsyncTestCompleter,
inject,
describe,
it,
expect,
beforeEach,
createTestInjector,
beforeEachBindings,
SpyObject,
proxy
} from 'angular2/test_lib';
import {createPairedMessageBuses} from '../shared/web_worker_test_util';
import {Serializer, PRIMITIVE} from 'angular2/src/web_workers/shared/serializer';
import {ServiceMessageBroker} from 'angular2/src/web_workers/shared/service_message_broker';
import {ObservableWrapper, PromiseWrapper} from 'angular2/src/facade/async';
import {bind} from 'angular2/di';
import {ON_WEB_WORKER} from 'angular2/src/web_workers/shared/api';
import {RenderProtoViewRefStore} from 'angular2/src/web_workers/shared/render_proto_view_ref_store';
import {
RenderViewWithFragmentsStore
} from 'angular2/src/web_workers/shared/render_view_with_fragments_store';
export function main() {
const CHANNEL = "UIMessageBroker Test Channel";
const TEST_METHOD = "TEST_METHOD";
const PASSED_ARG_1 = 5;
const PASSED_ARG_2 = 'TEST';
const RESULT = 20;
const ID = "methodId";
beforeEachBindings(() => [
bind(ON_WEB_WORKER)
.toValue(true),
RenderProtoViewRefStore,
RenderViewWithFragmentsStore
]);
describe("UIMessageBroker", () => {
var messageBuses;
beforeEach(() => { messageBuses = createPairedMessageBuses(); });
it("should call registered method with correct arguments",
inject([Serializer], (serializer) => {
var broker = new ServiceMessageBroker(messageBuses.ui, serializer, CHANNEL);
broker.registerMethod(TEST_METHOD, [PRIMITIVE, PRIMITIVE], (arg1, arg2) => {
expect(arg1).toEqual(PASSED_ARG_1);
expect(arg2).toEqual(PASSED_ARG_2);
});
ObservableWrapper.callNext(messageBuses.worker.to(CHANNEL),
{'method': TEST_METHOD, 'args': [PASSED_ARG_1, PASSED_ARG_2]});
}));
it("should return promises to the worker", inject([Serializer], (serializer) => {
var broker = new ServiceMessageBroker(messageBuses.ui, serializer, CHANNEL);
broker.registerMethod(TEST_METHOD, [PRIMITIVE], (arg1) => {
expect(arg1).toEqual(PASSED_ARG_1);
return PromiseWrapper.wrap(() => { return RESULT; });
});
ObservableWrapper.callNext(messageBuses.worker.to(CHANNEL),
{'method': TEST_METHOD, 'id': ID, 'args': [PASSED_ARG_1]});
ObservableWrapper.subscribe(messageBuses.worker.from(CHANNEL), (data: any) => {
expect(data.type).toEqual("result");
expect(data.id).toEqual(ID);
expect(data.value).toEqual(RESULT);
});
}));
});
}
@@ -0,0 +1,59 @@
import {StringMap, StringMapWrapper, ListWrapper} from 'angular2/src/facade/collection';
import {
MessageBusSink,
MessageBusSource,
MessageBus
} from 'angular2/src/web_workers/shared/message_bus';
import {MockEventEmitter} from './mock_event_emitter';
/**
* Returns two MessageBus instances that are attached to each other.
* Such that whatever goes into one's sink comes out the others source.
*/
export function createPairedMessageBuses(): PairedMessageBuses {
var firstChannels: StringMap<string, MockEventEmitter> = {};
var workerMessageBusSink = new MockMessageBusSink(firstChannels);
var uiMessageBusSource = new MockMessageBusSource(firstChannels);
var secondChannels: StringMap<string, MockEventEmitter> = {};
var uiMessageBusSink = new MockMessageBusSink(secondChannels);
var workerMessageBusSource = new MockMessageBusSource(secondChannels);
return new PairedMessageBuses(new MockMessageBus(uiMessageBusSink, uiMessageBusSource),
new MockMessageBus(workerMessageBusSink, workerMessageBusSource));
}
export class PairedMessageBuses {
constructor(public ui: MessageBus, public worker: MessageBus) {}
}
export class MockMessageBusSource implements MessageBusSource {
constructor(private _channels: StringMap<string, MockEventEmitter>) {}
from(channel: string): MockEventEmitter {
if (!StringMapWrapper.contains(this._channels, channel)) {
this._channels[channel] = new MockEventEmitter();
}
return this._channels[channel];
}
}
export class MockMessageBusSink implements MessageBusSink {
constructor(private _channels: StringMap<string, MockEventEmitter>) {}
to(channel: string): MockEventEmitter {
if (!StringMapWrapper.contains(this._channels, channel)) {
this._channels[channel] = new MockEventEmitter();
}
return this._channels[channel];
}
}
export class MockMessageBus extends MessageBus {
constructor(public sink: MockMessageBusSink, public source: MockMessageBusSource) { super(); }
to(channel: string): MockEventEmitter { return this.sink.to(channel); }
from(channel: string): MockEventEmitter { return this.source.from(channel); }
}