Skip to content

Commit b233961

Browse files
committed
C#: Add assertion tests
1 parent 6811d52 commit b233961

File tree

16 files changed

+273
-97
lines changed

16 files changed

+273
-97
lines changed

csharp/ql/src/semmle/code/csharp/commons/Assertions.qll

Lines changed: 81 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,20 @@
11
/** Provides classes for assertions. */
22
private import semmle.code.csharp.frameworks.system.Diagnostics
33
private import semmle.code.csharp.frameworks.test.VisualStudio
4+
private import semmle.code.csharp.frameworks.System
45

56
/** An assertion method. */
67
abstract class AssertMethod extends Method {
78
/** Gets the index of the parameter being asserted. */
89
abstract int getAssertionIndex();
10+
11+
/** Gets the parameter being asserted. */
12+
final Parameter getAssertionParameter() {
13+
result = this.getParameter(this.getAssertionIndex())
14+
}
15+
16+
/** Gets the exception being thrown if the assertion fails, if any. */
17+
abstract ExceptionClass getExceptionClass();
918
}
1019

1120
/** A positive assertion method. */
@@ -24,6 +33,36 @@ abstract class AssertNullMethod extends AssertMethod {
2433
abstract class AssertNonNullMethod extends AssertMethod {
2534
}
2635

36+
/** An assertion, that is, a call to an assertion method. */
37+
class Assertion extends MethodCall {
38+
private AssertMethod target;
39+
40+
Assertion() { this.getTarget() = target }
41+
42+
/** Gets the assertion method targeted by this assertion. */
43+
AssertMethod getAssertMethod() { result = target }
44+
45+
/** Gets the expression that this assertion pertains to. */
46+
Expr getExpr() {
47+
result = this.getArgumentForParameter(target.getAssertionParameter())
48+
}
49+
}
50+
51+
/** A trivially failing assertion, for example `Debug.Assert(false)`. */
52+
class FailingAssertion extends Assertion {
53+
FailingAssertion() {
54+
exists(AssertMethod am, Expr e |
55+
am = this.getAssertMethod() and
56+
e = this.getExpr() |
57+
am instanceof AssertTrueMethod and
58+
e.(BoolLiteral).getBoolValue() = false
59+
or
60+
am instanceof AssertFalseMethod and
61+
e.(BoolLiteral).getBoolValue() = true
62+
)
63+
}
64+
}
65+
2766
/**
2867
* A `System.Diagnostics.Debug` assertion method.
2968
*/
@@ -33,6 +72,12 @@ class SystemDiagnosticsDebugAssertTrueMethod extends AssertTrueMethod {
3372
}
3473

3574
override int getAssertionIndex() { result = 0 }
75+
76+
override ExceptionClass getExceptionClass() {
77+
// A failing assertion generates a message box, see
78+
// https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.debug.assert
79+
none()
80+
}
3681
}
3782

3883
/** A Visual Studio assertion method. */
@@ -42,6 +87,8 @@ class VSTestAssertTrueMethod extends AssertTrueMethod {
4287
}
4388

4489
override int getAssertionIndex() { result = 0 }
90+
91+
override AssertFailedExceptionClass getExceptionClass() { any() }
4592
}
4693

4794
/** A Visual Studio negated assertion method. */
@@ -51,6 +98,8 @@ class VSTestAssertFalseMethod extends AssertFalseMethod {
5198
}
5299

53100
override int getAssertionIndex() { result = 0 }
101+
102+
override AssertFailedExceptionClass getExceptionClass() { any() }
54103
}
55104

56105
/** A Visual Studio `null` assertion method. */
@@ -60,6 +109,8 @@ class VSTestAssertNullMethod extends AssertNullMethod {
60109
}
61110

62111
override int getAssertionIndex() { result = 0 }
112+
113+
override AssertFailedExceptionClass getExceptionClass() { any() }
63114
}
64115

