// 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()));
}
}