Skip to content

Commit a3126c6

Browse files
committed
Add unit tests for ByRefLikeReference & subtypes
1 parent a70bac3 commit a3126c6

2 files changed

Lines changed: 152 additions & 0 deletions

File tree

src/Castle.Core.Tests/Castle.Core.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
<PropertyGroup>
66
<TargetFrameworks>net8.0;net9.0;net10.0;net462</TargetFrameworks>
7+
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
78
</PropertyGroup>
89

910
<PropertyGroup>
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
// Copyright 2004-2026 Castle Project - http://www.castleproject.org/
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#if FEATURE_BYREFLIKE
16+
17+
#nullable enable
18+
#pragma warning disable CS8500
19+
20+
namespace Castle.DynamicProxy.Tests.ByRefLikeSupport
21+
{
22+
using System;
23+
#if NET9_0_OR_GREATER
24+
using System.Runtime.CompilerServices;
25+
#endif
26+
27+
using NUnit.Framework;
28+
29+
/// <summary>
30+
/// Tests for the substitute types used by DynamicProxy to implement by-ref-like parameter and return type support.
31+
/// </summary>
32+
[TestFixture]
33+
public class ByRefLikeReferenceTestCase
34+
{
35+
#region `ByRefLikeReference`
36+
37+
[Test]
38+
public unsafe void Ctor_throws_if_non_by_ref_like_type()
39+
{
40+
Assert.Throws<ArgumentOutOfRangeException>(() =>
41+
{
42+
bool local = default;
43+
_ = new ByRefLikeReference(typeof(bool), &local);
44+
});
45+
}
46+
47+
[Test]
48+
public unsafe void Ctor_succeeds_if_by_ref_like_type()
49+
{
50+
ReadOnlySpan<char> local = default;
51+
_ = new ByRefLikeReference(typeof(ReadOnlySpan<char>), &local);
52+
}
53+
54+
[Test]
55+
public unsafe void Invalidate_throws_if_address_mismatch()
56+
{
57+
ReadOnlySpan<char> local = default;
58+
var reference = new ByRefLikeReference(typeof(ReadOnlySpan<char>), &local);
59+
Assert.Throws<AccessViolationException>(() =>
60+
{
61+
ReadOnlySpan<char> otherLocal = default;
62+
reference.Invalidate(&otherLocal);
63+
});
64+
}
65+
66+
[Test]
67+
public unsafe void Invalidate_succeeds_if_address_match()
68+
{
69+
ReadOnlySpan<char> local = default;
70+
var reference = new ByRefLikeReference(typeof(ReadOnlySpan<char>), &local);
71+
reference.Invalidate(&local);
72+
}
73+
74+
[Test]
75+
public unsafe void GetPtr_throws_if_type_mismatch()
76+
{
77+
ReadOnlySpan<char> local = default;
78+
var reference = new ByRefLikeReference(typeof(ReadOnlySpan<char>), &local);
79+
Assert.Throws<AccessViolationException>(() => reference.GetPtr(typeof(bool)));
80+
}
81+
82+
[Test]
83+
public unsafe void GetPtr_returns_ctor_address_if_type_match()
84+
{
85+
ReadOnlySpan<char> local = default;
86+
var reference = new ByRefLikeReference(typeof(ReadOnlySpan<char>), &local);
87+
var ptr = reference.GetPtr(typeof(ReadOnlySpan<char>));
88+
Assert.True(ptr == &local);
89+
}
90+
91+
[Test]
92+
public unsafe void GetPtr_throws_after_Invalidate()
93+
{
94+
ReadOnlySpan<char> local = default;
95+
var reference = new ByRefLikeReference(typeof(ReadOnlySpan<char>), &local);
96+
reference.Invalidate(&local);
97+
Assert.Throws<AccessViolationException>(() => reference.GetPtr(typeof(ReadOnlySpan<char>)));
98+
}
99+
100+
#endregion
101+
102+
#region `ReadOnlySpanReference<T>`
103+
104+
// We do not repeat the above tests for `ReadOnlySpanReference<T>`
105+
// since it inherits the tested methods from `ByRefLikeReference`.
106+
107+
public unsafe void ReadOnlySpanReference_ctor_throws_if_type_mismatch()
108+
{
109+
Assert.Throws<ArgumentOutOfRangeException>(() =>
110+
{
111+
ReadOnlySpan<bool> local = default;
112+
_ = new ReadOnlySpanReference<char>(typeof(ReadOnlySpan<bool>), &local);
113+
});
114+
}
115+
116+
public unsafe void ReadOnlySpanReference_Value_returns_equal_span()
117+
{
118+
ReadOnlySpan<char> local = "foo".AsSpan();
119+
var reference = new ReadOnlySpanReference<char>(typeof(ReadOnlySpan<char>), &local);
120+
Assert.True(reference.Value == "foo".AsSpan());
121+
}
122+
123+
#if NET9_0_OR_GREATER
124+
[Test]
125+
public unsafe void ReadOnlySpanReference_Value_returns_same_span()
126+
{
127+
ReadOnlySpan<char> local = "foo".AsSpan();
128+
var reference = new ReadOnlySpanReference<char>(typeof(ReadOnlySpan<char>), &local);
129+
Assert.True(Unsafe.AreSame(ref reference.Value, ref local));
130+
}
131+
#endif
132+
133+
[Test]
134+
public unsafe void ReadOnlySpanReference_Value_can_update_original()
135+
{
136+
ReadOnlySpan<char> local = "foo".AsSpan();
137+
var reference = new ReadOnlySpanReference<char>(typeof(ReadOnlySpan<char>), &local);
138+
reference.Value = "bar".AsSpan();
139+
Assert.True(local == "bar".AsSpan());
140+
}
141+
142+
#endregion
143+
144+
// We do not test `ByRefLikeReference<TByRefLike>` and `SpanReference<T>`
145+
// since these two types are practically identical to `ReadOnlySpanReference<T>`.
146+
}
147+
}
148+
149+
#pragma warning restore CS8500
150+
151+
#endif

0 commit comments

Comments
 (0)