65116
/** A Visual Studio non-`null` assertion method. */
@@ -69,28 +120,50 @@ class VSTestAssertNonNullMethod extends AssertNonNullMethod {
69120
}
70121

71122
override int getAssertionIndex() { result = 0 }
123+
124+
override AssertFailedExceptionClass getExceptionClass() { any() }
72125
}
73126

74127
/** A method that forwards to another assertion method. */
75128
class ForwarderAssertMethod extends AssertMethod {
76129
AssertMethod forwardee;
77-
int assertionIndex;
130+
Parameter p;
78131

79132
ForwarderAssertMethod() {
80-
exists(AssignableDefinitions::ImplicitParameterDefinition def, ParameterRead pr |
81-
def.getParameter() = this.getParameter(assertionIndex) and
82-
pr = def.getAReachableRead() and
83-
pr.getAControlFlowNode().postDominates(this.getEntryPoint()) and
84-
forwardee.getParameter(forwardee.getAssertionIndex()).getAnAssignedArgument() = pr
133+
p = this.getAParameter() and
134+
strictcount(AssignableDefinition def | def.getTarget() = p) = 1 and
135+
forex(ControlFlowElement body |
136+
body = this.getABody() |
137+
exists(Assertion a |
138+
body = getAnAssertingElement(a) and
139+
a.getExpr() = p.getAnAccess() and
140+
forwardee = a.getAssertMethod()
141+
)
85142
)
86143
}
87144

88-
override int getAssertionIndex() { result = assertionIndex }
145+
override int getAssertionIndex() { result = p.getPosition() }
89146

147+
override ExceptionClass getExceptionClass() {
148+
result = forwardee.getExceptionClass()
149+
}
150+
90151
/** Gets the underlying assertion method that is being forwarded to. */
91152
AssertMethod getUnderlyingAssertMethod() { result = forwardee }
92153
}
93154

155+
private ControlFlowElement getAnAssertingElement(Assertion a) {
156+
result = a
157+
or
158+
result = getAnAssertingStmt(a)
159+
}
160+
161+
private Stmt getAnAssertingStmt(Assertion a) {
162+
result.(ExprStmt).getExpr() = getAnAssertingElement(a)
163+
or
164+
result.(BlockStmt).getFirstStmt() = getAnAssertingElement(a)
165+
}
166+
94167
/** A method that forwards to a positive assertion method. */
95168
class ForwarderAssertTrueMethod extends ForwarderAssertMethod, AssertTrueMethod {
96169
ForwarderAssertTrueMethod() {
@@ -121,8 +194,5 @@ class ForwarderAssertNonNullMethod extends ForwarderAssertMethod, AssertNonNullM
121194

122195
/** Holds if expression `e` appears in an assertion. */
123196
predicate isExprInAssertion(Expr e) {
124-
exists(MethodCall call, AssertMethod assertMethod |
125-
call.getTarget() = assertMethod |
126-
e = call.getArgument(assertMethod.getAssertionIndex()).getAChildExpr*()
127-
)
197+
e = any(Assertion a).getExpr().getAChildExpr*()
128198
}

csharp/ql/src/semmle/code/csharp/frameworks/test/VisualStudio.qll

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,14 @@ class VSTestAssertClass extends Class {
107107
}
108108
}
109109

110+
/** The `Microsoft.VisualStudio.TestTools.UnitTesting.AssertFailedException` class. */
111+
class AssertFailedExceptionClass extends ExceptionClass {
112+
AssertFailedExceptionClass() {
113+
this.getNamespace() instanceof VSTestNamespace and
114+
this.hasName("AssertFailedException")
115+
}
116+
}
117+
110118
/** DEPRECATED. Gets the `Microsoft.VisualStudio.TestTools.UnitTesting.Assert` class. */
111119
deprecated
112120
VSTestAssertClass getVSTestAssertClass() { any() }

0 commit comments

Comments
 (0)