Initial commit of Python queries and QL libraries.

This commit is contained in:
Mark Shannon
2018-11-19 13:13:39 +00:00
committed by Mark Shannon
parent 90c75cd362
commit 5f58824d1b
725 changed files with 63520 additions and 0 deletions

View 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

View 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>

View 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."

View 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

View 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
)
}