First pass at unit tests, fix some backend code accordingly

pull/16/head
Miepee 3 years ago
parent cd20ab9d88
commit 24806f618d

@ -13,6 +13,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Atomic.Gtk", "Atomic.Gtk\At
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Atomic.Mac", "Atomic.Mac\Atomic.Mac.csproj", "{A0481858-CA50-4DB8-A547-193BD1D57D21}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AtomicLibTests", "AtomicLibTests\AtomicLibTests.csproj", "{705598A0-F06A-432C-869E-A98D480161FD}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -39,6 +41,10 @@ Global
{A0481858-CA50-4DB8-A547-193BD1D57D21}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A0481858-CA50-4DB8-A547-193BD1D57D21}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A0481858-CA50-4DB8-A547-193BD1D57D21}.Release|Any CPU.Build.0 = Release|Any CPU
{705598A0-F06A-432C-869E-A98D480161FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{705598A0-F06A-432C-869E-A98D480161FD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{705598A0-F06A-432C-869E-A98D480161FD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{705598A0-F06A-432C-869E-A98D480161FD}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

@ -10,7 +10,7 @@ namespace AtomicLib;
[XmlRoot("config")]
public class Config
{
public static string ConfigFilePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Atomic", "config.xml");
public static readonly string ConfigFilePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData, Environment.SpecialFolderOption.Create), "Atomic", "config.xml");
/// <summary>
/// Language used for the modpacker.

@ -35,6 +35,15 @@ public static class Core
public static void CreateModPack(ModCreationInfo modInfo, string output)
{
if (modInfo is null)
throw new NullReferenceException(nameof(modInfo));
if (modInfo.Profile is null)
throw new NullReferenceException(nameof(modInfo.Profile));
if (!File.Exists(modInfo.AM2R11Path))
throw new FileNotFoundException("AM2R_11 file path could not be found!");
if (modInfo.Profile.SupportsAndroid && !File.Exists(modInfo.ApkModPath))
throw new FileNotFoundException("Android is marked as supported, but the APK path (" + modInfo.ApkModPath + ") could not be found!");
ProfileOperatingSystems profileOS = Enum.Parse<ProfileOperatingSystems>(modInfo.Profile.OperatingSystem);
string modZipPath = profileOS switch
{
@ -43,7 +52,10 @@ public static class Core
ProfileOperatingSystems.Mac => modInfo.MacModPath,
_ => throw new NotSupportedException("The current operating system is not supported!")
};
if (!File.Exists(modZipPath))
throw new FileNotFoundException("The file path (" + modZipPath + ") for the OS (" + modInfo.Profile.OperatingSystem + ") could not be found!");
// Cleanup in case of previous errors
if (Directory.Exists($"{Path.GetTempPath()}/Atomic"))
Directory.Delete($"{Path.GetTempPath()}/Atomic", true);
@ -55,6 +67,7 @@ public static class Core
string tempProfilePath = Directory.CreateDirectory($"{tempPath}/profile").FullName;
// Extract 1.1 and modded AM2R to their own directories in temp work
// We *probably* should check for 1.1 validity before extracting, *HOWEVER* that makes it kinda difficult to test against.
ZipFile.ExtractToDirectory(modInfo.AM2R11Path, tempOriginalPath);
ZipFile.ExtractToDirectory(modZipPath, tempModPath);
@ -65,7 +78,9 @@ public static class Core
{
case ProfileOperatingSystems.Windows:
if (modInfo.Profile.UsesYYC)
{
CreatePatch($"{tempOriginalPath}/data.win", $"{tempModPath}/AM2R.exe", $"{tempProfilePath}/AM2R.xdelta");
}
else
{
CreatePatch($"{tempOriginalPath}/data.win", $"{tempModPath}/data.win", $"{tempProfilePath}/data.xdelta");
@ -95,7 +110,7 @@ public static class Core
// Extract APK first in order to create patch from the data.win
// - java -jar apktool.jar d "AM2RWrapper_old.apk"
RunJavaJar($"\"{localPath}/utilities/android/apktool.jar\" d -f -o \"{tempAndroid}\" \"{modInfo.ApkModPath}\"", tempAndroid);
RunJavaJar($"\"{localPath}/utilities/android/apktool.jar\" d -f -o \"{tempAndroid}\" \"{modInfo.ApkModPath}\"");
// Create game.droid patch
CreatePatch($"{tempOriginalPath}/data.win", $"{tempAndroid}/assets/game.droid", $"{tempProfilePath}/droid.xdelta");
@ -200,7 +215,7 @@ public static class Core
public static void RunJavaJar(string arguments = null, string workingDirectory = null)
{
workingDirectory ??= Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
workingDirectory ??= Directory.GetCurrentDirectory();
string proc = "",
javaArgs = "";

@ -74,7 +74,7 @@ public static class OS
/// <summary>
/// Checks if the Launcher is ran from a Flatpak.
/// </summary>
/// <returns>see langword="true"/> if run from a Flatpak, <see langword="false"/> if not.</returns>
/// <returns><see langword="true"/> if run from a Flatpak, <see langword="false"/> if not.</returns>
private static bool CheckIfRunFromFlatpak()
{
if (!IsLinux) return false;

@ -0,0 +1,47 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="3.1.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<!-- For local dev environments that don't want to bother with env vars-->
<None Update="AM2R_11.zip">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="GameLin.zip">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="GameMac.zip">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="GameWin.zip">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="GameAndroid.apk">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\AtomicLib\AtomicLib.csproj" />
</ItemGroup>
</Project>

@ -0,0 +1,211 @@
using System.IO.Compression;
using System.Xml.Serialization;
using AtomicLib;
using AtomicLib.XML;
using Xunit;
using Xunit.Abstractions;
using Xunit.Sdk;
namespace AtomicLibTests;
public class CoreTests : IDisposable
{
private string am2r_11Path;
private ITestOutputHelper console;
private string testTempDir;
public CoreTests(ITestOutputHelper output)
{
console = output;
testTempDir = Path.GetTempPath() + Guid.NewGuid() + "/";
// TODO: use a custom "AM2R_11" which is just a modified am2r_sever. the backend doesnt check for 1.1 validity, so we can do that.
Directory.CreateDirectory(testTempDir);
var am2rEnvVar = Environment.GetEnvironmentVariable("AM2R_11PATH");
if (am2rEnvVar is not null && File.Exists(am2rEnvVar))
{
am2r_11Path = am2rEnvVar;
return;
}
if (File.Exists("AM2R_11.zip"))
{
am2r_11Path = "AM2R_11.zip";
return;
}
Assert.Fail("AM2R 1.1 file could not be found! Please place it into PWD or provide it via the AM2R_11PATH environment variable.");
}
public void Dispose()
{
Directory.Delete(testTempDir, true);
}
[Fact]
public void CreateModPack_ShouldThrowWithNullModInfo()
{
Assert.Throws<NullReferenceException>(() => Core.CreateModPack(null, testTempDir + "foo.zip"));
}
[Fact]
public void CreateModPack_ShouldThrowWithNullModInfoProfile()
{
var modInfo = new ModCreationInfo();
modInfo.Profile = null;
Assert.Throws<NullReferenceException>(() => Core.CreateModPack(modInfo, testTempDir + "foo.zip"));
}
[Fact]
public void CreateModPack_ShouldThrowWithAM2R11PathNotSet()
{
var modInfo = new ModCreationInfo();
modInfo.Profile = new ModProfileXML();
modInfo.Profile.OperatingSystem = "Windows";
Assert.Throws<FileNotFoundException>(() => Core.CreateModPack(modInfo, testTempDir + "foo.zip"));
}
[Fact]
public void CreateModPack_ShouldThrowWithUnknownProfileOS()
{
var modInfo = new ModCreationInfo();
modInfo.Profile = new ModProfileXML();
modInfo.Profile.OperatingSystem = "asdfasdf";
modInfo.AM2R11Path = am2r_11Path;
Assert.Throws<ArgumentException>(() => Core.CreateModPack(modInfo, testTempDir + "foo.zip"));
}
[Fact]
public void CreateModPack_ShouldThrowWhenProfileSetButNotOSpath()
{
var modInfo = new ModCreationInfo();
modInfo.Profile = new ModProfileXML();
modInfo.Profile.OperatingSystem = "Windows";
modInfo.AM2R11Path = am2r_11Path;
Assert.Throws<FileNotFoundException>(() => Core.CreateModPack(modInfo, testTempDir + "foo.zip"));
}
[Fact]
public void CreateModPack_ShouldThrowWhenAndroidIsMarkedAsSupportedButPathDoesNotExist()
{
var modInfo = new ModCreationInfo();
modInfo.Profile = new ModProfileXML();
modInfo.Profile.OperatingSystem = "Windows";
modInfo.Profile.SupportsAndroid = true;
modInfo.AM2R11Path = am2r_11Path;
Assert.Throws<FileNotFoundException>(() => Core.CreateModPack(modInfo, testTempDir + "foo.zip"));
}
[Theory]
[InlineData("Windows", false, false, false)]
[InlineData("Windows", false, false, true)]
[InlineData("Windows", false, true, false)]
[InlineData("Windows", false, true, true)]
[InlineData("Windows", true, false, false)]
[InlineData("Windows", true, false, true)]
[InlineData("Windows", true, true, false)]
[InlineData("Windows", true, true, true)]
[InlineData("Linux", false, false, false)]
[InlineData("Linux", false, false, true)]
[InlineData("Linux", false, true, false)]
[InlineData("Linux", false, true, true)]
[InlineData("Linux", true, false, false)]
[InlineData("Linux", true, false, true)]
[InlineData("Linux", true, true, false)]
[InlineData("Linux", true, true, true)]
[InlineData("Mac", false, false, false)]
[InlineData("Mac", false, false, true)]
[InlineData("Mac", false, true, false)]
[InlineData("Mac", false, true, true)]
[InlineData("Mac", true, false, false)]
[InlineData("Mac", true, false, true)]
[InlineData("Mac", true, true, false)]
[InlineData("Mac", true, true, true)]
public void CreateModPack_AllOptionsShouldCauseValidModpacks(string operatingSystem, bool usesCustomMusic, bool supportsAndroid, bool isYYC)
{
var modInfo = new ModCreationInfo();
modInfo.Profile = new ModProfileXML();
modInfo.Profile.OperatingSystem = operatingSystem;
modInfo.Profile.UsesCustomMusic = usesCustomMusic;
modInfo.Profile.SupportsAndroid = supportsAndroid;
if (supportsAndroid)
modInfo.ApkModPath = "GameAndroid.apk";
modInfo.Profile.UsesYYC = isYYC;
modInfo.Profile.Name = "Cool Mod";
modInfo.Profile.Version = "cool version";
modInfo.Profile.ProfileNotes = "This is my very own cool mod";
modInfo.AM2R11Path = am2r_11Path;
switch (operatingSystem)
{
case "Windows": modInfo.WindowsModPath = "GameWin.zip"; break;
case "Linux": modInfo.LinuxModPath = "GameLin.zip"; break;
case "Mac": modInfo.MacModPath = "GameMac.zip"; break;
default: Assert.Fail(nameof(CreateModPack_AllOptionsShouldCauseValidModpacks) + " was called with improper parameters?"); break;
}
Core.CreateModPack(modInfo, testTempDir + "foo.zip");
// TODO: assert on proper packaging, by investigating contents of zip
ZipArchive archive = ZipFile.OpenRead(testTempDir + "foo.zip");
Assert.True(archive.Entries.FirstOrDefault(f => f.FullName == "profile.xml") is not null);
// TODO: try to deserialize xml to check it has what we put in
if (isYYC)
{
if (operatingSystem == "Windows")
{
Assert.True(archive.Entries.FirstOrDefault(f => f.FullName == "AM2R.xdelta") is not null);
Assert.True(archive.Entries.FirstOrDefault(f => f.FullName == "game.xdelta") is null);
}
}
else
{
// Unix has both in YYC and non-YYC
Assert.True(archive.Entries.FirstOrDefault(f => f.FullName == "AM2R.xdelta") is not null);
if (operatingSystem == "Windows")
Assert.True(archive.Entries.FirstOrDefault(f => f.FullName == "data.xdelta") is not null);
else
Assert.True(archive.Entries.FirstOrDefault(f => f.FullName == "game.xdelta") is not null);
}
if (supportsAndroid)
{
Assert.True(archive.Entries.FirstOrDefault(f => f.FullName == "droid.xdelta") is not null);
Assert.True(archive.Entries.FirstOrDefault(f => f.FullName == "android/AM2RWrapper.apk") is not null);
if (isYYC)
Assert.True(archive.Entries.FirstOrDefault(f => f.FullName == "android/AM2R.ini") is not null);
// TODO: atomic currently always copies the file if it exists. Should it only copy it if we're dealing with yyc?
//else
//Assert.True(archive.Entries.FirstOrDefault(f => f.FullName == "android/AM2R.ini") is null);
}
if (operatingSystem is "Linux" or "Mac")
{
Assert.True(archive.Entries.FirstOrDefault(f => f.FullName == "files_to_copy/icon.png") is not null);
Assert.True(archive.Entries.FirstOrDefault(f => f.FullName == "files_to_copy/splash.png") is not null);
if (operatingSystem == "Mac")
{
Assert.True(archive.Entries.FirstOrDefault(f => f.FullName == "Info.plist") is not null);
Assert.True(archive.Entries.FirstOrDefault(f => f.FullName == "files_to_copy/yoyorunner.config") is not null);
Assert.True(archive.Entries.FirstOrDefault(f => f.FullName == "files_to_copy/gamecontrollerdb.txt") is not null);
Assert.True(archive.Entries.FirstOrDefault(f => f.FullName == "files_to_copy/english.lproj/MainMenu.nib") is not null);
}
}
else
{
Assert.True(archive.Entries.FirstOrDefault(f => f.FullName == "files_to_copy/icon.png") is null);
Assert.True(archive.Entries.FirstOrDefault(f => f.FullName == "files_to_copy/splash.png") is null);
}
if (usesCustomMusic)
{
// TODO: check for custom music. do that after we provided a fake am2r_11.
}
else
{
// TODO: make sure that if we're providing lowercase songs, that they'll be also blacklisted.
}
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2022 milesthenerd
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Loading…
Cancel
Save