2019-06-18 17:33:10 +03:00
|
|
|
|
using System;
|
|
|
|
|
using System.IO;
|
|
|
|
|
|
|
|
|
|
namespace SkiaSharp.Tests
|
|
|
|
|
{
|
|
|
|
|
public abstract class BaseTest
|
|
|
|
|
{
|
Re-work the managed-native types (#900)
Changes:
- Added `GCHandleProxy` to debug builds
- this is used to track all `GCHandle` `Alloc` and `Free` calls to ensure that all allocations are freed.
- added some unit tests to make sure this is actually enforced
- as a result, several object are now freed correctly
- Added `ISKReferenceCounted` and `ISKNonVirtualReferenceCounted` interfaces to represent the reference counting types used in the native library
- this helps with automatically de-referencing objects
- `SKAbstractManagedStream`, `SKAbstractManagedWStream` and `SKDrawable` have been re-written to use better delegates
- instead of passing each of the delegates as parameters, they are now a struct that is passed as a single object
- better for extensions (which there shouldn't be) and only a single static field on the type
- removed the usage of `Marshal.GetFunctionPointerForDelegate`, which should help out with WASM (see #876)
- the objects now only keep weak references, meaning that they can now be garbage collected
- instead of trying to resolve the instances with a dictionary, a delegate is used and passed as "user context"
- Moved some of the repetitive logic from the types into the base `SKObject` and `SKNativeObject`
- some logic is automatically executed if the concrete type is `ISKReferenceCounted` or `ISKNonVirtualReferenceCounted`
- with the more centralized logic and stricter patterns, better tests can be written to make sure all memory is freed correctly and timely
- `SKData`, `SKFontManager` and `SKTypeface` now correctly prevent disposal of the "static" instances
- `SKPaint` now references the `Shader`, `MaskFilter`, `ColorFilter`, `ImageFilter`, `Typeface` and `PathEffect` properties
- this prevents accidental collection, or non-collection when the object goes out of scope
- the `SKPath` iterators (`Iterator` and `RawIterator`) and op builder (`OpBuilder`) now correctly own and dispose their native objects
- `SKRegion` objects are now disposed on the native side
- `SKTypeface` construction from a `SKManagedStream` (via both `SKTypeface` and `SKFontManager`) now copy the contents of the .NET `Stream` into a native memory
- typeface construction requires multiple seeks (previously, the stream was copied only if it was non-seekable)
- it also requires "duplicating" the stream, which is not supported on .NET streams
- duplicates or forks of a stream means that each of the streams need to be read concurrently from different locations
- .NET streams can only have a single position
- Updated the NuGets used for the tests
- using the `Xunit.AssemblyFixture` and `Xunit.SkippableFact` NuGets instead of using the code directly
- removed the `Xunit.Categories` NuGet as it was preventing tests from running
This PR has a big set of changes that may be breaking due to bug fixes:
- The `SKAbstractManagedStream`, `SKAbstractManagedWStream` and `SKDrawable` no longer prevent the GC from collecting them. This means that if code no longer references them, they will be disposed.
- As far as I can tell, this should not be a problem for the streams as they are never kept around - they are just used for reading and writing and typically only need to live for as long as a single method, and then need to be disposed by the caller. The `SKTypeface` and `SKDocument` do keep it around for a bit, but then they also take ownership of the stream and keep a hard reference to the streams themselves. They will dispose the streams when they are disposed.
- `SKDrawable` is never kept around and is entirely a user-controlled object. If it goes out of scope, skia doesn't have a reference anyway.
- The `SKFontManager` and `SKTypeface` no longer use the managed streams (`SKManagedStream` or `Stream`) directly
- they make a copy.
- This is simply because skia streams can do things that are not possible for .NET - they can be read concurrently from different positions. If a `SKFileStream` or `SKMemoryStream` are passed, then the streams are not copied.
- Further optimizations can be made in the case of a `MemoryStream` or `byte[]` to not actually copy but use GC pinning to get a handle to the managed data and work with pointers. But this can be done later so that this PR can be merged and tested.
2019-07-30 04:26:21 +03:00
|
|
|
|
protected const string CategoryKey = "Category";
|
|
|
|
|
|
2019-06-18 17:33:10 +03:00
|
|
|
|
protected const string GpuCategory = "GPU";
|
Re-work the managed-native types (#900)
Changes:
- Added `GCHandleProxy` to debug builds
- this is used to track all `GCHandle` `Alloc` and `Free` calls to ensure that all allocations are freed.
- added some unit tests to make sure this is actually enforced
- as a result, several object are now freed correctly
- Added `ISKReferenceCounted` and `ISKNonVirtualReferenceCounted` interfaces to represent the reference counting types used in the native library
- this helps with automatically de-referencing objects
- `SKAbstractManagedStream`, `SKAbstractManagedWStream` and `SKDrawable` have been re-written to use better delegates
- instead of passing each of the delegates as parameters, they are now a struct that is passed as a single object
- better for extensions (which there shouldn't be) and only a single static field on the type
- removed the usage of `Marshal.GetFunctionPointerForDelegate`, which should help out with WASM (see #876)
- the objects now only keep weak references, meaning that they can now be garbage collected
- instead of trying to resolve the instances with a dictionary, a delegate is used and passed as "user context"
- Moved some of the repetitive logic from the types into the base `SKObject` and `SKNativeObject`
- some logic is automatically executed if the concrete type is `ISKReferenceCounted` or `ISKNonVirtualReferenceCounted`
- with the more centralized logic and stricter patterns, better tests can be written to make sure all memory is freed correctly and timely
- `SKData`, `SKFontManager` and `SKTypeface` now correctly prevent disposal of the "static" instances
- `SKPaint` now references the `Shader`, `MaskFilter`, `ColorFilter`, `ImageFilter`, `Typeface` and `PathEffect` properties
- this prevents accidental collection, or non-collection when the object goes out of scope
- the `SKPath` iterators (`Iterator` and `RawIterator`) and op builder (`OpBuilder`) now correctly own and dispose their native objects
- `SKRegion` objects are now disposed on the native side
- `SKTypeface` construction from a `SKManagedStream` (via both `SKTypeface` and `SKFontManager`) now copy the contents of the .NET `Stream` into a native memory
- typeface construction requires multiple seeks (previously, the stream was copied only if it was non-seekable)
- it also requires "duplicating" the stream, which is not supported on .NET streams
- duplicates or forks of a stream means that each of the streams need to be read concurrently from different locations
- .NET streams can only have a single position
- Updated the NuGets used for the tests
- using the `Xunit.AssemblyFixture` and `Xunit.SkippableFact` NuGets instead of using the code directly
- removed the `Xunit.Categories` NuGet as it was preventing tests from running
This PR has a big set of changes that may be breaking due to bug fixes:
- The `SKAbstractManagedStream`, `SKAbstractManagedWStream` and `SKDrawable` no longer prevent the GC from collecting them. This means that if code no longer references them, they will be disposed.
- As far as I can tell, this should not be a problem for the streams as they are never kept around - they are just used for reading and writing and typically only need to live for as long as a single method, and then need to be disposed by the caller. The `SKTypeface` and `SKDocument` do keep it around for a bit, but then they also take ownership of the stream and keep a hard reference to the streams themselves. They will dispose the streams when they are disposed.
- `SKDrawable` is never kept around and is entirely a user-controlled object. If it goes out of scope, skia doesn't have a reference anyway.
- The `SKFontManager` and `SKTypeface` no longer use the managed streams (`SKManagedStream` or `Stream`) directly
- they make a copy.
- This is simply because skia streams can do things that are not possible for .NET - they can be read concurrently from different positions. If a `SKFileStream` or `SKMemoryStream` are passed, then the streams are not copied.
- Further optimizations can be made in the case of a `MemoryStream` or `byte[]` to not actually copy but use GC pinning to get a handle to the managed data and work with pointers. But this can be done later so that this PR can be merged and tested.
2019-07-30 04:26:21 +03:00
|
|
|
|
protected const string MatchCharacterCategory = "MatchCharacter";
|
2019-06-18 17:33:10 +03:00
|
|
|
|
|
2020-06-19 02:49:29 +03:00
|
|
|
|
protected static bool IsLinux = PlatformConfiguration.IsLinux;
|
|
|
|
|
protected static bool IsMac = PlatformConfiguration.IsMac;
|
|
|
|
|
protected static bool IsUnix = PlatformConfiguration.IsUnix;
|
|
|
|
|
protected static bool IsWindows = PlatformConfiguration.IsWindows;
|
2019-06-18 17:33:10 +03:00
|
|
|
|
|
Re-work the managed-native types (#900)
Changes:
- Added `GCHandleProxy` to debug builds
- this is used to track all `GCHandle` `Alloc` and `Free` calls to ensure that all allocations are freed.
- added some unit tests to make sure this is actually enforced
- as a result, several object are now freed correctly
- Added `ISKReferenceCounted` and `ISKNonVirtualReferenceCounted` interfaces to represent the reference counting types used in the native library
- this helps with automatically de-referencing objects
- `SKAbstractManagedStream`, `SKAbstractManagedWStream` and `SKDrawable` have been re-written to use better delegates
- instead of passing each of the delegates as parameters, they are now a struct that is passed as a single object
- better for extensions (which there shouldn't be) and only a single static field on the type
- removed the usage of `Marshal.GetFunctionPointerForDelegate`, which should help out with WASM (see #876)
- the objects now only keep weak references, meaning that they can now be garbage collected
- instead of trying to resolve the instances with a dictionary, a delegate is used and passed as "user context"
- Moved some of the repetitive logic from the types into the base `SKObject` and `SKNativeObject`
- some logic is automatically executed if the concrete type is `ISKReferenceCounted` or `ISKNonVirtualReferenceCounted`
- with the more centralized logic and stricter patterns, better tests can be written to make sure all memory is freed correctly and timely
- `SKData`, `SKFontManager` and `SKTypeface` now correctly prevent disposal of the "static" instances
- `SKPaint` now references the `Shader`, `MaskFilter`, `ColorFilter`, `ImageFilter`, `Typeface` and `PathEffect` properties
- this prevents accidental collection, or non-collection when the object goes out of scope
- the `SKPath` iterators (`Iterator` and `RawIterator`) and op builder (`OpBuilder`) now correctly own and dispose their native objects
- `SKRegion` objects are now disposed on the native side
- `SKTypeface` construction from a `SKManagedStream` (via both `SKTypeface` and `SKFontManager`) now copy the contents of the .NET `Stream` into a native memory
- typeface construction requires multiple seeks (previously, the stream was copied only if it was non-seekable)
- it also requires "duplicating" the stream, which is not supported on .NET streams
- duplicates or forks of a stream means that each of the streams need to be read concurrently from different locations
- .NET streams can only have a single position
- Updated the NuGets used for the tests
- using the `Xunit.AssemblyFixture` and `Xunit.SkippableFact` NuGets instead of using the code directly
- removed the `Xunit.Categories` NuGet as it was preventing tests from running
This PR has a big set of changes that may be breaking due to bug fixes:
- The `SKAbstractManagedStream`, `SKAbstractManagedWStream` and `SKDrawable` no longer prevent the GC from collecting them. This means that if code no longer references them, they will be disposed.
- As far as I can tell, this should not be a problem for the streams as they are never kept around - they are just used for reading and writing and typically only need to live for as long as a single method, and then need to be disposed by the caller. The `SKTypeface` and `SKDocument` do keep it around for a bit, but then they also take ownership of the stream and keep a hard reference to the streams themselves. They will dispose the streams when they are disposed.
- `SKDrawable` is never kept around and is entirely a user-controlled object. If it goes out of scope, skia doesn't have a reference anyway.
- The `SKFontManager` and `SKTypeface` no longer use the managed streams (`SKManagedStream` or `Stream`) directly
- they make a copy.
- This is simply because skia streams can do things that are not possible for .NET - they can be read concurrently from different positions. If a `SKFileStream` or `SKMemoryStream` are passed, then the streams are not copied.
- Further optimizations can be made in the case of a `MemoryStream` or `byte[]` to not actually copy but use GC pinning to get a handle to the managed data and work with pointers. But this can be done later so that this PR can be merged and tested.
2019-07-30 04:26:21 +03:00
|
|
|
|
protected static bool IsRuntimeMono;
|
|
|
|
|
|
2019-06-18 17:33:10 +03:00
|
|
|
|
protected static readonly string[] UnicodeFontFamilies;
|
|
|
|
|
protected static readonly string DefaultFontFamily;
|
2021-02-18 16:10:08 +03:00
|
|
|
|
|
|
|
|
|
public static readonly string PathToAssembly;
|
|
|
|
|
public static readonly string PathToFonts;
|
|
|
|
|
public static readonly string PathToImages;
|
2019-06-18 17:33:10 +03:00
|
|
|
|
|
|
|
|
|
static BaseTest()
|
|
|
|
|
{
|
|
|
|
|
// the the base paths
|
2021-02-21 19:11:28 +03:00
|
|
|
|
#if __ANDROID__ || __IOS__
|
2021-02-18 16:10:08 +03:00
|
|
|
|
PathToAssembly = Xamarin.Essentials.FileSystem.CacheDirectory;
|
|
|
|
|
#else
|
2019-06-18 17:33:10 +03:00
|
|
|
|
PathToAssembly = Directory.GetCurrentDirectory();
|
2021-02-18 16:10:08 +03:00
|
|
|
|
#endif
|
2019-06-18 17:33:10 +03:00
|
|
|
|
PathToFonts = Path.Combine(PathToAssembly, "fonts");
|
|
|
|
|
PathToImages = Path.Combine(PathToAssembly, "images");
|
|
|
|
|
|
|
|
|
|
// some platforms run the tests from a temporary location, so copy the native files
|
2021-04-04 23:28:38 +03:00
|
|
|
|
#if !NETCOREAPP && !__ANDROID__ && !__IOS__ && !NET6_0_OR_GREATER
|
2019-06-18 17:33:10 +03:00
|
|
|
|
var skiaRoot = Path.GetDirectoryName(typeof(SkiaSharp.SKImageInfo).Assembly.Location);
|
|
|
|
|
var harfRoot = Path.GetDirectoryName(typeof(HarfBuzzSharp.Buffer).Assembly.Location);
|
|
|
|
|
|
|
|
|
|
foreach (var file in Directory.GetFiles(PathToAssembly))
|
|
|
|
|
{
|
|
|
|
|
var fname = Path.GetFileNameWithoutExtension(file);
|
|
|
|
|
|
|
|
|
|
var skiaDest = Path.Combine(skiaRoot, Path.GetFileName(file));
|
|
|
|
|
if (fname == "libSkiaSharp" && !File.Exists(skiaDest))
|
|
|
|
|
{
|
|
|
|
|
File.Copy(file, skiaDest, true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var harfDest = Path.Combine(harfRoot, Path.GetFileName(file));
|
|
|
|
|
if (fname == "libHarfBuzzSharp" && !File.Exists(harfDest))
|
|
|
|
|
{
|
|
|
|
|
File.Copy(file, harfDest, true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
Re-work the managed-native types (#900)
Changes:
- Added `GCHandleProxy` to debug builds
- this is used to track all `GCHandle` `Alloc` and `Free` calls to ensure that all allocations are freed.
- added some unit tests to make sure this is actually enforced
- as a result, several object are now freed correctly
- Added `ISKReferenceCounted` and `ISKNonVirtualReferenceCounted` interfaces to represent the reference counting types used in the native library
- this helps with automatically de-referencing objects
- `SKAbstractManagedStream`, `SKAbstractManagedWStream` and `SKDrawable` have been re-written to use better delegates
- instead of passing each of the delegates as parameters, they are now a struct that is passed as a single object
- better for extensions (which there shouldn't be) and only a single static field on the type
- removed the usage of `Marshal.GetFunctionPointerForDelegate`, which should help out with WASM (see #876)
- the objects now only keep weak references, meaning that they can now be garbage collected
- instead of trying to resolve the instances with a dictionary, a delegate is used and passed as "user context"
- Moved some of the repetitive logic from the types into the base `SKObject` and `SKNativeObject`
- some logic is automatically executed if the concrete type is `ISKReferenceCounted` or `ISKNonVirtualReferenceCounted`
- with the more centralized logic and stricter patterns, better tests can be written to make sure all memory is freed correctly and timely
- `SKData`, `SKFontManager` and `SKTypeface` now correctly prevent disposal of the "static" instances
- `SKPaint` now references the `Shader`, `MaskFilter`, `ColorFilter`, `ImageFilter`, `Typeface` and `PathEffect` properties
- this prevents accidental collection, or non-collection when the object goes out of scope
- the `SKPath` iterators (`Iterator` and `RawIterator`) and op builder (`OpBuilder`) now correctly own and dispose their native objects
- `SKRegion` objects are now disposed on the native side
- `SKTypeface` construction from a `SKManagedStream` (via both `SKTypeface` and `SKFontManager`) now copy the contents of the .NET `Stream` into a native memory
- typeface construction requires multiple seeks (previously, the stream was copied only if it was non-seekable)
- it also requires "duplicating" the stream, which is not supported on .NET streams
- duplicates or forks of a stream means that each of the streams need to be read concurrently from different locations
- .NET streams can only have a single position
- Updated the NuGets used for the tests
- using the `Xunit.AssemblyFixture` and `Xunit.SkippableFact` NuGets instead of using the code directly
- removed the `Xunit.Categories` NuGet as it was preventing tests from running
This PR has a big set of changes that may be breaking due to bug fixes:
- The `SKAbstractManagedStream`, `SKAbstractManagedWStream` and `SKDrawable` no longer prevent the GC from collecting them. This means that if code no longer references them, they will be disposed.
- As far as I can tell, this should not be a problem for the streams as they are never kept around - they are just used for reading and writing and typically only need to live for as long as a single method, and then need to be disposed by the caller. The `SKTypeface` and `SKDocument` do keep it around for a bit, but then they also take ownership of the stream and keep a hard reference to the streams themselves. They will dispose the streams when they are disposed.
- `SKDrawable` is never kept around and is entirely a user-controlled object. If it goes out of scope, skia doesn't have a reference anyway.
- The `SKFontManager` and `SKTypeface` no longer use the managed streams (`SKManagedStream` or `Stream`) directly
- they make a copy.
- This is simply because skia streams can do things that are not possible for .NET - they can be read concurrently from different positions. If a `SKFileStream` or `SKMemoryStream` are passed, then the streams are not copied.
- Further optimizations can be made in the case of a `MemoryStream` or `byte[]` to not actually copy but use GC pinning to get a handle to the managed data and work with pointers. But this can be done later so that this PR can be merged and tested.
2019-07-30 04:26:21 +03:00
|
|
|
|
IsRuntimeMono = Type.GetType("Mono.Runtime") != null;
|
|
|
|
|
|
2019-06-18 17:33:10 +03:00
|
|
|
|
// set the test fields
|
2021-02-18 16:10:08 +03:00
|
|
|
|
#if __ANDROID__
|
|
|
|
|
DefaultFontFamily = "sans-serif";
|
|
|
|
|
UnicodeFontFamilies = new[] { "Noto Color Emoji" };
|
2021-02-21 19:11:28 +03:00
|
|
|
|
#elif __IOS__
|
|
|
|
|
DefaultFontFamily = "Arial";
|
|
|
|
|
UnicodeFontFamilies = new[] { "Apple Color Emoji" };
|
2021-02-18 16:10:08 +03:00
|
|
|
|
#else
|
2019-06-18 17:33:10 +03:00
|
|
|
|
DefaultFontFamily = IsLinux ? "DejaVu Sans" : "Arial";
|
|
|
|
|
UnicodeFontFamilies =
|
|
|
|
|
IsLinux ? new[] { "Symbola" } :
|
|
|
|
|
IsMac ? new[] { "Apple Color Emoji" } :
|
|
|
|
|
new[] { "Segoe UI Emoji", "Segoe UI Symbol" };
|
2021-02-18 16:10:08 +03:00
|
|
|
|
#endif
|
2019-06-18 17:33:10 +03:00
|
|
|
|
}
|
|
|
|
|
|
Re-work the managed-native types (#900)
Changes:
- Added `GCHandleProxy` to debug builds
- this is used to track all `GCHandle` `Alloc` and `Free` calls to ensure that all allocations are freed.
- added some unit tests to make sure this is actually enforced
- as a result, several object are now freed correctly
- Added `ISKReferenceCounted` and `ISKNonVirtualReferenceCounted` interfaces to represent the reference counting types used in the native library
- this helps with automatically de-referencing objects
- `SKAbstractManagedStream`, `SKAbstractManagedWStream` and `SKDrawable` have been re-written to use better delegates
- instead of passing each of the delegates as parameters, they are now a struct that is passed as a single object
- better for extensions (which there shouldn't be) and only a single static field on the type
- removed the usage of `Marshal.GetFunctionPointerForDelegate`, which should help out with WASM (see #876)
- the objects now only keep weak references, meaning that they can now be garbage collected
- instead of trying to resolve the instances with a dictionary, a delegate is used and passed as "user context"
- Moved some of the repetitive logic from the types into the base `SKObject` and `SKNativeObject`
- some logic is automatically executed if the concrete type is `ISKReferenceCounted` or `ISKNonVirtualReferenceCounted`
- with the more centralized logic and stricter patterns, better tests can be written to make sure all memory is freed correctly and timely
- `SKData`, `SKFontManager` and `SKTypeface` now correctly prevent disposal of the "static" instances
- `SKPaint` now references the `Shader`, `MaskFilter`, `ColorFilter`, `ImageFilter`, `Typeface` and `PathEffect` properties
- this prevents accidental collection, or non-collection when the object goes out of scope
- the `SKPath` iterators (`Iterator` and `RawIterator`) and op builder (`OpBuilder`) now correctly own and dispose their native objects
- `SKRegion` objects are now disposed on the native side
- `SKTypeface` construction from a `SKManagedStream` (via both `SKTypeface` and `SKFontManager`) now copy the contents of the .NET `Stream` into a native memory
- typeface construction requires multiple seeks (previously, the stream was copied only if it was non-seekable)
- it also requires "duplicating" the stream, which is not supported on .NET streams
- duplicates or forks of a stream means that each of the streams need to be read concurrently from different locations
- .NET streams can only have a single position
- Updated the NuGets used for the tests
- using the `Xunit.AssemblyFixture` and `Xunit.SkippableFact` NuGets instead of using the code directly
- removed the `Xunit.Categories` NuGet as it was preventing tests from running
This PR has a big set of changes that may be breaking due to bug fixes:
- The `SKAbstractManagedStream`, `SKAbstractManagedWStream` and `SKDrawable` no longer prevent the GC from collecting them. This means that if code no longer references them, they will be disposed.
- As far as I can tell, this should not be a problem for the streams as they are never kept around - they are just used for reading and writing and typically only need to live for as long as a single method, and then need to be disposed by the caller. The `SKTypeface` and `SKDocument` do keep it around for a bit, but then they also take ownership of the stream and keep a hard reference to the streams themselves. They will dispose the streams when they are disposed.
- `SKDrawable` is never kept around and is entirely a user-controlled object. If it goes out of scope, skia doesn't have a reference anyway.
- The `SKFontManager` and `SKTypeface` no longer use the managed streams (`SKManagedStream` or `Stream`) directly
- they make a copy.
- This is simply because skia streams can do things that are not possible for .NET - they can be read concurrently from different positions. If a `SKFileStream` or `SKMemoryStream` are passed, then the streams are not copied.
- Further optimizations can be made in the case of a `MemoryStream` or `byte[]` to not actually copy but use GC pinning to get a handle to the managed data and work with pointers. But this can be done later so that this PR can be merged and tested.
2019-07-30 04:26:21 +03:00
|
|
|
|
public static void CollectGarbage()
|
|
|
|
|
{
|
|
|
|
|
GC.Collect();
|
|
|
|
|
GC.WaitForPendingFinalizers();
|
|
|
|
|
}
|
2019-06-18 17:33:10 +03:00
|
|
|
|
}
|
|
|
|
|
}
|