From ff784582b9c536e9e6fddeb84405763fb9f39455 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Sim=C3=B5es?= Date: Fri, 20 Sep 2024 15:27:39 +0100 Subject: [PATCH] Enable destructor and finalizer tests (#215) ***NO_CI*** --- .runsettings | 1 + .../UnitTestDestructorTests.cs | 202 +++++++----- Tests/NFUnitTestSystemLib/UnitTestGCTest.cs | 303 ++++++++++-------- 3 files changed, 291 insertions(+), 215 deletions(-) diff --git a/.runsettings b/.runsettings index 90d26e0b..3d17b679 100644 --- a/.runsettings +++ b/.runsettings @@ -11,5 +11,6 @@ Verbose False + --forcegc \ No newline at end of file diff --git a/Tests/NFUnitTestClasses/UnitTestDestructorTests.cs b/Tests/NFUnitTestClasses/UnitTestDestructorTests.cs index 97d94e48..7b7bcb11 100644 --- a/Tests/NFUnitTestClasses/UnitTestDestructorTests.cs +++ b/Tests/NFUnitTestClasses/UnitTestDestructorTests.cs @@ -6,8 +6,6 @@ using nanoFramework.TestFramework; using System; -using System.Diagnostics; -using System.Reflection; namespace NFUnitTestClasses { @@ -15,92 +13,102 @@ namespace NFUnitTestClasses class UnitTestDestructorTests { // Removing as using something out of mscorlib - //[TestMethod] - //public void Destructors3_Test() - //{ - // //Ported from Destructors3.cs - // // Section 10.11 - // // Destructors implement the actions required to - // // destruct the instances of a class. - // // - // // Note: This test may fail due to lengthy garbage collection, look for Destructor messages in later logs - // Assert.IsTrue(DestructorsTestClass3.testMethod()); - //} - - //[TestMethod] - //public void Destructors4_Test() - //{ - // //Ported from Destructors4.cs - // // Section 10.11 - // // Destructors implement the actions required to - // // destruct the instances of a class. - // // - // // Note: This test may fail due to lengthy garbage collection, look for Destructor messages in later logs - // Assert.IsTrue(DestructorsTestClass4.testMethod()); - //} - - // Removed as using a class out of mscorlib - //[TestMethod] - //public void Destructors7_Test() - //{ - // //Ported from Destructors7.cs - // // Section 10.12 - // // Destructors are not inherited. Thus, a class - // // has no other destructors than those that are - // // actually declared in the class. - // // - // // Note: This test may fail due to lengthy garbage collection, look for Destructor messages in later logs - // Assert.IsTrue(DestructorsTestClass7.testMethod()); - //} - - //class DestructorsTestClass3 - //{ - - // static int intI = 1; - - // ~DestructorsTestClass3() - // { - // // Calling Destructor for Test Class 3 - // intI = 2; - // } - - // public static bool testMethod() - // { - // DestructorsTestClass3 mc = new DestructorsTestClass3(); - // mc = null; - // nanoFramework.Runtime.Native.GC.Run(true); - // int sleepTime = 5000; - // int slept = 0; - // while (intI != 2 && slept < sleepTime) - // { - // System.Threading.Thread.Sleep(10); - // slept += 10; - // } - // // Thread has slept for - // OutputHelper.WriteLine(slept.ToString()); - // if (intI == 2) - // { - // return true; - // } - // else - // { - // return false; - // } - // } - //} - - - class DestructorsTestClass4_Base + [TestMethod] + public void Destructors3_Test() + { + //Ported from Destructors3.cs + // Section 10.11 + // Destructors implement the actions required to + // destruct the instances of a class. + // + // Note: This test may fail due to lengthy garbage collection, look for Destructor messages in later logs + Assert.IsTrue(DestructorsTestClass3.TestMethod()); + } + + [TestMethod] + public void Destructors4_Test() + { + //Ported from Destructors4.cs + // Section 10.11 + // Destructors implement the actions required to + // destruct the instances of a class. + // + // Note: This test may fail due to lengthy garbage collection, look for Destructor messages in later logs + Assert.IsTrue(DestructorsTestClass4.TestMethod()); + } + + [TestMethod] + public void Destructors7_Test() + { + //Ported from Destructors7.cs + // Section 10.12 + // Destructors are not inherited. Thus, a class + // has no other destructors than those that are + // actually declared in the class. + // + // Note: This test may fail due to lengthy garbage collection, look for Destructor messages in later logs + Assert.IsTrue(DestructorsTestClass7.TestMethod()); + } + + public class DestructorsTestClass3 + { + static int intI = 1; + + ~DestructorsTestClass3() + { + // Calling Destructor for Test Class 3 + intI = 2; + + Console.WriteLine("Calling Destructor for Test Class 3"); + } + + public static bool TestMethod() + { + DestructorsTestClass3 mc = new DestructorsTestClass3(); + mc = null; + + // the following call has been "replaced" with the setting commanding a GC run before new allocations, which will have the desired effect of + // nanoFramework.Runtime.Native.GC.Run(true); + + int sleepTime = 5000; + int slept = 0; + + while (intI != 2 && slept < sleepTime) + { + // force GC run caused by memory allocation + var dummyArray = new byte[1024 * 1024 * 1]; + + System.Threading.Thread.Sleep(10); + slept += 10; + } + + // Thread has slept for + OutputHelper.WriteLine($"Thread as slept for{slept}"); + + if (intI == 2) + { + return true; + } + else + { + return false; + } + } + } + + public class DestructorsTestClass4_Base { public static int intI = 2; ~DestructorsTestClass4_Base() { - intI = intI * 2; // Calling Destructor for Test Class 4 Base + intI = intI * 2; + + Console.WriteLine("Calling Destructor for Test Class 4 Base"); } } - class DestructorsTestClass4 : DestructorsTestClass4_Base + public class DestructorsTestClass4 : DestructorsTestClass4_Base { ~DestructorsTestClass4() @@ -109,20 +117,29 @@ class DestructorsTestClass4 : DestructorsTestClass4_Base // Calling Destructor for Test Class 4 } - public static bool testMethod() + public static bool TestMethod() { DestructorsTestClass4 mc = new DestructorsTestClass4(); mc = null; + + // the following call has been "replaced" with the setting commanding a GC run before new allocations, which will have the desired effect of // nanoFramework.Runtime.Native.GC.Run(true); + int sleepTime = 5000; int slept = 0; + while (intI != 8 && slept < sleepTime) { + // force GC run caused by memory allocation + var dummyArray = new byte[1024 * 1024 * 1]; + System.Threading.Thread.Sleep(10); slept += 10; } + // Thread has slept for - OutputHelper.WriteLine(slept.ToString()); + OutputHelper.WriteLine($"Thread as slept for{slept}"); + if (intI == 8) { return true; @@ -134,34 +151,45 @@ public static bool testMethod() } } - class DestructorsTestClass7_Base + public class DestructorsTestClass7_Base { public static int intI = 2; } - class DestructorsTestClass7 : DestructorsTestClass7_Base + public class DestructorsTestClass7 : DestructorsTestClass7_Base { ~DestructorsTestClass7() { - intI = 3; // Calling Destructor for Test Class 7 + intI = 3; + + Console.WriteLine("Calling Destructor for Test Class 7"); } - public static bool testMethod() + public static bool TestMethod() { DestructorsTestClass7 mc = new DestructorsTestClass7(); mc = null; + + // the following call has been "replaced" with the setting commanding a GC run before new allocations, which will have the desired effect of //nanoFramework.Runtime.Native.GC.Run(true); + int sleepTime = 5000; int slept = 0; + while (intI != 3 && slept < sleepTime) { + // force GC run caused by memory allocation + var dummyArray = new byte[1024 * 1024 * 1]; + System.Threading.Thread.Sleep(10); slept += 10; } + // Thread has slept for - OutputHelper.WriteLine(slept.ToString()); + OutputHelper.WriteLine($"Thread as slept for{slept}"); + if (intI == 3) { return true; diff --git a/Tests/NFUnitTestSystemLib/UnitTestGCTest.cs b/Tests/NFUnitTestSystemLib/UnitTestGCTest.cs index 1f017a38..18358d7e 100644 --- a/Tests/NFUnitTestSystemLib/UnitTestGCTest.cs +++ b/Tests/NFUnitTestSystemLib/UnitTestGCTest.cs @@ -6,20 +6,19 @@ using nanoFramework.TestFramework; using System; -using System.Diagnostics; namespace NFUnitTestSystemLib { [TestClass] - class UnitTestGCTest + public class UnitTestGCTest { - class FinalizeObject + internal class FinalizeObject { public static FinalizeObject m_currentInstance = null; ~FinalizeObject() { - if (m_hasFinalized1 == false) + if (!m_hasFinalized1) { // First finalization @@ -46,130 +45,178 @@ class FinalizeObject static bool m_hasFinalized2 = false; static bool m_Test1Result = false; - //[TestMethod] - //public void SystemGC1_Test() - //{ - // /// - // /// 1. Create a FinalizeObject. - // /// 2. Release the reference - // /// 3. Allow for GC - // /// 4. Run ReRegisterForFinalize - // /// 5. Allow for GC - // /// 6. Verify that object has been collected - // /// - // /// - // // Tests ReRegisterForFinalize - // // Create a FinalizeObject. - // FinalizeObject mfo = new FinalizeObject(); - // m_hasFinalized1 = false; - // m_hasFinalized2 = false; - - // // Release reference - // mfo = null; - - // // Allow GC - // GC.WaitForPendingFinalizers(); - // int sleepTime = 1000; - // int slept = 0; - // while (m_hasFinalized1 == false && slept < sleepTime) - // { - // System.Threading.Thread.Sleep(10); - // slept += 10; - // } - // OutputHelper.WriteLine("GC took " + slept); - - // // At this point mfo will have gone through the first Finalize. - // // There should now be a reference to mfo in the static - // // FinalizeObject.m_currentInstance field. Setting this value - // // to null and forcing another garbage collection will now - // // cause the object to Finalize permanently. - // // Reregister and allow for GC - // FinalizeObject.m_currentInstance = null; - // GC.WaitForPendingFinalizers(); - // sleepTime = 1000; - // slept = 0; - // while (m_hasFinalized2 == false && slept < sleepTime) - // { - // System.Threading.Thread.Sleep(10); - // slept += 10; - // } - // OutputHelper.WriteLine("GC took " + slept); - - // m_Test1Result = m_hasFinalized2; - // Assert.IsTrue(m_hasFinalized2); - //} - - //[TestMethod] - //public void SystemGC2_Test() - //{ - // /// - // /// 1. Create a FinalizeObject. - // /// 2. Release the reference - // /// 3. SupressFinalize - // /// 3. Allow for GC - // /// 6. Verify that object has not been collected - // /// - // /// - // // Tests SuppressFinalize - // // Create a FinalizeObject. - // FinalizeObject mfo = new FinalizeObject(); - // m_hasFinalized1 = false; - // m_hasFinalized2 = false; - - // // Releasing - // System.GC.SuppressFinalize(mfo); - // mfo = null; - - // // Allow GC - // GC.WaitForPendingFinalizers(); - // int sleepTime = 1000; - // int slept = 0; - // while (m_hasFinalized1 == false && slept < sleepTime) - // { - // System.Threading.Thread.Sleep(10); - // slept += 10; - // } - // OutputHelper.WriteLine("GC took " + slept); - - // Assert.IsFalse(m_hasFinalized1); - //} - - //[TestMethod] - //public void SystemGC3_Test() - //{ - // /// - // /// 1. Create a FinalizeObject. - // /// 2. Release the reference - // /// 3. SupressFinalize - // /// 3. Allow for GC - // /// 6. Verify that object has not been collected - // /// - // /// - // // Tests WaitForPendingFinalizers, dependant on test 1 - // // will auto-fail if test 1 fails. - // Assert.IsTrue(m_Test1Result); - - // // Create a FinalizeObject. - // FinalizeObject mfo = new FinalizeObject(); - // m_hasFinalized1 = false; - // m_hasFinalized2 = false; - - // // Releasing - // mfo = null; - - // // Wait for GC - // GC.WaitForPendingFinalizers(); - // System.GC.WaitForPendingFinalizers(); - - // // Releasing again - // FinalizeObject.m_currentInstance = null; - - // // Wait for GC - // GC.WaitForPendingFinalizers(); - // System.GC.WaitForPendingFinalizers(); - - // Assert.IsTrue(m_hasFinalized2); - //} + [TestMethod] + public void SystemGC1_Test() + { + /// + /// 1. Create a FinalizeObject. + /// 2. Release the reference + /// 3. Allow for GC + /// 4. Run ReRegisterForFinalize + /// 5. Allow for GC + /// 6. Verify that object has been collected + /// + /// + // Tests ReRegisterForFinalize + // Create a FinalizeObject. + FinalizeObject mfo = new FinalizeObject(); + m_hasFinalized1 = false; + m_hasFinalized2 = false; + + // Release reference + mfo = null; + + // Allow GC + GC.WaitForPendingFinalizers(); + + int sleepTime = 1000; + int slept = 0; + + while (!m_hasFinalized1 && slept < sleepTime) + { + // force GC run caused by memory allocation + var dummyArray = new byte[1024 * 1024 * 1]; + + System.Threading.Thread.Sleep(10); + slept += 10; + } + + OutputHelper.WriteLine($"GC took {slept}"); + + // At this point mfo will have gone through the first Finalize. + // There should now be a reference to mfo in the static + // FinalizeObject.m_currentInstance field. Setting this value + // to null and forcing another garbage collection will now + // cause the object to Finalize permanently. + // Reregister and allow for GC + FinalizeObject.m_currentInstance = null; + + GC.WaitForPendingFinalizers(); + + sleepTime = 1000; + slept = 0; + + while (!m_hasFinalized2 && slept < sleepTime) + { + // force GC run caused by memory allocation + var dummyArray = new byte[1024 * 1024 * 1]; + + System.Threading.Thread.Sleep(10); + slept += 10; + } + + OutputHelper.WriteLine($"GC took {slept}"); + + m_Test1Result = m_hasFinalized2; + Assert.IsTrue(m_hasFinalized2); + } + + [TestMethod] + public void SystemGC2_Test() + { + /// + /// 1. Create a FinalizeObject. + /// 2. Release the reference + /// 3. SupressFinalize + /// 3. Allow for GC + /// 6. Verify that object has not been collected + /// + /// + // Tests SuppressFinalize + // Create a FinalizeObject. + FinalizeObject mfo = new FinalizeObject(); + m_hasFinalized1 = false; + m_hasFinalized2 = false; + + // Releasing + GC.SuppressFinalize(mfo); + mfo = null; + + // Allow GC + GC.WaitForPendingFinalizers(); + + int sleepTime = 1000; + int slept = 0; + + while (!m_hasFinalized1 && slept < sleepTime) + { + // force GC run caused by memory allocation + var dummyArray = new byte[1024 * 1024 * 1]; + + System.Threading.Thread.Sleep(10); + slept += 10; + } + OutputHelper.WriteLine($"GC took {slept}"); + + Assert.IsFalse(m_hasFinalized1); + } + + [TestMethod] + public void SystemGC3_Test() + { + /// + /// 1. Create a FinalizeObject. + /// 2. Release the reference + /// 3. SupressFinalize + /// 3. Allow for GC + /// 6. Verify that object has not been collected + /// + /// + + // Tests WaitForPendingFinalizers, dependant on test 1 + // will auto-fail if test 1 fails. + OutputHelper.Write("Tests WaitForPendingFinalizers, dependant on test 1"); + OutputHelper.WriteLine("will auto-fail if test 1 fails."); + + Assert.IsTrue(m_Test1Result); + + // Create a FinalizeObject. + FinalizeObject mfo = new FinalizeObject(); + m_hasFinalized1 = false; + m_hasFinalized2 = false; + + // Releasing + mfo = null; + + int sleepTime = 1000; + int slept = 0; + + while (!m_hasFinalized1 && slept < sleepTime) + { + // force GC run caused by memory allocation + var dummyArray = new byte[1024 * 1024 * 1]; + + System.Threading.Thread.Sleep(10); + slept += 10; + } + + OutputHelper.WriteLine($"GC took {slept}"); + + // Wait for GC + GC.WaitForPendingFinalizers(); + + // Releasing again + FinalizeObject.m_currentInstance = null; + + sleepTime = 1000; + slept = 0; + + while (!m_hasFinalized2 && slept < sleepTime) + { + // force GC run caused by memory allocation + var dummyArray = new byte[1024 * 1024 * 1]; + + System.Threading.Thread.Sleep(10); + slept += 10; + } + + OutputHelper.WriteLine($"GC took {slept}"); + + // Wait for GC + GC.WaitForPendingFinalizers(); + + Assert.IsTrue(m_hasFinalized2); + } } }