Skip to content

Commit 9aa4fb1

Browse files
Merge pull request #61 from AvionBlock/dev
v1.6.7 update.
2 parents f49493c + 0147dd6 commit 9aa4fb1

22 files changed

Lines changed: 356 additions & 21 deletions

File tree

Directory.Build.props

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
<Project>
22
<PropertyGroup>
3-
<OpusSharpVersion>1.6.6</OpusSharpVersion>
4-
<OpusSharpCoreVersion>1.6.1.2</OpusSharpCoreVersion>
5-
<OpusSharpNativesVersion>1.6.1.2</OpusSharpNativesVersion>
3+
<OpusSharpVersion>1.6.7</OpusSharpVersion>
4+
<OpusSharpCoreVersion>1.6.1.3</OpusSharpCoreVersion>
5+
<OpusSharpNativesVersion>1.6.1.3</OpusSharpNativesVersion>
66
<PackageProjectUrl>https://avionblock.github.io/OpusSharp/index.html</PackageProjectUrl>
77
<RepositoryUrl>https://github.com/AvionBlock/OpusSharp</RepositoryUrl>
88
</PropertyGroup>

OpusSharp.Core.Tests/NativeLibrarySmokeTests.cs

Lines changed: 299 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
using System.Runtime.InteropServices;
2+
using System.Reflection;
3+
using System.Text;
4+
using System.Text.RegularExpressions;
25
using OpusSharp.Core;
36
using Xunit;
47

@@ -53,6 +56,283 @@ public void DefaultOpusExceptionCtor_ProducesExceptionInstance()
5356
Assert.NotNull(exception);
5457
}
5558

