コード例 #1
0
        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();
        }
コード例 #2
0
        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]));
        }
コード例 #3
0
        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();
        }
コード例 #4
0
        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());
            }
        }
コード例 #5
0
        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();
        }
コード例 #6
0
        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();
        }
コード例 #7
0
        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();
        }
コード例 #8
0
        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);
        }
コード例 #9
0
        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);
        }
コード例 #10
0
        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
            }
        }
コード例 #11
0
        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
            }
        }