Skip to content

Commit 70aa608

Browse files
authored
Merge pull request #89 from rsdn/feature/CodeAssertions-revisit
Assertions revisited
2 parents 41fbf50 + b4dd9d1 commit 70aa608

19 files changed

+400
-310
lines changed

CodeJam.Main.Tests/Assertions/CodeTests.cs

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Collections.Generic;
23
using System.Diagnostics;
34
using System.Diagnostics.CodeAnalysis;
45
using System.IO;
@@ -31,7 +32,7 @@ public void TestLogging()
3132
ts.Switch.Level = SourceLevels.All;
3233
ts.Listeners.Add(listener);
3334

34-
var ex = Assert.Throws<ArgumentNullException>(() => Code.NotNull<object>(null, "arg00"));
35+
var ex = Assert.Throws<ArgumentNullException>(() => Code.NotNull(default(object), "arg00"));
3536
Assert.That(ex.Message, Does.Contain("arg00"));
3637

3738
var logOutput = logWriter.ToString();
@@ -48,20 +49,33 @@ public void TestLogging()
4849
ts.Switch.Level = logLevel;
4950
}
5051
}
52+
5153
[Test]
5254
public void TestNotNull()
5355
{
54-
var ex = Assert.Throws<ArgumentNullException>(() => Code.NotNull<object>(null, "arg00"));
56+
var ex = Assert.Throws<ArgumentNullException>(() => Code.NotNull(default(object), "arg00"));
5557
Assert.That(ex.Message, Does.Contain("arg00"));
5658

5759
Assert.DoesNotThrow(() => Code.NotNull<object>("Hello!", "arg00"));
5860
}
5961

62+
[Test]
63+
public void TestGenericNotNull()
64+
{
65+
var ex = Assert.Throws<ArgumentNullException>(() => Code.GenericNotNull(default(object), "arg00"));
66+
Assert.That(ex.Message, Does.Contain("arg00"));
67+
68+
Assert.DoesNotThrow(() => Code.GenericNotNull<object>("Hello!", "arg00"));
69+
70+
// They made me to write this
71+
Assert.DoesNotThrow(() => Code.GenericNotNull(default(int), "arg00"));
72+
}
73+
6074
[Test]
6175
public void TestDebugNotNull()
6276
{
6377
#if DEBUG
64-
var ex = Assert.Throws<ArgumentNullException>(() => DebugCode.NotNull<object>(null, "arg00"));
78+
var ex = Assert.Throws<ArgumentNullException>(() => DebugCode.NotNull(default(object), "arg00"));
6579
Assert.That(ex.Message, Does.Contain("arg00"));
6680
#else
6781
// ReSharper disable once InvocationIsSkipped
@@ -73,9 +87,9 @@ public void TestDebugNotNull()
7387
}
7488

7589
[Test]
76-
public void TestNotNullNorEmpty()
90+
public void TestStringNotNullNorEmpty()
7791
{
78-
Assert.Throws<ArgumentException>(() => Code.NotNullNorEmpty(null, "arg00"));
92+
Assert.Throws<ArgumentException>(() => Code.NotNullNorEmpty(default, "arg00"));
7993
var ex = Assert.Throws<ArgumentException>(() => Code.NotNullNorEmpty("", "arg00"));
8094
Assert.That(ex.Message, Does.Contain("arg00"));
8195
Assert.That(ex.Message, Does.Contain("String 'arg00' should be neither null nor empty"));
@@ -84,6 +98,26 @@ public void TestNotNullNorEmpty()
8498
Assert.DoesNotThrow(() => Code.NotNullNorEmpty("Hello!", "arg00"));
8599
}
86100