59+
[Fact]
60+
public void MultistreamCtlNoArgumentOverloads_AreManagedWrappers()
61+
{
62+
AssertPublicCtlOverloadIsManagedWrapper(typeof(NativeOpus), nameof(NativeOpus.opus_ms_encoder_ctl), 2);
63+
AssertPublicCtlOverloadIsManagedWrapper(typeof(NativeOpus), nameof(NativeOpus.opus_ms_decoder_ctl), 2);
64+
AssertPublicCtlOverloadIsManagedWrapper(typeof(StaticNativeOpus), nameof(StaticNativeOpus.opus_ms_encoder_ctl), 2);
65+
AssertPublicCtlOverloadIsManagedWrapper(typeof(StaticNativeOpus), nameof(StaticNativeOpus.opus_ms_decoder_ctl), 2);
66+
67+
AssertPrivateLibraryImportSymbol(typeof(NativeOpus), "opus_multistream_encoder_ctl");
68+
AssertPrivateLibraryImportSymbol(typeof(NativeOpus), "opus_multistream_decoder_ctl");
69+
AssertPrivateLibraryImportSymbol(typeof(StaticNativeOpus), "opus_multistream_encoder_ctl");
70+
AssertPrivateLibraryImportSymbol(typeof(StaticNativeOpus), "opus_multistream_decoder_ctl");
71+
}
72+
73+
[Fact]
74+
public void CtlOverloadsWithVariadicArguments_AreManagedWrappers()
75+
{
76+
foreach (var method in GetPublicCtlMethods(typeof(NativeOpus), typeof(StaticNativeOpus))
77+
.Where(method => method.GetParameters().Length > 2))
78+
{
79+
Assert.Empty(method.GetCustomAttributes<LibraryImportAttribute>());
80+
Assert.Empty(method.GetCustomAttributes<DllImportAttribute>());
81+
}
82+
}
83+
84+
[Fact]
85+
public void ManagedCtlShimImports_AreDeclaredInNativeShimSource()
86+
{
87+
var shimSource = File.ReadAllText(Path.Combine(
88+
TestNativeLibraryBootstrapper.GetRepositoryRoot(),
89+
"OpusSharp.Natives",
90+
"opus_shim.c"));
91+
92+
foreach (var symbol in GetManagedCtlShimSymbols())
93+
{
94+
Assert.Contains($"SHIM_EXPORT int {symbol}(", shimSource);
95+
}
96+
}
97+
98+
[Fact]
99+
public void NativeRuntimeLibrary_ExportsManagedCtlShimSymbols()
100+
{
101+
var libraryPath = TestNativeLibraryBootstrapper.GetOutputNativeLibraryPath();
102+
var handle = NativeLibrary.Load(libraryPath);
103+
104+
try
105+
{
106+
foreach (var symbol in GetManagedCtlShimSymbols())
107+
{
108+
Assert.True(
109+
NativeLibrary.TryGetExport(handle, symbol, out _),
110+
$"Expected '{Path.GetFileName(libraryPath)}' to export '{symbol}'.");
111+
}
112+
}
113+
finally
114+
{
115+
NativeLibrary.Free(handle);
116+
}
117+
}
118+
119+
[Fact]
120+
public void IosNativeArchives_ContainManagedCtlShimSymbols()
121+
{
122+
var iosNativeRoot = Path.Combine(
123+
TestNativeLibraryBootstrapper.GetRepositoryRoot(),
124+
"OpusSharp.Natives",
125+
"runtimes",
126+
"ios",
127+
"native",
128+
"libopus.xcframework");
129+
var archivePaths = Directory.GetFiles(iosNativeRoot, "libopus.a", SearchOption.AllDirectories);
130+
131+
Assert.NotEmpty(archivePaths);
132+
133+
foreach (var archivePath in archivePaths)
134+
{
135+
foreach (var symbol in GetManagedCtlShimSymbols())
136+
{
137+
AssertFileContainsAsciiSymbol(archivePath, symbol);
138+
}
139+
}
140+
}
141+
142+
[Fact]
143+
public void SupportedNativeRuntimeLibraries_ContainManagedCtlShimSymbols()
144+
{
145+
var nativeFiles = GetSupportedNativeRuntimeFiles().ToArray();
146+
147+
Assert.NotEmpty(nativeFiles);
148+
149+
foreach (var nativeFile in nativeFiles)
150+
{
151+
foreach (var symbol in GetManagedCtlShimSymbols())
152+
{
153+
AssertFileContainsAsciiSymbol(nativeFile, symbol);
154+
}
155+
}
156+
}
157+
158+
[Fact]
159+
public void PackageSmoke_UsesCurrentPackageVersion()
160+
{
161+
var props = File.ReadAllText(Path.Combine(
162+
TestNativeLibraryBootstrapper.GetRepositoryRoot(),
163+
"samples",
164+
"PackageSmoke",
165+
"Directory.Build.props"));
166+
167+
Assert.Contains(@"<Import Project=""..\..\Directory.Build.props"" />", props);
168+
Assert.Contains("<OpusSharpSmokeVersion>$(OpusSharpVersion)</OpusSharpSmokeVersion>", props);
169+
}
170+
171+
[Fact]
172+
public void NativesPackage_ExcludesUnsupportedWindowsRuntimes()
173+
{
174+
var project = File.ReadAllText(Path.Combine(
175+
TestNativeLibraryBootstrapper.GetRepositoryRoot(),
176+
"OpusSharp.Natives",
177+
"OpusSharp.Natives.csproj"));
178+
179+
Assert.Contains("runtimes\\win-arm\\**\\*", project);
180+
Assert.Contains("runtimes\\win-x86\\**\\*", project);
181+
}
182+
183+
[Fact]
184+
public void WindowsNativeBuildWorkflow_HandlesDecoratedX86OpusSymbols()
185+
{
186+
var workflow = File.ReadAllText(Path.Combine(
187+
TestNativeLibraryBootstrapper.GetRepositoryRoot(),
188+
".github",
189+
"workflows",
190+
"OpusCompile.yml"));
191+
192+
Assert.Contains("_?opus_\\S+", workflow);
193+
Assert.Contains("$raw.Substring(1) + '=' + $raw", workflow);
194+
}
195+
196+
private static void AssertPublicCtlOverloadIsManagedWrapper(Type type, string methodName, int parameterCount)
197+
{
198+
var method = Assert.Single(
199+
type.GetMethods(BindingFlags.Public | BindingFlags.Static),
200+
method => method.Name == methodName && method.GetParameters().Length == parameterCount);
201+
202+
Assert.Empty(method.GetCustomAttributes<LibraryImportAttribute>());
203+
Assert.Empty(method.GetCustomAttributes<DllImportAttribute>());
204+
}
205+
206+
private static void AssertPrivateLibraryImportSymbol(Type type, string methodName)
207+
{
208+
var method = Assert.Single(
209+
type.GetMethods(BindingFlags.NonPublic | BindingFlags.Static),
210+
method => method.Name == methodName && method.GetParameters().Length == 2);
211+
212+
Assert.Equal(methodName, GetImportedSymbol(method));
213+
}
214+
215+
private static IEnumerable<MethodInfo> GetPublicCtlMethods(params Type[] types)
216+
{
217+
return types.SelectMany(type => type.GetMethods(BindingFlags.Public | BindingFlags.Static))
218+
.Where(method =>
219+
method.Name.StartsWith("opus_", StringComparison.Ordinal) &&
220+
method.Name.EndsWith("_ctl", StringComparison.Ordinal));
221+
}
222+
223+
private static IEnumerable<string> GetManagedCtlShimSymbols()
224+
{
225+
return GetCtlImportMethods(typeof(NativeOpus), typeof(StaticNativeOpus))
226+
.Select(GetImportedSymbol)
227+
.Where(symbol => symbol.StartsWith("opussharp_", StringComparison.Ordinal))
228+
.Distinct()
229+
.Order(StringComparer.Ordinal);
230+
}
231+
232+
private static IEnumerable<MethodInfo> GetCtlImportMethods(params Type[] types)
233+
{
234+
return types.SelectMany(type => type.GetMethods(
235+
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static))
236+
.Where(method =>
237+
method.Name.Contains("_ctl", StringComparison.Ordinal) &&
238+
method.GetCustomAttributes<LibraryImportAttribute>().Any());
239+
}
240+
241+
private static string GetImportedSymbol(MethodInfo method)
242+
{
243+
var importAttribute = Assert.Single(method.GetCustomAttributes<LibraryImportAttribute>());
244+
245+
return string.IsNullOrEmpty(importAttribute.EntryPoint)
246+
? method.Name
247+
: importAttribute.EntryPoint;
248+
}
249+
250+
private static IEnumerable<string> GetSupportedNativeRuntimeFiles()
251+
{
252+
var nativesRoot = Path.Combine(
253+
TestNativeLibraryBootstrapper.GetRepositoryRoot(),
254+
"OpusSharp.Natives");
255+
var runtimesRoot = Path.Combine(nativesRoot, "runtimes");
256+
var readmePath = Path.Combine(nativesRoot, "README.md");
257+
var runtimePathPattern = new Regex(@"^- [^:]+: (?<path>\S+)$", RegexOptions.CultureInvariant);
258+
259+
foreach (var line in File.ReadLines(readmePath))
260+
{
261+
var match = runtimePathPattern.Match(line);
262+
263+
if (!match.Success)
264+
{
265+
continue;
266+
}
267+
268+
var relativePath = match.Groups["path"].Value.Replace('/', Path.DirectorySeparatorChar);
269+
var fullPath = Path.Combine(runtimesRoot, relativePath);
270+
271+
if (File.Exists(fullPath))
272+
{
273+
if (IsNativeLibraryFile(fullPath))
274+
{
275+
yield return fullPath;
276+
}
277+
278+
continue;
279+
}
280+
281+
Assert.True(Directory.Exists(fullPath), $"Expected supported native runtime path '{fullPath}' to exist.");
282+
283+
foreach (var nativeFile in Directory.GetFiles(fullPath, "*", SearchOption.AllDirectories)
284+
.Where(IsNativeLibraryFile))
285+
{
286+
yield return nativeFile;
287+
}
288+
}
289+
}
290+
291+
private static bool IsNativeLibraryFile(string filePath)
292+
{
293+
return Path.GetExtension(filePath) switch
294+
{
295+
".a" or ".dll" or ".dylib" or ".so" => true,
296+
_ => false
297+
};
298+
}
299+
300+
private static void AssertFileContainsAsciiSymbol(string filePath, string symbol)
301+
{
302+
var fileBytes = File.ReadAllBytes(filePath);
303+
var symbolBytes = Encoding.ASCII.GetBytes(symbol);
304+
305+
Assert.True(
306+
IndexOf(fileBytes, symbolBytes) >= 0,
307+
$"Expected '{filePath}' to contain symbol '{symbol}'.");
308+
}
309+
310+
private static int IndexOf(byte[] source, byte[] value)
311+
{
312+
for (var i = 0; i <= source.Length - value.Length; i++)
313+
{
314+
var found = true;
315+
316+
for (var j = 0; j < value.Length; j++)
317+
{
318+
if (source[i + j] == value[j])
319+
{
320+
continue;
321+
}
322+
323+
found = false;
324+
break;
325+
}
326+
327+
if (found)
328+
{
329+
return i;
330+
}
331+
}
332+
333+
return -1;
334+
}
335+
56336
private static class TestNativeLibraryBootstrapper
57337
{
58338
private static bool _initialized;
@@ -75,12 +355,29 @@ public static void EnsureInitialized()
75355
_initialized = true;
76356
}
77357

