DotMemory offre la potenzialità di verificare di non avere leak tramite test, di seguito alcuni esempi. Per degli esempi in formato script vedi questo link.

  • Verifica di non aver alcun oggetto in memoria con un determinato namespace
dotMemory.Check(memory =>
	{
		Assert.That(memory.GetObjects(@where => @where.Namespace.Like("GameOfLife.*")).ObjectsCount, Is.EqualTo(0));
	});
  • Verifica di non aver alcun oggetto di un determinato tipo
dotMemory.Check(memory =>
                    Assert.That(memory.GetObjects(where => where.
                                Type.Is<PetriDish>())
                            .ObjectsCount,
                        Is.EqualTo(0)));
  • Verificare che la memoria complessiva allocata di un determinato codice non superi X byte.
const int Mb = 1024 * 1024;
 
[Test]
[AssertTraffic(AllocatedSizeInBytes = 2 * Mb)] // --assert
public void WholeRunTraffic()
{
	// --act
	// ...
}
  • Verifico che un determinato codice non crei dei nuovi oggetti di un determinato tipo
[Test]
public void DontRecreateArrays()
{
	// --arrange
	var target = new PetriDish(160, 100, timer);
 
	// Prendo uno snapshot della memoria
	var memoryPoint1 = dotMemory.Check();
 
	// --act
	target.PerformOneStep();
 
	// --assert
	// Confronto la meoria attuale (memory) con quella presa prima (memoryPoint1) e verifico di non aver alcun nuovo oggetto di tipo Cell.
	dotMemory.Check(memory =>
		Assert.That(memory.GetDifference(memoryPoint1)
				.GetNewObjects(where => where.Type.Is<Cell[,]>())
				.ObjectsCount,
			Is.EqualTo(0)));
}
  • Contare il numero di oggetti in una determinata Generation
dotMemory.Check(memory =>
{
	var objectSet = memory
		.GetObjects(where => where.Namespace.Like("Production"))
		.GetObjects(where => where.Generation.Is(Generation.Gen2));
 
	Assert.That(objectSet.ObjectsCount, Is.EqualTo(0));
}
                );
  • Verificare che metodo non crei alcun nuovo oggetto (del mio namespace) tranne oggetti di un determinato tipo.
[Test]
public void NoNewObjectsExceptShapesTest()
{
	using (new ShapeGenerator(_ => { }, TimeSpan.FromMilliseconds(100)))
	{
		var memoryCheckPoint = dotMemory.Check();
		Thread.Sleep(1000); // generate ~10 shapes
 
		dotMemory.Check(memory =>
		{
		    // Conto tutti i miei oggetti nuovi (namespace "Production")
			var newTotalCount =
				memory
					.GetDifference(memoryCheckPoint)
					.GetNewObjects(where => where.Namespace.Like("Production"))
					.ObjectsCount;
 
			// Conto gli oggetti nuovi di tipo IShape
			var newShapesCount =
				memory
					.GetDifference(memoryCheckPoint)
					.GetNewObjects(where => where.Interface.Is<IShape>())
					.ObjectsCount;
 
			// Verifico che i due valori siano uguali
			Assert.That(newTotalCount - newShapesCount, Is.EqualTo(0));
		});
	}
}

Attributi

E’ possibile configurare il comportamento dei test usando l’attributo DotMemoryUnit con determinate opzioni. Per approfondire vedi questo link.

  • FailIfRunWithoutSupport: se impostato a false non fa fallire il test se questo viene lanciato senza il supporto a dotMemory. Utile nel caso in cui i test vengano lanciati con Jenkins o quando si vogliono lanciare spesso e si vogliono evitare i test di leak che sono molto lenti. [DotMemoryUnit(FailIfRunWithoutSupport = false)]

Esempio completo

[Test]  
[DotMemoryUnit(FailIfRunWithoutSupport = false)]  
public void SaveFormatSync_AfterSomeModifications_DoNotLeak()  
{  
    // Arrange  
    var thermalView = Substitute.For<IView>();  
    thermalView.Head.Returns(_thermalHead);  
    var tridimensionalView = Substitute.For<IView>();  
    tridimensionalView.Head.Returns(_tridimensionalHead);  
    var views = new List<IView> { tridimensionalView, thermalView };  
    // Creo un formato con una posa con due viste, una 3d e una termo  
    SetupFormat(views);  
    var sut = new ManagePosesViewModel(_headsManager, _formatManager);  
  
    // Prendo uno snapshot della memoria  
    var memoryPoint1 = dotMemory.Check();  
  
    // Aggiungo un nuova posa  
    sut.AddAfterCommand.Execute(null);  
    // La prima posa solo 3d (quindi tolgo la view termo)  
    sut.Poses[0].Views[0].Enabled = true;  
    sut.Poses[0].Views[1].Enabled = false;  
    // La seconda posa solo termo (quindi tolgo la view 3d)  
    sut.Poses[1].Views[0].Enabled = true;  
    sut.Poses[1].Views[1].Enabled = true;  
  
    // Act  
    sut.SaveFormatSync();  
  
    // Verifico di avere lo stesso numero di IFormato di prima, di avere solo una posa in più e una vista in più rispetto a prima  
    dotMemory.Check(memory =>  
    {  
        var snapshotDifference = memory.GetDifference(memoryPoint1);  
        // Non posso usare IPose ma _formatManager.CurrentFormat.Poses[0].GetType(); in quanto non è una vera IPose ma un mock di NSubstitute.  
        var formatType = _formatManager.CurrentFormat.GetType();  
        var poseType = _formatManager.CurrentFormat.Poses[0].GetType();  
        var viewType = _formatManager.CurrentFormat.Poses[0].Views[0].GetType();  
        // Rispetto a prima non devo avere nuovi formati  
        snapshotDifference.GetNewObjects(where => where.Type == formatType).ObjectsCount.Should()  
            .Be(0);  
        // Rispetto a prima devo avere una sola posa in più  
        snapshotDifference.GetNewObjects(where => where.Type == poseType).ObjectsCount.Should()  
            .Be(1);  
        // Rispetto a prima devo avere una sola vista in più (dato che ho tolto una vista vecchia e la nuova posa ha solo una vista)  
        snapshotDifference.GetNewObjects(where => where.Type == viewType).ObjectsCount.Should()  
            .Be(1);  
    });}