Add test for System.Diagnostics.Process.Responding (#46142)
authorAdam Sitnik <adam.sitnik@gmail.com>
Mon, 21 Dec 2020 20:45:46 +0000 (21:45 +0100)
committerGitHub <noreply@github.com>
Mon, 21 Dec 2020 20:45:46 +0000 (21:45 +0100)
* ensure that MainWindowHandle_GetWithGui_ShouldRefresh_Windows and MainWindowTitle_GetWithGui_ShouldRefresh_Windows tests are not ignored for CI, but only excluded for Nano

* give a clear error when Notepad is missing

* apply code review suggestion and use reflection to test all refreshable private fields

* be explicit about field names

Co-authored-by: Eirik Tsarpalis <eirik.tsarpalis@gmail.com>
src/libraries/System.Diagnostics.Process/tests/ProcessTests.cs

index 7f1d741..f270f51 100644 (file)
@@ -8,6 +8,7 @@ using System.IO;
 using System.IO.Pipes;
 using System.Linq;
 using System.Net;
+using System.Reflection;
 using System.Security;
 using System.Text;
 using System.Threading;
@@ -1556,15 +1557,14 @@ namespace System.Diagnostics.Tests
             var process = new Process();
             Assert.Throws<InvalidOperationException>(() => process.MainWindowHandle);
         }
-        
-        [Fact]
-        [OuterLoop]
-        [Trait(XunitConstants.Category, XunitConstants.IgnoreForCI)] // Pops UI
+
+        [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsNanoServer))] // it needs Notepad
+        [OuterLoop("Pops UI")]
         [PlatformSpecific(TestPlatforms.Windows)]
         public void MainWindowHandle_GetWithGui_ShouldRefresh_Windows()
         {
             const string ExePath = "notepad.exe";
-            Assert.True(IsProgramInstalled(ExePath));
+            Assert.True(IsProgramInstalled(ExePath), "Notepad is not installed");
 
             using (Process process = Process.Start(ExePath))
             {
@@ -1591,16 +1591,15 @@ namespace System.Diagnostics.Tests
             }
         }
 
-        [Fact]
-        [OuterLoop]
-        [Trait(XunitConstants.Category, XunitConstants.IgnoreForCI)] // Pops UI
+        [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotWindowsNanoServer))] // it needs Notepad
+        [OuterLoop("Pops UI")]
         [PlatformSpecific(TestPlatforms.Windows)]
         public void MainWindowTitle_GetWithGui_ShouldRefresh_Windows()
         {
             const string ExePath = "notepad.exe";
-            Assert.True(IsProgramInstalled(ExePath));
+            Assert.True(IsProgramInstalled(ExePath), "Notepad is not installed");
 
-            using (Process process = Process.Start(ExePath))
+            using (Process process = Process.Start(new ProcessStartInfo(ExePath)))
             {
                 try
                 {
@@ -1627,6 +1626,76 @@ namespace System.Diagnostics.Tests
             }
         }
 
+        [Fact]
+        public void RefreshResetsAllRefreshableFields()
+        {
+            // testing Process.Responding using a real unresponsive process would be very hard to do properly
+            // instead of this, we just test the implementation to ensure that #36768 is not coming back
+            var process = new Process();
+
+            VerifyPrivateFieldsValues(process, shouldHaveDefaultValues: true);
+
+            SetPrivateFieldsToNonDefaultValues(process);
+
+            VerifyPrivateFieldsValues(process, shouldHaveDefaultValues: false);
+
+            process.Refresh();
+
+            VerifyPrivateFieldsValues(process, shouldHaveDefaultValues: true);
+
+            static void VerifyPrivateFieldsValues(Process process, bool shouldHaveDefaultValues)
+            {
+                Assert.Equal(shouldHaveDefaultValues, !(bool)GetPrivateFieldValue(process, "_exited"));
+                Assert.Equal(shouldHaveDefaultValues, !(bool)GetPrivateFieldValue(process, "_haveWorkingSetLimits"));
+                Assert.Equal(shouldHaveDefaultValues, !(bool)GetPrivateFieldValue(process, "_haveProcessorAffinity"));
+                Assert.Equal(shouldHaveDefaultValues, !(bool)GetPrivateFieldValue(process, "_havePriorityClass"));
+                Assert.Equal(shouldHaveDefaultValues, !(bool)GetPrivateFieldValue(process, "_haveExitTime"));
+                Assert.Equal(shouldHaveDefaultValues, !(bool)GetPrivateFieldValue(process, "_havePriorityBoostEnabled"));
+
+                Assert.Equal(shouldHaveDefaultValues, null == GetPrivateFieldValue(process, "_processInfo"));
+                Assert.Equal(shouldHaveDefaultValues, null == GetPrivateFieldValue(process, "_threads"));
+                Assert.Equal(shouldHaveDefaultValues, null == GetPrivateFieldValue(process, "_modules"));
+
+                if (OperatingSystem.IsWindows())
+                {
+                    Assert.Equal(shouldHaveDefaultValues, null == GetPrivateFieldValue(process, "_mainWindowTitle"));
+                    Assert.Equal(shouldHaveDefaultValues, !(bool)GetPrivateFieldValue(process, "_signaled"));
+                    Assert.Equal(shouldHaveDefaultValues, !(bool)GetPrivateFieldValue(process, "_haveMainWindow"));
+                    Assert.Equal(shouldHaveDefaultValues, !(bool)GetPrivateFieldValue(process, "_haveResponding"));
+                }
+            }
+
+            static void SetPrivateFieldsToNonDefaultValues(Process process)
+            {
+                SetPrivateFieldValue(process, "_exited", true);
+                SetPrivateFieldValue(process, "_haveWorkingSetLimits", true);
+                SetPrivateFieldValue(process, "_haveProcessorAffinity", true);
+                SetPrivateFieldValue(process, "_havePriorityClass", true);
+                SetPrivateFieldValue(process, "_haveExitTime", true);
+                SetPrivateFieldValue(process, "_havePriorityBoostEnabled", true);
+
+                SetPrivateFieldValue(process, "_processInfo", typeof(Process).Assembly.GetType("System.Diagnostics.ProcessInfo").GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, Array.Empty<Type>()).Invoke(null));
+                SetPrivateFieldValue(process, "_threads", new ProcessThreadCollection(Array.Empty<ProcessThread>()));
+                SetPrivateFieldValue(process, "_modules",  new ProcessModuleCollection(Array.Empty<ProcessModule>()));
+
+                if (OperatingSystem.IsWindows())
+                {
+                    SetPrivateFieldValue(process, "_signaled", true);
+                    SetPrivateFieldValue(process, "_haveMainWindow", true);
+                    SetPrivateFieldValue(process, "_mainWindowTitle", "notNull");
+                    SetPrivateFieldValue(process, "_haveResponding", true);
+                }
+            }
+
+            static object GetPrivateFieldValue(Process process, string fieldName) => typeof(Process)
+                .GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance)
+                .GetValue(process);
+
+            static void SetPrivateFieldValue(Process process, string fieldName, object value) => typeof(Process)
+                .GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance)
+                .SetValue(process, value);
+        }
+
         [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
         public void MainWindowTitle_NoWindow_ReturnsEmpty()
         {