358+
public static string GetRepositoryRoot()
359+
{
360+
return Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, "../../../../"));
361+
}
362+
363+
public static string GetOutputNativeLibraryPath()
364+
{
365+
EnsureInitialized();
366+
367+
return Path.Combine(AppContext.BaseDirectory, Path.GetFileName(GetNativeLibraryPath()));
368+
}
369+
78370
private static string GetNativeLibraryPath()
79371
{
80-
var repoRoot = Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, "../../../../"));
81372
var runtimeFolder = GetRuntimeFolder();
82373
var fileName = GetNativeLibraryFileName();
83-
var path = Path.Combine(repoRoot, "OpusSharp.Natives", "runtimes", runtimeFolder, "native", fileName);
374+
var path = Path.Combine(
375+
GetRepositoryRoot(),
376+
"OpusSharp.Natives",
377+
"runtimes",
378+
runtimeFolder,
379+
"native",
380+
fileName);
84381

85382
if (!File.Exists(path))
86383
{

OpusSharp.Core/NativeOpus.cs

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1070,11 +1070,20 @@ public static unsafe int opus_dred_decoder_ctl(OpusDREDDecoderSafeHandle dred_de
10701070
/// <returns><see cref="OpusErrorCodes"/></returns>
10711071
#if NET8_0_OR_GREATER
10721072
[LibraryImport(DllName)]
1073-
public static partial int opus_ms_encoder_ctl(OpusMSEncoderSafeHandle st, int request); //Apparently GenericCTL.OPUS_RESET_STATE exists.
1073+
private static partial int opus_multistream_encoder_ctl(OpusMSEncoderSafeHandle st, int request);
10741074
#else
10751075
[DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
1076-
public static extern int opus_ms_encoder_ctl(OpusMSEncoderSafeHandle st, int request); //Apparently GenericCTL.OPUS_RESET_STATE exists.
1076+
private static extern int opus_multistream_encoder_ctl(OpusMSEncoderSafeHandle st, int request);
10771077
#endif
1078+
1079+
/// <summary>
1080+
/// Perform a CTL function on a <see cref="OpusMSEncoderSafeHandle"/>.
1081+
/// </summary>
1082+
/// <param name="st">Multistream encoder state.</param>
1083+
/// <param name="request">This and all remaining parameters should be replaced by one of the convenience macros in <see cref="GenericCTL"/>, <see cref="EncoderCTL"/>, or <see cref="MultistreamCTL"/> specific encoder and decoder CTLs.</param>
1084+
/// <returns><see cref="OpusErrorCodes"/></returns>
1085+
public static int opus_ms_encoder_ctl(OpusMSEncoderSafeHandle st, int request) =>
1086+
opus_multistream_encoder_ctl(st, request); //Apparently GenericCTL.OPUS_RESET_STATE exists.
10781087

10791088
#if NET8_0_OR_GREATER
10801089
[LibraryImport(DllName)]
@@ -1277,11 +1286,20 @@ public static unsafe int opus_ms_encoder_ctl(OpusMSEncoderSafeHandle st, int req
12771286
/// <returns><see cref="OpusErrorCodes"/></returns>
12781287
#if NET8_0_OR_GREATER
12791288
[LibraryImport(DllName)]
1280-
public static partial int opus_ms_decoder_ctl(OpusMSDecoderSafeHandle st, int request); //Apparently GenericCTL.OPUS_RESET_STATE exists.
1289+
private static partial int opus_multistream_decoder_ctl(OpusMSDecoderSafeHandle st, int request);
12811290
#else
12821291
[DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
1283-
public static extern int opus_ms_decoder_ctl(OpusMSDecoderSafeHandle st, int request); //Apparently GenericCTL.OPUS_RESET_STATE exists.
1292+
private static extern int opus_multistream_decoder_ctl(OpusMSDecoderSafeHandle st, int request);
12841293
#endif
1294+
1295+
/// <summary>
1296+
/// Perform a CTL function on a <see cref="OpusMSEncoderSafeHandle"/>.
1297+
/// </summary>
1298+
/// <param name="st">Multistream decoder state.</param>
1299+
/// <param name="request">This and all remaining parameters should be replaced by one of the convenience macros in <see cref="GenericCTL"/>, <see cref="DecoderCTL"/>, or <see cref="MultistreamCTL"/> specific encoder and decoder CTLs.</param>
1300+
/// <returns><see cref="OpusErrorCodes"/></returns>
1301+
public static int opus_ms_decoder_ctl(OpusMSDecoderSafeHandle st, int request) =>
1302+
opus_multistream_decoder_ctl(st, request); //Apparently GenericCTL.OPUS_RESET_STATE exists.
12851303

12861304
#if NET8_0_OR_GREATER
12871305
[LibraryImport(DllName)]
@@ -1356,4 +1374,4 @@ public static unsafe int opus_ms_decoder_ctl(OpusMSDecoderSafeHandle st, int req
13561374
#endif
13571375
}
13581376
}
1359-
#pragma warning restore CA1401
1377+
#pragma warning restore CA1401

0 commit comments

Comments
 (0)