blob: a57529f960717d84b872d4a9654494165b424be4 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// ignore_for_file: implementation_imports
import 'dart:async';
import 'dart:collection';
import 'package:fidl/fidl.dart' show AsyncBinding, AsyncProxyController;
import 'package:fidl/src/interface.dart';
import 'package:fidl_fuchsia_auth/fidl_async.dart' as fidl_auth;
import 'package:fidl_fuchsia_modular/fidl_async.dart' as fidl;
import 'package:fidl_fuchsia_sys/fidl_async.dart';
import 'package:fuchsia_logger/logger.dart';
import 'package:fuchsia_modular/lifecycle.dart';
import 'package:fuchsia_modular/src/agent/agent_task_handler.dart';
import 'package:fuchsia_modular/src/agent/internal/_agent_impl.dart';
import 'package:fuchsia_services/services.dart';
import 'package:mockito/mockito.dart';
import 'package:test/test.dart';
// Mock classes
class MockLifecycle extends Mock implements Lifecycle {}
class MockStartupContext extends Mock implements StartupContext {}
class MockAgentContext extends Mock implements fidl.AgentContext {}
class MockOutgoing extends Mock implements Outgoing {}
class MockAsyncBinding extends Mock implements AsyncBinding {}
class MockAsyncProxyController<T> extends Mock
implements AsyncProxyController<T> {}
class MockInterfaceRequest<T> extends Mock implements InterfaceRequest<T> {}
class MockTokenManagerProxy extends Mock
implements fidl_auth.TokenManagerProxy {}
void main() {
setupLogger();
test('verify agent is exposed during construction of AgentImpl', () {
final mockStartupContext = MockStartupContext();
final mockOutgoingImpl = MockOutgoing();
when(mockStartupContext.outgoing).thenReturn(mockOutgoingImpl);
AgentImpl(startupContext: mockStartupContext);
verify(mockOutgoingImpl.addPublicService(any, fidl.Agent.$serviceName));
});
test('verify Lifecycle init during the construction of AgentImpl', () {
final mockLifecycle = MockLifecycle();
AgentImpl(lifecycle: mockLifecycle);
verify(mockLifecycle.addTerminateListener(any));
});
test('verify exposeService arguments', () {
expect(() {
AgentImpl().exposeService(null);
}, throwsArgumentError);
});
test('verify exposeServiceProvider arguments', () {
expect(() {
AgentImpl().exposeServiceProvider(null, fidl.AgentData());
}, throwsArgumentError);
expect(() {
AgentImpl().exposeServiceProvider(() => null, null);
}, throwsArgumentError);
});
group('service bindings tests', () {
AgentImpl agentImpl;
ServiceProviderImpl serviceProviderImpl;
setUp(() {
// Create a new instance and inject it to AgentImpl so that we can mimic
// a connectToService call inside the tests
serviceProviderImpl = ServiceProviderImpl();
agentImpl = AgentImpl(serviceProviderImpl: serviceProviderImpl);
});
test('verify exposeService binds the correct service on connect request',
() async {
final service = DummyService();
final mockServiceBindings = service.getServiceData().getBinding();
agentImpl.exposeService(service);
// Mimic a this call as if the framework is asking us to connect.
await serviceProviderImpl.connectToService(
service.getServiceData().getName(),
null, // don't care about the actual request for testing
);
await untilCalled(mockServiceBindings.bind(service, any));
});
test(
'verify exposeService waits for all futures and binds the correct '
'service on connect request', () async {
final service = DummyService();
final mockServiceBindings = service.getServiceData().getBinding();
final serviceGetterCompleter = Completer();
final futureService =
Future(() => Future.delayed(Duration(microseconds: 1), () {
serviceGetterCompleter.complete();
return service;
}));
agentImpl.exposeService(futureService);
// Block until the service is exposed
await serviceGetterCompleter.future;
// Mimic a this call as if the framework is asking us to connect.
await serviceProviderImpl.connectToService(
service.getServiceData().getName(),
null, // don't care about the actual request for testing
);
await untilCalled(mockServiceBindings.bind(service, any));
});
test(
'verify exposeServiceProvider waits for all futures and binds the '
'correct service on connect request', () async {
final service = DummyService();
final mockServiceBindings = service.getServiceData().getBinding();
final futureServiceProvider = Future(
() => Future.delayed(Duration(microseconds: 1), () => () => service));
agentImpl.exposeServiceProvider(
futureServiceProvider, service.getServiceData());
// Mimic a this call as if the framework is asking us to connect.
await serviceProviderImpl.connectToService(
service.getServiceData().getName(),
null, // don't care about the actual request for testing
);
await untilCalled(mockServiceBindings.bind(service, any));
});
test(
'verify exposeServiceProvider waits binds the correct service on '
'connect request', () async {
final service = DummyService();
final mockServiceBindings = service.getServiceData().getBinding();
agentImpl.exposeServiceProvider(() => service, service.getServiceData());
// Mimic a this call as if the framework is asking us to connect.
await serviceProviderImpl.connectToService(
service.getServiceData().getName(),
null, // don't care about the actual request for testing
);
await untilCalled(mockServiceBindings.bind(service, any));
});
});
test('verify getTokenManager should call context.getTokenManager', () {
final mockAgentContext = MockAgentContext();
final mockTokenManagerProxy = MockTokenManagerProxy();
final mockedCtrl = MockAsyncProxyController<fidl_auth.TokenManager>();
when(mockTokenManagerProxy.ctrl).thenReturn(mockedCtrl);
when(mockedCtrl.request()).thenReturn(MockInterfaceRequest());
AgentImpl(
agentContext: mockAgentContext,
tokenManagerProxy: mockTokenManagerProxy,
).getTokenManager();
verify(mockAgentContext.getTokenManager(any));
});
group('Agent Tasks:', () {
test('verify calling registerTaskHandler with null task throws', () {
expect(() {
AgentImpl().registerTaskHandler(null);
}, throwsArgumentError);
});
test('verify calling registerTaskHandler twice should throw', () {
expect(() {
AgentImpl()
..registerTaskHandler(MyAgentTaskHandler())
..registerTaskHandler(MyAgentTaskHandler());
}, throwsException);
});
test('verify runTask invokes registered taskHandler', () {
final mockAgentContext = MockAgentContext();
final handler = MyAgentTaskHandler();
AgentImpl impl = AgentImpl(agentContext: mockAgentContext)
..registerTaskHandler(handler);
expect(handler.runTasks.isEmpty, isTrue);
impl.runTask('1');
expect(handler.runTasks.first, equals('1'));
});
test(
'verify out of band runTasks are queued up and run after task handler '
'is registered ', () {
final mockAgentContext = MockAgentContext();
final handler = MyAgentTaskHandler();
AgentImpl impl = AgentImpl(agentContext: mockAgentContext)
..runTask('1')
..runTask('2')
..runTask('3');
expect(handler.runTasks.isEmpty, isTrue);
impl
..registerTaskHandler(handler)
..runTask('4');
expect(
handler.runTasks, equals(Queue<String>.from(['1', '2', '3', '4'])));
});
});
}
class MyAgentTaskHandler extends AgentTaskHandler {
final Queue<String> runTasks = Queue<String>();
@override
Future<void> runTask(String taskId) async {
runTasks.add(taskId);
}
}
/// This is a dummyService used for testing.
///
/// I chose to extend from fidl.Agent for no particular reason, any FIDL
/// interface can be used.
class DummyService extends fidl.Agent {
final _fakeAgentData = FakeAgentData();
@override
Future<void> connect(
String requestorUrl, InterfaceRequest<ServiceProvider> services) {
throw UnimplementedError();
}
@override
Future<void> runTask(String taskId) {
throw UnimplementedError();
}
@override
fidl.AgentData get $serviceData => _fakeAgentData;
fidl.AgentData getServiceData() {
return _fakeAgentData;
}
}
/// Hijacking the AgentData so that I can inject a Mocked [AsyncBinding] to
/// verify it's method calls in the tests above.
class FakeAgentData implements fidl.AgentData {
final _mockAsyncBinding = MockAsyncBinding();
@override
String getName() {
return fidl.Agent.$serviceName;
}
@override
MockAsyncBinding getBinding() {
return _mockAsyncBinding;
}
}