Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions QuickLook.Plugin/QuickLook.Plugin.AppViewer.Tests/CgbiPngTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Copyright © 2017-2026 QL-Win Contributors
//
// This file is part of QuickLook program.
//
// This program 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 <http://www.gnu.org/licenses/>.

using Microsoft.VisualStudio.TestTools.UnitTesting;
using QuickLook.Plugin.AppViewer.PackageParsers.Ipa;
using System.Drawing;
using System.IO;

namespace QuickLook.Plugin.AppViewer.Tests;

[TestClass]
public class CgbiPngTests
{
[TestMethod]
public void ConvertToStandardPng_RestoresRedAndBlueChannels()
{
byte[] cgbi = CgbiSample.Create(2, 2, 7, 139, 237, 255);

Assert.IsTrue(CgbiPng.IsCgbi(cgbi));

byte[] png = CgbiPng.ConvertToStandardPng(cgbi);

Assert.IsFalse(CgbiPng.IsCgbi(png));
using var stream = new MemoryStream(png);
using var bitmap = new Bitmap(stream);
Color color = bitmap.GetPixel(0, 0);
Assert.AreEqual(237, color.R);
Assert.AreEqual(139, color.G);
Assert.AreEqual(7, color.B);
Assert.AreEqual(255, color.A);
}

[TestMethod]
public void ConvertToStandardPng_LeavesRegularPngUntouched()
{
byte[] png = System.Convert.FromBase64String(
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==");

Assert.IsFalse(CgbiPng.IsCgbi(png));
Assert.AreSame(png, CgbiPng.ConvertToStandardPng(png));
}
}
113 changes: 113 additions & 0 deletions QuickLook.Plugin/QuickLook.Plugin.AppViewer.Tests/CgbiSample.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
// Copyright © 2017-2026 QL-Win Contributors
//
// This file is part of QuickLook program.
//
// This program 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 <http://www.gnu.org/licenses/>.

using ICSharpCode.SharpZipLib.Checksum;
using ICSharpCode.SharpZipLib.Zip.Compression;
using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
using System;
using System.IO;
using System.Text;

namespace QuickLook.Plugin.AppViewer.Tests;

internal static class CgbiSample
{
private static readonly byte[] Signature = [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A];

public static byte[] Create(int width, int height, byte blue, byte green, byte red, byte alpha)
{
int rowBytes = width * 4;
byte[] raw = new byte[(rowBytes + 1) * height];

for (int y = 0; y < height; y++)
{
int row = y * (rowBytes + 1) + 1;
for (int x = 0; x < width; x++)
{
int pixel = row + x * 4;
raw[pixel] = blue;
raw[pixel + 1] = green;
raw[pixel + 2] = red;
raw[pixel + 3] = alpha;
}
}

byte[] compressed;
using (var buffer = new MemoryStream())
{
using (DeflaterOutputStream deflater = new(buffer, new Deflater(Deflater.BEST_COMPRESSION, true))
{
IsStreamOwner = false,
})
{
deflater.Write(raw, 0, raw.Length);
}

compressed = buffer.ToArray();
}

using var output = new MemoryStream();
output.Write(Signature, 0, Signature.Length);

WriteChunk(output, "CgBI", []);

byte[] header = new byte[13];
WriteInt32BigEndian(header, 0, width);
WriteInt32BigEndian(header, 4, height);
header[8] = 8;
header[9] = 6;
WriteChunk(output, "IHDR", header);

WriteChunk(output, "IDAT", compressed);
WriteChunk(output, "IEND", []);

return output.ToArray();
}

public static void WriteChunk(Stream stream, string type, byte[] data)
{
byte[] typeBytes = Encoding.ASCII.GetBytes(type);
WriteInt32BigEndian(stream, data.Length);
stream.Write(typeBytes, 0, typeBytes.Length);
stream.Write(data, 0, data.Length);

Crc32 crc = new();
crc.Update(typeBytes);
if (data.Length > 0)
{
crc.Update(data);
}

WriteInt32BigEndian(stream, unchecked((int)crc.Value));
}

private static void WriteInt32BigEndian(byte[] buffer, int offset, int value)
{
buffer[offset] = (byte)(value >> 24);
buffer[offset + 1] = (byte)(value >> 16);
buffer[offset + 2] = (byte)(value >> 8);
buffer[offset + 3] = (byte)value;
}

private static void WriteInt32BigEndian(Stream stream, int value)
{
stream.WriteByte((byte)(value >> 24));
stream.WriteByte((byte)(value >> 16));
stream.WriteByte((byte)(value >> 8));
stream.WriteByte((byte)value);
}
}
106 changes: 106 additions & 0 deletions QuickLook.Plugin/QuickLook.Plugin.AppViewer.Tests/IpaReaderTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// Copyright © 2017-2026 QL-Win Contributors
//
// This file is part of QuickLook program.
//
// This program 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 <http://www.gnu.org/licenses/>.

using ICSharpCode.SharpZipLib.Zip;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using QuickLook.Plugin.AppViewer.PackageParsers.Ipa;
using System;
using System.Drawing;
using System.IO;
using System.Text;

namespace QuickLook.Plugin.AppViewer.Tests;

[TestClass]
public class IpaReaderTests
{
private const string InfoPlist = """
<?xml version="1.0" encoding="UTF-8"?>
<plist version="1.0">
<dict>
<!-- Display name -->
<key>CFBundleDisplayName</key>
<string>Sample</string>
<key>CFBundleShortVersionString</key>
<string>1.2.3</string>
<key>CFBundleVersion</key>
<string>123</string>
<key>CFBundleIdentifier</key>
<string>com.example.sample</string>
<key>MinimumOSVersion</key>
<string>6.1</string>
<key>UIDeviceFamily</key>
<array>
<integer>1</integer>
<integer>2</integer>
</array>
<!-- Primary icon names -->
<key>CFBundleIconFiles</key>
<array>
<string>AppIcon57x57</string>
<string>AppIcon60x60</string>
</array>
</dict>
</plist>
""";

[TestMethod]
public void Parse_ReadsInfoPlistWithCommentsAndConvertsCgbiIcon()
{
byte[] cgbi = CgbiSample.Create(2, 2, 7, 139, 237, 255);

using var stream = new MemoryStream();
using (var zip = new ZipOutputStream(stream)
{
IsStreamOwner = false,
})
{
AddEntry(zip, "Payload/Sample.app/Info.plist", Encoding.UTF8.GetBytes(InfoPlist));
AddEntry(zip, "Payload/Sample.app/AppIcon60x60@2x.png", cgbi);
}

stream.Position = 0;
IpaReader reader = new(stream);

Assert.AreEqual("Sample", reader.DisplayName);
Assert.AreEqual("1.2.3", reader.ShortVersionString);
Assert.AreEqual("123", reader.Version);
Assert.AreEqual("com.example.sample", reader.Identifier);
Assert.AreEqual("iPhone, iPad", reader.DeviceFamily);
Assert.IsNotNull(reader.Icon);

using var iconStream = new MemoryStream(reader.Icon);
using var bitmap = new Bitmap(iconStream);
Color color = bitmap.GetPixel(0, 0);
Assert.AreEqual(237, color.R);
Assert.AreEqual(139, color.G);
Assert.AreEqual(7, color.B);
}

private static void AddEntry(ZipOutputStream zip, string name, byte[] data)
{
ZipEntry entry = new(name)
{
Size = data.Length,
DateTime = new DateTime(2020, 1, 1),
};

zip.PutNextEntry(entry);
zip.Write(data, 0, data.Length);
zip.CloseEntry();
}
}
56 changes: 56 additions & 0 deletions QuickLook.Plugin/QuickLook.Plugin.AppViewer.Tests/PlistTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Copyright © 2017-2026 QL-Win Contributors
//
// This file is part of QuickLook program.
//
// This program 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 <http://www.gnu.org/licenses/>.

using Microsoft.VisualStudio.TestTools.UnitTesting;
using QuickLook.Plugin.AppViewer.PackageParsers.Ipa;
using System.Collections.Generic;
using System.Text;

namespace QuickLook.Plugin.AppViewer.Tests;

[TestClass]
public class PlistTests
{
[TestMethod]
public void ReadPlist_IgnoresXmlComments()
{
const string xml = """
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<!-- The app name -->
<key>CFBundleDisplayName</key>
<string>Test App</string>
<!-- Icon variants -->
<key>CFBundleIconFiles</key>
<array>
<!-- Legacy icon -->
<string>Icon-57.png</string>
<string>Icon-180.png</string>
</array>
</dict>
</plist>
""";

var result = (Dictionary<string, object>)Plist.ReadPlist(Encoding.UTF8.GetBytes(xml));

Assert.AreEqual("Test App", result["CFBundleDisplayName"]);
var icons = (List<object>)result["CFBundleIconFiles"];
CollectionAssert.AreEqual(new object[] { "Icon-57.png", "Icon-180.png" }, icons);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net462</TargetFramework>
<IsPackable>false</IsPackable>
<LangVersion>latest</LangVersion>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageReference Include="MSTest.TestAdapter" Version="3.6.4" />
<PackageReference Include="MSTest.TestFramework" Version="3.6.4" />
<PackageReference Include="SharpZipLib" Version="1.4.2" />
</ItemGroup>

<ItemGroup>
<Reference Include="System.Drawing" />
</ItemGroup>

<ItemGroup>
<Compile Include="..\QuickLook.Plugin.AppViewer\PackageParsers\Ipa\CgbiPng.cs" Link="CgbiPng.cs" />
<Compile Include="..\QuickLook.Plugin.AppViewer\PackageParsers\Ipa\IpaInfo.cs" Link="IpaInfo.cs" />
<Compile Include="..\QuickLook.Plugin.AppViewer\PackageParsers\Ipa\IpaParser.cs" Link="IpaParser.cs" />
<Compile Include="..\QuickLook.Plugin.AppViewer\PackageParsers\Ipa\IpaReader.cs" Link="IpaReader.cs" />
<Compile Include="..\QuickLook.Plugin.AppViewer\PackageParsers\Ipa\Plist.cs" Link="Plist.cs" />
</ItemGroup>

</Project>
Loading