public async void TryInvokeFromCacheAsync_WithTypeParameter_CreatesInvocationRequestAndCallsTryInvokeCoreAsync() { // Arrange const int dummyResult = 1; const string dummyModuleCacheIdentifier = "dummyModuleCacheIdentifier"; const string dummyExportName = "dummyExportName"; var dummyArgs = new object[0]; var dummyCancellationToken = new CancellationToken(); Mock <HttpNodeJSService> mockHttpNodeJSService = CreateMockHttpNodeJSService(); mockHttpNodeJSService. Setup(t => t.TryInvokeFromCacheAsync <int>(dummyModuleCacheIdentifier, dummyExportName, dummyArgs, dummyCancellationToken)). ReturnsAsync((true, dummyResult)); #pragma warning disable IDE0067 HttpNodeJSPoolService testSubject = CreateHttpNodeJSPoolService(new[] { mockHttpNodeJSService.Object }); #pragma warning disable IDE0067 // Act (bool success, int result) = await testSubject.TryInvokeFromCacheAsync <int>(dummyModuleCacheIdentifier, dummyExportName, dummyArgs, dummyCancellationToken).ConfigureAwait(false); // Assert Assert.True(success); Assert.Equal(dummyResult, result); _mockRepository.VerifyAll(); }
public void GetHttpNodeJSService_ReturnsEachHttpNodeJSServiceAnEqualNumberOfTimes() { // Arrange const int dummyNumHttpNodeJSServices = 5; var dummyHttpNodeJSServices = new HttpNodeJSService[dummyNumHttpNodeJSServices]; for (int i = 0; i < dummyNumHttpNodeJSServices; i++) { dummyHttpNodeJSServices[i] = CreateHttpNodeJSService(); } HttpNodeJSPoolService testSubject = CreateHttpNodeJSPoolService(dummyHttpNodeJSServices); // Act var results = new ConcurrentBag <HttpNodeJSService>(); const int numThreads = 5; const int numGetsPerThread = 10; var threads = new List <Thread>(); for (int i = 0; i < numThreads; i++) { var thread = new Thread(() => { for (int j = 0; j < numGetsPerThread; j++) { results.Add(testSubject.GetHttpNodeJSService()); } }); threads.Add(thread); thread.Start(); } foreach (Thread thread in threads) { thread.Join(); } // Assert const int expectedNumPerHttpNodeJSService = numThreads * numGetsPerThread / dummyNumHttpNodeJSServices; Assert.Equal(expectedNumPerHttpNodeJSService, results.Count(httpNodeJSService => httpNodeJSService == dummyHttpNodeJSServices[0])); Assert.Equal(expectedNumPerHttpNodeJSService, results.Count(httpNodeJSService => httpNodeJSService == dummyHttpNodeJSServices[1])); Assert.Equal(expectedNumPerHttpNodeJSService, results.Count(httpNodeJSService => httpNodeJSService == dummyHttpNodeJSServices[2])); Assert.Equal(expectedNumPerHttpNodeJSService, results.Count(httpNodeJSService => httpNodeJSService == dummyHttpNodeJSServices[3])); Assert.Equal(expectedNumPerHttpNodeJSService, results.Count(httpNodeJSService => httpNodeJSService == dummyHttpNodeJSServices[4])); }
public async void InvokeFromStringAsync_WithoutTypeParameter_WithRawStringModule_InvokesFromString() { // Arrange const string dummyModuleString = "dummyModuleString"; const string dummyNewCacheIdentifier = "dummyNewCacheIdentifier"; const string dummyExportName = "dummyExportName"; var dummyArgs = new object[0]; var dummyCancellationToken = new CancellationToken(); Mock <HttpNodeJSService> mockHttpNodeJSService = CreateMockHttpNodeJSService(); mockHttpNodeJSService.Setup(t => t.InvokeFromStringAsync(dummyModuleString, dummyNewCacheIdentifier, dummyExportName, dummyArgs, dummyCancellationToken)); HttpNodeJSPoolService testSubject = CreateHttpNodeJSPoolService(new[] { mockHttpNodeJSService.Object }); // Act await testSubject.InvokeFromStringAsync(dummyModuleString, dummyNewCacheIdentifier, dummyExportName, dummyArgs, dummyCancellationToken).ConfigureAwait(false); // Assert _mockRepository.VerifyAll(); }
public void AllInvokeMethods_InvokeJavascriptInMultipleProcesses() { // Arrange const int dummyNumProcesses = 5; const string dummyModule = @"module.exports = (callback) => { callback(null, process.pid); }"; HttpNodeJSPoolService testSubject = CreateHttpNodeJSPoolService(dummyNumProcesses); // Act var results = new ConcurrentBag <int>(); const int numThreads = 5; const int numInvocationsPerThread = 10; var threads = new List <Thread>(); for (int i = 0; i < numThreads; i++) { var thread = new Thread(() => { for (int j = 0; j < numInvocationsPerThread; j++) { results.Add(testSubject.InvokeFromStringAsync <int>(dummyModule, "dummyCacheIdentifier").GetAwaiter().GetResult()); } }); threads.Add(thread); thread.Start(); } foreach (Thread thread in threads) { thread.Join(); } // Assert const int expectedNumInvocationsPerProcess = numThreads * numInvocationsPerThread / dummyNumProcesses; IEnumerable <IGrouping <int, int> > groupedResults = results.GroupBy(pid => pid); Assert.Equal(dummyNumProcesses, groupedResults.Count()); foreach (IGrouping <int, int> group in groupedResults) { Assert.Equal(expectedNumInvocationsPerProcess, group.Count()); } }
public async void InvokeFromStreamAsync_WithoutTypeParameter_WithModuleFactory_IfModuleIsCachedInvokesFromCacheOtherwiseInvokesFromStream() { // Arrange Func <Stream> dummyModuleFactory = () => new MemoryStream(); const string dummyCacheIdentifier = "dummyCacheIdentifier"; const string dummyExportName = "dummyExportName"; var dummyArgs = new object[0]; var dummyCancellationToken = new CancellationToken(); Mock <HttpNodeJSService> mockHttpNodeJSService = CreateMockHttpNodeJSService(); mockHttpNodeJSService. Setup(t => t.InvokeFromStreamAsync(dummyModuleFactory, dummyCacheIdentifier, dummyExportName, dummyArgs, dummyCancellationToken)); HttpNodeJSPoolService testSubject = CreateHttpNodeJSPoolService(new[] { mockHttpNodeJSService.Object }); // Act await testSubject.InvokeFromStreamAsync(dummyModuleFactory, dummyCacheIdentifier, dummyExportName, dummyArgs, dummyCancellationToken).ConfigureAwait(false); // Assert _mockRepository.VerifyAll(); }
public async void TryInvokeFromCacheAsync_WithoutTypeParameter_CreatesInvocationRequestAndCallsTryInvokeCoreAsync() { // Arrange const string dummyModuleCacheIdentifier = "dummyModuleCacheIdentifier"; const string dummyExportName = "dummyExportName"; var dummyArgs = new object[0]; var dummyCancellationToken = new CancellationToken(); Mock <HttpNodeJSService> mockHttpNodeJSService = CreateMockHttpNodeJSService(); mockHttpNodeJSService. Setup(t => t.TryInvokeFromCacheAsync(dummyModuleCacheIdentifier, dummyExportName, dummyArgs, dummyCancellationToken)). ReturnsAsync(true); HttpNodeJSPoolService testSubject = CreateHttpNodeJSPoolService(new[] { mockHttpNodeJSService.Object }); // Act bool success = await testSubject.TryInvokeFromCacheAsync(dummyModuleCacheIdentifier, dummyExportName, dummyArgs, dummyCancellationToken).ConfigureAwait(false); // Assert Assert.True(success); _mockRepository.VerifyAll(); }
public async void InvokeFromFileAsync_WithoutTypeParameter_InvokesFromFile() { // Arrange const string dummyModulePath = "dummyModulePath"; const string dummyExportName = "dummyExportName"; var dummyArgs = new object[0]; var dummyCancellationToken = new CancellationToken(); Mock <HttpNodeJSService> mockHttpNodeJSService = CreateMockHttpNodeJSService(); mockHttpNodeJSService. Setup(t => t.InvokeFromFileAsync(dummyModulePath, dummyExportName, dummyArgs, dummyCancellationToken)); #pragma warning disable IDE0067 HttpNodeJSPoolService testSubject = CreateHttpNodeJSPoolService(new[] { mockHttpNodeJSService.Object }); #pragma warning disable IDE0067 // Act await testSubject.InvokeFromFileAsync(dummyModulePath, dummyExportName, dummyArgs, dummyCancellationToken).ConfigureAwait(false); // Assert _mockRepository.VerifyAll(); }
public async void InvokeFromFileAsync_WithTypeParameter_InvokesFromFile() { // Arrange const int dummyResult = 1; const string dummyModulePath = "dummyModulePath"; const string dummyExportName = "dummyExportName"; var dummyArgs = new object[0]; var dummyCancellationToken = new CancellationToken(); Mock <HttpNodeJSService> mockHttpNodeJSService = CreateMockHttpNodeJSService(); mockHttpNodeJSService. Setup(t => t.InvokeFromFileAsync <int>(dummyModulePath, dummyExportName, dummyArgs, dummyCancellationToken)). ReturnsAsync(dummyResult); HttpNodeJSPoolService testSubject = CreateHttpNodeJSPoolService(new[] { mockHttpNodeJSService.Object }); // Act int result = await testSubject.InvokeFromFileAsync <int>(dummyModulePath, dummyExportName, dummyArgs, dummyCancellationToken).ConfigureAwait(false); // Assert _mockRepository.VerifyAll(); Assert.Equal(dummyResult, result); }
public async void InvokeFromStringAsync_WithTypeParameter_WithModuleFactory_IfModuleIsCachedInvokesFromCacheOtherwiseInvokesFromString() { // Arrange const int dummyResult = 1; Func <string> dummyModuleFactory = () => "dummyModule"; const string dummyCacheIdentifier = "dummyCacheIdentifier"; const string dummyExportName = "dummyExportName"; var dummyArgs = new object[0]; var dummyCancellationToken = new CancellationToken(); Mock <HttpNodeJSService> mockHttpNodeJSService = CreateMockHttpNodeJSService(); mockHttpNodeJSService. Setup(t => t.InvokeFromStringAsync <int>(dummyModuleFactory, dummyCacheIdentifier, dummyExportName, dummyArgs, dummyCancellationToken)). ReturnsAsync(dummyResult); HttpNodeJSPoolService testSubject = CreateHttpNodeJSPoolService(new[] { mockHttpNodeJSService.Object }); // Act int result = await testSubject.InvokeFromStringAsync <int>(dummyModuleFactory, dummyCacheIdentifier, dummyExportName, dummyArgs, dummyCancellationToken).ConfigureAwait(false); // Assert _mockRepository.VerifyAll(); Assert.Equal(dummyResult, result); }
public async void FileWatching_RespectsGracefulShutdownOptionWhenItsTrue() { // Arrange const int dummyNumProcesses = 5; Uri tempWatchDirectoryUri = CreateWatchDirectoryUri(); // Create initial module string dummylongRunningTriggerPath = new Uri(tempWatchDirectoryUri, "dummyTriggerFile").AbsolutePath; // fs.watch can't deal with backslashes in paths File.WriteAllText(dummylongRunningTriggerPath, string.Empty); // fs.watch returns immediately if path to watch doesn't exist string dummyInitialModule = $@"module.exports = {{ getPid: (callback) => callback(null, process.pid), longRunning: (callback) => {{ fs.watch('{dummylongRunningTriggerPath}', null, () => {{ callback(null, process.pid); }} ); }} }}"; string dummyModuleFilePath = new Uri(tempWatchDirectoryUri, "dummyModule.js").AbsolutePath; File.WriteAllText(dummyModuleFilePath, dummyInitialModule); var dummyServices = new ServiceCollection(); dummyServices.Configure <OutOfProcessNodeJSServiceOptions>(options => { options.EnableFileWatching = true; options.WatchPath = _tempWatchDirectory; // Graceful shutdown is true by default }); HttpNodeJSPoolService testSubject = CreateHttpNodeJSPoolService(dummyNumProcesses, dummyServices); // Act var getPidTasks = new Task <int> [dummyNumProcesses]; for (int i = 0; i < dummyNumProcesses; i++) { getPidTasks[i] = testSubject.InvokeFromFileAsync <int>(dummyModuleFilePath, "getPid"); } Task.WaitAll(getPidTasks); int[] initialProcessID1s = getPidTasks.Select(task => task.Result).ToArray(); var initialProcesses = new Process[dummyNumProcesses]; for (int i = 0; i < dummyNumProcesses; i++) { // Create Process instances for initial processes so we can verify that they get killed initialProcesses[i] = Process.GetProcessById(initialProcessID1s[i]); } var longRunningTasks = new Task <int> [dummyNumProcesses]; for (int i = 0; i < dummyNumProcesses; i++) { longRunningTasks[i] = testSubject.InvokeFromFileAsync <int>(dummyModuleFilePath, "longRunning"); } File.WriteAllText(dummyModuleFilePath, "module.exports = { getPid: (callback) => callback(null, process.pid) }");// Trigger shifts to new processes // Wait for all processes to shift var newProcessIDs = new List <int>(dummyNumProcesses); do { int newProcessID = await testSubject.InvokeFromFileAsync <int>(dummyModuleFilePath, "getPid").ConfigureAwait(false); if (!initialProcessID1s.Contains(newProcessID) && !newProcessIDs.Contains(newProcessID)) { newProcessIDs.Add(newProcessID); } }while (newProcessIDs.Count != dummyNumProcesses); // Poll until we've shifted to new processes. If we don't successfully shift to new processes, test will timeout. // End long running invocations File.AppendAllText(dummylongRunningTriggerPath, "dummyContent"); Task.WaitAll(longRunningTasks); int[] initialProcessID2s = longRunningTasks.Select(task => task.Result).ToArray(); foreach (Process initialProcess in initialProcesses) { initialProcess.WaitForExit(); // Should exit after the long running invocations complete } // Assert foreach (int initialProcessID1 in initialProcessID1s) { Assert.Contains(initialProcessID1, initialProcessID2s); // Long running invocations complete in initial processes } }
public async void FileWatching_RespectsGracefulShutdownOptionWhenItsFalse() { // Arrange const int dummyNumProcesses = 5; // Create initial module const string dummyInitialModule = @"module.exports = { getPid: (callback) => callback(null, process.pid), longRunning: (callback) => setInterval(() => { /* Do nothing */ }, 1000) }"; string dummyModuleFilePath = new Uri(CreateWatchDirectoryUri(), "dummyModule.js").AbsolutePath; File.WriteAllText(dummyModuleFilePath, dummyInitialModule); var resultStringBuilder = new StringBuilder(); var dummyServices = new ServiceCollection(); dummyServices.Configure <OutOfProcessNodeJSServiceOptions>(options => { options.EnableFileWatching = true; options.WatchPath = _tempWatchDirectory; options.GracefulProcessShutdown = false; }); HttpNodeJSPoolService testSubject = CreateHttpNodeJSPoolService(dummyNumProcesses, dummyServices, resultStringBuilder); // Act var getPidTasks = new Task <int> [dummyNumProcesses]; for (int i = 0; i < dummyNumProcesses; i++) { getPidTasks[i] = testSubject.InvokeFromFileAsync <int>(dummyModuleFilePath, "getPid"); } Task.WaitAll(getPidTasks); int[] intialProcessIDs = getPidTasks.Select(task => task.Result).ToArray(); var initialProcesses = new Process[dummyNumProcesses]; for (int i = 0; i < dummyNumProcesses; i++) { // Create Process instances for initial processes so we can verify that they get killed initialProcesses[i] = Process.GetProcessById(intialProcessIDs[i]); } var longRunningTasks = new Task <int> [dummyNumProcesses]; for (int i = 0; i < dummyNumProcesses; i++) { longRunningTasks[i] = testSubject.InvokeFromFileAsync <int>(dummyModuleFilePath, "longRunning"); } // Trigger shifts to new processes const string dummyNewModule = @"module.exports = { getPid: (callback) => callback(null, process.pid), longRunning: (callback) => callback(null, process.pid) }"; File.WriteAllText(dummyModuleFilePath, dummyNewModule); // Wait for initial processes to exit foreach (Process initialProcess in initialProcesses) { initialProcess.WaitForExit(); } // Wait for all processes to shift var newProcessID1s = new List <int>(dummyNumProcesses); do { int newProcessID = await testSubject.InvokeFromFileAsync <int>(dummyModuleFilePath, "getPid").ConfigureAwait(false); if (!intialProcessIDs.Contains(newProcessID) && !newProcessID1s.Contains(newProcessID)) { newProcessID1s.Add(newProcessID); } }while (newProcessID1s.Count != dummyNumProcesses); // Poll until we've shifted to new processes. If we don't successfully shift to new processes, test will timeout. // Because graceful shutdown is disabled, long running invocations should fail in initial processes and retry successfully in new processes Task.WaitAll(longRunningTasks); int[] newProcessID2s = longRunningTasks.Select(task => task.Result).ToArray(); // Assert string resultLog = resultStringBuilder.ToString(); Assert.Equal(dummyNumProcesses, Regex.Matches(resultLog, nameof(HttpRequestException)).Count); // HttpRequestException thrown when initial processes are killed foreach (int newProcessID1 in newProcessID1s) { Assert.Contains(newProcessID1, newProcessID2s); // Long running invocations should complete in new processes } }