// Copyright (C) Stichting Deltares 2019. All rights reserved. // // This file is part of the Delta Shell Light Library. // // The Delta Shell Light Library 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 NUnit.Framework; namespace Deltares.Dam.Tests.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 { [TearDown] public void TearDown() { objectLeakMonitor = null; directCompositeChildrenLeakMonitors = null; } private ObjectLeakMonitor objectLeakMonitor; private ObjectLeakMonitor[] directCompositeChildrenLeakMonitors; /// /// Clear all static caches that should be cleared at end of application /// protected virtual void ClearStaticCaches() { // Clear all static, non-GUI caches here } protected void MonitorMemoryLeakage(Func createMonitoredObject, Func> getMonitoredChildren) where T : class, IDisposable { PerformMemoryMeasurements(createMonitoredObject, getMonitoredChildren); string report; var isLeaking = CreateMemoryLeakReport(objectLeakMonitor, directCompositeChildrenLeakMonitors, out report); if (!isLeaking) { Console.WriteLine(report); } Assert.IsFalse(isLeaking, report); } protected void MonitorMemoryLeakage(Func> getMonitoredChildren) where T : class, IDisposable, new() { PerformMemoryMeasurements(() => new T(), getMonitoredChildren); string report; var isLeaking = CreateMemoryLeakReport(objectLeakMonitor, directCompositeChildrenLeakMonitors, out report); if (!isLeaking) { Console.WriteLine(report); } Assert.IsFalse(isLeaking, report); } /// /// 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 (var 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 (var compositeChildrenLeakMonitor in compositeChildrenLeakMonitors) { report += "\tChild (" + compositeChildrenLeakMonitor.MonitoredObjectType + ") garbage collected: " + !compositeChildrenLeakMonitor.ObjectIsAlive() + Environment.NewLine; } } report += "================================================================="; return monitor.ObjectIsAlive() || (compositeChildrenLeakMonitors != null && compositeChildrenLeakMonitors.Any(c => c.ObjectIsAlive())); } } }