blob: 8c6d3cbd97e37312cd9402276381d3e87730ff66 [file] [log] [blame]
/**
* @fileoverview Tests for provider_manager.
*/
goog.setTestOnly('provider_manager_test');
goog.require('mr.CancellablePromise');
goog.require('mr.MediaSourceUtils');
goog.require('mr.MirrorAnalytics');
goog.require('mr.Module');
goog.require('mr.PersistentDataManager');
goog.require('mr.PresentationConnectionCloseReason');
goog.require('mr.PresentationConnectionState');
goog.require('mr.ProviderManager');
goog.require('mr.Route');
goog.require('mr.RouteMessage');
goog.require('mr.RouteRequestError');
goog.require('mr.RouteRequestResultCode');
goog.require('mr.Sink');
goog.require('mr.SinkAvailability');
goog.require('mr.SinkList');
goog.require('mr.UnitTestUtils');
goog.require('mr.mirror.Error');
goog.require('mr.mirror.ServiceName');
describe('Tests ProviderManager', function() {
let sourceUrn;
let mockMediaRouterService;
let providerManager;
let mockProvider1;
let mockProvider1Name;
let mockProvider2;
let mockBrokenProvider;
let mockCastMirrorService;
let mockWebrtcMirrorService;
let mirrorServiceMap;
const provider1Routes = [];
const provider2Routes = [];
const providerMethods = [
'getName', 'initialize', 'getAvailableSinks', 'createRoute',
'terminateRoute', 'sendRouteMessage', 'getSinkById', 'getMirrorSettings',
'getMirrorServiceName', 'canRoute', 'startObservingMediaSinks',
'stopObservingMediaSinks', 'startObservingMediaRoutes',
'stopObservingMediaRoutes', 'getRoutes', 'canJoin', 'searchSinks',
'createMediaRouteController'
];
const mirrorServiceMethods = [
'initialize',
'getName',
'startMirroring',
'stopCurrentMirroring',
'createMirrorSession',
'updateMirroring',
];
const castMirrorServiceMethods = mirrorServiceMethods.slice();
const presentationId = 'presentationId';
const routeId = '123';
let mockClock;
beforeEach(function() {
mr.PersistentDataManager.clear();
mr.Module.clearForTest();
mr.UnitTestUtils.mockMojoApi();
sourceUrn = 'urn:dial-multiscreen-org:dial:application:YouTube';
mockClock = null;
window['chrome'] = chrome || {};
chrome['runtime'] = chrome.runtime || {};
chrome.runtime.id = '123';
chrome.runtime.getManifest = function() {
return {version: 'fakeVersion'};
};
mockProvider1 = jasmine.createSpyObj('provider1', providerMethods);
mockProvider1Name = 'p1';
mockProvider1.getName.and.returnValue(mockProvider1Name);
mockProvider1.getRoutes.and.callFake(() => provider1Routes);
mockProvider2 = jasmine.createSpyObj('provider2', providerMethods);
mockProvider2.getName.and.returnValue('p2');
mockProvider2.getRoutes.and.callFake(() => provider2Routes);
mockBrokenProvider =
jasmine.createSpyObj('brokenProvider', providerMethods);
mockBrokenProvider.initialize.and.throwError(
new Error('I forgot how to initialize. @_@'));
mockCastMirrorService =
jasmine.createSpyObj('castMirrorService', castMirrorServiceMethods);
mockCastMirrorService.getName.and.returnValue(
mr.mirror.ServiceName.CAST_STREAMING);
mockWebrtcMirrorService =
jasmine.createSpyObj('webrtcMirrorService', mirrorServiceMethods);
mockWebrtcMirrorService.getName.and.returnValue(
mr.mirror.ServiceName.WEBRTC);
mirrorServiceMap = new Map();
mirrorServiceMap.set(
mr.mirror.ServiceName.CAST_STREAMING, mockCastMirrorService);
mirrorServiceMap.set(mr.mirror.ServiceName.WEBRTC, mockWebrtcMirrorService);
// These two not needed?
spyOn(mr.mirror.cast, 'Service').and.returnValue(mockCastMirrorService);
spyOn(mr.mirror.webrtc, 'WebRtcService')
.and.returnValue(mockWebrtcMirrorService);
mockMediaRouterService = jasmine.createSpyObj('mrService', [
'setKeepAlive', 'getKeepAlive', 'setHandlers',
'onPresentationConnectionClosed', 'onPresentationConnectionStateChanged',
'onRoutesUpdated', 'onSinkAvailabilityUpdated', 'onSinksReceived',
'start', 'onSearchSinkIdReceived', 'onRouteMessagesReceived'
]);
const mockCloudComponentProvider = {
getIdentityService: function() {
return {};
}
};
spyOn(mr.cloud.CloudComponentProvider, 'getInstance')
.and.returnValue(mockCloudComponentProvider);
providerManager = new mr.ProviderManager();
expect(mockMediaRouterService.start.calls.count()).toBe(0);
providerManager.initialize(mockMediaRouterService, []);
mockMediaRouterService.start.and.returnValue('instance123');
spyOn(providerManager.mrRouteMessageSender_, 'send').and.callThrough();
spyOn(providerManager, 'getMirrorService').and.callFake(serviceName => {
return Promise.resolve(mirrorServiceMap.get(serviceName));
});
});
afterEach(function() {
if (mockClock) {
mr.UnitTestUtils.restoreRealClockAndPromises();
}
});
describe('Test presentation connection state changes', function() {
it('onPresentationConnectionStateChanged', function() {
const state = mr.PresentationConnectionState.TERMINATED;
providerManager.onPresentationConnectionStateChanged(routeId, state);
expect(mockMediaRouterService.onPresentationConnectionStateChanged)
.toHaveBeenCalledWith(routeId, state);
});
it('onPresentationConnectionStateClosed', function() {
const closeReason = mr.PresentationConnectionCloseReason.WENT_AWAY;
const message = 'Connection went away';
providerManager.onPresentationConnectionClosed(
routeId, closeReason, message);
expect(mockMediaRouterService.onPresentationConnectionClosed)
.toHaveBeenCalledWith(routeId, closeReason, message);
});
});
describe('Test registerAllProviders', function() {
it('Provider is initialized', function() {
providerManager.registerAllProviders(
[mockProvider1, mockProvider2, mockBrokenProvider]);
expect(mockProvider1.initialize.calls.count()).toBe(1);
expect(mockProvider2.initialize.calls.count()).toBe(1);
expect(mockBrokenProvider.initialize.calls.count()).toBe(1);
expect(providerManager.getProviders().some(
p => p.getName() == mockProvider1.getName()))
.toBe(true);
expect(providerManager.getProviders().some(
p => p.getName() == mockProvider2.getName()))
.toBe(true);
expect(!providerManager.getProviders().some(
p => p.getName() == mockBrokenProvider.getName()))
.toBe(true);
});
it('Route message event is listened to', function() {
mockClock = mr.UnitTestUtils.useMockClockAndPromises();
const route = new mr.Route(routeId, 'pId', '0', null, false, '', null);
const message = 'msg';
providerManager.registerAllProviders([mockProvider1]);
providerManager.onRouteAdded(mockProvider1, route);
providerManager.onRouteMessage(mockProvider1, routeId, message, true);
providerManager.startListeningForRouteMessages(routeId);
mockClock.tick(mr.RouteMessageSender.SEND_MESSAGE_INTERVAL_MILLIS);
expect(mockMediaRouterService.onRouteMessagesReceived)
.toHaveBeenCalledWith(
routeId, [new mr.RouteMessage(routeId, message)]);
});
it('Message of a closed route is not forwarded', function() {
const message = 'msg';
providerManager.registerAllProviders([mockProvider1]);
providerManager.onRouteMessage(mockProvider1, routeId, message);
expect(providerManager.mrRouteMessageSender_.send).not.toHaveBeenCalled();
});
it('InternalMessage sends and is not forwarded to app', function() {
const route = new mr.Route(routeId, 'pId', '0', null, false, '', null);
const message = 'msg';
providerManager.registerAllProviders([mockProvider1]);
providerManager.onRouteAdded(mockProvider1, route);
providerManager.onInternalMessage(mockProvider1, routeId, message);
expect(providerManager.mrRouteMessageSender_.send).not.toHaveBeenCalled();
});
it('start/stopObservingMediaRoutes is passed to provider', function() {
mockProvider1.getRoutes.and.returnValue([]);
mockProvider2.getRoutes.and.returnValue([]);
providerManager.registerAllProviders([mockProvider1, mockProvider2]);
expect(mockProvider1.startObservingMediaRoutes.calls.count()).toBe(0);
expect(mockProvider2.startObservingMediaRoutes.calls.count()).toBe(0);
expect(mockProvider1.stopObservingMediaRoutes.calls.count()).toBe(0);
expect(mockProvider2.stopObservingMediaRoutes.calls.count()).toBe(0);
providerManager.startObservingMediaRoutes(sourceUrn);
expect(mockProvider1.startObservingMediaRoutes.calls.count()).toBe(1);
expect(mockProvider2.startObservingMediaRoutes.calls.count()).toBe(1);
expect(mockProvider1.stopObservingMediaRoutes.calls.count()).toBe(0);
expect(mockProvider2.stopObservingMediaRoutes.calls.count()).toBe(0);
providerManager.stopObservingMediaRoutes(sourceUrn);
expect(mockProvider1.startObservingMediaRoutes.calls.count()).toBe(1);
expect(mockProvider2.startObservingMediaRoutes.calls.count()).toBe(1);
expect(mockProvider1.stopObservingMediaRoutes.calls.count()).toBe(1);
expect(mockProvider2.stopObservingMediaRoutes.calls.count()).toBe(1);
});
it('Routes query result is sent back to MR initially', function() {
providerManager.startObservingMediaRoutes(sourceUrn);
expect(mockMediaRouterService.onRoutesUpdated.calls.count()).toBe(1);
providerManager.stopObservingMediaRoutes(sourceUrn);
expect(mockMediaRouterService.onRoutesUpdated.calls.count()).toBe(1);
});
it('Routes query result is sent back to MR on events', function() {
mockClock = mr.UnitTestUtils.useMockClockAndPromises();
const route = new mr.Route(routeId, 'pId', '0', null, false, '', null);
providerManager.startObservingMediaRoutes(sourceUrn);
// Send routes right away
expect(mockMediaRouterService.onRoutesUpdated.calls.count()).toBe(1);
// Call to MR is throttled.
mockClock.tick(mr.ProviderManager.ALL_QUERIES_INTERVAL_MS_ / 2);
providerManager.onRouteAdded(mockProvider1, route);
expect(mockMediaRouterService.onRoutesUpdated.calls.count()).toBe(1);
providerManager.onRouteRemoved(mockProvider1, route);
expect(mockMediaRouterService.onRoutesUpdated.calls.count()).toBe(1);
providerManager.onRouteAdded(mockProvider1, route);
expect(mockMediaRouterService.onRoutesUpdated.calls.count()).toBe(1);
mockClock.tick(mr.ProviderManager.ALL_QUERIES_INTERVAL_MS_ + 1);
expect(mockMediaRouterService.onRoutesUpdated.calls.count()).toBe(2);
});
it('Routes query result is not sent back to MR if stopped', function() {
mockClock = mr.UnitTestUtils.useMockClockAndPromises();
const route = new mr.Route(routeId, 'pId', '0', null, false, '', null);
providerManager.startObservingMediaRoutes(sourceUrn);
expect(mockMediaRouterService.onRoutesUpdated.calls.count()).toBe(1);
mockClock.tick(mr.ProviderManager.ALL_QUERIES_INTERVAL_MS_ / 2);
providerManager.onRouteAdded(mockProvider1, route);
expect(mockMediaRouterService.onRoutesUpdated.calls.count()).toBe(1);
providerManager.onRouteRemoved(mockProvider1, route);
providerManager.onRouteAdded(mockProvider1, route);
providerManager.stopObservingMediaRoutes(sourceUrn);
// Query was stopped to MR is not called.
mockClock.tick(mr.ProviderManager.ALL_QUERIES_INTERVAL_MS_ / 2 + 1);
expect(mockMediaRouterService.onRoutesUpdated.calls.count()).toBe(1);
});
it('Multiple route queries do not cause duplicate routes', function() {
mockClock = mr.UnitTestUtils.useMockClockAndPromises();
const route = new mr.Route(routeId, 'pId', '0', null, false, '', null);
const sourceUrn2 = '';
// Only one route will be returned.
mockProvider1.getRoutes.and.returnValue([route]);
mockProvider1.canJoin.and.returnValue(false);
providerManager.registerAllProviders([mockProvider1]);
// Multiple route queries means the one route should be returned for
// multiple route queries.
providerManager.startObservingMediaRoutes(sourceUrn);
providerManager.startObservingMediaRoutes(sourceUrn2);
// Call to MR is throttled - so this will only happen once.
expect(mockMediaRouterService.onRoutesUpdated.calls.count()).toBe(1);
expect(mockMediaRouterService.onRoutesUpdated)
.toHaveBeenCalledWith([route], sourceUrn, []);
mockClock.tick(mr.ProviderManager.ALL_QUERIES_INTERVAL_MS_ + 1);
// This will fire twice because there are two route queries, bringing the
// total to 3 times.
expect(mockMediaRouterService.onRoutesUpdated.calls.count()).toBe(3);
expect(mockMediaRouterService.onRoutesUpdated)
.toHaveBeenCalledWith([route], sourceUrn, []);
expect(mockMediaRouterService.onRoutesUpdated)
.toHaveBeenCalledWith([route], sourceUrn2, []);
});
});
describe('Test keep alive', function() {
it('Keep alive for 1 provider', function() {
providerManager.requestKeepAlive(mockProvider1, true);
expect(mockMediaRouterService.setKeepAlive).toHaveBeenCalledWith(true);
providerManager.requestKeepAlive(mockProvider1, false);
expect(mockMediaRouterService.setKeepAlive).toHaveBeenCalledWith(false);
});
it('Keep alive for more than 1 provider', function() {
providerManager.requestKeepAlive(mockProvider1, true);
expect(mockMediaRouterService.setKeepAlive).toHaveBeenCalledWith(true);
providerManager.requestKeepAlive(mockProvider2, true);
expect(mockMediaRouterService.setKeepAlive).toHaveBeenCalledWith(true);
providerManager.requestKeepAlive(mockProvider1, false);
expect(mockMediaRouterService.setKeepAlive).toHaveBeenCalledWith(true);
providerManager.requestKeepAlive(mockProvider2, false);
expect(mockMediaRouterService.setKeepAlive).toHaveBeenCalledWith(false);
});
});
describe('Test createRoute', function() {
let sinkId;
let localRoute;
beforeEach(function() {
sourceUrn = 'urn:dial-multiscreen-org:dial:application:YouTube';
sinkId = 'sink1';
localRoute = new mr.Route('r2', 'pId', sinkId, sourceUrn, true, '', null);
providerManager.registerAllProviders([mockProvider1, mockProvider2]);
});
it('No provider can handle it', function(done) {
mockProvider1.canRoute.and.returnValue(false);
mockProvider2.canRoute.and.returnValue(false);
providerManager.createRoute(sourceUrn, sinkId, presentationId)
.catch(e => {
expect(e instanceof mr.RouteRequestError).toBe(true);
expect(e.errorCode)
.toEqual(mr.RouteRequestResultCode.NO_SUPPORTED_PROVIDER);
expect(mockProvider1.canRoute)
.toHaveBeenCalledWith(sourceUrn, sinkId);
expect(mockProvider2.canRoute)
.toHaveBeenCalledWith(sourceUrn, sinkId);
expect(mockMediaRouterService.setKeepAlive.calls.count()).toBe(0);
done();
});
});
it('One provider can handle, but fail to create session', function(done) {
mockProvider1.canRoute.and.returnValue(true);
mockProvider2.canRoute.and.returnValue(false);
mockProvider1.createRoute.and.returnValue(
mr.CancellablePromise.reject(Error('err')));
providerManager.createRoute(sourceUrn, sinkId, presentationId)
.catch(e => {
expect(e instanceof mr.RouteRequestError).toBe(true);
expect(e.errorCode)
.toEqual(mr.RouteRequestResultCode.UNKNOWN_ERROR);
expect(mockProvider1.createRoute.calls.count()).toBe(1);
expect(mockProvider1.createRoute)
.toHaveBeenCalledWith(
sourceUrn, sinkId, presentationId, false,
mr.ProviderManager.CREATE_ROUTE_TIMEOUT_MS, undefined,
undefined);
expect(mockProvider2.createRoute.calls.count()).toBe(0);
expect(mockMediaRouterService.setKeepAlive.calls.argsFor(0))
.toEqual([true]);
expect(mockMediaRouterService.setKeepAlive.calls.argsFor(1))
.toEqual([false]);
done();
});
});
it('One provider can handle it, and created session', function(done) {
mockProvider1.canRoute.and.returnValue(true);
mockProvider2.canRoute.and.returnValue(false);
mockProvider1.createRoute.and.callFake(() => {
providerManager.onRouteAdded(mockProvider1, localRoute);
return mr.CancellablePromise.resolve(localRoute);
});
providerManager.createRoute(sourceUrn, sinkId, presentationId).then(r => {
expect(r).toEqual(localRoute);
expect(mockProvider1.createRoute.calls.count()).toBe(1);
expect(mockProvider1.createRoute)
.toHaveBeenCalledWith(
sourceUrn, sinkId, presentationId, false,
mr.ProviderManager.CREATE_ROUTE_TIMEOUT_MS, undefined,
undefined);
expect(mockProvider2.createRoute.calls.count()).toBe(0);
done();
});
});
it('createRoute with custom timeout', function(done) {
mockProvider1.canRoute.and.returnValue(true);
mockProvider2.canRoute.and.returnValue(false);
mockProvider1.createRoute.and.callFake(() => {
providerManager.onRouteAdded(mockProvider1, localRoute);
return mr.CancellablePromise.resolve(localRoute);
});
const timeoutMillis = 12345;
providerManager
.createRoute(
sourceUrn, sinkId, presentationId, undefined, undefined,
timeoutMillis)
.then(r => {
expect(r).toEqual(localRoute);
expect(mockProvider1.createRoute.calls.count()).toBe(1);
expect(mockProvider1.createRoute)
.toHaveBeenCalledWith(
sourceUrn, sinkId, presentationId, false, timeoutMillis,
undefined, undefined);
expect(mockProvider2.createRoute.calls.count()).toBe(0);
done();
});
});
it('a mirror session is created', function(done) {
sourceUrn = mr.MediaSourceUtils.DESKTOP_MIRROR_URN;
localRoute = new mr.Route('r2', 'pId', sinkId, sourceUrn, true, '', null);
mockProvider1.canRoute.and.returnValue(true);
mockProvider2.canRoute.and.returnValue(false);
const mirrorSettings = {};
mockProvider1.getMirrorSettings.and.returnValue(mirrorSettings);
mockProvider1.getMirrorServiceName.and.returnValue(
mr.mirror.ServiceName.CAST_STREAMING);
mockProvider1.createRoute.and.callFake(() => {
providerManager.onRouteAdded(this, localRoute);
return mr.CancellablePromise.resolve(localRoute);
});
mockCastMirrorService.startMirroring.and.returnValue(
mr.CancellablePromise.resolve(localRoute));
expect(providerManager.getLastUsedMirrorService()).toBeNull();
providerManager.createRoute(sourceUrn, sinkId, presentationId)
.then(route => {
providerManager.startMirroring(mockProvider1, route, presentationId)
.promise.then(r => {
expect(mockProvider1.createRoute.calls.count()).toBe(1);
expect(mockProvider1.createRoute)
.toHaveBeenCalledWith(
sourceUrn, sinkId, presentationId, false,
mr.ProviderManager.CREATE_ROUTE_TIMEOUT_MS, undefined,
undefined);
expect(mockProvider2.createRoute.calls.count()).toBe(0);
expect(mockCastMirrorService.startMirroring.calls.count())
.toBe(1);
expect(mockCastMirrorService.startMirroring)
.toHaveBeenCalledWith(
localRoute, sourceUrn, mirrorSettings, presentationId,
undefined);
expect(providerManager.routeIdToProvider_.has(localRoute.id))
.toBe(true);
expect(providerManager.routeIdToMirrorServiceName_.has(
localRoute.id))
.toBe(true);
expect(providerManager.getLastUsedMirrorService())
.toBe(mr.mirror.ServiceName.CAST_STREAMING);
done();
});
});
});
it('a mirror session is not created', function(done) {
sourceUrn = mr.MediaSourceUtils.DESKTOP_MIRROR_URN;
localRoute = new mr.Route('r2', 'pId', sinkId, sourceUrn, true, '', null);
mockProvider1.canRoute.and.returnValue(true);
mockProvider2.canRoute.and.returnValue(false);
const mirrorSettings = {};
mockProvider1.getMirrorSettings.and.returnValue(mirrorSettings);
mockProvider1.getMirrorServiceName.and.returnValue(
mr.mirror.ServiceName.CAST_STREAMING);
mockProvider1.createRoute.and.callFake(() => {
providerManager.onRouteAdded(mockProvider1, localRoute);
return mr.CancellablePromise.resolve(localRoute);
});
const error = Error('failed to mirror');
mockCastMirrorService.startMirroring.and.callFake(
() => mr.CancellablePromise.reject(error));
providerManager.createRoute(sourceUrn, sinkId, presentationId)
.then(route => {
providerManager.startMirroring(mockProvider1, route, presentationId)
.promise.catch(err => {
expect(err).toEqual(error);
expect(mockProvider1.createRoute.calls.count()).toBe(1);
expect(mockProvider1.createRoute)
.toHaveBeenCalledWith(
sourceUrn, sinkId, presentationId, false,
mr.ProviderManager.CREATE_ROUTE_TIMEOUT_MS, undefined,
undefined);
expect(mockProvider2.createRoute.calls.count()).toBe(0);
expect(mockCastMirrorService.startMirroring.calls.count())
.toBe(1);
expect(mockCastMirrorService.startMirroring)
.toHaveBeenCalledWith(
localRoute, sourceUrn, mirrorSettings, presentationId,
undefined);
// Call onMirrorSessionEnded as the mirror service would.
providerManager.onMirrorSessionEnded(route.id);
// Call onRouteRemoved as the provider would.
providerManager.onRouteRemoved(mockProvider1, route);
expect(providerManager.routeIdToProvider_.has(localRoute.id))
.toBe(false);
expect(providerManager.routeIdToMirrorServiceName_.has(
localRoute.id))
.toBe(false);
done();
});
});
});
it('an mr.RouteRequestError is returned for user cancellation', (done) => {
mockProvider1.canRoute.and.returnValue(true);
mockProvider1.getMirrorSettings.and.returnValue({});
mockProvider1.getMirrorServiceName.and.returnValue(
mr.mirror.ServiceName.CAST_STREAMING);
const error = new mr.mirror.Error(
'',
mr.MirrorAnalytics.CapturingFailure
.CAPTURE_DESKTOP_FAIL_ERROR_USER_CANCEL);
mockCastMirrorService.startMirroring.and.callFake(
() => mr.CancellablePromise.reject(error));
providerManager
.startMirroring(
mockProvider1,
new mr.Route(
'r2', 'pId', sinkId, mr.MediaSourceUtils.DESKTOP_MIRROR_URN,
true, '', null),
presentationId)
.promise.catch(err => {
expect(err instanceof mr.RouteRequestError).toBe(true);
expect(err.errorCode).toBe(mr.RouteRequestResultCode.CANCELLED);
done();
});
});
it('createRoute with default timeout', function(done) {
mockClock = mr.UnitTestUtils.useMockClockAndPromises();
mockProvider1.canRoute.and.returnValue(true);
mockProvider2.canRoute.and.returnValue(false);
// The provider will never resolve the promise. Provider manager will
// reject it on timeout.
mockProvider1.createRoute.and.returnValue(
new mr.CancellablePromise(() => {}));
providerManager.createRoute(sourceUrn, sinkId, presentationId)
.then(
r => {
fail('Route unexpectedly created');
},
e => {
expect(e instanceof mr.RouteRequestError).toBe(true);
expect(e.errorCode)
.toEqual(mr.RouteRequestResultCode.TIMED_OUT);
expect(e.message).toMatch('timeout');
done();
});
mockClock.tick(mr.ProviderManager.CREATE_ROUTE_TIMEOUT_MS + 1);
});
it('createRoute with custom timeout', function(done) {
mockClock = mr.UnitTestUtils.useMockClockAndPromises();
mockProvider1.canRoute.and.returnValue(true);
mockProvider2.canRoute.and.returnValue(false);
// The provider will never resolve the promise. Provider manager will
// reject it on timeout.
mockProvider1.createRoute.and.returnValue(
new mr.CancellablePromise(() => {}));
const timeoutMillis = 20 * 1000;
providerManager
.createRoute(
sourceUrn, sinkId, 'presentationId', 'origin', 0, timeoutMillis)
.then(
r => {
fail('Route unexpectedly created');
},
e => {
expect(e instanceof mr.RouteRequestError).toBe(true);
expect(e.errorCode)
.toEqual(mr.RouteRequestResultCode.TIMED_OUT);
expect(e.message).toMatch('timeout');
done();
});
mockClock.tick(timeoutMillis + 1);
});
it('update mirror session', function(done) {
localRoute = new mr.Route('r2', 'pId', sinkId, sourceUrn, true, '', null);
mockProvider1.canRoute.and.returnValue(true);
mockProvider2.canRoute.and.returnValue(false);
const mirrorSettings = {};
mockProvider1.getMirrorSettings.and.returnValue(mirrorSettings);
mockProvider1.getMirrorServiceName.and.returnValue(
mr.mirror.ServiceName.CAST_STREAMING);
mockProvider1.createRoute.and.callFake(() => {
providerManager.onRouteAdded(this, localRoute);
return mr.CancellablePromise.resolve(localRoute);
});
mockCastMirrorService.startMirroring.and.returnValue(
mr.CancellablePromise.resolve(localRoute));
mockCastMirrorService.updateMirroring.and.returnValue(
mr.CancellablePromise.resolve(localRoute));
providerManager.createRoute(sourceUrn, sinkId, presentationId)
.then(route => {
providerManager.startMirroring(mockProvider1, route, presentationId)
.promise.then(r => {
expect(mockProvider1.createRoute.calls.count()).toBe(1);
expect(mockProvider1.createRoute)
.toHaveBeenCalledWith(
sourceUrn, sinkId, presentationId, false,
mr.ProviderManager.CREATE_ROUTE_TIMEOUT_MS, undefined,
undefined);
expect(mockProvider2.createRoute.calls.count()).toBe(0);
expect(mockCastMirrorService.startMirroring.calls.count())
.toBe(1);
expect(mockCastMirrorService.startMirroring)
.toHaveBeenCalledWith(
localRoute, sourceUrn, mirrorSettings, presentationId,
undefined);
expect(providerManager.routeIdToProvider_.has(localRoute.id))
.toBe(true);
expect(providerManager.routeIdToMirrorServiceName_.has(
localRoute.id))
.toBe(true);
const newSourceUrn = mr.MediaSourceUtils.DESKTOP_MIRROR_URN;
const callback = function() {};
providerManager
.updateMirroring(
mockProvider1, r, newSourceUrn, presentationId,
undefined, callback)
.promise.then(updatedRoute => {
expect(
mockCastMirrorService.updateMirroring.calls.count())
.toBe(1);
expect(mockCastMirrorService.updateMirroring)
.toHaveBeenCalledWith(
r, newSourceUrn, mirrorSettings, presentationId,
undefined, callback);
done();
});
});
});
});
it('updateMirroring before startMirroring fails', function(done) {
localRoute = new mr.Route('r2', 'pId', sinkId, sourceUrn, true, '', null);
const callback = function() {};
providerManager
.updateMirroring(
mockProvider1, localRoute, sourceUrn, undefined, callback)
.promise.catch(() => done());
});
});
describe('Test PersistentData', function() {
it('ProviderManager is restored properly as PersistentData', function() {
providerManager.registerAllProviders([mockProvider1]);
providerManager.sinkQueries_.add(sourceUrn);
providerManager.routeQueries_.add(sourceUrn);
providerManager.routeIdToProvider_.set(routeId, mockProvider1);
providerManager.sinkAvailabilityMap_.set(
mockProvider1Name, mr.SinkAvailability.AVAILABLE);
const expectedSinkQueries = new Set(providerManager.sinkQueries_);
const expectedRouteQueries = new Set(providerManager.routeQueries_);
const data = providerManager.getData();
expect(data.length).toEqual(1);
const tempData = data[0];
expect(tempData.providerNames).toEqual([mockProvider1Name]);
expect(tempData.sinkQueries).toEqual([sourceUrn]);
expect(tempData.routeQueries).toEqual([sourceUrn]);
expect(tempData.routeIdToProviderName).toEqual([
[routeId, mockProvider1Name]
]);
expect(tempData.sinkAvailabilityMap).toEqual([
[mockProvider1Name, mr.SinkAvailability.AVAILABLE]
]);
// Data is saved to localStorage.
mr.PersistentDataManager.onSuspend_();
// Make PersistentDataManager forget providerManager, so it can be
// registered again.
mr.PersistentDataManager.dataInstances_.clear();
providerManager.routeIdToProvider_.clear();
providerManager.sinkQueries_.clear();
providerManager.routeQueries_.clear();
providerManager.sinkAvailabilityMap_.clear();
// Load data back to providerManager.
mockProvider1.getAvailableSinks.and.returnValue(mr.SinkList.EMPTY);
mr.PersistentDataManager.register(providerManager);
expect(providerManager.sinkQueries_).toEqual(expectedSinkQueries);
expect(providerManager.routeQueries_).toEqual(expectedRouteQueries);
expect(providerManager.routeIdToProvider_.get(routeId))
.toEqual(mockProvider1);
expect(providerManager.sinkAvailabilityMap_.get(mockProvider1Name))
.toEqual(mr.SinkAvailability.AVAILABLE);
});
});
describe('Test onRouteRemoved', function() {
let sourceUrn;
let sinkId;
let localRoute;
beforeEach(function() {
sourceUrn = 'urn:dial-multiscreen-org:dial:application:YouTube';
sinkId = 'sink1';
localRoute = new mr.Route('r2', 'pId', sinkId, sourceUrn, true, '', null);
providerManager.registerAllProviders([mockProvider1]);
spyOn(providerManager.mrRouteMessageSender_, 'onRouteRemoved')
.and.callThrough();
sourceUrn = mr.MediaSourceUtils.DESKTOP_MIRROR_URN;
localRoute = new mr.Route('r2', 'pId', sinkId, sourceUrn, true, '', null);
mockProvider1.canRoute.and.returnValue(true);
const mirrorSettings = {};
mockProvider1.getMirrorSettings.and.returnValue(mirrorSettings);
mockProvider1.getMirrorServiceName.and.returnValue(
mr.mirror.ServiceName.CAST_STREAMING);
mockProvider1.createRoute.and.callFake(() => {
providerManager.onRouteAdded(mockProvider1, localRoute);
return mr.CancellablePromise.resolve(localRoute);
});
mockCastMirrorService.startMirroring.and.returnValue(
mr.CancellablePromise.resolve(localRoute));
});
it('On a mirror session stopped', function(done) {
let count = 0;
const checkDone = () => {
if (++count == 2) {
expect(providerManager.routeIdToProvider_.has(localRoute.id))
.toBe(false);
expect(providerManager.routeIdToMirrorServiceName_.has(localRoute.id))
.toBe(false);
expect(mockCastMirrorService.stopCurrentMirroring).toHaveBeenCalled();
expect(providerManager.mrRouteMessageSender_.onRouteRemoved)
.toHaveBeenCalledWith(localRoute.id);
done();
}
};
mockCastMirrorService.stopCurrentMirroring.and.callFake(checkDone);
providerManager.mrRouteMessageSender_.onRouteRemoved.and.callFake(
checkDone);
providerManager.createRoute(sourceUrn, sinkId, presentationId)
.then(route => {
providerManager.startMirroring(mockProvider1, route)
.promise.then(r => {
expect(route.id).toEqual(localRoute.id);
expect(providerManager.routeIdToProvider_.has(localRoute.id))
.toBe(true);
expect(providerManager.routeIdToMirrorServiceName_.has(
localRoute.id))
.toBe(true);
providerManager.onRouteRemoved(mockProvider1, localRoute);
});
});
});
});
it('Test add/remove MediaSinksQuery', function() {
const sourceUrn = 'urn:x-org.chromium.media:source:desktop';
const sink1 = new mr.Sink('s1', 'sink1');
const sink2 = new mr.Sink('s2', 'sink2');
const origins = ['https://www.google.com', 'https://youtube.com'];
providerManager.registerAllProviders([mockProvider1, mockProvider2]);
mockProvider1.getAvailableSinks.and.returnValue(
new mr.SinkList([sink1, sink2], origins));
mockProvider2.getAvailableSinks.and.returnValue(mr.SinkList.EMPTY);
providerManager.startObservingMediaSinks(sourceUrn);
expect(mockProvider1.startObservingMediaSinks)
.toHaveBeenCalledWith(sourceUrn);
expect(mockProvider2.startObservingMediaSinks)
.toHaveBeenCalledWith(sourceUrn);
expect(mockMediaRouterService.onSinksReceived)
.toHaveBeenCalledWith(sourceUrn, [sink1, sink2], jasmine.any(Object));
const originList =
mockMediaRouterService.onSinksReceived.calls.argsFor(0)[2];
expect(originList.length).toBe(2);
expect(originList[0].scheme).toBe('https');
expect(originList[0].host).toBe('www.google.com');
expect(originList[1].scheme).toBe('https');
expect(originList[1].host).toBe('youtube.com');
// add again
providerManager.startObservingMediaSinks(sourceUrn);
expect(mockProvider1.startObservingMediaSinks.calls.count()).toBe(1);
expect(mockProvider2.startObservingMediaSinks.calls.count()).toBe(1);
expect(mockMediaRouterService.onSinksReceived.calls.count()).toBe(2);
// remove
providerManager.stopObservingMediaSinks(sourceUrn);
expect(mockProvider1.stopObservingMediaSinks)
.toHaveBeenCalledWith(sourceUrn);
expect(mockProvider2.stopObservingMediaSinks)
.toHaveBeenCalledWith(sourceUrn);
// add again
providerManager.startObservingMediaSinks(sourceUrn);
expect(mockProvider1.startObservingMediaSinks.calls.count()).toBe(2);
expect(mockProvider2.startObservingMediaSinks.calls.count()).toBe(2);
expect(mockMediaRouterService.onSinksReceived.calls.count()).toBe(3);
});
it('Test onSinkAvailabilityUpdated', function() {
providerManager.registerAllProviders([mockProvider1, mockProvider2]);
providerManager.onSinkAvailabilityUpdated(
mockProvider1, mr.SinkAvailability.UNAVAILABLE);
expect(mockMediaRouterService.onSinkAvailabilityUpdated)
.not.toHaveBeenCalled();
providerManager.onSinkAvailabilityUpdated(
mockProvider1, mr.SinkAvailability.PER_SOURCE);
expect(mockMediaRouterService.onSinkAvailabilityUpdated)
.toHaveBeenCalledWith(mr.SinkAvailability.PER_SOURCE);
providerManager.onSinkAvailabilityUpdated(
mockProvider2, mr.SinkAvailability.PER_SOURCE);
expect(mockMediaRouterService.onSinkAvailabilityUpdated.calls.count())
.toBe(1);
providerManager.onSinkAvailabilityUpdated(
mockProvider2, mr.SinkAvailability.AVAILABLE);
expect(mockMediaRouterService.onSinkAvailabilityUpdated)
.toHaveBeenCalledWith(mr.SinkAvailability.AVAILABLE);
providerManager.onSinkAvailabilityUpdated(
mockProvider1, mr.SinkAvailability.UNAVAILABLE);
expect(mockMediaRouterService.onSinkAvailabilityUpdated.calls.count())
.toBe(2);
providerManager.onSinkAvailabilityUpdated(
mockProvider2, mr.SinkAvailability.UNAVAILABLE);
expect(mockMediaRouterService.onSinkAvailabilityUpdated)
.toHaveBeenCalledWith(mr.SinkAvailability.UNAVAILABLE);
});
describe('searchSinks', function() {
const getSearchCriteria = function(input) {
return {'input': input, 'domain': 'google.com'};
};
let pseudoId;
beforeEach(function() {
sourceUrn = 'urn:x-org.chromium.media:source:desktop';
pseudoId = 'pseudo:' + mockProvider1Name;
providerManager.registerAllProviders([mockProvider1, mockProvider2]);
});
it('returns the sink id if the provider returns a sink', done => {
const sinkId = 'sinkId1';
const sinkName = 'sink name';
const foundSink = new mr.Sink(sinkId, sinkName);
mockProvider1.searchSinks.and.returnValue(Promise.resolve(foundSink));
providerManager
.searchSinks(pseudoId, sourceUrn, getSearchCriteria(sinkName))
.then((resolvedSinkId) => {
expect(resolvedSinkId).toBe(sinkId);
done();
});
});
it('returns nothing if the provider returns nothing', done => {
mockProvider1.searchSinks.and.returnValue(
Promise.resolve(new mr.Sink('', '')));
providerManager
.searchSinks(pseudoId, sourceUrn, getSearchCriteria('sink name'))
.then((resolvedSinkId) => expect(resolvedSinkId).toBe(''))
.then(done, done.fail);
});
it('returns nothing when no provider owns the pseudo sink', done => {
pending(
'This may never have worked, because the test ' +
'wasn\'t checking what it was supposed to check.');
pseudoId = 'pseudo:bad';
providerManager
.searchSinks(pseudoId, sourceUrn, getSearchCriteria('sink name'))
.then((resolvedSinkId) => expect(resolvedSinkId).toBe(''))
.then(done, done.fail);
});
});
describe('updateMediaSinks', function() {
const sink1 = new mr.Sink('s1', 'sink1');
const sink2 = new mr.Sink('s2', 'sink2');
beforeEach(function() {
sourceUrn = 'urn:x-org.chromium.media:source:desktop';
providerManager.registerAllProviders([mockProvider1, mockProvider2]);
mockProvider1.getAvailableSinks.and.returnValue(
new mr.SinkList([sink1, sink2], ['https://www.google.com']));
mockProvider2.getAvailableSinks.and.returnValue(mr.SinkList.EMPTY);
});
it('queries providers with desktop source', function() {
providerManager.updateMediaSinks(sourceUrn);
expect(mockProvider1.getAvailableSinks).toHaveBeenCalledWith(sourceUrn);
expect(mockProvider2.getAvailableSinks).toHaveBeenCalledWith(sourceUrn);
expect(mockMediaRouterService.onSinksReceived)
.toHaveBeenCalledWith(
sourceUrn, [sink1, sink2], [jasmine.any(Object)]);
const originList =
mockMediaRouterService.onSinksReceived.calls.argsFor(0)[2];
expect(originList.length).toBe(1);
expect(originList[0].scheme).toBe('https');
expect(originList[0].host).toBe('www.google.com');
});
it('returns immediately if query already exists', function() {
providerManager.sinkQueries_.add(sourceUrn);
// updateMediaSinks should return and not touch providers
providerManager.updateMediaSinks(sourceUrn);
expect(mockProvider1.getAvailableSinks).not.toHaveBeenCalled();
expect(mockProvider2.getAvailableSinks).not.toHaveBeenCalled();
expect(mockMediaRouterService.onSinksReceived).not.toHaveBeenCalled();
});
});
describe('calls terminateRoute', function() {
const routeIdToTerminate = 'deadbeef';
beforeEach(function() {
providerManager.registerAllProviders([mockProvider1]);
providerManager.routeIdToProvider_.set(routeIdToTerminate, mockProvider1);
});
it('with correct routeId', function(done) {
mockProvider1.terminateRoute.and.callFake(routeId => {
expect(routeId).toBe(routeIdToTerminate);
done();
});
providerManager.terminateRoute(routeIdToTerminate);
});
it('and returns a rejected promise when it fails', function(done) {
mockProvider1.terminateRoute.and.callFake(routeId => {
expect(routeId).toBe(routeIdToTerminate);
return Promise.reject(new Error('Some error'));
});
providerManager.terminateRoute(routeIdToTerminate)
.then(done.fail, () => done());
});
it('and terminates a mirroring route', function(done) {
providerManager.routeIdToMirrorServiceName_.set(
routeIdToTerminate, mr.mirror.ServiceName.CAST_STREAMING);
providerManager.terminateRoute(routeIdToTerminate).then(() => {
expect(mockProvider1.terminateRoute)
.toHaveBeenCalledWith(routeIdToTerminate);
expect(mockCastMirrorService.stopCurrentMirroring).toHaveBeenCalled();
expect(
providerManager.routeIdToMirrorServiceName_.has(routeIdToTerminate))
.toBe(false);
done();
});
});
});
describe('createMediaRouteController tests', () => {
const routeId = 'deadbeef';
let controller;
let observer;
beforeEach(() => {
controller = mr.UnitTestUtils.createMojoRequestSpyObj();
observer = mr.UnitTestUtils.createMojoMediaStatusObserverSpyObj();
providerManager.registerAllProviders([mockProvider1]);
providerManager.routeIdToProvider_.set(routeId, mockProvider1);
});
it('succeeds', done => {
mockProvider1.createMediaRouteController.and.callFake(
() => Promise.resolve());
providerManager.createMediaRouteController(routeId, controller, observer)
.then(() => {
expect(mockProvider1.createMediaRouteController).toHaveBeenCalled();
expect(controller.close).not.toHaveBeenCalled();
expect(observer.ptr.reset).not.toHaveBeenCalled();
done();
}, fail);
});
it('fails if route does not exist', done => {
providerManager
.createMediaRouteController(
'nonexistentRouteId', controller, observer)
.then(fail, () => {
expect(controller.close).toHaveBeenCalled();
expect(observer.ptr.reset).toHaveBeenCalled();
done();
});
});
it('fails if provider fails', done => {
mockProvider1.createMediaRouteController.and.callFake(
() => Promise.reject('Foo'));
providerManager.createMediaRouteController(routeId, controller, observer)
.then(fail, () => {
expect(mockProvider1.createMediaRouteController).toHaveBeenCalled();
expect(controller.close).toHaveBeenCalled();
expect(observer.ptr.reset).toHaveBeenCalled();
done();
});
});
});
});