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