mirror of
https://github.com/github/codeql.git
synced 2026-05-03 12:45:27 +02:00
Initial commit of Python queries and QL libraries.
This commit is contained in:
committed by
Mark Shannon
parent
90c75cd362
commit
5f58824d1b
9
python/ql/src/Testing/ImpreciseAssert.py
Normal file
9
python/ql/src/Testing/ImpreciseAssert.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from unittest import TestCase
|
||||
|
||||
class MyTest(TestCase):
|
||||
|
||||
|
||||
def testInts(self):
|
||||
self.assertTrue(1 == 1)
|
||||
self.assertFalse(1 > 2)
|
||||
self.assertTrue(1 in []) #This will fail
|
||||
37
python/ql/src/Testing/ImpreciseAssert.qhelp
Normal file
37
python/ql/src/Testing/ImpreciseAssert.qhelp
Normal file
@@ -0,0 +1,37 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
|
||||
<overview>
|
||||
<p>The class <code>unittest.TestCase</code> provides a range of assertion methods. As well as the general forms <code>assertTrue()</code> and <code>assertFalse()</code>
|
||||
more specific forms such as <code>assertGreaterEquals()</code> and <code>assertNotIn()</code> are provided.
|
||||
By using the more specific forms it is possible to get more precise and informative failure messages in the event of a test failing. This can speed up the debugging process.
|
||||
</p>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>Replace all calls to <code>assertTrue()</code> and <code>assertFalse()</code> that do not provide a custom failure message with a more specific variant.
|
||||
Alternatively, provide a tailored failure message using the <code>assertTrue(condition, message)</code> form.
|
||||
</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<p>In this example, <code>assertTrue()</code> and <code>assertFalse()</code> are used.</p>
|
||||
<sample src="ImpreciseAssert.py" />
|
||||
<p>
|
||||
This will make it more difficult to determine what has gone wrong when <code>self.assertTrue(1 in [])</code> fails.
|
||||
The failure message "AssertionError: False is not true" is not very helpful.
|
||||
</p>
|
||||
|
||||
<p>A more useful error message can be generated by changing the asserts to the more specific forms as in the following example.</p>
|
||||
<sample src="ImpreciseAssert2.py" />
|
||||
<p>In this case, the failure message "AssertionError: 1 not found in []" is much more informative.</p>
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>Python library reference: <a href="https://docs.python.org/library/unittest.html#unittest.TestCase.assertEqual">TestCase.assertEqual</a>.</li>
|
||||
</references>
|
||||
|
||||
</qhelp>
|
||||
101
python/ql/src/Testing/ImpreciseAssert.ql
Normal file
101
python/ql/src/Testing/ImpreciseAssert.ql
Normal file
@@ -0,0 +1,101 @@
|
||||
/**
|
||||
* @name Imprecise assert
|
||||
* @description Using 'assertTrue' or 'assertFalse' rather than a more specific assertion can give uninformative failure messages.
|
||||
* @kind problem
|
||||
* @tags maintainability
|
||||
* testability
|
||||
* @problem.severity recommendation
|
||||
* @sub-severity high
|
||||
* @precision very-high
|
||||
* @id py/imprecise-assert
|
||||
*/
|
||||
|
||||
import python
|
||||
|
||||
/* Helper predicate for CallToAssertOnComparison class */
|
||||
predicate callToAssertOnComparison(Call call, string assertName, Cmpop op) {
|
||||
call.getFunc().(Attribute).getName() = assertName
|
||||
and
|
||||
(assertName = "assertTrue" or assertName = "assertFalse")
|
||||
and
|
||||
exists(Compare cmp |
|
||||
cmp = call.getArg(0) and
|
||||
/* Exclude complex comparisons like: a < b < c */
|
||||
not exists(cmp.getOp(1)) and
|
||||
op = cmp.getOp(0)
|
||||
)
|
||||
}
|
||||
|
||||
class CallToAssertOnComparison extends Call {
|
||||
|
||||
CallToAssertOnComparison() {
|
||||
callToAssertOnComparison(this, _, _)
|
||||
}
|
||||
|
||||
Cmpop getOperator() {
|
||||
callToAssertOnComparison(this, _, result)
|
||||
}
|
||||
|
||||
string getMethodName() {
|
||||
callToAssertOnComparison(this, result, _)
|
||||
}
|
||||
|
||||
string getBetterName() {
|
||||
exists(Cmpop op |
|
||||
callToAssertOnComparison(this, "assertTrue", op) and
|
||||
(
|
||||
op instanceof Eq and result = "assertEqual"
|
||||
or
|
||||
op instanceof NotEq and result = "assertNotEqual"
|
||||
or
|
||||
op instanceof Lt and result = "assertLess"
|
||||
or
|
||||
op instanceof LtE and result = "assertLessEqual"
|
||||
or
|
||||
op instanceof Gt and result = "assertGreater"
|
||||
or
|
||||
op instanceof GtE and result = "assertGreaterEqual"
|
||||
or
|
||||
op instanceof In and result = "assertIn"
|
||||
or
|
||||
op instanceof NotIn and result = "assertNotIn"
|
||||
or
|
||||
op instanceof Is and result = "assertIs"
|
||||
or
|
||||
op instanceof IsNot and result = "assertIsNot"
|
||||
)
|
||||
or
|
||||
callToAssertOnComparison(this, "assertFalse", op) and
|
||||
(
|
||||
op instanceof NotEq and result = "assertEqual"
|
||||
or
|
||||
op instanceof Eq and result = "assertNotEqual"
|
||||
or
|
||||
op instanceof GtE and result = "assertLess"
|
||||
or
|
||||
op instanceof Gt and result = "assertLessEqual"
|
||||
or
|
||||
op instanceof LtE and result = "assertGreater"
|
||||
or
|
||||
op instanceof Lt and result = "assertGreaterEqual"
|
||||
or
|
||||
op instanceof NotIn and result = "assertIn"
|
||||
or
|
||||
op instanceof In and result = "assertNotIn"
|
||||
or
|
||||
op instanceof IsNot and result = "assertIs"
|
||||
or
|
||||
op instanceof Is and result = "assertIsNot"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
from CallToAssertOnComparison call
|
||||
where
|
||||
/* Exclude cases where an explicit message is provided*/
|
||||
not exists(call.getArg(1))
|
||||
select call, call.getMethodName() + "(a " + call.getOperator().getSymbol() + " b) " +
|
||||
"cannot provide an informative message. Using " + call.getBetterName() + "(a, b) instead will give more informative messages."
|
||||
9
python/ql/src/Testing/ImpreciseAssert2.py
Normal file
9
python/ql/src/Testing/ImpreciseAssert2.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from unittest import TestCase
|
||||
|
||||
class MyTest(TestCase):
|
||||
|
||||
|
||||
def testInts(self):
|
||||
self.assertEqual(1, 1)
|
||||
self.assertLessEqual(1, 2)
|
||||
self.assertIn(1, []) #This will fail
|
||||
18
python/ql/src/Testing/Mox.qll
Normal file
18
python/ql/src/Testing/Mox.qll
Normal file
@@ -0,0 +1,18 @@
|
||||
import python
|
||||
|
||||
/** Whether `mox` or `.StubOutWithMock()` is used in thin module `m`.
|
||||
*/
|
||||
predicate useOfMoxInModule(Module m) {
|
||||
exists(ModuleObject mox |
|
||||
mox.getName() = "mox" or mox.getName() = "mox3.mox" |
|
||||
exists(ControlFlowNode use |
|
||||
use.refersTo(mox) and
|
||||
use.getScope().getEnclosingModule() = m
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(Call call|
|
||||
call.getFunc().(Attribute).getName() = "StubOutWithMock" and
|
||||
call.getEnclosingModule() = m
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user