Merge branch 'main' into develop
This commit is contained in:
Коммит
120b63c466
|
@ -306,6 +306,8 @@ namespace SkiaSharp
|
|||
{
|
||||
if (stream == null)
|
||||
throw new ArgumentNullException (nameof (stream));
|
||||
if (stream is SKFileStream filestream && !filestream.IsValid)
|
||||
throw new ArgumentException ("File stream was not valid.", nameof(stream));
|
||||
|
||||
fixed (SKCodecResult* r = &result) {
|
||||
var codec = GetObject (SkiaApi.sk_codec_new_from_stream (stream.Handle, r));
|
||||
|
|
|
@ -511,6 +511,9 @@ namespace SkiaSharp
|
|||
public SKData EncodedData =>
|
||||
SKData.GetObject (SkiaApi.sk_image_ref_encoded (Handle));
|
||||
|
||||
public SKImageInfo Info =>
|
||||
new SKImageInfo (Width, Height, ColorType, AlphaType, ColorSpace);
|
||||
|
||||
// ToShader
|
||||
|
||||
public SKShader ToShader () =>
|
||||
|
|
126
build.cake
126
build.cake
|
@ -137,6 +137,13 @@ Task ("libs")
|
|||
|
||||
Task ("tests")
|
||||
.Description ("Run all tests.")
|
||||
.IsDependentOn ("tests-netfx")
|
||||
.IsDependentOn ("tests-netcore")
|
||||
.IsDependentOn ("tests-android")
|
||||
.IsDependentOn ("tests-ios");
|
||||
|
||||
Task ("tests-netfx")
|
||||
.Description ("Run all Full .NET Framework tests.")
|
||||
.IsDependentOn ("externals")
|
||||
.Does (() =>
|
||||
{
|
||||
|
@ -166,7 +173,6 @@ Task ("tests")
|
|||
CleanDirectories ($"{PACKAGE_CACHE_PATH}/skiasharp*");
|
||||
CleanDirectories ($"{PACKAGE_CACHE_PATH}/harfbuzzsharp*");
|
||||
|
||||
// Full .NET Framework
|
||||
if (IsRunningOnWindows ()) {
|
||||
RunDesktopTest ("x86");
|
||||
RunDesktopTest ("x64");
|
||||
|
@ -176,7 +182,23 @@ Task ("tests")
|
|||
RunDesktopTest ("x64");
|
||||
}
|
||||
|
||||
// .NET Core
|
||||
if (failedTests > 0) {
|
||||
if (THROW_ON_TEST_FAILURE)
|
||||
throw new Exception ($"There were {failedTests} failed tests.");
|
||||
else
|
||||
Warning ($"There were {failedTests} failed tests.");
|
||||
}
|
||||
});
|
||||
|
||||
Task ("tests-netcore")
|
||||
.Description ("Run all .NET Core tests.")
|
||||
.IsDependentOn ("externals")
|
||||
.Does (() =>
|
||||
{
|
||||
var failedTests = 0;
|
||||
|
||||
CleanDirectories ($"{PACKAGE_CACHE_PATH}/skiasharp*");
|
||||
CleanDirectories ($"{PACKAGE_CACHE_PATH}/harfbuzzsharp*");
|
||||
|
||||
// SkiaSharp.NetCore.Tests.csproj
|
||||
RunMSBuild ("./tests/SkiaSharp.NetCore.Tests.sln");
|
||||
|
@ -195,25 +217,94 @@ Task ("tests")
|
|||
}
|
||||
}
|
||||
|
||||
if (failedTests > 0)
|
||||
if (failedTests > 0) {
|
||||
if (THROW_ON_TEST_FAILURE)
|
||||
throw new Exception ($"There were {failedTests} failed tests.");
|
||||
else
|
||||
Warning ($"There were {failedTests} failed tests.");
|
||||
|
||||
}
|
||||
if (COVERAGE) {
|
||||
try {
|
||||
RunProcess ("reportgenerator", new ProcessSettings {
|
||||
Arguments = "-reports:./tests/**/Coverage/**/*.xml -targetdir:./output/coverage -reporttypes:HtmlInline_AzurePipelines;Cobertura"
|
||||
});
|
||||
} catch (Exception ex) {
|
||||
Error ("Make sure to install the 'dotnet-reportgenerator-globaltool' .NET Core global tool.");
|
||||
Error (ex);
|
||||
throw;
|
||||
}
|
||||
var xml = "./output/coverage/Cobertura.xml";
|
||||
var root = FindRegexMatchGroupsInFile (xml, @"<source>(.*)<\/source>", 0)[1].Value;
|
||||
ReplaceTextInFiles (xml, root, "");
|
||||
RunCodeCoverage ("./tests/**/Coverage/**/*.xml", "./output/coverage");
|
||||
}
|
||||
});
|
||||
|
||||
Task ("tests-android")
|
||||
.Description ("Run all Android tests.")
|
||||
.IsDependentOn ("externals")
|
||||
.Does (() =>
|
||||
{
|
||||
var failedTests = 0;
|
||||
|
||||
CleanDirectories ($"{PACKAGE_CACHE_PATH}/skiasharp*");
|
||||
CleanDirectories ($"{PACKAGE_CACHE_PATH}/harfbuzzsharp*");
|
||||
|
||||
// SkiaSharp.Android.Tests.csproj
|
||||
try {
|
||||
// build the solution to copy all the files
|
||||
RunMSBuild ("./tests/SkiaSharp.Android.Tests.sln", configuration: "Debug");
|
||||
// package the app
|
||||
FilePath csproj = "./tests/SkiaSharp.Android.Tests/SkiaSharp.Android.Tests.csproj";
|
||||
RunMSBuild (csproj,
|
||||
targets: new [] { "SignAndroidPackage" },
|
||||
platform: "AnyCPU",
|
||||
configuration: "Debug");
|
||||
// run the tests
|
||||
DirectoryPath results = "./output/testlogs/SkiaSharp.Android.Tests";
|
||||
RunCake ("./cake/xharness-android.cake", "Default", new Dictionary<string, string> {
|
||||
{ "project", MakeAbsolute(csproj).FullPath },
|
||||
{ "configuration", "Debug" },
|
||||
{ "exclusive", "true" },
|
||||
{ "results", MakeAbsolute(results).FullPath },
|
||||
});
|
||||
} catch {
|
||||
failedTests++;
|
||||
}
|
||||
|
||||
if (failedTests > 0) {
|
||||
if (THROW_ON_TEST_FAILURE)
|
||||
throw new Exception ($"There were {failedTests} failed tests.");
|
||||
else
|
||||
Warning ($"There were {failedTests} failed tests.");
|
||||
}
|
||||
});
|
||||
|
||||
Task ("tests-ios")
|
||||
.Description ("Run all iOS tests.")
|
||||
.IsDependentOn ("externals")
|
||||
.Does (() =>
|
||||
{
|
||||
var failedTests = 0;
|
||||
|
||||
CleanDirectories ($"{PACKAGE_CACHE_PATH}/skiasharp*");
|
||||
CleanDirectories ($"{PACKAGE_CACHE_PATH}/harfbuzzsharp*");
|
||||
|
||||
// SkiaSharp.iOS.Tests.csproj
|
||||
try {
|
||||
// build the solution to copy all the files
|
||||
RunMSBuild ("./tests/SkiaSharp.iOS.Tests.sln", configuration: "Debug");
|
||||
// package the app
|
||||
FilePath csproj = "./tests/SkiaSharp.iOS.Tests/SkiaSharp.iOS.Tests.csproj";
|
||||
RunMSBuild (csproj,
|
||||
properties: new Dictionary<string, string> { { "BuildIpa", "true" } },
|
||||
platform: "iPhoneSimulator",
|
||||
configuration: "Debug");
|
||||
// run the tests
|
||||
DirectoryPath results = "./output/testlogs/SkiaSharp.iOS.Tests";
|
||||
RunCake ("./cake/xharness-ios.cake", "Default", new Dictionary<string, string> {
|
||||
{ "project", MakeAbsolute(csproj).FullPath },
|
||||
{ "configuration", "Debug" },
|
||||
{ "exclusive", "true" },
|
||||
{ "results", MakeAbsolute(results).FullPath },
|
||||
});
|
||||
} catch {
|
||||
failedTests++;
|
||||
}
|
||||
|
||||
if (failedTests > 0) {
|
||||
if (THROW_ON_TEST_FAILURE)
|
||||
throw new Exception ($"There were {failedTests} failed tests.");
|
||||
else
|
||||
Warning ($"There were {failedTests} failed tests.");
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -241,11 +332,12 @@ Task ("tests-wasm")
|
|||
serverProc?.Kill();
|
||||
}
|
||||
|
||||
if (failedTests > 0)
|
||||
if (failedTests > 0) {
|
||||
if (THROW_ON_TEST_FAILURE)
|
||||
throw new Exception ($"There were {failedTests} failed tests.");
|
||||
else
|
||||
Warning ($"There were {failedTests} failed tests.");
|
||||
}
|
||||
});
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -101,6 +101,26 @@ void RunNetCorePublish(FilePath testProject, DirectoryPath output)
|
|||
DotNetCorePublish(testProject.GetFilename().ToString(), settings);
|
||||
}
|
||||
|
||||
void RunCodeCoverage(string testResultsGlob, DirectoryPath output)
|
||||
{
|
||||
try {
|
||||
RunProcess ("reportgenerator", new ProcessSettings {
|
||||
Arguments =
|
||||
$"-reports:{testResultsGlob} " +
|
||||
$"-targetdir:{output} " +
|
||||
$"-reporttypes:HtmlInline_AzurePipelines;Cobertura " +
|
||||
$"-assemblyfilters:-*.Tests"
|
||||
});
|
||||
} catch (Exception ex) {
|
||||
Error ("Make sure to install the 'dotnet-reportgenerator-globaltool' .NET Core global tool.");
|
||||
Error (ex);
|
||||
throw;
|
||||
}
|
||||
var xml = $"{output}/Cobertura.xml";
|
||||
var root = FindRegexMatchGroupsInFile (xml, @"<source>(.*)<\/source>", 0)[1].Value;
|
||||
ReplaceTextInFiles (xml, root, "");
|
||||
}
|
||||
|
||||
IEnumerable<(string Name, string Value)> CreateTraitsDictionary(string args)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(args)) {
|
||||
|
|
|
@ -7,15 +7,21 @@ void RunMSBuild(
|
|||
string platform = "Any CPU",
|
||||
string platformTarget = null,
|
||||
bool restore = true,
|
||||
bool restoreOnly = false,
|
||||
bool bl = true)
|
||||
bool bl = true,
|
||||
string[] targets = null,
|
||||
string configuration = null,
|
||||
Dictionary<string, string> properties = null)
|
||||
{
|
||||
var nugetSources = new [] { OUTPUT_NUGETS_PATH.FullPath, "https://api.nuget.org/v3/index.json" };
|
||||
var nugetSources = new [] {
|
||||
OUTPUT_NUGETS_PATH.FullPath,
|
||||
"https://api.nuget.org/v3/index.json",
|
||||
"https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-eng/nuget/v3/index.json"
|
||||
};
|
||||
|
||||
EnsureDirectoryExists(OUTPUT_NUGETS_PATH);
|
||||
|
||||
MSBuild(solution, c => {
|
||||
c.Configuration = CONFIGURATION;
|
||||
c.Configuration = configuration ?? CONFIGURATION;
|
||||
c.Verbosity = VERBOSITY;
|
||||
c.MaxCpuCount = 0;
|
||||
|
||||
|
@ -33,12 +39,13 @@ void RunMSBuild(
|
|||
}
|
||||
|
||||
c.NoLogo = VERBOSITY == Verbosity.Minimal;
|
||||
c.Restore = restore;
|
||||
|
||||
if (restoreOnly) {
|
||||
if (targets?.Length > 0) {
|
||||
c.Targets.Clear();
|
||||
c.Targets.Add("Restore");
|
||||
} else {
|
||||
c.Restore = restore;
|
||||
foreach (var target in targets) {
|
||||
c.Targets.Add(target);
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(platformTarget)) {
|
||||
|
@ -55,6 +62,12 @@ void RunMSBuild(
|
|||
|
||||
c.Properties ["RestoreNoCache"] = new [] { "true" };
|
||||
c.Properties ["RestorePackagesPath"] = new [] { PACKAGE_CACHE_PATH.FullPath };
|
||||
|
||||
if (properties != null) {
|
||||
foreach (var prop in properties) {
|
||||
c.Properties [prop.Key] = new [] { prop.Value };
|
||||
}
|
||||
}
|
||||
// c.Properties ["RestoreSources"] = nugetSources;
|
||||
var sep = IsRunningOnWindows() ? ";" : "%3B";
|
||||
c.ArgumentCustomization = args => args.Append($"/p:RestoreSources=\"{string.Join(sep, nugetSources)}\"");
|
||||
|
|
|
@ -0,0 +1,182 @@
|
|||
#addin nuget:?package=Cake.Android.Adb&version=3.2.0
|
||||
#addin nuget:?package=Cake.Android.AvdManager&version=2.2.0
|
||||
|
||||
DirectoryPath ROOT_PATH = MakeAbsolute(Directory(".."));
|
||||
|
||||
#load "shared.cake"
|
||||
|
||||
// required
|
||||
FilePath PROJECT = Argument("project", EnvironmentVariable("ANDROID_TEST_PROJECT") ?? "");
|
||||
string TEST_DEVICE = Argument("device", EnvironmentVariable("ANDROID_TEST_DEVICE") ?? "android-emulator-32_30");
|
||||
string DEVICE_NAME = Argument("skin", EnvironmentVariable("ANDROID_TEST_SKIN") ?? "Nexus 5X");
|
||||
|
||||
// optional
|
||||
var TEST_APP = Argument("app", EnvironmentVariable("ANDROID_TEST_APP") ?? "");
|
||||
var TEST_APP_PACKAGE_NAME = Argument("package", EnvironmentVariable("ANDROID_TEST_APP_PACKAGE_NAME") ?? "");
|
||||
var TEST_APP_INSTRUMENTATION = Argument("instrumentation", EnvironmentVariable("ANDROID_TEST_APP_INSTRUMENTATION") ?? "");
|
||||
var TEST_RESULTS = Argument("results", EnvironmentVariable("ANDROID_TEST_RESULTS") ?? "");
|
||||
|
||||
// other
|
||||
string ANDROID_AVD = "DEVICE_TESTS_EMULATOR";
|
||||
string DEVICE_ID = "";
|
||||
string DEVICE_ARCH = "";
|
||||
|
||||
// set up env
|
||||
var ANDROID_SDK_ROOT = Argument("android", EnvironmentVariable("ANDROID_SDK_ROOT") ?? EnvironmentVariable("ANDROID_SDK_ROOT"));
|
||||
if (string.IsNullOrEmpty(ANDROID_SDK_ROOT)) {
|
||||
throw new Exception("Environment variable 'ANDROID_SDK_ROOT' must be set to the Android SDK root.");
|
||||
}
|
||||
System.Environment.SetEnvironmentVariable("PATH",
|
||||
$"{ANDROID_SDK_ROOT}/tools/bin" + System.IO.Path.PathSeparator +
|
||||
$"{ANDROID_SDK_ROOT}/platform-tools" + System.IO.Path.PathSeparator +
|
||||
$"{ANDROID_SDK_ROOT}/emulator" + System.IO.Path.PathSeparator +
|
||||
EnvironmentVariable("PATH"));
|
||||
|
||||
Information("Android SDK Root: {0}", ANDROID_SDK_ROOT);
|
||||
Information("Project File: {0}", PROJECT);
|
||||
Information("Build Configuration: {0}", CONFIGURATION);
|
||||
|
||||
var avdSettings = new AndroidAvdManagerToolSettings { SdkRoot = ANDROID_SDK_ROOT };
|
||||
var adbSettings = new AdbToolSettings { SdkRoot = ANDROID_SDK_ROOT };
|
||||
var emuSettings = new AndroidEmulatorToolSettings { SdkRoot = ANDROID_SDK_ROOT, ArgumentCustomization = args => args.Append("-no-window") };
|
||||
|
||||
AndroidEmulatorProcess emulatorProcess = null;
|
||||
|
||||
Setup(context =>
|
||||
{
|
||||
Information("Test Device: {0}", TEST_DEVICE);
|
||||
|
||||
// determine the device characteristics
|
||||
{
|
||||
var working = TEST_DEVICE.Trim().ToLower();
|
||||
var emulator = true;
|
||||
var api = 30;
|
||||
// version
|
||||
if (working.IndexOf("_") is int idx && idx > 0) {
|
||||
api = int.Parse(working.Substring(idx + 1));
|
||||
working = working.Substring(0, idx);
|
||||
}
|
||||
var parts = working.Split('-');
|
||||
// os
|
||||
if (parts[0] != "android")
|
||||
throw new Exception("Unexpected platform (expected: android) in device: " + TEST_DEVICE);
|
||||
// device/emulator
|
||||
if (parts[1] == "device")
|
||||
emulator = false;
|
||||
else if (parts[1] != "emulator" && parts[1] != "simulator")
|
||||
throw new Exception("Unexpected device type (expected: device|emulator) in device: " + TEST_DEVICE);
|
||||
// arch/bits
|
||||
if (parts[2] == "32") {
|
||||
if (emulator)
|
||||
DEVICE_ARCH = "x86";
|
||||
else
|
||||
DEVICE_ARCH = "armeabi-v7a";
|
||||
} else if (parts[2] == "64") {
|
||||
if (emulator)
|
||||
DEVICE_ARCH = "x86_64";
|
||||
else
|
||||
DEVICE_ARCH = "arm64-v8a";
|
||||
}
|
||||
DEVICE_ID = $"system-images;android-{api};google_apis_playstore;{DEVICE_ARCH}";
|
||||
|
||||
// we are not using a virtual device, so quit
|
||||
if (!emulator)
|
||||
return;
|
||||
}
|
||||
|
||||
Information("Test Device ID: {0}", DEVICE_ID);
|
||||
|
||||
// delete the AVD first, if it exists
|
||||
Information("Deleting AVD if exists: {0}...", ANDROID_AVD);
|
||||
try { AndroidAvdDelete(ANDROID_AVD, avdSettings); }
|
||||
catch { }
|
||||
|
||||
// create the new AVD
|
||||
Information("Creating AVD: {0}...", ANDROID_AVD);
|
||||
AndroidAvdCreate(ANDROID_AVD, DEVICE_ID, DEVICE_NAME, force: true, settings: avdSettings);
|
||||
|
||||
// start the emulator
|
||||
Information("Starting Emulator: {0}...", ANDROID_AVD);
|
||||
emulatorProcess = AndroidEmulatorStart(ANDROID_AVD, emuSettings);
|
||||
|
||||
// wait for it to finish booting (10 mins)
|
||||
var waited = 0;
|
||||
var total = 60 * 10;
|
||||
while (AdbShell("getprop sys.boot_completed", adbSettings).FirstOrDefault() != "1") {
|
||||
System.Threading.Thread.Sleep(1000);
|
||||
Information("Wating {0}/{1} seconds for the emulator to boot up.", waited, total);
|
||||
if (waited++ > total)
|
||||
break;
|
||||
}
|
||||
Information("Waited {0} seconds for the emulator to boot up.", waited);
|
||||
});
|
||||
|
||||
Teardown(context =>
|
||||
{
|
||||
// no virtual device was used
|
||||
if (emulatorProcess == null)
|
||||
return;
|
||||
|
||||
// stop and cleanup the emulator
|
||||
AdbEmuKill(adbSettings);
|
||||
|
||||
System.Threading.Thread.Sleep(5000);
|
||||
|
||||
// kill the process if it has not already exited
|
||||
try { emulatorProcess.Kill(); }
|
||||
catch { }
|
||||
|
||||
// delete the AVD
|
||||
try { AndroidAvdDelete(ANDROID_AVD, avdSettings); }
|
||||
catch { }
|
||||
});
|
||||
|
||||
Task("Default")
|
||||
.Does(() =>
|
||||
{
|
||||
if (string.IsNullOrEmpty(TEST_APP)) {
|
||||
if (string.IsNullOrEmpty(PROJECT.FullPath))
|
||||
throw new Exception("If no app was specified, an app must be provided.");
|
||||
var binDir = PROJECT.GetDirectory().Combine("bin").Combine(CONFIGURATION).FullPath;
|
||||
var apps = GetFiles(binDir + "/*-Signed.apk");
|
||||
if (apps.Any()) {
|
||||
TEST_APP = apps.FirstOrDefault().FullPath;
|
||||
} else {
|
||||
apps = GetFiles(binDir + "/*.apk");
|
||||
TEST_APP = apps.First().FullPath;
|
||||
}
|
||||
}
|
||||
if (string.IsNullOrEmpty(TEST_APP_PACKAGE_NAME)) {
|
||||
var appFile = (FilePath)TEST_APP;
|
||||
appFile = appFile.GetFilenameWithoutExtension();
|
||||
TEST_APP_PACKAGE_NAME = appFile.FullPath.Replace("-Signed", "");
|
||||
}
|
||||
if (string.IsNullOrEmpty(TEST_APP_INSTRUMENTATION)) {
|
||||
TEST_APP_INSTRUMENTATION = TEST_APP_PACKAGE_NAME + ".TestInstrumentation";
|
||||
}
|
||||
if (string.IsNullOrEmpty(TEST_RESULTS)) {
|
||||
TEST_RESULTS = TEST_APP + "-results";
|
||||
}
|
||||
|
||||
Information("Test App: {0}", TEST_APP);
|
||||
Information("Test App Package Name: {0}", TEST_APP_PACKAGE_NAME);
|
||||
Information("Test App Instrumentation: {0}", TEST_APP_INSTRUMENTATION);
|
||||
Information("Test Results Directory: {0}", TEST_RESULTS);
|
||||
|
||||
CleanDirectories(TEST_RESULTS);
|
||||
|
||||
RunProcess("xharness", "android test " +
|
||||
$"--app=\"{TEST_APP}\" " +
|
||||
$"--package-name=\"{TEST_APP_PACKAGE_NAME}\" " +
|
||||
$"--instrumentation=\"{TEST_APP_INSTRUMENTATION}\" " +
|
||||
$"--device-arch=\"{DEVICE_ARCH}\" " +
|
||||
$"--output-directory=\"{TEST_RESULTS}\" " +
|
||||
$"--verbosity=\"Debug\" ");
|
||||
|
||||
var failed = XmlPeek($"{TEST_RESULTS}/TestResults.xml", "/assemblies/assembly[@failed > 0 or @errors > 0]/@failed");
|
||||
if (!string.IsNullOrEmpty(failed)) {
|
||||
throw new Exception($"At least {failed} test(s) failed.");
|
||||
}
|
||||
});
|
||||
|
||||
RunTarget(TARGET);
|
|
@ -0,0 +1,60 @@
|
|||
DirectoryPath ROOT_PATH = MakeAbsolute(Directory(".."));
|
||||
|
||||
#load "shared.cake"
|
||||
|
||||
// required
|
||||
FilePath PROJECT = Argument("project", EnvironmentVariable("IOS_TEST_PROJECT") ?? "");
|
||||
string TEST_DEVICE = Argument("device", EnvironmentVariable("IOS_TEST_DEVICE") ?? "ios-simulator-64_14.4"); // comma separated in the form <platform>-<device|simulator>[-<32|64>][_<version>] (eg: ios-simulator-64_13.4,[...])
|
||||
|
||||
// optional
|
||||
var TEST_APP = Argument("app", EnvironmentVariable("IOS_TEST_APP") ?? "");
|
||||
var TEST_RESULTS = Argument("results", EnvironmentVariable("IOS_TEST_RESULTS") ?? "");
|
||||
|
||||
// other
|
||||
string PLATFORM = TEST_DEVICE.ToLower().Contains("simulator") ? "iPhoneSimulator" : "iPhone";
|
||||
|
||||
Information("Project File: {0}", PROJECT);
|
||||
|
||||
Task("Default")
|
||||
.Does(() =>
|
||||
{
|
||||
if (string.IsNullOrEmpty(TEST_APP)) {
|
||||
if (string.IsNullOrEmpty(PROJECT.FullPath))
|
||||
throw new Exception("If no app was specified, an app must be provided.");
|
||||
var binDir = PROJECT.GetDirectory().Combine("bin").Combine(PLATFORM).Combine(CONFIGURATION).FullPath;
|
||||
var apps = GetDirectories(binDir + "/*.app");
|
||||
TEST_APP = apps.First().FullPath;
|
||||
}
|
||||
if (string.IsNullOrEmpty(TEST_RESULTS)) {
|
||||
TEST_RESULTS = TEST_APP + "-results";
|
||||
}
|
||||
|
||||
Information("Test App: {0}", TEST_APP);
|
||||
Information("Test Device: {0}", TEST_DEVICE);
|
||||
Information("Test App: {0}", TEST_APP);
|
||||
Information("Test Results Directory: {0}", TEST_RESULTS);
|
||||
|
||||
CleanDirectories(TEST_RESULTS);
|
||||
|
||||
try {
|
||||
RunProcess("xharness", "apple test " +
|
||||
$"--app=\"{TEST_APP}\" " +
|
||||
$"--targets=\"{TEST_DEVICE}\" " +
|
||||
$"--output-directory=\"{TEST_RESULTS}\" " +
|
||||
$"--verbosity=\"Debug\" ");
|
||||
} finally {
|
||||
// ios test result files are weirdly named, so fix it up
|
||||
var resultsFile = GetFiles($"{TEST_RESULTS}/xunit-test-*.xml").FirstOrDefault();
|
||||
if (FileExists(resultsFile)) {
|
||||
CopyFile(resultsFile, resultsFile.GetDirectory().CombineWithFilePath("TestResults.xml"));
|
||||
}
|
||||
}
|
||||
|
||||
// this _may_ not be needed, but just in case
|
||||
var failed = XmlPeek($"{TEST_RESULTS}/TestResults.xml", "/assemblies/assembly[@failed > 0 or @errors > 0]/@failed");
|
||||
if (!string.IsNullOrEmpty(failed)) {
|
||||
throw new Exception($"At least {failed} test(s) failed.");
|
||||
}
|
||||
});
|
||||
|
||||
RunTarget(TARGET);
|
|
@ -8,7 +8,7 @@
|
|||
<OutputType>WinExe</OutputType>
|
||||
<RootNamespace>SkiaSharpSample</RootNamespace>
|
||||
<AssemblyName>SkiaSharpSample</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.7</TargetFrameworkVersion>
|
||||
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
<TargetFrameworkProfile />
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{17521786-7B50-46C5-BBCE-999FDEBB45A3}</ProjectGuid>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFrameworkVersion>v4.7</TargetFrameworkVersion>
|
||||
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
|
||||
<RootNamespace>SkiaSharpSample</RootNamespace>
|
||||
<TargetFrameworkProfile />
|
||||
<LangVersion>8.0</LangVersion>
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
<OutputType>WinExe</OutputType>
|
||||
<RootNamespace>SkiaSharpSample</RootNamespace>
|
||||
<AssemblyName>SkiaSharpSample</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.7</TargetFrameworkVersion>
|
||||
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
<OutputType>WinExe</OutputType>
|
||||
<RootNamespace>SkiaSharpSample.GTK</RootNamespace>
|
||||
<AssemblyName>SkiaSharpSample.GTK</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.7</TargetFrameworkVersion>
|
||||
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
|
||||
<RootNamespace>SkiaSharpSample</RootNamespace>
|
||||
<TargetFrameworkProfile />
|
||||
<LangVersion>8.0</LangVersion>
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<OutputType>WinExe</OutputType>
|
||||
<RootNamespace>SkiaSharpSample.WPF</RootNamespace>
|
||||
<AssemblyName>SkiaSharpSample.WPF</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.7</TargetFrameworkVersion>
|
||||
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>SkiaSharpSample</RootNamespace>
|
||||
<AssemblyName>SkiaSharpSample</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.7</TargetFrameworkVersion>
|
||||
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<ApplicationManifest>app.manifest</ApplicationManifest>
|
||||
<TargetFrameworkProfile />
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>SkiaSharpSample</RootNamespace>
|
||||
<AssemblyName>SkiaSharpSample</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.7</TargetFrameworkVersion>
|
||||
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
<OutputType>WinExe</OutputType>
|
||||
<RootNamespace>SkiaSharpSample.Platform</RootNamespace>
|
||||
<AssemblyName>SkiaSharpSample.Platform</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.7</TargetFrameworkVersion>
|
||||
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
|
|
|
@ -22,7 +22,7 @@ variables:
|
|||
MANAGED_LINUX_PACKAGES: ttf-ancient-fonts ninja-build
|
||||
MONO_VERSION_MACOS: 'Latest'
|
||||
MONO_VERSION_LINUX: ''
|
||||
XCODE_VERSION: 12.3
|
||||
XCODE_VERSION: 12.4
|
||||
DOTNET_VERSION: 3.1.405
|
||||
CONFIGURATION: 'Release'
|
||||
VM_IMAGE_WINDOWS: windows-2019
|
||||
|
@ -457,12 +457,32 @@ stages:
|
|||
- native_linux
|
||||
- native_wasm
|
||||
jobs:
|
||||
- template: azure-templates-bootstrapper.yml # Tests (Windows)
|
||||
- template: azure-templates-bootstrapper.yml # Tests|netfx (Windows)
|
||||
parameters:
|
||||
name: tests_windows
|
||||
displayName: Windows
|
||||
name: tests_netfx_windows
|
||||
displayName: Windows (.NET Framework)
|
||||
vmImage: $(VM_IMAGE_WINDOWS)
|
||||
target: tests
|
||||
target: tests-netfx
|
||||
additionalArgs: --skipExternals="all" --throwOnTestFailure=$(THROW_ON_TEST_FAILURE) --coverage=$(ENABLE_CODE_COVERAGE)
|
||||
installWindowsSdk: false
|
||||
shouldPublish: false
|
||||
requiredArtifacts:
|
||||
- native_win32_x86_windows
|
||||
- native_win32_x64_windows
|
||||
postBuildSteps:
|
||||
- task: PublishTestResults@2
|
||||
displayName: Publish the .NET Framework test results
|
||||
condition: always()
|
||||
inputs:
|
||||
testResultsFormat: xUnit
|
||||
testResultsFiles: 'tests/SkiaSharp*.Desktop.Tests/**/TestResults.xml'
|
||||
testRunTitle: 'Windows .NET Framework Tests'
|
||||
- template: azure-templates-bootstrapper.yml # Tests|netcore (Windows)
|
||||
parameters:
|
||||
name: tests_netcore_windows
|
||||
displayName: Windows (.NET Core)
|
||||
vmImage: $(VM_IMAGE_WINDOWS)
|
||||
target: tests-netcore
|
||||
additionalArgs: --skipExternals="all" --throwOnTestFailure=$(THROW_ON_TEST_FAILURE) --coverage=$(ENABLE_CODE_COVERAGE)
|
||||
installWindowsSdk: false
|
||||
shouldPublish: false
|
||||
|
@ -472,13 +492,6 @@ stages:
|
|||
tools:
|
||||
- dotnet-reportgenerator-globaltool
|
||||
postBuildSteps:
|
||||
- task: PublishTestResults@2
|
||||
displayName: Publish the .NET Framework test results
|
||||
condition: always()
|
||||
inputs:
|
||||
testResultsFormat: xUnit
|
||||
testResultsFiles: 'tests/SkiaSharp*.Desktop.Tests/**/TestResults.xml'
|
||||
testRunTitle: 'Windows .NET Framework Tests'
|
||||
- task: PublishTestResults@2
|
||||
displayName: Publish the .NET Core test results
|
||||
condition: always()
|
||||
|
@ -489,20 +502,18 @@ stages:
|
|||
- task: PublishBuildArtifacts@1
|
||||
displayName: 'Publish the code coverage results'
|
||||
inputs:
|
||||
artifactName: coverage_windows
|
||||
artifactName: coverage_netcore_windows
|
||||
pathToPublish: 'output/coverage'
|
||||
- template: azure-templates-bootstrapper.yml # Tests (macOS)
|
||||
- template: azure-templates-bootstrapper.yml # Tests|netfx (macOS)
|
||||
parameters:
|
||||
name: tests_macos
|
||||
displayName: macOS
|
||||
name: tests_netfx_macos
|
||||
displayName: macOS (.NET Framework)
|
||||
vmImage: $(VM_IMAGE_MAC)
|
||||
target: tests
|
||||
target: tests-netfx
|
||||
additionalArgs: --skipExternals="all" --throwOnTestFailure=$(THROW_ON_TEST_FAILURE) --coverage=$(ENABLE_CODE_COVERAGE)
|
||||
shouldPublish: false
|
||||
requiredArtifacts:
|
||||
- native_macos_macos
|
||||
tools:
|
||||
- dotnet-reportgenerator-globaltool
|
||||
postBuildSteps:
|
||||
- task: PublishTestResults@2
|
||||
displayName: Publish the Mono test results
|
||||
|
@ -511,6 +522,19 @@ stages:
|
|||
testResultsFormat: xUnit
|
||||
testResultsFiles: 'tests/SkiaSharp*.Desktop.Tests/**/TestResults.xml'
|
||||
testRunTitle: 'macOS Mono Tests'
|
||||
- template: azure-templates-bootstrapper.yml # Tests|netcore (macOS)
|
||||
parameters:
|
||||
name: tests_netcore_macos
|
||||
displayName: macOS (.NET Core)
|
||||
vmImage: $(VM_IMAGE_MAC)
|
||||
target: tests-netcore
|
||||
additionalArgs: --skipExternals="all" --throwOnTestFailure=$(THROW_ON_TEST_FAILURE) --coverage=$(ENABLE_CODE_COVERAGE)
|
||||
shouldPublish: false
|
||||
requiredArtifacts:
|
||||
- native_macos_macos
|
||||
tools:
|
||||
- dotnet-reportgenerator-globaltool
|
||||
postBuildSteps:
|
||||
- task: PublishTestResults@2
|
||||
displayName: Publish the .NET Core test results
|
||||
condition: always()
|
||||
|
@ -521,21 +545,87 @@ stages:
|
|||
- task: PublishBuildArtifacts@1
|
||||
displayName: 'Publish the code coverage results'
|
||||
inputs:
|
||||
artifactName: coverage_macos
|
||||
artifactName: coverage_netcore_macos
|
||||
pathToPublish: 'output/coverage'
|
||||
- template: azure-templates-bootstrapper.yml # Tests (Linux)
|
||||
- template: azure-templates-bootstrapper.yml # Tests|android (macOS)
|
||||
parameters:
|
||||
name: tests_linux
|
||||
displayName: Linux
|
||||
name: tests_android_macos
|
||||
displayName: Android (macOS)
|
||||
vmImage: $(VM_IMAGE_MAC)
|
||||
target: tests-android
|
||||
additionalArgs: --device=android-emulator-32_30 --skipExternals="all" --throwOnTestFailure=$(THROW_ON_TEST_FAILURE) --coverage=$(ENABLE_CODE_COVERAGE)
|
||||
shouldPublish: false
|
||||
requiredArtifacts:
|
||||
- native_android_x86_macos
|
||||
- native_android_x64_macos
|
||||
- native_android_arm_macos
|
||||
- native_android_arm64_macos
|
||||
preBuildSteps:
|
||||
- pwsh: |
|
||||
dotnet tool install Microsoft.DotNet.XHarness.CLI `
|
||||
--global `
|
||||
--add-source https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-eng/nuget/v3/index.json `
|
||||
--version "1.0.0-prerelease*"
|
||||
displayName: Install the xharness .NET Core tool
|
||||
- bash: sh -c "echo \"y\" | $ANDROID_HOME/tools/bin/sdkmanager \"system-images;android-30;google_apis_playstore;x86\""
|
||||
displayName: Install the Android emulator
|
||||
postBuildSteps:
|
||||
- task: PublishTestResults@2
|
||||
displayName: Publish the Android test results
|
||||
condition: always()
|
||||
inputs:
|
||||
testResultsFormat: xUnit
|
||||
testResultsFiles: 'output/testlogs/SkiaSharp.Android.Tests/**/TestResults.xml'
|
||||
testRunTitle: 'Android Tests'
|
||||
- task: PublishBuildArtifacts@1
|
||||
displayName: Publish the test logs
|
||||
condition: always()
|
||||
inputs:
|
||||
artifactName: testlogs_android
|
||||
pathToPublish: 'output/testlogs'
|
||||
- template: azure-templates-bootstrapper.yml # Tests|ios (macOS)
|
||||
parameters:
|
||||
name: tests_ios_macos
|
||||
displayName: iOS (macOS)
|
||||
vmImage: $(VM_IMAGE_MAC)
|
||||
target: tests-ios
|
||||
additionalArgs: --device=ios-simulator-64 --skipExternals="all" --throwOnTestFailure=$(THROW_ON_TEST_FAILURE) --coverage=$(ENABLE_CODE_COVERAGE)
|
||||
shouldPublish: false
|
||||
requiredArtifacts:
|
||||
- native_ios_macos
|
||||
preBuildSteps:
|
||||
- template: azure-templates-provisioning-profiles.yml
|
||||
- pwsh: |
|
||||
dotnet tool install Microsoft.DotNet.XHarness.CLI `
|
||||
--global `
|
||||
--add-source https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-eng/nuget/v3/index.json `
|
||||
--version "1.0.0-prerelease*"
|
||||
displayName: Install the xharness .NET Core tool
|
||||
postBuildSteps:
|
||||
- task: PublishTestResults@2
|
||||
displayName: Publish the iOS test results
|
||||
condition: always()
|
||||
inputs:
|
||||
testResultsFormat: xUnit
|
||||
testResultsFiles: 'output/testlogs/SkiaSharp.iOS.Tests/**/TestResults.xml'
|
||||
testRunTitle: 'iOS Tests'
|
||||
- task: PublishBuildArtifacts@1
|
||||
displayName: Publish the test logs
|
||||
condition: always()
|
||||
inputs:
|
||||
artifactName: testlogs_ios
|
||||
pathToPublish: 'output/testlogs'
|
||||
- template: azure-templates-bootstrapper.yml # Tests|netfx (Linux)
|
||||
parameters:
|
||||
name: tests_netfx_linux
|
||||
displayName: Linux (.NET Framework)
|
||||
vmImage: $(VM_IMAGE_LINUX)
|
||||
packages: $(MANAGED_LINUX_PACKAGES)
|
||||
target: tests
|
||||
target: tests-netfx
|
||||
additionalArgs: --skipExternals="all" --throwOnTestFailure=$(THROW_ON_TEST_FAILURE) --coverage=$(ENABLE_CODE_COVERAGE)
|
||||
shouldPublish: false
|
||||
requiredArtifacts:
|
||||
- native_linux_x64_linux
|
||||
tools:
|
||||
- dotnet-reportgenerator-globaltool
|
||||
postBuildSteps:
|
||||
- task: PublishTestResults@2
|
||||
displayName: Publish the Mono test results
|
||||
|
@ -544,6 +634,20 @@ stages:
|
|||
testResultsFormat: xUnit
|
||||
testResultsFiles: 'tests/SkiaSharp*.Desktop.Tests/**/TestResults.xml'
|
||||
testRunTitle: 'Linux Mono Tests'
|
||||
- template: azure-templates-bootstrapper.yml # Tests|netcore (Linux)
|
||||
parameters:
|
||||
name: tests_netcore_linux
|
||||
displayName: Linux (.NET Core)
|
||||
vmImage: $(VM_IMAGE_LINUX)
|
||||
packages: $(MANAGED_LINUX_PACKAGES)
|
||||
target: tests-netcore
|
||||
additionalArgs: --skipExternals="all" --throwOnTestFailure=$(THROW_ON_TEST_FAILURE) --coverage=$(ENABLE_CODE_COVERAGE)
|
||||
shouldPublish: false
|
||||
requiredArtifacts:
|
||||
- native_linux_x64_linux
|
||||
tools:
|
||||
- dotnet-reportgenerator-globaltool
|
||||
postBuildSteps:
|
||||
- task: PublishTestResults@2
|
||||
displayName: Publish the .NET Core test results
|
||||
condition: always()
|
||||
|
@ -554,7 +658,7 @@ stages:
|
|||
- task: PublishBuildArtifacts@1
|
||||
displayName: 'Publish the code coverage results'
|
||||
inputs:
|
||||
artifactName: coverage_linux
|
||||
artifactName: coverage_netcore_linux
|
||||
pathToPublish: 'output/coverage'
|
||||
- template: azure-templates-bootstrapper.yml # Tests [WASM] (Linux)
|
||||
parameters:
|
||||
|
@ -585,26 +689,26 @@ stages:
|
|||
pool:
|
||||
vmImage: $(VM_IMAGE_LINUX)
|
||||
dependsOn:
|
||||
- tests_windows
|
||||
- tests_macos
|
||||
- tests_linux
|
||||
- tests_netcore_windows
|
||||
- tests_netcore_macos
|
||||
- tests_netcore_linux
|
||||
steps:
|
||||
- checkout: self
|
||||
- template: azure-templates-variables.yml
|
||||
- task: DownloadBuildArtifacts@0
|
||||
displayName: Download the coverage_windows artifact
|
||||
displayName: Download the coverage_netcore_windows artifact
|
||||
inputs:
|
||||
artifactName: coverage_windows
|
||||
artifactName: coverage_netcore_windows
|
||||
downloadPath: output
|
||||
- task: DownloadBuildArtifacts@0
|
||||
displayName: Download the coverage_macos artifact
|
||||
displayName: Download the coverage_netcore_macos artifact
|
||||
inputs:
|
||||
artifactName: coverage_macos
|
||||
artifactName: coverage_netcore_macos
|
||||
downloadPath: output
|
||||
- task: DownloadBuildArtifacts@0
|
||||
displayName: Download the coverage_linux artifact
|
||||
displayName: Download the coverage_netcore_linux artifact
|
||||
inputs:
|
||||
artifactName: coverage_linux
|
||||
artifactName: coverage_netcore_linux
|
||||
downloadPath: output
|
||||
- task: PublishCodeCoverageResults@1
|
||||
displayName: 'Publish the code coverage results'
|
||||
|
@ -639,21 +743,7 @@ stages:
|
|||
requiredArtifacts:
|
||||
- nuget
|
||||
preBuildSteps:
|
||||
- task: InstallAppleCertificate@2
|
||||
inputs:
|
||||
certSecureFile: 'SkiaSharp iOS Certificate.p12'
|
||||
- task: InstallAppleCertificate@2
|
||||
inputs:
|
||||
certSecureFile: 'SkiaSharp Mac Certificate.p12'
|
||||
- task: InstallAppleProvisioningProfile@1
|
||||
inputs:
|
||||
provProfileSecureFile: 'SkiaSharp iOS Provisioning.mobileprovision'
|
||||
- task: InstallAppleProvisioningProfile@1
|
||||
inputs:
|
||||
provProfileSecureFile: 'SkiaSharp Mac Provisioning.provisionprofile'
|
||||
- task: InstallAppleProvisioningProfile@1
|
||||
inputs:
|
||||
provProfileSecureFile: 'SkiaSharp tvOS Provisioning.mobileprovision'
|
||||
- template: azure-templates-provisioning-profiles.yml
|
||||
- pwsh: |
|
||||
New-Item '.\output\nugets\' -Type Directory -Force | Out-Null
|
||||
Get-ChildItem '.\output\*.nupkg' | Move-Item -Destination '.\output\nugets\'
|
||||
|
|
|
@ -33,6 +33,7 @@ jobs:
|
|||
# displayName: ${{ parameters.displayName }}
|
||||
# vmImage: ${{ parameters.vmImage }}
|
||||
# condition: ${{ parameters.condition }}
|
||||
# postBuildSteps: ${{ parameters.postBuildSteps }}
|
||||
# buildExternals: ${{ parameters.buildExternals }}
|
||||
|
||||
# - ${{ if or(eq(parameters.buildExternals, ''), not(startsWith(parameters.name, 'native_'))) }}:
|
||||
|
|
|
@ -4,6 +4,7 @@ parameters:
|
|||
vmImage: '' # the VM image
|
||||
condition: succeeded() # whether or not to run this template
|
||||
buildExternals: '' # the build number to download externals from
|
||||
postBuildSteps: [] # any additional steps to run after the build
|
||||
|
||||
jobs:
|
||||
- job: ${{ parameters.name }}
|
||||
|
@ -33,4 +34,5 @@ jobs:
|
|||
displayName: Publish the ${{ parameters.name }} artifacts
|
||||
inputs:
|
||||
artifactName: ${{ parameters.name }}
|
||||
pathToPublish: 'output'
|
||||
pathToPublish: 'output'
|
||||
- ${{ parameters.postBuildSteps }}
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
steps:
|
||||
- task: InstallAppleCertificate@2
|
||||
inputs:
|
||||
certSecureFile: 'SkiaSharp iOS Certificate.p12'
|
||||
- task: InstallAppleCertificate@2
|
||||
inputs:
|
||||
certSecureFile: 'SkiaSharp Mac Certificate.p12'
|
||||
- task: InstallAppleProvisioningProfile@1
|
||||
inputs:
|
||||
provProfileSecureFile: 'SkiaSharp iOS Provisioning.mobileprovision'
|
||||
- task: InstallAppleProvisioningProfile@1
|
||||
inputs:
|
||||
provProfileSecureFile: 'SkiaSharp Mac Provisioning.provisionprofile'
|
||||
- task: InstallAppleProvisioningProfile@1
|
||||
inputs:
|
||||
provProfileSecureFile: 'SkiaSharp tvOS Provisioning.mobileprovision'
|
|
@ -1,4 +1,5 @@
|
|||
using CoreVideo;
|
||||
using System;
|
||||
using CoreVideo;
|
||||
using Xamarin.Forms;
|
||||
|
||||
using SKFormsView = SkiaSharp.Views.Forms.SKGLView;
|
||||
|
@ -46,7 +47,7 @@ namespace SkiaSharp.Views.Forms
|
|||
var nativeView = Control;
|
||||
nativeView?.BeginInvokeOnMainThread(() =>
|
||||
{
|
||||
if (nativeView != null)
|
||||
if (nativeView.Handle != IntPtr.Zero)
|
||||
nativeView.NeedsDisplay = true;
|
||||
});
|
||||
return;
|
||||
|
@ -59,6 +60,15 @@ namespace SkiaSharp.Views.Forms
|
|||
var nativeView = Control;
|
||||
var formsView = Element;
|
||||
|
||||
// stop the render loop if this was a one-shot, or the views are disposed
|
||||
if (nativeView == null || formsView == null || nativeView.Handle == IntPtr.Zero || !formsView.HasRenderLoop)
|
||||
{
|
||||
displayLink.Stop();
|
||||
displayLink.Dispose();
|
||||
displayLink = null;
|
||||
return CVReturn.Success;
|
||||
}
|
||||
|
||||
// redraw the view
|
||||
nativeView?.BeginInvokeOnMainThread(() =>
|
||||
{
|
||||
|
@ -66,14 +76,6 @@ namespace SkiaSharp.Views.Forms
|
|||
nativeView.NeedsDisplay = true;
|
||||
});
|
||||
|
||||
// stop the render loop if this was a one-shot, or the views are disposed
|
||||
if (nativeView == null || formsView == null || !formsView.HasRenderLoop)
|
||||
{
|
||||
displayLink.Stop();
|
||||
displayLink.Dispose();
|
||||
displayLink = null;
|
||||
}
|
||||
|
||||
return CVReturn.Success;
|
||||
});
|
||||
displayLink.Start();
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using CoreAnimation;
|
||||
using System;
|
||||
using CoreAnimation;
|
||||
using Foundation;
|
||||
using Xamarin.Forms;
|
||||
|
||||
|
@ -39,7 +40,7 @@ namespace SkiaSharp.Views.Forms
|
|||
displayLink.Dispose();
|
||||
displayLink = null;
|
||||
}
|
||||
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
|
@ -57,7 +58,11 @@ namespace SkiaSharp.Views.Forms
|
|||
if (oneShot)
|
||||
{
|
||||
var nativeView = Control;
|
||||
nativeView?.BeginInvokeOnMainThread(() => nativeView?.Display());
|
||||
nativeView?.BeginInvokeOnMainThread(() =>
|
||||
{
|
||||
if (nativeView.Handle != IntPtr.Zero)
|
||||
nativeView.Display();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -67,16 +72,17 @@ namespace SkiaSharp.Views.Forms
|
|||
var nativeView = Control;
|
||||
var formsView = Element;
|
||||
|
||||
// redraw the view
|
||||
nativeView?.Display();
|
||||
|
||||
// stop the render loop if this was a one-shot, or the views are disposed
|
||||
if (nativeView == null || formsView == null || !formsView.HasRenderLoop)
|
||||
if (nativeView == null || formsView == null || nativeView.Handle == IntPtr.Zero || !formsView.HasRenderLoop)
|
||||
{
|
||||
displayLink.Invalidate();
|
||||
displayLink.Dispose();
|
||||
displayLink = null;
|
||||
return;
|
||||
}
|
||||
|
||||
// redraw the view
|
||||
nativeView.Display();
|
||||
});
|
||||
displayLink.AddToRunLoop(NSRunLoop.Current, NSRunLoop.NSDefaultRunLoopMode);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using System;
|
||||
using Android.Graphics;
|
||||
using Android.Graphics;
|
||||
using Windows.Graphics.Display;
|
||||
using Windows.UI.Xaml;
|
||||
|
||||
|
@ -7,20 +6,20 @@ namespace SkiaSharp.Views.UWP
|
|||
{
|
||||
public partial class SKXamlCanvas : FrameworkElement
|
||||
{
|
||||
private Bitmap bitmap;
|
||||
private SKImageInfo info;
|
||||
private SurfaceFactory surfaceFactory;
|
||||
|
||||
public SKXamlCanvas()
|
||||
{
|
||||
surfaceFactory = new SurfaceFactory();
|
||||
Initialize();
|
||||
SetWillNotDraw(false);
|
||||
}
|
||||
|
||||
partial void DoUnloaded() =>
|
||||
FreeBitmap();
|
||||
surfaceFactory.Dispose();
|
||||
|
||||
private SKSize GetCanvasSize() =>
|
||||
info.Size;
|
||||
surfaceFactory.Info.Size;
|
||||
|
||||
private void DoInvalidate()
|
||||
{
|
||||
|
@ -38,11 +37,11 @@ namespace SkiaSharp.Views.UWP
|
|||
var display = DisplayInformation.GetForCurrentView();
|
||||
var scale = display.LogicalDpi / 96.0f;
|
||||
|
||||
info = new SKImageInfo((int)(w * scale), (int)(h * scale), SKColorType.Rgba8888, SKAlphaType.Premul);
|
||||
surfaceFactory.UpdateCanvasSize((int)(w * scale), (int)(h * scale));
|
||||
}
|
||||
else
|
||||
{
|
||||
info = new SKImageInfo(w, h, SKColorType.Rgba8888, SKAlphaType.Premul);
|
||||
surfaceFactory.UpdateCanvasSize(w, h);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -53,52 +52,23 @@ namespace SkiaSharp.Views.UWP
|
|||
if (designMode)
|
||||
return;
|
||||
|
||||
if (info.Width == 0 || info.Height == 0 || Visibility != Visibility.Visible || !isVisible)
|
||||
// bail out if the view is not actually visible
|
||||
if (Visibility != Visibility.Visible || !isVisible)
|
||||
{
|
||||
FreeBitmap();
|
||||
surfaceFactory.FreeBitmap();
|
||||
return;
|
||||
}
|
||||
|
||||
// create the bitmap data if we need it
|
||||
if (bitmap == null || bitmap.Handle == IntPtr.Zero || bitmap.Width != info.Width || bitmap.Height != info.Height)
|
||||
{
|
||||
FreeBitmap();
|
||||
bitmap = Bitmap.CreateBitmap(info.Width, info.Height, Bitmap.Config.Argb8888);
|
||||
}
|
||||
// create a skia surface
|
||||
var surface = surfaceFactory.CreateSurface(out var info);
|
||||
if (surface == null)
|
||||
return;
|
||||
|
||||
// create a surface
|
||||
using (var surface = SKSurface.Create(info, bitmap.LockPixels(), info.RowBytes))
|
||||
{
|
||||
// draw using SkiaSharp
|
||||
OnPaintSurface(new SKPaintSurfaceEventArgs(surface, info));
|
||||
// draw using SkiaSharp
|
||||
OnPaintSurface(new SKPaintSurfaceEventArgs(surface, info));
|
||||
|
||||
surface.Canvas.Flush();
|
||||
}
|
||||
bitmap.UnlockPixels();
|
||||
|
||||
// draw bitmap to canvas
|
||||
if (IgnorePixelScaling)
|
||||
{
|
||||
var src = new Rect(0, 0, info.Rect.Width, info.Rect.Height);
|
||||
var dst = new RectF(0, 0, (float)Width, (float)Height);
|
||||
canvas.DrawBitmap(bitmap, src, dst, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
canvas.DrawBitmap(bitmap, 0, 0, null);
|
||||
}
|
||||
}
|
||||
|
||||
private void FreeBitmap()
|
||||
{
|
||||
if (bitmap != null)
|
||||
{
|
||||
// free and recycle the bitmap data
|
||||
if (bitmap.Handle != IntPtr.Zero && !bitmap.IsRecycled)
|
||||
bitmap.Recycle();
|
||||
bitmap.Dispose();
|
||||
bitmap = null;
|
||||
}
|
||||
// draw the surface to the view
|
||||
surfaceFactory.DrawSurface(surface, canvas);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
<Compile Include="..\..\SkiaSharp.Views\SkiaSharp.Views.Android\GLTextureView.cs" />
|
||||
<Compile Include="..\..\SkiaSharp.Views\SkiaSharp.Views.Android\SKGLTextureView.cs" />
|
||||
<Compile Include="..\..\SkiaSharp.Views\SkiaSharp.Views.Android\SKGLTextureViewRenderer.cs" />
|
||||
<Compile Include="..\..\SkiaSharp.Views\SkiaSharp.Views.Android\SurfaceFactory.cs" />
|
||||
<Compile Include="..\..\SkiaSharp.Views\SkiaSharp.Views.UWP\UWPExtensions.cs" />
|
||||
<Compile Include="..\SkiaSharp.Views.Uno\**\*.cs" Link="%(RecursiveDir)%(Filename)%(Extension)" />
|
||||
</ItemGroup>
|
||||
|
|
|
@ -10,11 +10,9 @@ namespace SkiaSharp.Views.Android
|
|||
{
|
||||
public class SKCanvasView : View
|
||||
{
|
||||
private Bitmap bitmap;
|
||||
private SKImageInfo info;
|
||||
private bool ignorePixelScaling;
|
||||
|
||||
private bool designMode;
|
||||
private SurfaceFactory surfaceFactory;
|
||||
|
||||
public SKCanvasView(Context context)
|
||||
: base(context)
|
||||
|
@ -43,15 +41,10 @@ namespace SkiaSharp.Views.Android
|
|||
private void Initialize()
|
||||
{
|
||||
designMode = !Extensions.IsValidEnvironment;
|
||||
|
||||
if (designMode)
|
||||
return;
|
||||
|
||||
// create the initial info
|
||||
info = new SKImageInfo(0, 0, SKColorType.Rgba8888, SKAlphaType.Premul);
|
||||
surfaceFactory = new SurfaceFactory();
|
||||
}
|
||||
|
||||
public SKSize CanvasSize => info.Size;
|
||||
public SKSize CanvasSize => surfaceFactory.Info.Size;
|
||||
|
||||
public bool IgnorePixelScaling
|
||||
{
|
||||
|
@ -71,37 +64,26 @@ namespace SkiaSharp.Views.Android
|
|||
if (designMode)
|
||||
return;
|
||||
|
||||
if (info.Width == 0 || info.Height == 0 || Visibility != ViewStates.Visible)
|
||||
// bail out if the view is not actually visible
|
||||
if (Visibility != ViewStates.Visible)
|
||||
{
|
||||
FreeBitmap();
|
||||
surfaceFactory.FreeBitmap();
|
||||
return;
|
||||
}
|
||||
|
||||
// create the bitmap data if we need it
|
||||
if (bitmap == null || bitmap.Handle == IntPtr.Zero || bitmap.Width != info.Width || bitmap.Height != info.Height)
|
||||
{
|
||||
FreeBitmap();
|
||||
bitmap = Bitmap.CreateBitmap(info.Width, info.Height, Bitmap.Config.Argb8888);
|
||||
}
|
||||
// create a skia surface
|
||||
var surface = surfaceFactory.CreateSurface(out var info);
|
||||
if (surface == null)
|
||||
return;
|
||||
|
||||
// create a surface
|
||||
using (var surface = SKSurface.Create(info, bitmap.LockPixels(), info.RowBytes))
|
||||
{
|
||||
// draw using SkiaSharp
|
||||
OnPaintSurface(new SKPaintSurfaceEventArgs(surface, info));
|
||||
// draw using SkiaSharp
|
||||
OnPaintSurface(new SKPaintSurfaceEventArgs(surface, info));
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
OnDraw(surface, info);
|
||||
OnDraw(surface, info);
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
|
||||
surface.Canvas.Flush();
|
||||
}
|
||||
bitmap.UnlockPixels();
|
||||
|
||||
// draw bitmap to canvas
|
||||
if (IgnorePixelScaling)
|
||||
canvas.DrawBitmap(bitmap, info.Rect.ToRect(), new RectF(0, 0, Width, Height), null);
|
||||
else
|
||||
canvas.DrawBitmap(bitmap, 0, 0, null);
|
||||
// draw the surface to the view
|
||||
surfaceFactory.DrawSurface(surface, canvas);
|
||||
}
|
||||
|
||||
protected override void OnSizeChanged(int w, int h, int oldw, int oldh)
|
||||
|
@ -112,24 +94,6 @@ namespace SkiaSharp.Views.Android
|
|||
UpdateCanvasSize(w, h);
|
||||
}
|
||||
|
||||
private void UpdateCanvasSize(int w, int h)
|
||||
{
|
||||
if (designMode)
|
||||
return;
|
||||
|
||||
if (IgnorePixelScaling)
|
||||
{
|
||||
var scale = Resources.DisplayMetrics.Density;
|
||||
info.Width = (int)(w / scale);
|
||||
info.Height = (int)(h / scale);
|
||||
}
|
||||
else
|
||||
{
|
||||
info.Width = w;
|
||||
info.Height = h;
|
||||
}
|
||||
}
|
||||
|
||||
public event EventHandler<SKPaintSurfaceEventArgs> PaintSurface;
|
||||
|
||||
protected virtual void OnPaintSurface(SKPaintSurfaceEventArgs e)
|
||||
|
@ -137,7 +101,7 @@ namespace SkiaSharp.Views.Android
|
|||
PaintSurface?.Invoke(this, e);
|
||||
}
|
||||
|
||||
[EditorBrowsable (EditorBrowsableState.Never)]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
[Obsolete("Use OnPaintSurface(SKPaintSurfaceEventArgs) instead.")]
|
||||
protected virtual void OnDraw(SKSurface surface, SKImageInfo info)
|
||||
{
|
||||
|
@ -145,29 +109,27 @@ namespace SkiaSharp.Views.Android
|
|||
|
||||
protected override void OnDetachedFromWindow()
|
||||
{
|
||||
FreeBitmap();
|
||||
surfaceFactory.Dispose();
|
||||
|
||||
base.OnDetachedFromWindow();
|
||||
}
|
||||
|
||||
protected override void OnAttachedToWindow()
|
||||
{
|
||||
base.OnAttachedToWindow();
|
||||
|
||||
UpdateCanvasSize(Width, Height);
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
FreeBitmap();
|
||||
surfaceFactory.Dispose();
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
private void FreeBitmap()
|
||||
{
|
||||
if (bitmap != null)
|
||||
{
|
||||
// free and recycle the bitmap data
|
||||
if (bitmap.Handle != IntPtr.Zero && !bitmap.IsRecycled)
|
||||
bitmap.Recycle();
|
||||
bitmap.Dispose();
|
||||
bitmap = null;
|
||||
}
|
||||
}
|
||||
private void UpdateCanvasSize(int w, int h) =>
|
||||
surfaceFactory.UpdateCanvasSize(w, h, IgnorePixelScaling ? Resources.DisplayMetrics.Density : 1);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,34 +5,27 @@ namespace SkiaSharp.Views.Android
|
|||
public class SKLockedSurface
|
||||
{
|
||||
private readonly Canvas canvas;
|
||||
private readonly Bitmap bitmap;
|
||||
private readonly SurfaceFactory surfaceFactory;
|
||||
|
||||
internal SKLockedSurface(Canvas canvas, Bitmap bitmap)
|
||||
internal SKLockedSurface(Canvas canvas, SurfaceFactory surfaceFactory)
|
||||
{
|
||||
this.bitmap = bitmap;
|
||||
this.canvas = canvas;
|
||||
this.surfaceFactory = surfaceFactory;
|
||||
|
||||
// create a surface
|
||||
ImageInfo = new SKImageInfo(bitmap.Width, bitmap.Height, SKColorType.Rgba8888, SKAlphaType.Premul);
|
||||
Surface = SKSurface.Create(ImageInfo, bitmap.LockPixels(), ImageInfo.RowBytes);
|
||||
Surface = surfaceFactory.CreateSurface(out var info);
|
||||
ImageInfo = info;
|
||||
}
|
||||
|
||||
public SKImageInfo ImageInfo { get; private set; }
|
||||
public SKImageInfo ImageInfo { get; }
|
||||
|
||||
public SKSurface Surface { get; private set; }
|
||||
public SKSurface Surface { get; }
|
||||
|
||||
public SKCanvas Canvas => Surface.Canvas;
|
||||
|
||||
internal Canvas Post()
|
||||
{
|
||||
// dispose our canvas
|
||||
Surface.Canvas.Flush();
|
||||
Surface.Dispose();
|
||||
|
||||
// unlock the bitmap data and write to canvas
|
||||
bitmap.UnlockPixels();
|
||||
canvas.DrawBitmap(bitmap, 0, 0, null);
|
||||
|
||||
surfaceFactory.DrawSurface(Surface, canvas);
|
||||
return canvas;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
using System;
|
||||
using Android.Content;
|
||||
using Android.Content;
|
||||
using Android.Graphics;
|
||||
using Android.Runtime;
|
||||
using Android.Util;
|
||||
using Android.Views;
|
||||
|
||||
|
@ -9,7 +7,7 @@ namespace SkiaSharp.Views.Android
|
|||
{
|
||||
public class SKSurfaceView : SurfaceView, ISurfaceHolderCallback
|
||||
{
|
||||
private Bitmap bitmap;
|
||||
private SurfaceFactory surfaceFactory;
|
||||
|
||||
public SKSurfaceView(Context context)
|
||||
: base(context)
|
||||
|
@ -31,26 +29,28 @@ namespace SkiaSharp.Views.Android
|
|||
|
||||
private void Initialize()
|
||||
{
|
||||
surfaceFactory = new SurfaceFactory();
|
||||
Holder.AddCallback(this);
|
||||
}
|
||||
|
||||
public SKSize CanvasSize => bitmap == null ? SKSize.Empty : new SKSize(bitmap.Width, bitmap.Height);
|
||||
public SKSize CanvasSize => surfaceFactory.Info.Size;
|
||||
|
||||
// ISurfaceHolderCallback
|
||||
|
||||
public virtual void SurfaceChanged(ISurfaceHolder holder, [GeneratedEnum] Format format, int width, int height)
|
||||
public virtual void SurfaceChanged(ISurfaceHolder holder, Format format, int width, int height)
|
||||
{
|
||||
CreateBitmap(width, height);
|
||||
surfaceFactory.UpdateCanvasSize(width, height);
|
||||
}
|
||||
|
||||
public virtual void SurfaceCreated(ISurfaceHolder holder)
|
||||
{
|
||||
CreateBitmap(holder.SurfaceFrame.Width(), holder.SurfaceFrame.Height());
|
||||
var surfaceFrame = Holder.SurfaceFrame.ToSKRect();
|
||||
surfaceFactory.UpdateCanvasSize(surfaceFrame.Width, surfaceFrame.Height);
|
||||
}
|
||||
|
||||
public virtual void SurfaceDestroyed(ISurfaceHolder holder)
|
||||
{
|
||||
FreeBitmap();
|
||||
surfaceFactory.Dispose();
|
||||
}
|
||||
|
||||
// lock / unlock the SKSurface
|
||||
|
@ -58,7 +58,11 @@ namespace SkiaSharp.Views.Android
|
|||
public SKLockedSurface LockSurface()
|
||||
{
|
||||
var canvas = Holder.LockCanvas();
|
||||
return new SKLockedSurface(canvas, bitmap);
|
||||
if (canvas == null)
|
||||
return null;
|
||||
|
||||
surfaceFactory.UpdateCanvasSize(canvas.Width, canvas.Height);
|
||||
return new SKLockedSurface(canvas, surfaceFactory);
|
||||
}
|
||||
|
||||
public void UnlockSurfaceAndPost(SKLockedSurface surface)
|
||||
|
@ -71,35 +75,9 @@ namespace SkiaSharp.Views.Android
|
|||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
FreeBitmap();
|
||||
}
|
||||
surfaceFactory.Dispose();
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
private void CreateBitmap(int width, int height)
|
||||
{
|
||||
// create the bitmap data
|
||||
if (bitmap == null || bitmap.Handle == IntPtr.Zero || bitmap.Width != width || bitmap.Height != height)
|
||||
{
|
||||
FreeBitmap();
|
||||
bitmap = Bitmap.CreateBitmap(width, height, Bitmap.Config.Argb8888);
|
||||
}
|
||||
}
|
||||
|
||||
private void FreeBitmap()
|
||||
{
|
||||
if (bitmap != null)
|
||||
{
|
||||
// free and recycle the bitmap data
|
||||
if (bitmap.Handle != IntPtr.Zero && !bitmap.IsRecycled)
|
||||
{
|
||||
bitmap.Recycle();
|
||||
}
|
||||
bitmap.Dispose();
|
||||
bitmap = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
using System;
|
||||
using Android.Graphics;
|
||||
|
||||
#if HAS_UNO
|
||||
namespace SkiaSharp.Views.UWP
|
||||
#else
|
||||
namespace SkiaSharp.Views.Android
|
||||
#endif
|
||||
{
|
||||
internal class SurfaceFactory : IDisposable
|
||||
{
|
||||
private Bitmap bitmap;
|
||||
|
||||
public SKImageInfo Info { get; private set; }
|
||||
|
||||
public void UpdateCanvasSize(int w, int h, float density = 1f)
|
||||
{
|
||||
if (density != 1)
|
||||
Info = CreateInfo((int)(w / density), (int)(h / density));
|
||||
else
|
||||
Info = CreateInfo(w, h);
|
||||
|
||||
// if there are no pixels, clean up
|
||||
if (Info.Width == 0 || Info.Height == 0)
|
||||
FreeBitmap();
|
||||
}
|
||||
|
||||
public SKSurface CreateSurface(out SKImageInfo info)
|
||||
{
|
||||
// get context details
|
||||
info = Info;
|
||||
|
||||
// if there are no pixels, clean up and return
|
||||
if (info.Width == 0 || info.Height == 0)
|
||||
{
|
||||
Dispose();
|
||||
return null;
|
||||
}
|
||||
|
||||
// if the memory size has changed, then reset the underlying memory
|
||||
if (bitmap?.Handle == IntPtr.Zero || bitmap?.Width != info.Width || bitmap?.Height != info.Height)
|
||||
FreeBitmap();
|
||||
|
||||
// create the bitmap data if we need it
|
||||
if (bitmap == null)
|
||||
bitmap = Bitmap.CreateBitmap(info.Width, info.Height, Bitmap.Config.Argb8888);
|
||||
|
||||
return SKSurface.Create(info, bitmap.LockPixels(), info.RowBytes);
|
||||
}
|
||||
|
||||
public void DrawSurface(SKSurface surface, Canvas canvas)
|
||||
{
|
||||
// clean up skia objects
|
||||
surface.Canvas.Flush();
|
||||
surface.Dispose();
|
||||
|
||||
// get the bitmap ready for drawing
|
||||
bitmap.UnlockPixels();
|
||||
|
||||
// get the bounds
|
||||
var src = new Rect(0, 0, Info.Width, Info.Height);
|
||||
var dst = new RectF(0, 0, canvas.Width, canvas.Height);
|
||||
|
||||
// draw bitmap to the view canvas
|
||||
canvas.DrawBitmap(bitmap, src, dst, null);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
FreeBitmap();
|
||||
Info = CreateInfo(0, 0);
|
||||
}
|
||||
|
||||
public void FreeBitmap()
|
||||
{
|
||||
if (bitmap == null)
|
||||
return;
|
||||
|
||||
// free and recycle the bitmap data
|
||||
if (bitmap.Handle != IntPtr.Zero && !bitmap.IsRecycled)
|
||||
bitmap.Recycle();
|
||||
|
||||
bitmap.Dispose();
|
||||
bitmap = null;
|
||||
}
|
||||
|
||||
private SKImageInfo CreateInfo(int width, int height) =>
|
||||
new SKImageInfo(width, height, SKColorType.Rgba8888, SKAlphaType.Premul);
|
||||
}
|
||||
}
|
|
@ -23,7 +23,6 @@ namespace SkiaSharp.Views.Mac
|
|||
public SKCanvasView(CGRect frame)
|
||||
: base(frame)
|
||||
{
|
||||
|
||||
Initialize();
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.31004.235
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SkiaSharp.Android.Tests", "SkiaSharp.Android.Tests\SkiaSharp.Android.Tests.csproj", "{CB2072E0-A437-4811-AE17-16CAE0DDA1B1}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HarfBuzzSharp.Android", "..\binding\HarfBuzzSharp.Android\HarfBuzzSharp.Android.csproj", "{7F5C82DF-976E-4973-AFDF-C2E178B19774}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SkiaSharp.Android", "..\binding\SkiaSharp.Android\SkiaSharp.Android.csproj", "{D7E86B8F-4E7D-4BE9-A740-D0F5AC9392E5}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SkiaSharp.HarfBuzz", "..\source\SkiaSharp.HarfBuzz\SkiaSharp.HarfBuzz\SkiaSharp.HarfBuzz.csproj", "{4C5F53B5-9DFD-4CC6-8FAE-98B4F40CB33A}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SkiaSharp.Views.Android", "..\source\SkiaSharp.Views\SkiaSharp.Views.Android\SkiaSharp.Views.Android.csproj", "{16922662-C540-4865-876D-94ED3211521B}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SkiaSharp.Views.Forms.Android", "..\source\SkiaSharp.Views.Forms\SkiaSharp.Views.Forms.Android\SkiaSharp.Views.Forms.Android.csproj", "{F962E49D-DC1F-4E93-9F6B-335E2746BCF1}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{CB2072E0-A437-4811-AE17-16CAE0DDA1B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{CB2072E0-A437-4811-AE17-16CAE0DDA1B1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{CB2072E0-A437-4811-AE17-16CAE0DDA1B1}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
|
||||
{CB2072E0-A437-4811-AE17-16CAE0DDA1B1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{CB2072E0-A437-4811-AE17-16CAE0DDA1B1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{CB2072E0-A437-4811-AE17-16CAE0DDA1B1}.Release|Any CPU.Deploy.0 = Release|Any CPU
|
||||
{7F5C82DF-976E-4973-AFDF-C2E178B19774}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{7F5C82DF-976E-4973-AFDF-C2E178B19774}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{7F5C82DF-976E-4973-AFDF-C2E178B19774}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7F5C82DF-976E-4973-AFDF-C2E178B19774}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D7E86B8F-4E7D-4BE9-A740-D0F5AC9392E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D7E86B8F-4E7D-4BE9-A740-D0F5AC9392E5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D7E86B8F-4E7D-4BE9-A740-D0F5AC9392E5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D7E86B8F-4E7D-4BE9-A740-D0F5AC9392E5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{4C5F53B5-9DFD-4CC6-8FAE-98B4F40CB33A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4C5F53B5-9DFD-4CC6-8FAE-98B4F40CB33A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4C5F53B5-9DFD-4CC6-8FAE-98B4F40CB33A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4C5F53B5-9DFD-4CC6-8FAE-98B4F40CB33A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{16922662-C540-4865-876D-94ED3211521B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{16922662-C540-4865-876D-94ED3211521B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{16922662-C540-4865-876D-94ED3211521B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{16922662-C540-4865-876D-94ED3211521B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{F962E49D-DC1F-4E93-9F6B-335E2746BCF1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F962E49D-DC1F-4E93-9F6B-335E2746BCF1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F962E49D-DC1F-4E93-9F6B-335E2746BCF1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F962E49D-DC1F-4E93-9F6B-335E2746BCF1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {E393D9B5-76D6-469A-A011-3C3A4D8DF8A5}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -0,0 +1,31 @@
|
|||
using System.Reflection;
|
||||
using Android.App;
|
||||
using Android.Content.PM;
|
||||
using Android.OS;
|
||||
using Xunit.Runners.UI;
|
||||
|
||||
namespace SkiaSharp.Tests
|
||||
{
|
||||
[Activity(MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
|
||||
public class MainActivity : RunnerActivity
|
||||
{
|
||||
protected override void OnCreate(Bundle bundle)
|
||||
{
|
||||
Xamarin.Essentials.Platform.Init(this, bundle);
|
||||
|
||||
AssetCopier.CopyAssets();
|
||||
|
||||
AddTestAssembly(Assembly.GetExecutingAssembly());
|
||||
AddExecutionAssembly(Assembly.GetExecutingAssembly());
|
||||
|
||||
base.OnCreate(bundle);
|
||||
}
|
||||
|
||||
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults)
|
||||
{
|
||||
Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
|
||||
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.mono.skiasharp.tests" android:installLocation="auto">
|
||||
<uses-sdk android:minSdkVersion="19" android:targetSdkVersion="29" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<application android:label="@string/app_name" android:icon="@drawable/icon" android:theme="@style/MainTheme"></application>
|
||||
</manifest>
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 1.4 KiB |
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">Tests</string>
|
||||
</resources>
|
|
@ -0,0 +1,32 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<resources>
|
||||
|
||||
<style name="MainTheme" parent="MainTheme.Base">
|
||||
</style>
|
||||
|
||||
<!-- Base theme applied no matter what API -->
|
||||
<style name="MainTheme.Base" parent="Theme.AppCompat.Light.DarkActionBar">
|
||||
<!--If you are using revision 22.1 please use just windowNoTitle. Without android:-->
|
||||
<item name="windowNoTitle">true</item>
|
||||
<!--We will be using the toolbar so no need to show ActionBar-->
|
||||
<item name="windowActionBar">false</item>
|
||||
<!-- Set theme colors from http://www.google.com/design/spec/style/color.html#color-color-palette -->
|
||||
<!-- colorPrimary is used for the default action bar background -->
|
||||
<item name="colorPrimary">#2196F3</item>
|
||||
<!-- colorPrimaryDark is used for the status bar -->
|
||||
<item name="colorPrimaryDark">#1976D2</item>
|
||||
<!-- colorAccent is used as the default value for colorControlActivated
|
||||
which is used to tint widgets -->
|
||||
<item name="colorAccent">#FF4081</item>
|
||||
<!-- You can also set colorControlNormal, colorControlActivated
|
||||
colorControlHighlight and colorSwitchThumbNormal. -->
|
||||
<item name="windowActionModeOverlay">true</item>
|
||||
|
||||
<item name="android:datePickerDialogTheme">@style/AppCompatDialogStyle</item>
|
||||
</style>
|
||||
|
||||
<style name="AppCompatDialogStyle" parent="Theme.AppCompat.Light.Dialog">
|
||||
<item name="colorAccent">#FF4081</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
|
@ -0,0 +1,121 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{CB2072E0-A437-4811-AE17-16CAE0DDA1B1}</ProjectGuid>
|
||||
<ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<OutputType>Library</OutputType>
|
||||
<RootNamespace>SkiaSharp.Tests</RootNamespace>
|
||||
<AssemblyName>SkiaSharp.Tests</AssemblyName>
|
||||
<TargetFrameworkVersion>v10.0</TargetFrameworkVersion>
|
||||
<AndroidApplication>True</AndroidApplication>
|
||||
<AndroidUseIntermediateDesignerFile>true</AndroidUseIntermediateDesignerFile>
|
||||
<AndroidResgenClass>Resource</AndroidResgenClass>
|
||||
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
|
||||
<MonoAndroidResourcePrefix>Resources</MonoAndroidResourcePrefix>
|
||||
<MonoAndroidAssetsPrefix>Assets</MonoAndroidAssetsPrefix>
|
||||
<AndroidEnableSGenConcurrent>true</AndroidEnableSGenConcurrent>
|
||||
<AndroidUseAapt2>true</AndroidUseAapt2>
|
||||
<AndroidHttpClientHandlerType>Xamarin.Android.Net.AndroidClientHandler</AndroidHttpClientHandlerType>
|
||||
<SkipGenerateAssemblyVersionInfo>true</SkipGenerateAssemblyVersionInfo>
|
||||
<SkipMDocGenerateDocs>true</SkipMDocGenerateDocs>
|
||||
<SkipCopyToOutputDirectory>true</SkipCopyToOutputDirectory>
|
||||
<SignAssembly>false</SignAssembly>
|
||||
<AndroidSupportedAbis>armeabi-v7a;x86;x86_64;arm64-v8a</AndroidSupportedAbis>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug</OutputPath>
|
||||
<DefineConstants>DEBUG;USE_LIBRARY_LOADER;</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<AndroidLinkMode>None</AndroidLinkMode>
|
||||
<JavaMaximumHeapSize>1G</JavaMaximumHeapSize>
|
||||
<AotAssemblies>false</AotAssemblies>
|
||||
<EnableLLVM>false</EnableLLVM>
|
||||
<AndroidEnableProfiledAot>false</AndroidEnableProfiledAot>
|
||||
<BundleAssemblies>false</BundleAssemblies>
|
||||
<AndroidUseSharedRuntime>false</AndroidUseSharedRuntime>
|
||||
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release</OutputPath>
|
||||
<DefineConstants>USE_LIBRARY_LOADER;</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<AndroidManagedSymbols>true</AndroidManagedSymbols>
|
||||
<AndroidUseSharedRuntime>false</AndroidUseSharedRuntime>
|
||||
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
|
||||
<JavaMaximumHeapSize>1G</JavaMaximumHeapSize>
|
||||
<AndroidLinkMode>None</AndroidLinkMode>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Mono.Android" />
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Xamarin.Essentials" Version="1.6.1" />
|
||||
<PackageReference Include="Xamarin.Forms" Version="4.5.0.725" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.devices" Version="2.5.25" />
|
||||
<PackageReference Include="xunit.skippablefact" Version="1.4.13" />
|
||||
<PackageReference Include="Validation" Version="2.4.22" />
|
||||
<PackageReference Include="Microsoft.DotNet.XHarness.TestRunners.Xunit" Version="1.0.0-prerelease.20602.1" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\binding\HarfBuzzSharp.Android\HarfBuzzSharp.Android.csproj">
|
||||
<Project>{7f5c82df-976e-4973-afdf-c2e178b19774}</Project>
|
||||
<Name>HarfBuzzSharp.Android</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\binding\SkiaSharp.Android\SkiaSharp.Android.csproj">
|
||||
<Project>{d7e86b8f-4e7d-4be9-a740-d0f5ac9392e5}</Project>
|
||||
<Name>SkiaSharp.Android</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\source\SkiaSharp.HarfBuzz\SkiaSharp.HarfBuzz\SkiaSharp.HarfBuzz.csproj">
|
||||
<Project>{4c5f53b5-9dfd-4cc6-8fae-98b4f40cb33a}</Project>
|
||||
<Name>SkiaSharp.HarfBuzz</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\source\SkiaSharp.Views.Forms\SkiaSharp.Views.Forms.Android\SkiaSharp.Views.Forms.Android.csproj">
|
||||
<Project>{f962e49d-dc1f-4e93-9f6b-335e2746bcf1}</Project>
|
||||
<Name>SkiaSharp.Views.Forms.Android</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\source\SkiaSharp.Views\SkiaSharp.Views.Android\SkiaSharp.Views.Android.csproj">
|
||||
<Project>{16922662-c540-4865-876d-94ed3211521b}</Project>
|
||||
<Name>SkiaSharp.Views.Android</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="MainActivity.cs" />
|
||||
<Compile Include="TestInstrumentation.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="..\Tests\**\*.cs" Link="%(RecursiveDir)%(FileName)%(Extension)" Exclude="..\Tests\GlContexts\*\*;..\Tests\PlatformUtils\*\*" />
|
||||
<Compile Include="..\..\binding\Binding.Shared\LibraryLoader.cs" Link="PlatformUtils\LibraryLoader.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="..\Content\**\*" Link="Content\%(RecursiveDir)%(FileName)%(Extension)" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="Properties\AndroidManifest.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<AndroidResource Include="Resources\drawable\icon.png" />
|
||||
<AndroidResource Include="Resources\values\styles.xml" />
|
||||
<AndroidResource Include="Resources\values\strings.xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
|
||||
<Import Project="..\..\output\SkiaSharp\nuget\build\monoandroid1.0\SkiaSharp.targets" Condition="Exists('..\..\output\SkiaSharp\nuget\build\monoandroid1.0\SkiaSharp.targets')" />
|
||||
<Import Project="..\..\output\HarfBuzzSharp\nuget\build\monoandroid1.0\HarfBuzzSharp.targets" Condition="Exists('..\..\output\HarfBuzzSharp\nuget\build\monoandroid1.0\HarfBuzzSharp.targets')" />
|
||||
</Project>
|
|
@ -0,0 +1,134 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using Android.App;
|
||||
using Android.OS;
|
||||
using Android.Runtime;
|
||||
using Microsoft.DotNet.XHarness.TestRunners.Common;
|
||||
using Microsoft.DotNet.XHarness.TestRunners.Xunit;
|
||||
using Xamarin.Essentials;
|
||||
|
||||
namespace SkiaSharp.Tests
|
||||
{
|
||||
[Instrumentation(Name = "com.mono.skiasharp.tests.TestInstrumentation")]
|
||||
public class TestInstrumentation : Instrumentation
|
||||
{
|
||||
private string resultsFileName;
|
||||
|
||||
protected TestInstrumentation()
|
||||
{
|
||||
}
|
||||
|
||||
protected TestInstrumentation(IntPtr handle, JniHandleOwnership transfer)
|
||||
: base(handle, transfer)
|
||||
{
|
||||
}
|
||||
|
||||
public override void OnCreate(Bundle arguments)
|
||||
{
|
||||
base.OnCreate(arguments);
|
||||
|
||||
AssetCopier.CopyAssets();
|
||||
|
||||
resultsFileName = arguments.GetString("results-file-name", "TestResults.xml");
|
||||
|
||||
Start();
|
||||
}
|
||||
|
||||
public override async void OnStart()
|
||||
{
|
||||
base.OnStart();
|
||||
|
||||
var bundle = new Bundle();
|
||||
|
||||
var entryPoint = new TestsEntryPoint(resultsFileName);
|
||||
entryPoint.TestsCompleted += (sender, results) =>
|
||||
{
|
||||
var message =
|
||||
$"Tests run: {results.ExecutedTests} " +
|
||||
$"Passed: {results.PassedTests} " +
|
||||
$"Inconclusive: {results.InconclusiveTests} " +
|
||||
$"Failed: {results.FailedTests} " +
|
||||
$"Ignored: {results.SkippedTests}";
|
||||
bundle.PutString("test-execution-summary", message);
|
||||
|
||||
bundle.PutLong("return-code", results.FailedTests == 0 ? 0 : 1);
|
||||
};
|
||||
|
||||
await entryPoint.RunAsync();
|
||||
|
||||
if (File.Exists(entryPoint.TestsResultsFinalPath))
|
||||
bundle.PutString("test-results-path", entryPoint.TestsResultsFinalPath);
|
||||
|
||||
if (bundle.GetLong("return-code", -1) == -1)
|
||||
bundle.PutLong("return-code", 1);
|
||||
|
||||
Finish(Result.Ok, bundle);
|
||||
}
|
||||
|
||||
private class TestsEntryPoint : AndroidApplicationEntryPoint
|
||||
{
|
||||
private readonly string resultsPath;
|
||||
|
||||
public TestsEntryPoint(string resultsFileName)
|
||||
{
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
var root = DeviceInfo.Version >= new Version(11, 0)
|
||||
? Android.OS.Environment.ExternalStorageDirectory.AbsolutePath
|
||||
: Application.Context.GetExternalFilesDir(null)?.AbsolutePath ?? FileSystem.AppDataDirectory;
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
|
||||
var docsDir = Path.Combine(root, "Documents");
|
||||
|
||||
if (!Directory.Exists(docsDir))
|
||||
Directory.CreateDirectory(docsDir);
|
||||
|
||||
resultsPath = Path.Combine(docsDir, resultsFileName);
|
||||
}
|
||||
|
||||
protected override bool LogExcludedTests => true;
|
||||
|
||||
public override TextWriter Logger => null;
|
||||
|
||||
public override string TestsResultsFinalPath => resultsPath;
|
||||
|
||||
protected override int? MaxParallelThreads => System.Environment.ProcessorCount;
|
||||
|
||||
protected override IDevice Device { get; } = new TestDevice();
|
||||
|
||||
protected override IEnumerable<TestAssemblyInfo> GetTestAssemblies()
|
||||
{
|
||||
yield return new TestAssemblyInfo(Assembly.GetExecutingAssembly(), Assembly.GetExecutingAssembly().Location);
|
||||
}
|
||||
|
||||
protected override void TerminateWithSuccess()
|
||||
{
|
||||
}
|
||||
|
||||
protected override TestRunner GetTestRunner(LogWriter logWriter)
|
||||
{
|
||||
var testRunner = base.GetTestRunner(logWriter);
|
||||
return testRunner;
|
||||
}
|
||||
}
|
||||
|
||||
private class TestDevice : IDevice
|
||||
{
|
||||
public string BundleIdentifier => AppInfo.PackageName;
|
||||
|
||||
public string UniqueIdentifier => Guid.NewGuid().ToString("N");
|
||||
|
||||
public string Name => DeviceInfo.Name;
|
||||
|
||||
public string Model => DeviceInfo.Model;
|
||||
|
||||
public string SystemName => DeviceInfo.Platform.ToString();
|
||||
|
||||
public string SystemVersion => DeviceInfo.VersionString;
|
||||
|
||||
public string Locale => CultureInfo.CurrentCulture.Name;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.808.5
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SkiaSharp.iOS", "..\binding\SkiaSharp.iOS\SkiaSharp.iOS.csproj", "{A4146A87-DB60-4A17-A179-0E2E4255A08E}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HarfBuzzSharp.iOS", "..\binding\HarfBuzzSharp.iOS\HarfBuzzSharp.iOS.csproj", "{D958E2E9-DE32-42E8-AB10-D25E4186C4E1}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SkiaSharp.HarfBuzz", "..\source\SkiaSharp.HarfBuzz\SkiaSharp.HarfBuzz\SkiaSharp.HarfBuzz.csproj", "{A5614B8C-31C8-43A3-9BF9-2719E4BE4D36}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SkiaSharp.Views.iOS", "..\source\SkiaSharp.Views\SkiaSharp.Views.iOS\SkiaSharp.Views.iOS.csproj", "{549F8E22-A756-4E99-A84A-C4E74832DA95}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SkiaSharp.Views.Forms.iOS", "..\source\SkiaSharp.Views.Forms\SkiaSharp.Views.Forms.iOS\SkiaSharp.Views.Forms.iOS.csproj", "{0254162B-6B4A-459E-BD96-3A42A104C144}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SkiaSharp.iOS.Tests", "SkiaSharp.iOS.Tests\SkiaSharp.iOS.Tests.csproj", "{B73EB308-70BE-49FD-91A7-1D1495663D6D}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
Debug|iPhoneSimulator = Debug|iPhoneSimulator
|
||||
Release|iPhoneSimulator = Release|iPhoneSimulator
|
||||
Debug|iPhone = Debug|iPhone
|
||||
Release|iPhone = Release|iPhone
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{A4146A87-DB60-4A17-A179-0E2E4255A08E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A4146A87-DB60-4A17-A179-0E2E4255A08E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A4146A87-DB60-4A17-A179-0E2E4255A08E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A4146A87-DB60-4A17-A179-0E2E4255A08E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{A4146A87-DB60-4A17-A179-0E2E4255A08E}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{A4146A87-DB60-4A17-A179-0E2E4255A08E}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{A4146A87-DB60-4A17-A179-0E2E4255A08E}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{A4146A87-DB60-4A17-A179-0E2E4255A08E}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{A4146A87-DB60-4A17-A179-0E2E4255A08E}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{A4146A87-DB60-4A17-A179-0E2E4255A08E}.Debug|iPhone.Build.0 = Debug|Any CPU
|
||||
{A4146A87-DB60-4A17-A179-0E2E4255A08E}.Release|iPhone.ActiveCfg = Release|Any CPU
|
||||
{A4146A87-DB60-4A17-A179-0E2E4255A08E}.Release|iPhone.Build.0 = Release|Any CPU
|
||||
{D958E2E9-DE32-42E8-AB10-D25E4186C4E1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D958E2E9-DE32-42E8-AB10-D25E4186C4E1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D958E2E9-DE32-42E8-AB10-D25E4186C4E1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D958E2E9-DE32-42E8-AB10-D25E4186C4E1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D958E2E9-DE32-42E8-AB10-D25E4186C4E1}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{D958E2E9-DE32-42E8-AB10-D25E4186C4E1}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{D958E2E9-DE32-42E8-AB10-D25E4186C4E1}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{D958E2E9-DE32-42E8-AB10-D25E4186C4E1}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{D958E2E9-DE32-42E8-AB10-D25E4186C4E1}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{D958E2E9-DE32-42E8-AB10-D25E4186C4E1}.Debug|iPhone.Build.0 = Debug|Any CPU
|
||||
{D958E2E9-DE32-42E8-AB10-D25E4186C4E1}.Release|iPhone.ActiveCfg = Release|Any CPU
|
||||
{D958E2E9-DE32-42E8-AB10-D25E4186C4E1}.Release|iPhone.Build.0 = Release|Any CPU
|
||||
{A5614B8C-31C8-43A3-9BF9-2719E4BE4D36}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A5614B8C-31C8-43A3-9BF9-2719E4BE4D36}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A5614B8C-31C8-43A3-9BF9-2719E4BE4D36}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A5614B8C-31C8-43A3-9BF9-2719E4BE4D36}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{A5614B8C-31C8-43A3-9BF9-2719E4BE4D36}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{A5614B8C-31C8-43A3-9BF9-2719E4BE4D36}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{A5614B8C-31C8-43A3-9BF9-2719E4BE4D36}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{A5614B8C-31C8-43A3-9BF9-2719E4BE4D36}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{A5614B8C-31C8-43A3-9BF9-2719E4BE4D36}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{A5614B8C-31C8-43A3-9BF9-2719E4BE4D36}.Debug|iPhone.Build.0 = Debug|Any CPU
|
||||
{A5614B8C-31C8-43A3-9BF9-2719E4BE4D36}.Release|iPhone.ActiveCfg = Release|Any CPU
|
||||
{A5614B8C-31C8-43A3-9BF9-2719E4BE4D36}.Release|iPhone.Build.0 = Release|Any CPU
|
||||
{549F8E22-A756-4E99-A84A-C4E74832DA95}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{549F8E22-A756-4E99-A84A-C4E74832DA95}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{549F8E22-A756-4E99-A84A-C4E74832DA95}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{549F8E22-A756-4E99-A84A-C4E74832DA95}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{549F8E22-A756-4E99-A84A-C4E74832DA95}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{549F8E22-A756-4E99-A84A-C4E74832DA95}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{549F8E22-A756-4E99-A84A-C4E74832DA95}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{549F8E22-A756-4E99-A84A-C4E74832DA95}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{549F8E22-A756-4E99-A84A-C4E74832DA95}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{549F8E22-A756-4E99-A84A-C4E74832DA95}.Debug|iPhone.Build.0 = Debug|Any CPU
|
||||
{549F8E22-A756-4E99-A84A-C4E74832DA95}.Release|iPhone.ActiveCfg = Release|Any CPU
|
||||
{549F8E22-A756-4E99-A84A-C4E74832DA95}.Release|iPhone.Build.0 = Release|Any CPU
|
||||
{0254162B-6B4A-459E-BD96-3A42A104C144}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{0254162B-6B4A-459E-BD96-3A42A104C144}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{0254162B-6B4A-459E-BD96-3A42A104C144}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{0254162B-6B4A-459E-BD96-3A42A104C144}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{0254162B-6B4A-459E-BD96-3A42A104C144}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU
|
||||
{0254162B-6B4A-459E-BD96-3A42A104C144}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU
|
||||
{0254162B-6B4A-459E-BD96-3A42A104C144}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU
|
||||
{0254162B-6B4A-459E-BD96-3A42A104C144}.Release|iPhoneSimulator.Build.0 = Release|Any CPU
|
||||
{0254162B-6B4A-459E-BD96-3A42A104C144}.Debug|iPhone.ActiveCfg = Debug|Any CPU
|
||||
{0254162B-6B4A-459E-BD96-3A42A104C144}.Debug|iPhone.Build.0 = Debug|Any CPU
|
||||
{0254162B-6B4A-459E-BD96-3A42A104C144}.Release|iPhone.ActiveCfg = Release|Any CPU
|
||||
{0254162B-6B4A-459E-BD96-3A42A104C144}.Release|iPhone.Build.0 = Release|Any CPU
|
||||
{B73EB308-70BE-49FD-91A7-1D1495663D6D}.Debug|Any CPU.ActiveCfg = Debug|iPhoneSimulator
|
||||
{B73EB308-70BE-49FD-91A7-1D1495663D6D}.Debug|Any CPU.Build.0 = Debug|iPhoneSimulator
|
||||
{B73EB308-70BE-49FD-91A7-1D1495663D6D}.Release|Any CPU.ActiveCfg = Release|iPhoneSimulator
|
||||
{B73EB308-70BE-49FD-91A7-1D1495663D6D}.Release|Any CPU.Build.0 = Release|iPhoneSimulator
|
||||
{B73EB308-70BE-49FD-91A7-1D1495663D6D}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator
|
||||
{B73EB308-70BE-49FD-91A7-1D1495663D6D}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator
|
||||
{B73EB308-70BE-49FD-91A7-1D1495663D6D}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator
|
||||
{B73EB308-70BE-49FD-91A7-1D1495663D6D}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator
|
||||
{B73EB308-70BE-49FD-91A7-1D1495663D6D}.Debug|iPhone.ActiveCfg = Debug|iPhone
|
||||
{B73EB308-70BE-49FD-91A7-1D1495663D6D}.Debug|iPhone.Build.0 = Debug|iPhone
|
||||
{B73EB308-70BE-49FD-91A7-1D1495663D6D}.Release|iPhone.ActiveCfg = Release|iPhone
|
||||
{B73EB308-70BE-49FD-91A7-1D1495663D6D}.Release|iPhone.Build.0 = Release|iPhone
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {35C58E22-5C43-4580-AE38-8F774B6C98EC}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -0,0 +1,21 @@
|
|||
using System.Reflection;
|
||||
using Foundation;
|
||||
using UIKit;
|
||||
|
||||
namespace SkiaSharp.Tests
|
||||
{
|
||||
[Register(nameof(AppDelegate))]
|
||||
public partial class AppDelegate : Xunit.Runner.RunnerAppDelegate
|
||||
{
|
||||
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
|
||||
{
|
||||
// We need this to ensure the execution assembly is part of the app bundle
|
||||
AddExecutionAssembly(Assembly.GetExecutingAssembly());
|
||||
|
||||
// tests can be inside the main assembly
|
||||
AddTestAssembly(Assembly.GetExecutingAssembly());
|
||||
|
||||
return base.FinishedLaunching(app, options);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
<?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>
|
||||
</dict>
|
||||
</plist>
|
|
@ -0,0 +1,39 @@
|
|||
<?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>
|
||||
<key>UIDeviceFamily</key>
|
||||
<array>
|
||||
<integer>1</integer>
|
||||
<integer>2</integer>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>MinimumOSVersion</key>
|
||||
<string>10.0</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>Tests</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.mono.skiasharp.tests</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>Tests</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
</dict>
|
||||
</plist>
|
|
@ -0,0 +1,18 @@
|
|||
using System;
|
||||
using UIKit;
|
||||
|
||||
namespace SkiaSharp.Tests
|
||||
{
|
||||
public class Application
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
AssetCopier.CopyAssets();
|
||||
|
||||
if (args?.Length > 0 || Environment.GetEnvironmentVariable("NUNIT_AUTOEXIT")?.Length > 0) // usually means this is from xharness
|
||||
UIApplication.Main(args, null, nameof(TestApplicationDelegate));
|
||||
else
|
||||
UIApplication.Main(args, null, nameof(AppDelegate));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="6245" systemVersion="13F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="X5k-f2-b5h">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6238"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--View Controller-->
|
||||
<scene sceneID="gAE-YM-kbH">
|
||||
<objects>
|
||||
<viewController id="X5k-f2-b5h" sceneMemberID="viewController">
|
||||
<layoutGuides>
|
||||
<viewControllerLayoutGuide type="top" id="Y8P-hJ-Z43"/>
|
||||
<viewControllerLayoutGuide type="bottom" id="9ZL-r4-8FZ"/>
|
||||
</layoutGuides>
|
||||
<view key="view" contentMode="scaleToFill" id="yd7-JS-zBw">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="23" firstAttribute="centerY" secondItem="yd7-JS-zBw" secondAttribute="centerY" priority="1" id="39"/>
|
||||
<constraint firstItem="23" firstAttribute="centerX" secondItem="yd7-JS-zBw" secondAttribute="centerX" priority="1" id="41"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="XAI-xm-WK6" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="349" y="339"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
<resources>
|
||||
</resources>
|
||||
</document>
|
|
@ -0,0 +1,138 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">iPhoneSimulator</Platform>
|
||||
<ProductVersion>8.0.30703</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{B73EB308-70BE-49FD-91A7-1D1495663D6D}</ProjectGuid>
|
||||
<ProjectTypeGuids>{FEACFBD2-3405-455C-9665-78FE426C6842};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||
<OutputType>Exe</OutputType>
|
||||
<AssemblyName>SkiaSharp.Tests</AssemblyName>
|
||||
<RootNamespace>SkiaSharp.Tests</RootNamespace>
|
||||
<IPhoneResourcePrefix>Resources</IPhoneResourcePrefix>
|
||||
<SkipGenerateAssemblyVersionInfo>true</SkipGenerateAssemblyVersionInfo>
|
||||
<SkipMDocGenerateDocs>true</SkipMDocGenerateDocs>
|
||||
<SkipCopyToOutputDirectory>true</SkipCopyToOutputDirectory>
|
||||
<SignAssembly>false</SignAssembly>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|iPhoneSimulator' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\iPhoneSimulator\Debug</OutputPath>
|
||||
<DefineConstants>DEBUG;USE_LIBRARY_LOADER</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<ConsolePause>false</ConsolePause>
|
||||
<MtouchArch>x86_64</MtouchArch>
|
||||
<MtouchLink>None</MtouchLink>
|
||||
<MtouchDebug>true</MtouchDebug>
|
||||
<CodesignEntitlements>Entitlements.plist</CodesignEntitlements>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|iPhoneSimulator' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\iPhoneSimulator\Release</OutputPath>
|
||||
<DefineConstants>USE_LIBRARY_LOADER</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<MtouchLink>SdkOnly</MtouchLink>
|
||||
<MtouchExtraArgs>--linkskip=Xamarin.Forms.Platform.iOS --linkskip=Xamarin.Forms.Platform --linkskip=Xamarin.Forms.Core --linkskip=Xamarin.Forms.Xaml</MtouchExtraArgs>
|
||||
<MtouchArch>x86_64</MtouchArch>
|
||||
<ConsolePause>false</ConsolePause>
|
||||
<CodesignEntitlements>Entitlements.plist</CodesignEntitlements>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|iPhone' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\iPhone\Debug</OutputPath>
|
||||
<DefineConstants>DEBUG;USE_LIBRARY_LOADER</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<MtouchLink>SdkOnly</MtouchLink>
|
||||
<MtouchExtraArgs>--linkskip=Xamarin.Forms.Platform.iOS --linkskip=Xamarin.Forms.Platform --linkskip=Xamarin.Forms.Core --linkskip=Xamarin.Forms.Xaml</MtouchExtraArgs>
|
||||
<ConsolePause>false</ConsolePause>
|
||||
<MtouchArch>ARM64</MtouchArch>
|
||||
<CodesignKey>iPhone Developer</CodesignKey>
|
||||
<MtouchDebug>true</MtouchDebug>
|
||||
<CodesignEntitlements>Entitlements.plist</CodesignEntitlements>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|iPhone' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\iPhone\Release</OutputPath>
|
||||
<DefineConstants>USE_LIBRARY_LOADER</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<MtouchLink>SdkOnly</MtouchLink>
|
||||
<MtouchExtraArgs>--linkskip=Xamarin.Forms.Platform.iOS --linkskip=Xamarin.Forms.Platform --linkskip=Xamarin.Forms.Core --linkskip=Xamarin.Forms.Xaml</MtouchExtraArgs>
|
||||
<MtouchArch>ARM64</MtouchArch>
|
||||
<ConsolePause>false</ConsolePause>
|
||||
<CodesignKey>iPhone Developer</CodesignKey>
|
||||
<CodesignEntitlements>Entitlements.plist</CodesignEntitlements>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="Xamarin.iOS" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Xamarin.Essentials" Version="1.6.1" />
|
||||
<PackageReference Include="Xamarin.Forms" Version="4.5.0.725" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.devices" Version="2.5.25" />
|
||||
<PackageReference Include="xunit.skippablefact" Version="1.4.13" />
|
||||
<PackageReference Include="Validation" Version="2.4.22" />
|
||||
<PackageReference Include="Microsoft.DotNet.XHarness.TestRunners.Xunit" Version="1.0.0-prerelease.20602.1" />
|
||||
<PackageReference Include="System.Memory" Version="4.5.3" ExcludeAssets="all" />
|
||||
<PackageReference Include="System.Buffers" Version="4.5.1" ExcludeAssets="all" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Main.cs" />
|
||||
<Compile Include="AppDelegate.cs" />
|
||||
<Compile Include="TestApplicationDelegate.cs" />
|
||||
<None Include="Entitlements.plist" />
|
||||
<None Include="Info.plist" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="..\Tests\**\*.cs" Link="%(RecursiveDir)%(FileName)%(Extension)" Exclude="..\Tests\GlContexts\*\*;..\Tests\PlatformUtils\*\*" />
|
||||
<Compile Include="..\..\binding\Binding.Shared\LibraryLoader.cs" Link="PlatformUtils\LibraryLoader.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="..\Content\**\*" Link="Content\%(RecursiveDir)%(FileName)%(Extension)" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<InterfaceDefinition Include="Resources\LaunchScreen.storyboard" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\binding\HarfBuzzSharp.iOS\HarfBuzzSharp.iOS.csproj">
|
||||
<Project>{D958E2E9-DE32-42E8-AB10-D25E4186C4E1}</Project>
|
||||
<Name>HarfBuzzSharp.iOS</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\source\SkiaSharp.HarfBuzz\SkiaSharp.HarfBuzz\SkiaSharp.HarfBuzz.csproj">
|
||||
<Project>{A5614B8C-31C8-43A3-9BF9-2719E4BE4D36}</Project>
|
||||
<Name>SkiaSharp.HarfBuzz</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\binding\SkiaSharp.iOS\SkiaSharp.iOS.csproj">
|
||||
<Project>{A4146A87-DB60-4A17-A179-0E2E4255A08E}</Project>
|
||||
<Name>SkiaSharp.iOS</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\source\SkiaSharp.Views.Forms\SkiaSharp.Views.Forms.iOS\SkiaSharp.Views.Forms.iOS.csproj">
|
||||
<Project>{0254162B-6B4A-459E-BD96-3A42A104C144}</Project>
|
||||
<Name>SkiaSharp.Views.Forms.iOS</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\source\SkiaSharp.Views\SkiaSharp.Views.iOS\SkiaSharp.Views.iOS.csproj">
|
||||
<Project>{549F8E22-A756-4E99-A84A-C4E74832DA95}</Project>
|
||||
<Name>SkiaSharp.Views.iOS</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.CSharp.targets" />
|
||||
<Import Project="..\..\output\SkiaSharp\nuget\build\xamarinios1.0\SkiaSharp.targets" Condition="Exists('..\..\output\SkiaSharp\nuget\build\xamarinios1.0\SkiaSharp.targets')" />
|
||||
<Import Project="..\..\output\HarfBuzzSharp\nuget\build\xamarinios1.0\HarfBuzzSharp.targets" Condition="Exists('..\..\output\HarfBuzzSharp\nuget\build\xamarinios1.0\HarfBuzzSharp.targets')" />
|
||||
</Project>
|
|
@ -0,0 +1,86 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
using Foundation;
|
||||
using Microsoft.DotNet.XHarness.TestRunners.Common;
|
||||
using Microsoft.DotNet.XHarness.TestRunners.Xunit;
|
||||
using UIKit;
|
||||
using Xamarin.Essentials;
|
||||
|
||||
namespace SkiaSharp.Tests
|
||||
{
|
||||
[Register(nameof(TestApplicationDelegate))]
|
||||
public class TestApplicationDelegate : UIApplicationDelegate
|
||||
{
|
||||
public override UIWindow Window { get; set; }
|
||||
|
||||
public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
|
||||
{
|
||||
Window = new UIWindow(UIScreen.MainScreen.Bounds)
|
||||
{
|
||||
RootViewController = new ViewController()
|
||||
};
|
||||
Window.MakeKeyAndVisible();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
class ViewController : UIViewController
|
||||
{
|
||||
public override async void ViewDidLoad()
|
||||
{
|
||||
base.ViewDidLoad();
|
||||
|
||||
var entryPoint = new TestsEntryPoint();
|
||||
|
||||
await entryPoint.RunAsync();
|
||||
}
|
||||
}
|
||||
|
||||
class TestsEntryPoint : iOSApplicationEntryPoint
|
||||
{
|
||||
protected override bool LogExcludedTests => true;
|
||||
|
||||
protected override int? MaxParallelThreads => Environment.ProcessorCount;
|
||||
|
||||
protected override IDevice Device { get; } = new TestDevice();
|
||||
|
||||
protected override IEnumerable<TestAssemblyInfo> GetTestAssemblies()
|
||||
{
|
||||
yield return new TestAssemblyInfo(Assembly.GetExecutingAssembly(), Assembly.GetExecutingAssembly().Location);
|
||||
}
|
||||
|
||||
protected override void TerminateWithSuccess()
|
||||
{
|
||||
Console.WriteLine("Exiting test run with success");
|
||||
|
||||
var s = new ObjCRuntime.Selector("terminateWithSuccess");
|
||||
UIApplication.SharedApplication.PerformSelector(s, UIApplication.SharedApplication, 0);
|
||||
}
|
||||
|
||||
protected override TestRunner GetTestRunner(LogWriter logWriter)
|
||||
{
|
||||
var testRunner = base.GetTestRunner(logWriter);
|
||||
return testRunner;
|
||||
}
|
||||
}
|
||||
|
||||
class TestDevice : IDevice
|
||||
{
|
||||
public string BundleIdentifier => AppInfo.PackageName;
|
||||
|
||||
public string UniqueIdentifier => Guid.NewGuid().ToString("N");
|
||||
|
||||
public string Name => DeviceInfo.Name;
|
||||
|
||||
public string Model => DeviceInfo.Model;
|
||||
|
||||
public string SystemName => DeviceInfo.Platform.ToString();
|
||||
|
||||
public string SystemVersion => DeviceInfo.VersionString;
|
||||
|
||||
public string Locale => CultureInfo.CurrentCulture.Name;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -19,19 +19,24 @@ namespace SkiaSharp.Tests
|
|||
|
||||
protected static readonly string[] UnicodeFontFamilies;
|
||||
protected static readonly string DefaultFontFamily;
|
||||
protected static readonly string PathToAssembly;
|
||||
protected static readonly string PathToFonts;
|
||||
protected static readonly string PathToImages;
|
||||
|
||||
public static readonly string PathToAssembly;
|
||||
public static readonly string PathToFonts;
|
||||
public static readonly string PathToImages;
|
||||
|
||||
static BaseTest()
|
||||
{
|
||||
// the the base paths
|
||||
#if __ANDROID__ || __IOS__
|
||||
PathToAssembly = Xamarin.Essentials.FileSystem.CacheDirectory;
|
||||
#else
|
||||
PathToAssembly = Directory.GetCurrentDirectory();
|
||||
#endif
|
||||
PathToFonts = Path.Combine(PathToAssembly, "fonts");
|
||||
PathToImages = Path.Combine(PathToAssembly, "images");
|
||||
|
||||
// some platforms run the tests from a temporary location, so copy the native files
|
||||
#if !NET_STANDARD
|
||||
#if !NET_STANDARD && !__ANDROID__ && !__IOS__
|
||||
var skiaRoot = Path.GetDirectoryName(typeof(SkiaSharp.SKImageInfo).Assembly.Location);
|
||||
var harfRoot = Path.GetDirectoryName(typeof(HarfBuzzSharp.Buffer).Assembly.Location);
|
||||
|
||||
|
@ -56,11 +61,19 @@ namespace SkiaSharp.Tests
|
|||
IsRuntimeMono = Type.GetType("Mono.Runtime") != null;
|
||||
|
||||
// set the test fields
|
||||
#if __ANDROID__
|
||||
DefaultFontFamily = "sans-serif";
|
||||
UnicodeFontFamilies = new[] { "Noto Color Emoji" };
|
||||
#elif __IOS__
|
||||
DefaultFontFamily = "Arial";
|
||||
UnicodeFontFamilies = new[] { "Apple Color Emoji" };
|
||||
#else
|
||||
DefaultFontFamily = IsLinux ? "DejaVu Sans" : "Arial";
|
||||
UnicodeFontFamilies =
|
||||
IsLinux ? new[] { "Symbola" } :
|
||||
IsMac ? new[] { "Apple Color Emoji" } :
|
||||
new[] { "Segoe UI Emoji", "Segoe UI Symbol" };
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void CollectGarbage()
|
||||
|
|
|
@ -20,11 +20,6 @@ namespace SkiaSharp.Tests
|
|||
|
||||
public GarbageCleanupFixture()
|
||||
{
|
||||
var aliveObjects = HandleDictionary.instances.Values
|
||||
.Select(o => o.Target)
|
||||
.Where(o => IsExpectedToBeDead(o, null))
|
||||
.ToList();
|
||||
Assert.Empty(aliveObjects);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
@ -34,7 +29,7 @@ namespace SkiaSharp.Tests
|
|||
|
||||
var staticObjects = HandleDictionary.instances.Values
|
||||
.Select(o => o.Target)
|
||||
.Where(o => !IsExpectedToBeDead(o, null))
|
||||
.Where(o => !IsExpectedToBeDead(o))
|
||||
.Cast<SKObject>()
|
||||
.ToList();
|
||||
var staticChildren = staticObjects
|
||||
|
@ -44,7 +39,7 @@ namespace SkiaSharp.Tests
|
|||
// make sure nothing is alive
|
||||
var aliveObjects = HandleDictionary.instances.Values
|
||||
.Select(o => o.Target)
|
||||
.Where(o => IsExpectedToBeDead(o, staticChildren))
|
||||
.Where(o => IsExpectedToBeDead(o))
|
||||
.Cast<SKObject>()
|
||||
.ToList();
|
||||
foreach (var o in staticChildren)
|
||||
|
@ -65,7 +60,7 @@ namespace SkiaSharp.Tests
|
|||
#endif
|
||||
}
|
||||
|
||||
private bool IsExpectedToBeDead(object instance, IEnumerable<SKObject> exceptions)
|
||||
private bool IsExpectedToBeDead(object instance)
|
||||
{
|
||||
if (instance == null)
|
||||
return false;
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
using System.IO;
|
||||
|
||||
namespace SkiaSharp.Tests
|
||||
{
|
||||
internal static class AssetCopier
|
||||
{
|
||||
public static void CopyAssets()
|
||||
{
|
||||
var fontsRoot = BaseTest.PathToFonts;
|
||||
var imagesRoot = BaseTest.PathToImages;
|
||||
|
||||
var assembly = typeof(AssetCopier).Assembly;
|
||||
var prefix = assembly.GetName().Name + ".Content.";
|
||||
var fontsPrefix = "fonts.";
|
||||
var imagesPrefix = "images.";
|
||||
|
||||
var names = assembly.GetManifestResourceNames();
|
||||
|
||||
foreach (var name in names)
|
||||
{
|
||||
if (!name.StartsWith(prefix))
|
||||
continue;
|
||||
|
||||
var filename = name.Substring(prefix.Length);
|
||||
var root = "";
|
||||
|
||||
if (filename.StartsWith(fontsPrefix))
|
||||
{
|
||||
filename = filename.Substring(fontsPrefix.Length);
|
||||
root = fontsRoot;
|
||||
}
|
||||
else if (filename.StartsWith(imagesPrefix))
|
||||
{
|
||||
filename = filename.Substring(imagesPrefix.Length);
|
||||
root = imagesRoot;
|
||||
}
|
||||
|
||||
if (!Directory.Exists(root))
|
||||
Directory.CreateDirectory(root);
|
||||
|
||||
using var stream = assembly.GetManifestResourceStream(name);
|
||||
using var dest = File.Create(Path.Combine(root, filename));
|
||||
stream.CopyTo(dest);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -501,7 +501,11 @@ namespace SkiaSharp.Tests
|
|||
mask.FreeImage();
|
||||
}
|
||||
|
||||
#if __ANDROID__ || __IOS__
|
||||
[SkippableTheory(Skip = "Mobile devices sometimes run out of memory.")]
|
||||
#else
|
||||
[SkippableTheory]
|
||||
#endif
|
||||
[InlineData(100, 1000)]
|
||||
public static void ImageScalingMultipleThreadsTest(int numThreads, int numIterationsPerThread)
|
||||
{
|
||||
|
|
|
@ -180,12 +180,19 @@ namespace SkiaSharp.Tests
|
|||
[SkippableFact]
|
||||
public void PremultipliedColorsHaveCorrectBitShift()
|
||||
{
|
||||
var isARGB =
|
||||
#if __ANDROID__
|
||||
false;
|
||||
#else
|
||||
IsWindows || IsLinux;
|
||||
#endif
|
||||
|
||||
var color = (SKColor)0x12345678;
|
||||
|
||||
Assert.Equal(new SKColor(0x34, 0x56, 0x78, 0x12), color);
|
||||
|
||||
SKPMColor pmcolor;
|
||||
if (IsWindows || IsLinux) {
|
||||
if (isARGB) {
|
||||
pmcolor = (SKPMColor)0x12345678;
|
||||
} else {
|
||||
pmcolor = (SKPMColor)0x12785634;
|
||||
|
@ -203,7 +210,7 @@ namespace SkiaSharp.Tests
|
|||
Assert.Equal(0x78, color.Blue);
|
||||
Assert.Equal(0x78, pmcolor.Blue);
|
||||
|
||||
if (IsWindows || IsLinux) {
|
||||
if (isARGB) {
|
||||
// ARGB
|
||||
Assert.Equal(24, SKImageInfo.PlatformColorAlphaShift);
|
||||
Assert.Equal(16, SKImageInfo.PlatformColorRedShift);
|
||||
|
|
|
@ -13,16 +13,19 @@ namespace SkiaSharp.Tests
|
|||
var fonts = SKFontManager.Default;
|
||||
var emoji = "🚀";
|
||||
var emojiChar = StringUtilities.GetUnicodeCharacterCode(emoji, SKTextEncoding.Utf32);
|
||||
using (var typeface = fonts.MatchCharacter(emojiChar))
|
||||
using var typeface = fonts.MatchCharacter(emojiChar);
|
||||
Assert.NotNull(typeface);
|
||||
|
||||
var familyName = typeface.FamilyName;
|
||||
if (typeface.FamilyName.EndsWith("##fallback"))
|
||||
{
|
||||
Assert.NotNull(typeface);
|
||||
if (IsLinux)
|
||||
Assert.Equal("Symbola", typeface.FamilyName);
|
||||
else if (IsMac)
|
||||
Assert.Equal("Apple Color Emoji", typeface.FamilyName);
|
||||
else if (IsWindows)
|
||||
Assert.Contains(typeface.FamilyName, new[] { "Segoe UI Emoji", "Segoe UI Symbol" });
|
||||
using var stream = typeface.OpenStream();
|
||||
using var temp = SKTypeface.FromStream(stream);
|
||||
|
||||
familyName = temp.FamilyName;
|
||||
}
|
||||
|
||||
Assert.Contains(familyName, UnicodeFontFamilies);
|
||||
}
|
||||
|
||||
[SkippableFact]
|
||||
|
|
|
@ -59,7 +59,7 @@ namespace SkiaSharp.Tests
|
|||
}
|
||||
|
||||
[SkippableFact]
|
||||
public void ToRasterImageFalseReturnsNonLazy()
|
||||
public void ToRasterImageTrueFalseReturnsNonLazy()
|
||||
{
|
||||
using var data = SKData.Create(Path.Combine(PathToImages, "baboon.jpg"));
|
||||
using var image = SKImage.FromEncodedData(data);
|
||||
|
@ -74,6 +74,22 @@ namespace SkiaSharp.Tests
|
|||
Assert.Equal(nonLazy, nonLazy.ToRasterImage());
|
||||
}
|
||||
|
||||
[SkippableFact]
|
||||
public void ToRasterImageTrueTrueReturnsNonLazy()
|
||||
{
|
||||
using var data = SKData.Create(Path.Combine(PathToImages, "baboon.jpg"));
|
||||
using var image = SKImage.FromEncodedData(data);
|
||||
|
||||
Assert.True(image.IsLazyGenerated);
|
||||
Assert.Null(image.PeekPixels());
|
||||
|
||||
using var nonLazy = image.ToRasterImage(true);
|
||||
Assert.NotEqual(image, nonLazy);
|
||||
Assert.False(nonLazy.IsLazyGenerated);
|
||||
Assert.NotNull(nonLazy.PeekPixels());
|
||||
Assert.Equal(nonLazy, nonLazy.ToRasterImage(true));
|
||||
}
|
||||
|
||||
[SkippableFact]
|
||||
public void ImmutableBitmapsAreNotCopied()
|
||||
{
|
||||
|
|
|
@ -321,6 +321,7 @@ namespace SkiaSharp.Tests
|
|||
}
|
||||
}
|
||||
|
||||
#if !__ANDROID__ && !__IOS__
|
||||
[SkippableFact]
|
||||
public async Task DelayedConstructionDoesNotCreateInvalidState()
|
||||
{
|
||||
|
@ -437,6 +438,7 @@ namespace SkiaSharp.Tests
|
|||
Assert.True(SKObject.GetInstance<DelayedDestructionObject>(handle, out var final));
|
||||
Assert.Same(objSlow, final);
|
||||
}
|
||||
#endif
|
||||
|
||||
private class DelayedConstructionObject : SKObject
|
||||
{
|
||||
|
|
|
@ -145,6 +145,7 @@ namespace SkiaSharp.Tests
|
|||
{
|
||||
try
|
||||
{
|
||||
#if !__ANDROID__ && !__IOS__
|
||||
if (IsLinux)
|
||||
{
|
||||
return new GlxContext();
|
||||
|
@ -158,6 +159,7 @@ namespace SkiaSharp.Tests
|
|||
return new WglContext();
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
throw new PlatformNotSupportedException();
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче