// Copyright (C) Stichting Deltares 2025. All rights reserved. // // This file is part of the application DAM - UI. // // DAM - UI is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . // // All names, logos, and references to "Deltares" are registered trademarks of // Stichting Deltares and remain full property of Stichting Deltares at all times. // All rights reserved. using System; using System.Collections.Generic; using System.Linq; using Deltares.Dam.Tests.TestUtils; using NUnit.Framework; namespace Deltares.Dam.TestHelper.TestUtils; /// /// A base class providing template methods to be used for performing /// memory leak tests. /// /// This class is working purely on non-GUI level. [TestFixture] public abstract class MemoryLeakTestBase { private ObjectLeakMonitor objectLeakMonitor; private ObjectLeakMonitor[] directCompositeChildrenLeakMonitors; [TearDown] public void TearDown() { objectLeakMonitor = null; directCompositeChildrenLeakMonitors = null; } /// /// Clear all static caches that should be cleared at end of application /// private void ClearStaticCaches() { // Clear all static, non-GUI caches here } protected bool MonitorMemoryLeakage(Func> getMonitoredChildren, out string report) where T : class, IDisposable, new() { PerformMemoryMeasurements(() => new T(), getMonitoredChildren); bool isLeaking = CreateMemoryLeakReport(objectLeakMonitor, directCompositeChildrenLeakMonitors, out string memoryLeakReport); if (!isLeaking) { Console.WriteLine(memoryLeakReport); } report = memoryLeakReport; return isLeaking; } /// /// Performs the memory measurements. /// /// The object type to be monitored. It's default constructor will be used. /// The object creation method. /// The get monitored children. /// ///

/// The creation is performed in a separate method than where the are checked to /// ensure the objects will be garbage collected. ///

///
private void PerformMemoryMeasurements(Func createMonitoredObject, Func> getMonitoredChildren) where T : class, IDisposable { using (T objectToMonitor = createMonitoredObject()) { objectLeakMonitor = new ObjectLeakMonitor(objectToMonitor); directCompositeChildrenLeakMonitors = getMonitoredChildren != null ? getMonitoredChildren(objectToMonitor) .Select(mc => new ObjectLeakMonitor(mc)) .ToArray() : null; } ClearStaticCaches(); } /// /// Creates the memory leak report. /// /// The monitor for the object under test. /// The composite children leak monitors. /// Output: The memory leak report. /// True if a memory or object leak has been detected; False otherwise. private bool CreateMemoryLeakReport(ObjectLeakMonitor monitor, ObjectLeakMonitor[] compositeChildrenLeakMonitors, out string report) { report = Environment.NewLine + "=================================================================" + Environment.NewLine; report += "Memory leak report for " + monitor.MonitoredObjectType + Environment.NewLine; report += "Object garbage collected: " + !monitor.ObjectIsAlive() + Environment.NewLine; if ((compositeChildrenLeakMonitors != null) && compositeChildrenLeakMonitors.Any()) { report += "All monitored composite children garbage collected: " + compositeChildrenLeakMonitors.All(c => !c.ObjectIsAlive()) + Environment.NewLine; foreach (ObjectLeakMonitor compositeChildrenLeakMonitor in compositeChildrenLeakMonitors) { report += "\tChild (" + compositeChildrenLeakMonitor.MonitoredObjectType + ") garbage collected: " + !compositeChildrenLeakMonitor.ObjectIsAlive() + Environment.NewLine; } } report += "================================================================="; return monitor.ObjectIsAlive() || ((compositeChildrenLeakMonitors != null) && compositeChildrenLeakMonitors.Any(c => c.ObjectIsAlive())); } }