101+
[Test]
102+
public void TestCollectionNotNullNorEmpty()
103+
{
104+
var empty = new HashSet<int>();
105+
#if LESSTHAN_NET45
106+
var nonEmpty = (IList<int>)new ListEx<int> { 1 };
107+
#else
108+
var nonEmpty = (IList<int>)new List<int> { 1 };
109+
#endif
110+
var nonEmpty2 = (IReadOnlyCollection<int>)nonEmpty;
111+
112+
Assert.Throws<ArgumentNullException>(() => Code.NotNullNorEmpty(default(IList<int>), "arg00"));
113+
var ex = Assert.Throws<ArgumentException>(() => Code.NotNullNorEmpty(empty, "arg00"));
114+
Assert.That(ex.Message, Does.Contain("arg00"));
115+
Assert.That(ex.Message, Does.Contain("Collection 'arg00' must not be empty"));
116+
117+
Assert.DoesNotThrow(() => Code.NotNullNorEmpty(nonEmpty, "arg00"));
118+
Assert.DoesNotThrow(() => Code.NotNullNorEmpty(nonEmpty2, "arg00"));
119+
}
120+
87121
[Test]
88122
public void TestNotNullNorWhiteSpace()
89123
{

CodeJam.Main.Tests/IO/PathHelpersTests.cs

Lines changed: 31 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,23 @@ public enum PathKind
1717
ValidRelativePath,
1818
ValidAbsoluteContainerPath,
1919
ValidRelativeContainerPath,
20-
ValidSimpleName
20+
ValidFileName
2121
}
2222

