public async Task <object> Execute <TService>(object args = null) where TService : IViewService { Type type = typeof(TService); IViewService service; if (!ServiceMap.TryGetValue(type, out service)) { throw new InvalidOperationException($"Unknown service of type {type}"); } return(await service.Execute(Window, args)); }
/// <summary> /// Constructor. /// </summary> /// <param name="serviceMap">The service map describing this service and potentially other services.</param> /// <param name="name">The name of this service within <see cref="ServiceMap"/>.</param> /// <param name="branch">Optionally specifies the build branch.</param> /// <param name="commit">Optionally specifies the branch commit.</param> /// <param name="isDirty">Optionally specifies whether there are uncommit changes to the branch.</param> /// <exception cref="KeyNotFoundException"> /// Thrown if there is no service description for <paramref name="name"/> /// within <see cref="serviceMap"/>. /// </exception> /// <remarks> /// <para> /// For those of you using Git for source control, you'll want to pass the /// information about the branch and latest commit you're you service was /// built from here. We use the <a href="https://www.nuget.org/packages/GitInfo/">GitInfo</a> /// nuget package to obtain this information from the local Git repository. /// </para> /// <para> /// Alternatively, you could try to map properties from your source /// control environment to these parameters, pass your version string as /// <paramref name="branch"/>, or simply ignore these parameters. /// </para> /// </remarks> public KubeService( ServiceMap serviceMap, string name, string branch = null, string commit = null, bool isDirty = false, bool noProcessExit = false) { Covenant.Requires <ArgumentNullException>(serviceMap != null); Covenant.Requires <ArgumentNullException>(!string.IsNullOrEmpty(name)); if (!serviceMap.TryGetValue(name, out var description)) { throw new KeyNotFoundException($"The service map does not include a service definition for [{name}]."); } this.ServiceMap = serviceMap; this.Description = description; this.InProduction = !NeonHelper.IsDevWorkstation; this.Terminator = new ProcessTerminator(); this.environmentVariables = new Dictionary <string, string>(); this.configFiles = new Dictionary <string, FileInfo>(); // Git version info: this.GitVersion = null; if (!string.IsNullOrEmpty(branch)) { this.GitVersion = branch; if (!string.IsNullOrEmpty(commit)) { this.GitVersion += $"-{commit}"; } if (isDirty) { this.GitVersion += $"-dirty"; } } }
/// <summary> /// Used to start the fixture within a <see cref="ComposedFixture"/>. /// </summary> /// <param name="serviceCreator">Callback that creates and returns the new service instance.</param> /// <param name="serviceMap"> /// Optionally passed as the service map describing an emulated deployment. When this is /// not <c>null</c>, the fixture will call <see cref="NeonService.Name"/> on the service /// returned by your service creator function and then look up the service by name from /// the <paramref name="serviceMap"/>. If an entry exists for the service, the fixture will /// add any environment variables or configuration files from the <see cref="ServiceDescription"/> /// to the <see cref="NeonService"/> before starting it. /// </param> /// <param name="readyTimeout"> /// Optionally specifies the maximum time the fixture should wait for the service to transition /// to the <see cref="NeonServiceStatus.Running"/> state. This defaults to <b>30 seconds</b>. /// </param> /// <exception cref="TimeoutException"> /// Thrown if the service didn't transition to the running (or terminated) state /// within <paramref name="readyTimeout"/>. /// </exception> public void StartAsComposed(Func <TService> serviceCreator, ServiceMap serviceMap = null, TimeSpan readyTimeout = default) { Covenant.Requires <ArgumentNullException>(serviceCreator != null, nameof(serviceCreator)); if (readyTimeout == default) { readyTimeout = defaultRunningTimeout; } base.CheckWithinAction(); if (IsRunning) { return; } Service = serviceCreator(); Covenant.Assert(Service != null); // Indicate that the service is not running in production. Service.InProduction = false; // Configure the service is a service map was passed and there's a service description // for the service. if (serviceMap != null && serviceMap.TryGetValue(Service.Name, out var serviceDescription)) { foreach (var item in serviceDescription.TestEnvironmentVariables) { Service.SetEnvironmentVariable(item.Key, item.Value); } foreach (var item in serviceDescription.TestTextConfigFiles) { var path = item.Key; var text = item.Value; if (string.IsNullOrEmpty(path)) { continue; } Service.SetConfigFile(path, text ?? string.Empty); } foreach (var item in serviceDescription.TestBinaryConfigFiles) { var path = item.Key; var bytes = item.Value; if (string.IsNullOrEmpty(path)) { continue; } Service.SetConfigFile(path, bytes ?? Array.Empty <byte>()); } } // Start the service. serviceTask = Service.RunAsync(); // Wait for the service to signal that it's running or has terminated. try { NeonHelper.WaitFor(() => Service.Status == NeonServiceStatus.Running || Service.Status == NeonServiceStatus.Terminated, readyTimeout); if (Service.ExitException != null) { throw new Exception($"Unexpected [{Service.ExitException.GetType().Name}] thrown by the service's [OnRunAsync()] method.", Service.ExitException); } } catch (TimeoutException) { // Throw a nicer exception that explains what's happened in more detail. throw new TimeoutException($"Service [{Service.Name}]'s [{typeof(TService).Name}.OnRunAsync()] method did not call [{nameof(NeonService.StartedAsync)}()] within [{readyTimeout}] indicating that the service is ready. Ensure that [{nameof(NeonService.StartedAsync)}()] is being called or increase the timeout."); } IsRunning = Service.Status == NeonServiceStatus.Running; }