Skip to content

Commit

Permalink
Add Unit Tests for GC and strings (#209)
Browse files Browse the repository at this point in the history
***NO_CI***
  • Loading branch information
josesimoes committed Nov 8, 2024
1 parent 0defde4 commit 971ed70
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 0 deletions.
1 change: 1 addition & 0 deletions Tests/NFUnitTestGC/NFUnitTestGC.nfproj
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
<Import Project="$(NanoFrameworkProjectSystemPath)NFProjectSystem.props" Condition="Exists('$(NanoFrameworkProjectSystemPath)NFProjectSystem.props')" />
<ItemGroup>
<Compile Include="TestGCWithByteArrays.cs" />
<Compile Include="TestGCWithStringArrays.cs" />
<Compile Include="TestGCWithTimeSpanArrays.cs" />
<Compile Include="TestGCWithDateTimeArrays.cs" />
<Compile Include="TestGCWithObjectArrays.cs" />
Expand Down
103 changes: 103 additions & 0 deletions Tests/NFUnitTestGC/TestGCWithStringArrays.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
//
// Copyright (c) .NET Foundation and Contributors
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
// See LICENSE file in the project root for full license information.
//

using nanoFramework.TestFramework;
using System;

namespace NFUnitTestGC
{
[TestClass]
public class TestGCWithStringArrays
{
[TestMethod]
public void TestCompactionForNotFixedStringArray()
{
OutputHelper.WriteLine("Starting TestCompactionForNotFixedStringArray");

for (int loop = 0; loop < 10; loop++)
{
OutputHelper.WriteLine($"Starting iteration {loop}");

// First we create objects and holes that keeps some space that could be used by compaction.

// Small count so compaction does not happen.
HolderForString[] arrayOfStrings = new HolderForString[10];
RunStringAllocations(arrayOfStrings);

// This is the array that we expect to move in during compaction.
HolderForString[] testNativeBuffer = new HolderForString[100];
// Fill it, so it is not optimized out
for (int i = 0; i < testNativeBuffer.Length; i++)
{
testNativeBuffer[i] = new HolderForString(Guid.NewGuid().ToString());
}

OutputHelper.WriteLine("Large HolderForString array created");
OutputHelper.WriteLine("Forcing compaction to occurr");

// Causes compaction
InitiateStringCompaction();

OutputHelper.WriteLine("Compaction occurred");
OutputHelper.WriteLine("Checking arrays for corrupted data...");

int index = 0;

// Check that array content is not corrupted
foreach (HolderForString holder in testNativeBuffer)
{
Assert.AreEqual(
holder.StringHash,
holder.StringContent.GetHashCode(),
$"Array content comparison failed at position {index}. Expecting {holder.StringHash}, found {holder.StringContent.GetHashCode()}");

index++;
}

OutputHelper.WriteLine("No corruption detected in array");
}

OutputHelper.WriteLine("Completed TestCompactionForNotFixedArray");
}

// This function cause compaction to occur.
// It is not so trivial as it need to fragment heap with referenced objects.
void InitiateStringCompaction()
{
// Large count, so compaction happens during RunAllocations.
HolderForString[] arrayOfArrays = new HolderForString[400];
RunStringAllocations(arrayOfArrays);
}

private void RunStringAllocations(HolderForString[] arrObj)
{
for (int i = 1; i < arrObj.Length; i++)
{
// Creates referenced object, which stays in memory until InitiateCompaction exits
arrObj[i] = new HolderForString(Guid.NewGuid().ToString());

// Tries to create larger object that would be later hole
// This object could be garbage collected on each "i" cycle
HolderForString[] arr = new HolderForString[50 * i];

// Creates some usage for array elements, so it is not optimized out
arr[0] = new HolderForString(Guid.NewGuid().ToString());
}
}

private class HolderForString
{
public int StringHash { get; }
public string StringContent { get; }

public HolderForString(string value)
{
StringContent = value;
StringHash = value.GetHashCode();
}
}
}
}

0 comments on commit 971ed70

Please sign in to comment.