2323
[TestCase("", PathKind.Throws)]
2424
[TestCase(null, PathKind.Throws)]
25-
[TestCase(@"a", PathKind.ValidSimpleName)]
25+
[TestCase(@"a", PathKind.ValidFileName)]
26+
[TestCase(@"a ", PathKind.ValidFileName)]
27+
[TestCase(@"a ", PathKind.ValidFileName)]
28+
[TestCase(@"a\t", PathKind.ValidRelativePath)]
29+
[TestCase(@"a...", PathKind.ValidFileName)]
30+
[TestCase(@"a.", PathKind.ValidFileName)]
31+
32+
[TestCase(@" a", PathKind.ValidFileName)]
33+
[TestCase(@" a", PathKind.ValidFileName)]
34+
[TestCase(@"\ta", PathKind.Invalid)]
35+
[TestCase(@"...a", PathKind.ValidFileName)]
36+
2637
[TestCase(@"a\b", PathKind.ValidRelativePath)]
2738
[TestCase(@"a\b\", PathKind.ValidRelativeContainerPath)]
2839
[TestCase(@"a/b", PathKind.ValidRelativePath)]
@@ -40,8 +51,8 @@ public enum PathKind
4051
[TestCase(@"\\a\b\", PathKind.ValidAbsoluteContainerPath)]
4152
[TestCase(@"\\a\\b\", PathKind.Invalid)]
4253
[TestCase(@"\\a\b/", PathKind.Invalid)]
43-
[TestCase(@".a", PathKind.ValidSimpleName)]
44-
[TestCase(@".a.b", PathKind.ValidSimpleName)]
54+
[TestCase(@".a", PathKind.ValidFileName)]
55+
[TestCase(@".a.b", PathKind.ValidFileName)]
4556
[TestCase(@"..a\", PathKind.ValidRelativeContainerPath)]
4657
[TestCase(@"..a.b\", PathKind.ValidRelativeContainerPath)]
4758
[TestCase(@"..a..\", PathKind.ValidRelativeContainerPath)]
@@ -67,8 +78,8 @@ public enum PathKind
6778
[TestCase(@"a:\a\..\a..b\", PathKind.Invalid)]
6879
[TestCase(@"\", PathKind.Invalid)]
6980
[TestCase(@"/", PathKind.Invalid)]
70-
[TestCase(@"~", PathKind.ValidSimpleName)]
71-
[TestCase(@"a", PathKind.ValidSimpleName)]
81+
[TestCase(@"~", PathKind.ValidFileName)]
82+
[TestCase(@"a", PathKind.ValidFileName)]
7283
[TestCase(@"a:", PathKind.Invalid)]
7384
[TestCase(@"a\", PathKind.ValidRelativeContainerPath)]
7485
[TestCase(@"a/", PathKind.ValidRelativeContainerPath)]
@@ -112,8 +123,8 @@ public enum PathKind
112123
[TestCase(@"a:\/a", PathKind.Invalid)]
113124
[TestCase(@"a\\a", PathKind.ValidRelativePath)]
114125
[TestCase(@"a\/a", PathKind.ValidRelativePath)]
115-
[TestCase(@"com0", PathKind.ValidSimpleName)]
116-
[TestCase(@"aux", PathKind.ValidSimpleName)]
126+
[TestCase(@"com0", PathKind.ValidFileName)]
127+
[TestCase(@"aux", PathKind.ValidFileName)]
117128
public void TestIsWellFormedPath(string path, PathKind pathState)
118129
{
119130
switch (pathState)
@@ -123,49 +134,49 @@ public void TestIsWellFormedPath(string path, PathKind pathState)
123134
Assert.Throws<ArgumentException>(() => PathHelpers.IsWellFormedAbsolutePath(path));
124135
Assert.Throws<ArgumentException>(() => PathHelpers.IsWellFormedRelativePath(path));
125136
Assert.Throws<ArgumentException>(() => PathHelpers.IsWellFormedContainerPath(path));
126-
Assert.Throws<ArgumentException>(() => PathHelpers.IsWellFormedSimpleName(path));
137+
Assert.Throws<ArgumentException>(() => PathHelpers.IsWellFormedFileName(path));
127138
break;
128139
case PathKind.Invalid:
129140
Assert.AreEqual(PathHelpers.IsWellFormedPath(path), false);
130141
Assert.AreEqual(PathHelpers.IsWellFormedAbsolutePath(path), false);
131142
Assert.AreEqual(PathHelpers.IsWellFormedRelativePath(path), false);
132143
Assert.AreEqual(PathHelpers.IsWellFormedContainerPath(path), false);
133-
Assert.AreEqual(PathHelpers.IsWellFormedSimpleName(path), false);
144+
Assert.AreEqual(PathHelpers.IsWellFormedFileName(path), false);
134145
break;
135146
case PathKind.ValidAbsolutePath:
136147
Assert.AreEqual(PathHelpers.IsWellFormedPath(path), true);
137148
Assert.AreEqual(PathHelpers.IsWellFormedAbsolutePath(path), true);
138149
Assert.AreEqual(PathHelpers.IsWellFormedRelativePath(path), false);
139150
Assert.AreEqual(PathHelpers.IsWellFormedContainerPath(path), false);
140-
Assert.AreEqual(PathHelpers.IsWellFormedSimpleName(path), false);
151+
Assert.AreEqual(PathHelpers.IsWellFormedFileName(path), false);
141152
break;
142153
case PathKind.ValidAbsoluteContainerPath:
143154
Assert.AreEqual(PathHelpers.IsWellFormedPath(path), true);
144155
Assert.AreEqual(PathHelpers.IsWellFormedAbsolutePath(path), true);
145156
Assert.AreEqual(PathHelpers.IsWellFormedRelativePath(path), false);
146157
Assert.AreEqual(PathHelpers.IsWellFormedContainerPath(path), true);
147-
Assert.AreEqual(PathHelpers.IsWellFormedSimpleName(path), false);
158+
Assert.AreEqual(PathHelpers.IsWellFormedFileName(path), false);
148159
break;
149160
case PathKind.ValidRelativePath:
150161
Assert.AreEqual(PathHelpers.IsWellFormedPath(path), true);
151162
Assert.AreEqual(PathHelpers.IsWellFormedAbsolutePath(path), false);
152163
Assert.AreEqual(PathHelpers.IsWellFormedRelativePath(path), true);
153164
Assert.AreEqual(PathHelpers.IsWellFormedContainerPath(path), false);
154-
Assert.AreEqual(PathHelpers.IsWellFormedSimpleName(path), false);
165+
Assert.AreEqual(PathHelpers.IsWellFormedFileName(path), false);
155166
break;
156167
case PathKind.ValidRelativeContainerPath:
157168
Assert.AreEqual(PathHelpers.IsWellFormedPath(path), true);
158169
Assert.AreEqual(PathHelpers.IsWellFormedAbsolutePath(path), false);
159170
Assert.AreEqual(PathHelpers.IsWellFormedRelativePath(path), true);
160171
Assert.AreEqual(PathHelpers.IsWellFormedContainerPath(path), true);
161-
Assert.AreEqual(PathHelpers.IsWellFormedSimpleName(path), false);
172+
Assert.AreEqual(PathHelpers.IsWellFormedFileName(path), false);
162173
break;
163-
case PathKind.ValidSimpleName:
174+
case PathKind.ValidFileName:
164175
Assert.AreEqual(PathHelpers.IsWellFormedPath(path), true);
165176
Assert.AreEqual(PathHelpers.IsWellFormedAbsolutePath(path), false);
166177
Assert.AreEqual(PathHelpers.IsWellFormedRelativePath(path), true);
167178
Assert.AreEqual(PathHelpers.IsWellFormedContainerPath(path), false);
168-
Assert.AreEqual(PathHelpers.IsWellFormedSimpleName(path), true);
179+
Assert.AreEqual(PathHelpers.IsWellFormedFileName(path), true);
169180
break;
170181
default:
171182
throw new ArgumentOutOfRangeException(nameof(pathState), pathState, null);
@@ -236,18 +247,18 @@ public void TestEnsureContainerPath(string path, string result)
236247
[TestCase(@"a:/", false)]
237248
[TestCase(@"a:\a", false)]
238249
[TestCase(@"a:/a", false)]
239-
public void TestIsSimpleName(string path, bool? state)
250+
public void TestIsFileName(string path, bool? state)
240251
{
241252
switch (state)
242253
{
243254
case null:
244-
Assert.Throws<ArgumentException>(() => PathHelpers.IsSimpleName(path));
255+
Assert.Throws<ArgumentException>(() => PathHelpers.IsFileName(path));
245256
break;
246257
case true:
247-
Assert.AreEqual(PathHelpers.IsSimpleName(path), true);
258+
Assert.AreEqual(PathHelpers.IsFileName(path), true);
248259
break;
249260
case false:
250-
Assert.AreEqual(PathHelpers.IsSimpleName(path), false);
261+
Assert.AreEqual(PathHelpers.IsFileName(path), false);
251262
break;
252263
default:
253264
throw new ArgumentOutOfRangeException(nameof(state), state, null);

CodeJam.Main/Assertions/ArgumentAssertionExtensions.cs

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
using System;
2-
using System.Collections.Generic;
1+
using System.Collections.Generic;
32
using System.Diagnostics;
43
using System.Runtime.CompilerServices;
54

@@ -56,44 +55,44 @@ public static ArgumentAssertion<IEnumerable<T>> ItemNotNull<T>(this ArgumentAsse
5655
}
5756

5857
/// <summary>
59-
/// Ensures that supplied enumerable is not empty.
58+
/// Ensures that supplied enumerable is not null nor empty.
6059
/// </summary>
6160
/// <typeparam name="T">Type of item.</typeparam>
6261
/// <param name="arg">The argument.</param>
6362
/// <returns><paramref name="arg"/></returns>
6463
[DebuggerHidden, MethodImpl(AggressiveInlining)]
6564
[AssertionMethod]
66-
public static ArgumentAssertion<IEnumerable<T>> NotEmpty<T>(this ArgumentAssertion<IEnumerable<T>> arg)
65+
public static ArgumentAssertion<IEnumerable<T>> NotNullNorEmpty<T>(this ArgumentAssertion<IEnumerable<T>> arg)
6766
{
68-
Code.NotEmpty(arg.Argument, arg.ArgumentName);
67+
Code.NotNullNorEmpty(arg.Argument, arg.ArgumentName);
6968
return arg;
7069
}
7170

7271
/// <summary>
73-
/// Ensures that supplied collection is not empty.
72+
/// Ensures that supplied collection is not null nor empty.
7473
/// </summary>
7574
/// <typeparam name="T">Type of item.</typeparam>
7675
/// <param name="arg">The argument.</param>
7776
/// <returns><paramref name="arg"/></returns>
7877
[DebuggerHidden, MethodImpl(AggressiveInlining)]
7978
[AssertionMethod]
80-
public static ArgumentAssertion<ICollection<T>> NotEmpty<T>(this ArgumentAssertion<ICollection<T>> arg)
79+
public static ArgumentAssertion<ICollection<T>> NotNullNorEmpty<T>(this ArgumentAssertion<ICollection<T>> arg)
8180
{
82-
Code.NotEmpty(arg.Argument, arg.ArgumentName);
81+
Code.NotNullNorEmpty(arg.Argument, arg.ArgumentName);
8382
return arg;
8483
}
8584

8685
/// <summary>
87-
/// Ensures that supplied array is not empty.
86+
/// Ensures that supplied array is not null nor empty.
8887
/// </summary>
8988
/// <typeparam name="T">Type of item.</typeparam>
9089
/// <param name="arg">The argument.</param>
9190
/// <returns><paramref name="arg"/></returns>
9291
[DebuggerHidden, MethodImpl(AggressiveInlining)]
9392
[AssertionMethod]
94-
public static ArgumentAssertion<T[]> NotEmpty<T>(this ArgumentAssertion<T[]> arg)
93+
public static ArgumentAssertion<T[]> NotNullNorEmpty<T>(this ArgumentAssertion<T[]> arg)
9594
{
96-
Code.NotEmpty(arg.Argument, arg.ArgumentName);
95+
Code.NotNullNorEmpty(arg.Argument, arg.ArgumentName);
9796
return arg;
9897
}
9998

@@ -127,9 +126,10 @@ public static ArgumentAssertion<string> NotNullNorWhiteSpace(this ArgumentAssert
127126
/// <returns><paramref name="arg"/></returns>
128127
[DebuggerHidden, MethodImpl(AggressiveInlining)]
129128
[AssertionMethod]
129+
[ContractAnnotation("condition: false => halt")]
130130
public static ArgumentAssertion<T> Assert<T>(
131131
this ArgumentAssertion<T> arg,
132-
[AssertionCondition(AssertionConditionType.IS_TRUE)] bool condition,
132+
bool condition,
133133
[NotNull] string message)
134134
{
135135
Code.AssertArgument(condition, arg.ArgumentName, message);
@@ -145,9 +145,10 @@ public static ArgumentAssertion<T> Assert<T>(
145145
/// <returns><paramref name="arg"/></returns>
146146
[DebuggerHidden, MethodImpl(AggressiveInlining)]
147147
[AssertionMethod, StringFormatMethod("messageFormat")]
148+
[ContractAnnotation("condition: false => halt")]
148149
public static ArgumentAssertion<T> Assert<T>(
149150
this ArgumentAssertion<T> arg,
150-
[AssertionCondition(AssertionConditionType.IS_TRUE)] bool condition,
151+
bool condition,
151152
[NotNull] string messageFormat,
152153
[CanBeNull] params object[] args)
153154
{

CodeJam.Main/Assertions/Code.NonDebug.cs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ namespace CodeJam
1212
// Part that excluded from debug assertions generation.
1313
partial class Code
1414
{
15-
#region Argument validation
15+
#region Argument validation (DO NOT copy into DebugCode)
16+
// NB: Conditional methods cannot have a return type.
17+
1618
/// <summary>
1719
/// Creates <see cref="ArgumentAssertion{T}"/> for fluent assertions.
1820
/// </summary>
@@ -21,6 +23,7 @@ partial class Code
2123
/// <param name="argName">Argument name.</param>
2224
/// <returns><see cref="ArgumentAssertion{T}"/> instance.</returns>
2325
[DebuggerHidden, MethodImpl(AggressiveInlining)]
26+
[MustUseReturnValue]
2427
[AssertionMethod]
2528
public static ArgumentAssertion<T> Arg<T>(T arg, [InvokerParameterName] string argName) =>
2629
new ArgumentAssertion<T>(arg, argName);
@@ -88,8 +91,9 @@ public static void DisposedIf<TDisposable>(
8891
/// <param name="thisReference">The this reference.</param>
8992
[DebuggerHidden, MethodImpl(AggressiveInlining)]
9093
[AssertionMethod]
94+
[ContractAnnotation("resource: null => halt")]
9195
public static void DisposedIfNull<TResource, TDisposable>(
92-
[CanBeNull] TResource resource,
96+
[CanBeNull, NoEnumeration] TResource resource,
9397
[NotNull] TDisposable thisReference)
9498
where TResource : class
9599
where TDisposable : IDisposable
@@ -106,8 +110,9 @@ public static void DisposedIfNull<TResource, TDisposable>(
106110
/// <param name="message">The message.</param>
107111
[DebuggerHidden, MethodImpl(AggressiveInlining)]
108112
[AssertionMethod]
113+
[ContractAnnotation("resource: null => halt")]
109114
public static void DisposedIfNull<TResource, TDisposable>(
110-
[CanBeNull] TResource resource,
115+
[CanBeNull, NoEnumeration] TResource resource,
111116
[NotNull] TDisposable thisReference,
112117
[NotNull] string message)
113118
where TResource : class
@@ -126,8 +131,9 @@ public static void DisposedIfNull<TResource, TDisposable>(
126131
/// <param name="args">The arguments.</param>
127132
[DebuggerHidden, MethodImpl(AggressiveInlining)]
128133
[AssertionMethod, StringFormatMethod("messageFormat")]
134+
[ContractAnnotation("resource: null => halt")]
129135
public static void DisposedIfNull<TResource, TDisposable>(
130-
[CanBeNull] TResource resource,
136+
[CanBeNull, NoEnumeration] TResource resource,
131137
[NotNull] TDisposable thisReference,
132138
[NotNull] string messageFormat,
133139
[CanBeNull] params object[] args)

0 commit comments

Comments
 (0)