Skip to content

Commit

Permalink
feat: Add support for native runtimes #419
Browse files Browse the repository at this point in the history
Put native configuration in a new settings file
Catch UnauthorizedAccessException on windows when deleting files as on Windows you cannot delete a file thats in use, and Unity cannot unload native files
  • Loading branch information
robin-moss authored and JoC0de committed Mar 9, 2024
1 parent 25dcde8 commit e3d3976
Show file tree
Hide file tree
Showing 13 changed files with 499 additions and 2 deletions.
4 changes: 4 additions & 0 deletions src/NuGetForUnity.Packager/Assets/NuGet.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions src/NuGetForUnity.Tests/Assets/PlayTests.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

76 changes: 76 additions & 0 deletions src/NuGetForUnity.Tests/Assets/PlayTests/NuGetPlayTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
using System;
using System.Collections;
using System.Runtime.InteropServices;
using NugetForUnity;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;

[assembly: PrebuildSetup(typeof(NuGetPlayTests))]
[assembly: PostBuildCleanup(typeof(NuGetPlayTests))]

/// <summary>
/// Play mode tests allow us to install NuGet packages with Native code before play mode starts, then when play mode
/// runs the native libraries are available for use.
///
/// There seems to be some Unity internals that prevents an edit mode test from adding a Native library and finding it
/// in the same run.
/// </summary>
public class NuGetPlayTests : IPrebuildSetup, IPostBuildCleanup
{
readonly NugetPackageIdentifier sqlite = new NugetPackageIdentifier("SQLitePCLRaw.lib.e_sqlite3", "2.0.7");

/// <summary>
/// This is the version number of sqlite with periods replaced with zeros.
///
/// Note: Version of the SQLite library does not match the NuGet package version
/// </summary>
private readonly int _expectedVersion = 3035005;

[UnityTest]
public IEnumerator InstallAndRunSqlite()
{
yield return new WaitForFixedUpdate();

// Test the actual library by calling a "extern" method
var version = sqlite3_libversion_number();
Assert.That(version, Is.EqualTo(_expectedVersion));
}

/// <summary>
/// Call to the SQLite native file, this actually tests loading and access the library when called.
///
/// On windows the call to sqlite3_libversion returns a garbled string so use the integer
/// </summary>
/// <returns></returns>
[DllImport("e_sqlite3")]
private static extern int sqlite3_libversion_number();

public void Setup()
{
try
{
sqlite3_libversion_number();
Assert.Fail("e_sqlite3 dll loaded, but should not be available");
}
catch (DllNotFoundException)
{
}

// For windows we end up importing two identical DLLs temporarily, this causes an error log that NUnit detects
// and would fail the test if we don't tell it to ignore the Failing messages
NugetHelper.LoadNugetConfigFile();
NugetHelper.LoadSettingFile();
LogAssert.ignoreFailingMessages = true;
NugetHelper.InstallIdentifier(sqlite);
Assert.IsTrue(NugetHelper.IsInstalled(sqlite), "The package was NOT installed: {0} {1}", sqlite.Id,
sqlite.Version);
}

public void Cleanup()
{
NugetHelper.Uninstall(sqlite);
Assert.IsFalse(NugetHelper.IsInstalled(sqlite), "The packages are STILL installed: {0} {1}", sqlite.Id,
sqlite.Version);
}
}
11 changes: 11 additions & 0 deletions src/NuGetForUnity.Tests/Assets/PlayTests/NuGetPlayTests.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 22 additions & 0 deletions src/NuGetForUnity.Tests/Assets/PlayTests/PlayTest.asmdef
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"name": "PlayTest",
"rootNamespace": "",
"references": [
"UnityEngine.TestRunner",
"UnityEditor.TestRunner",
"NuGetForUnity"
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": true,
"precompiledReferences": [
"nunit.framework.dll"
],
"autoReferenced": false,
"defineConstraints": [
"UNITY_INCLUDE_TESTS"
],
"versionDefines": [],
"noEngineReferences": false
}
7 changes: 7 additions & 0 deletions src/NuGetForUnity.Tests/Assets/PlayTests/PlayTest.asmdef.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 19 additions & 0 deletions src/NuGetForUnity.Tests/Assets/Tests/Editor/NuGetTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -996,6 +996,25 @@ public void TestSourceCodePackageInstall(string packageId, string packageVersion
Assert.IsFalse(InstalledPackagesManager.IsInstalled(package, false), "The package is STILL installed: {0} {1}", package.Id, package.Version);
}

[Test]
[TestCase("win7-x64", BuildTarget.StandaloneWindows64)]
[TestCase("win7-x86", BuildTarget.StandaloneWindows)]
[TestCase("win-x64", BuildTarget.StandaloneWindows64)]
[TestCase("win-x86", BuildTarget.StandaloneWindows)]
[TestCase("linux-x64", BuildTarget.StandaloneLinux64)]
[TestCase("osx-x64", BuildTarget.StandaloneOSX)]
public void NativeSettingsTest(string key, BuildTarget buildTarget)
{
Settings.CreateDefault(NugetHelper.SettingsFilePath);
// Call load settings directly to ensure we're testing the deserialised file
var settings = Settings.Load(NugetHelper.SettingsFilePath);
var nativeRuntimes = settings.NativeRuntimesMappings;

Assert.IsTrue(nativeRuntimes.ContainsKey(key), $"Native mappings is missing {key}");
Assert.IsTrue(nativeRuntimes[key].Contains(buildTarget),
$"Native mapping for {key} is missing build target {buildTarget}");
}

private static void ConfigureNugetConfig(InstallMode installMode)
{
var nugetConfigFile = ConfigurationManager.NugetConfigFile;
Expand Down
19 changes: 17 additions & 2 deletions src/NuGetForUnity/Editor/Helper/FileSystemHelper.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.IO;
using System.Runtime.InteropServices;
using JetBrains.Annotations;
using UnityEngine;

Expand Down Expand Up @@ -104,7 +105,14 @@ internal static void DeleteDirectory([NotNull] string directoryPath, bool log)
directoryInfo.Attributes = FileAttributes.Normal;

// delete the directory
directoryInfo.Delete(true);
try
{
directoryInfo.Delete(true);
}
catch (UnauthorizedAccessException e) when (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && e.Message.Contains(".dll"))
{
Debug.LogError("Windows was unable to delete all the files, if you are using Native files this is because the file is in use by Unity. Please restart Unity to remove all the files");
}
}

/// <summary>
Expand All @@ -119,7 +127,14 @@ internal static void DeleteFile([NotNull] string filePath)
}

File.SetAttributes(filePath, FileAttributes.Normal);
File.Delete(filePath);
try
{
File.Delete(filePath);
}
catch (UnauthorizedAccessException e) when (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && e.Message.Contains(".dll"))
{
Debug.LogError("Windows was unable to delete all the files, if you are using Native files this is because the file is in use by Unity. Please restart Unity to remove all the files");
}
}

/// <summary>
Expand Down
Loading

0 comments on commit e3d3976

Please sign in to comment.