import parse import testdata import unittest import unittest.mock class TestCase(unittest.TestCase): ''' Base test case class ''' def __str__(self): ''' Overwrite the standard string representation by an easier to read version. ''' # Based on http://stackoverflow.com/a/18460031 return '.'.join([self.__class__.__module__, self.__class__.__name__, self._testMethodName]) def shortDescription(self): ''' Disable displaying the first line of the docstring instead of module.class.method. ''' # http://www.saltycrane.com/blog/2012/07/how-prevent-nose-unittest-using-docstring-when-verbosity-2/ return None class TestStrEnum(TestCase): def test_clean_representation(self): self.assertEqual(repr(parse.Type.group), 'Type.group') self.assertEqual(repr(parse.Type.brackets), 'Type.brackets') self.assertEqual(repr(parse.Modifier.negate), 'Modifier.negate') self.assertEqual(repr(parse.Modifier.plus), 'Modifier.plus') class TestJSTypes(TestCase): def test_equality(self): self.assertEqual(parse.JSBool(True), parse.JSBool(True)) self.assertEqual(parse.JSBool(False), parse.JSBool(False)) self.assertNotEqual(parse.JSBool(True), parse.JSBool(False)) self.assertNotEqual(parse.JSBool(False), parse.JSBool(True)) self.assertEqual(parse.JSInt(0), parse.JSInt(0)) self.assertEqual(parse.JSInt(42), parse.JSInt(42)) self.assertEqual(parse.JSInt(-13), parse.JSInt(-13)) self.assertNotEqual(parse.JSInt(0), parse.JSInt(42)) self.assertNotEqual(parse.JSInt(42), parse.JSInt(0)) self.assertNotEqual(parse.JSInt(0), parse.JSInt(-13)) self.assertNotEqual(parse.JSInt(-13), parse.JSInt(42)) self.assertEqual(parse.JSString(''), parse.JSString('')) self.assertEqual(parse.JSString('test'), parse.JSString('test')) self.assertNotEqual(parse.JSString(''), parse.JSString('test')) # Comparisons between different types are currently not supported (JS's equality algorithm is a nightmare) and always return False self.assertNotEqual(parse.JSBool(True), parse.JSInt(1)) self.assertNotEqual(parse.JSBool(True), parse.JSString('true')) def test_operators(self): self.assertEqual(parse.JSBool(True) + parse.JSBool(True), parse.JSInt(2)) self.assertEqual(parse.JSBool(True) + parse.JSInt(41), parse.JSInt(42)) self.assertEqual(parse.JSBool(True) + parse.JSString('42'), parse.JSString('true42')) self.assertEqual(parse.JSBool(True) - parse.JSBool(True), parse.JSInt(0)) self.assertEqual(parse.JSBool(False) - parse.JSBool(True), parse.JSInt(-1)) self.assertEqual(parse.JSBool(True) - parse.JSInt(3), parse.JSInt(-2)) self.assertEqual(parse.JSBool(True) - parse.JSString('14'), parse.JSInt(-13)) self.assertEqual(parse.JSBool(True) - parse.JSString(''), parse.JSInt(1)) #self.assertEqual(parse.JSBool(True) - parse.JSString('test'), parse.JSInt(NaN)) # Not supported #self.assertEqual(parse.JSBool(True) - parse.JSString('3test'), parse.JSInt(NaN)) # Not supported self.assertEqual(parse.JSBool(True) * parse.JSBool(True), parse.JSInt(1)) self.assertEqual(parse.JSBool(False) * parse.JSBool(True), parse.JSInt(0)) self.assertEqual(parse.JSBool(True) * parse.JSInt(42), parse.JSInt(42)) self.assertEqual(parse.JSBool(False) * parse.JSInt(42), parse.JSInt(0)) self.assertEqual(parse.JSBool(True) * parse.JSString('13'), parse.JSInt(13)) self.assertEqual(parse.JSBool(True) * parse.JSString(''), parse.JSInt(0)) #self.assertEqual(parse.JSBool(True) * parse.JSString('test'), parse.JSInt(NaN)) # Not supported #self.assertEqual(parse.JSBool(True) * parse.JSString('2test'), parse.JSInt(NaN)) # Not supported self.assertEqual(parse.JSInt(13) + parse.JSBool(True), parse.JSInt(14)) self.assertEqual(parse.JSInt(13) + parse.JSInt(29), parse.JSInt(42)) self.assertEqual(parse.JSInt(13) + parse.JSString('29'), parse.JSString('1329')) self.assertEqual(parse.JSInt(13) + parse.JSString(''), parse.JSString('13')) self.assertEqual(parse.JSInt(13) + parse.JSString('test'), parse.JSString('13test')) self.assertEqual(parse.JSInt(13) - parse.JSBool(True), parse.JSInt(12)) self.assertEqual(parse.JSInt(13) - parse.JSInt(12), parse.JSInt(1)) self.assertEqual(parse.JSInt(13) - parse.JSString('15'), parse.JSInt(-2)) self.assertEqual(parse.JSInt(13) - parse.JSString(''), parse.JSInt(13)) #self.assertEqual(parse.JSInt(13) - parse.JSString('test'), parse.JSInt(NaN)) # Not supported #self.assertEqual(parse.JSInt(13) - parse.JSString('13test'), parse.JSInt(NaN)) # Not supported self.assertEqual(parse.JSInt(3) * parse.JSBool(True), parse.JSInt(3)) self.assertEqual(parse.JSInt(3) * parse.JSInt(4), parse.JSInt(12)) self.assertEqual(parse.JSInt(3) * parse.JSString('4'), parse.JSInt(12)) self.assertEqual(parse.JSInt(3) * parse.JSString(''), parse.JSInt(0)) #self.assertEqual(parse.JSInt(3) * parse.JSString('test'), parse.JSInt(NaN)) # Not supported #self.assertEqual(parse.JSInt(3) * parse.JSString('1test'), parse.JSInt(NaN)) # Not supported self.assertEqual(parse.JSString('1') + parse.JSBool(True), parse.JSString('1true')) self.assertEqual(parse.JSString('') + parse.JSBool(False), parse.JSString('false')) self.assertEqual(parse.JSString('test') + parse.JSBool(True), parse.JSString('testtrue')) self.assertEqual(parse.JSString('1') + parse.JSInt(1), parse.JSString('11')) self.assertEqual(parse.JSString('1') + parse.JSString('1'), parse.JSString('11')) self.assertEqual(parse.JSString('13') - parse.JSBool(True), parse.JSInt(12)) self.assertEqual(parse.JSString('') - parse.JSBool(True), parse.JSInt(-1)) #self.assertEqual(parse.JSString('test') - parse.JSBool(True), parse.JSInt(NaN)) # Not supported #self.assertEqual(parse.JSString('3test') - parse.JSBool(True), parse.JSInt(NaN)) # Not supported self.assertEqual(parse.JSString('1') - parse.JSInt(3), parse.JSInt(-2)) self.assertEqual(parse.JSString('') - parse.JSInt(-3), parse.JSInt(3)) #self.assertEqual(parse.JSString('test') - parse.JSInt(13), parse.JSInt(NaN)) # Not supported #self.assertEqual(parse.JSString('13test') - parse.JSInt(13), parse.JSInt(NaN)) # Not supported self.assertEqual(parse.JSString('13') - parse.JSString('2'), parse.JSInt(11)) self.assertEqual(parse.JSString('13') - parse.JSString(''), parse.JSInt(13)) self.assertEqual(parse.JSString('') - parse.JSString('-3'), parse.JSInt(3)) #self.assertEqual(parse.JSString('test') - parse.JSString('2'), parse.JSInt(NaN)) # Not supported #self.assertEqual(parse.JSString('13test') - parse.JSString('2'), parse.JSInt(NaN)) # Not supported #self.assertEqual(parse.JSString('2') - parse.JSString('test'), parse.JSInt(NaN)) # Not supported #self.assertEqual(parse.JSString('2') - parse.JSString('13test'), parse.JSInt(NaN)) # Not supported self.assertEqual(parse.JSString('3') * parse.JSBool(True), parse.JSInt(3)) self.assertEqual(parse.JSString('') * parse.JSBool(True), parse.JSInt(0)) #self.assertEqual(parse.JSString('test') * parse.JSBool(True), parse.JSInt(NaN)) # Not supported #self.assertEqual(parse.JSString('13test') * parse.JSBool(True), parse.JSInt(NaN)) # Not supported self.assertEqual(parse.JSString('3') * parse.JSInt(2), parse.JSInt(6)) self.assertEqual(parse.JSString('') * parse.JSInt(13), parse.JSInt(0)) #self.assertEqual(parse.JSString('test') * parse.JSInt(1), parse.JSInt(NaN)) # Not supported #self.assertEqual(parse.JSString('13test') * parse.JSInt(1), parse.JSInt(NaN)) # Not supported self.assertEqual(parse.JSString('3') * parse.JSString('2'), parse.JSInt(6)) self.assertEqual(parse.JSString('3') * parse.JSString(''), parse.JSInt(0)) self.assertEqual(parse.JSString('') * parse.JSString('3'), parse.JSInt(0)) #self.assertEqual(parse.JSString('3') * parse.JSString('test'), parse.JSInt(NaN)) # Not supported #self.assertEqual(parse.JSString('') * parse.JSString('test'), parse.JSInt(NaN)) # Not supported #self.assertEqual(parse.JSString('test') * parse.JSString('3'), parse.JSInt(NaN)) # Not supported #self.assertEqual(parse.JSString('test') * parse.JSString(''), parse.JSInt(NaN)) # Not supported #self.assertEqual(parse.JSString('3test') * parse.JSString('3'), parse.JSInt(NaN)) # Not supported def test_casting(self): # Casting to Python objects self.assertEqual(bool(parse.JSBool(True)), True) self.assertEqual(bool(parse.JSBool(False)), False) self.assertEqual(int(parse.JSBool(True)), 1) self.assertEqual(int(parse.JSBool(False)), 0) self.assertEqual(str(parse.JSBool(True)), 'true') # JavaScript capitalisation! self.assertEqual(str(parse.JSBool(False)), 'false') self.assertEqual(bool(parse.JSInt(0)), False) self.assertEqual(bool(parse.JSInt(42)), True) self.assertEqual(int(parse.JSInt(0)), 0) self.assertEqual(int(parse.JSInt(42)), 42) self.assertEqual(str(parse.JSInt(0)), '0') self.assertEqual(str(parse.JSInt(42)), '42') self.assertEqual(bool(parse.JSString('')), False) self.assertEqual(bool(parse.JSString('test')), True) self.assertEqual(int(parse.JSString('')), 0) #self.assertEqual(int(parse.JSString('test')), NaN) # Not supported self.assertEqual(int(parse.JSString('0')), 0) self.assertEqual(int(parse.JSString('-13')), -13) self.assertEqual(str(parse.JSString('')), '') self.assertEqual(str(parse.JSString('test')), 'test') # Casting to JS types self.assertEqual(parse.JSBool(parse.JSBool(True)), parse.JSBool(True)) self.assertEqual(parse.JSBool(parse.JSBool(False)), parse.JSBool(False)) self.assertEqual(parse.JSInt(parse.JSBool(True)), parse.JSInt(1)) self.assertEqual(parse.JSInt(parse.JSBool(False)), parse.JSInt(0)) self.assertEqual(parse.JSString(parse.JSBool(True)), parse.JSString('true')) # JavaScript capitalisation! self.assertEqual(parse.JSString(parse.JSBool(False)), parse.JSString('false')) self.assertEqual(parse.JSBool(parse.JSInt(0)), parse.JSBool(False)) self.assertEqual(parse.JSBool(parse.JSInt(42)), parse.JSBool(True)) self.assertEqual(parse.JSInt(parse.JSInt(0)), parse.JSInt(0)) self.assertEqual(parse.JSInt(parse.JSInt(42)), parse.JSInt(42)) self.assertEqual(parse.JSString(parse.JSInt(0)), parse.JSString('0')) self.assertEqual(parse.JSString(parse.JSInt(42)), parse.JSString('42')) self.assertEqual(parse.JSBool(parse.JSString('')), parse.JSBool(False)) self.assertEqual(parse.JSBool(parse.JSString('test')), parse.JSBool(True)) #self.assertEqual(parse.JSInt(parse.JSString('')), parse.JSInt(0)) # Not supported (yet?) #self.assertEqual(parse.JSInt(parse.JSString('test')), parse.JSInt(NaN)) # Not supported (yet?) self.assertEqual(parse.JSInt(parse.JSString('0')), parse.JSInt(0)) self.assertEqual(parse.JSInt(parse.JSString('-13')), parse.JSInt(-13)) self.assertEqual(parse.JSString(parse.JSString('')), parse.JSString('')) self.assertEqual(parse.JSString(parse.JSString('test')), parse.JSString('test')) def test_repr(self): self.assertEqual(repr(parse.JSBool(True)), 'JSBool(True)') self.assertEqual(repr(parse.JSBool(False)), 'JSBool(False)') self.assertEqual(repr(parse.JSInt(0)), 'JSInt(0)') self.assertEqual(repr(parse.JSInt(-13)), 'JSInt(-13)') self.assertEqual(repr(parse.JSInt(42)), 'JSInt(42)') self.assertEqual(repr(parse.JSString('')), "JSString('')") self.assertEqual(repr(parse.JSString('test')), "JSString('test')") class TestItem(TestCase): def test_basic(self): item = parse.Item(parse.Type.group, [], values = []) item = parse.Item(parse.Type.group, [parse.Modifier.plus], values = []) with self.assertRaises(ValueError): parse.Item(-1, []) with self.assertRaises(TypeError): parse.Item(parse.Type.brackets, -1) with self.assertRaises(ValueError): parse.Item(parse.Type.brackets, [-1]) with self.assertRaises(ValueError): parse.Item(parse.Type.group, []) # no values def test_evaluate_brackets(self): def test_brackets(modifiers, result): self.assertEqual(parse.Item(parse.Type.brackets, modifiers).evaluate(), result) test_brackets([], parse.JSString('')) test_brackets([parse.Modifier.plus], parse.JSInt(0)) test_brackets([parse.Modifier.negate], parse.JSBool(False)) # ++[] -> syntax error test_brackets([parse.Modifier.plus, parse.Modifier.negate], parse.JSInt(0)) test_brackets([parse.Modifier.negate, parse.Modifier.plus], parse.JSBool(True)) test_brackets([parse.Modifier.negate, parse.Modifier.negate], parse.JSBool(True)) # +++[] -> syntax error # ++![] -> syntax error test_brackets([parse.Modifier.plus, parse.Modifier.negate, parse.Modifier.plus], parse.JSInt(1)) test_brackets([parse.Modifier.plus, parse.Modifier.negate, parse.Modifier.negate], parse.JSInt(1)) # !++[] -> syntax error test_brackets([parse.Modifier.negate, parse.Modifier.plus, parse.Modifier.negate], parse.JSBool(True)) test_brackets([parse.Modifier.negate, parse.Modifier.negate, parse.Modifier.plus], parse.JSBool(False)) test_brackets([parse.Modifier.negate, parse.Modifier.negate, parse.Modifier.negate], parse.JSBool(False)) def test_evaluate_group(self): item = parse.Item(parse.Type.group, [], values = []) with self.assertRaises(ValueError): item.evaluate() # No evaluateFunction specified with self.assertRaises(ValueError): item.evaluate(13) # Not a callable mock = unittest.mock.Mock(return_value = 42) item = parse.Item(parse.Type.group, [], values = [parse.Item(parse.Type.brackets, [parse.Modifier.plus])]) self.assertEqual(item.evaluate(evaluateFunction = mock), 42) self.assertEqual(mock.call_count, 1) self.assertEqual(len(mock.call_args), 2) self.assertEqual(len(mock.call_args[0]), 2) self.assertEqual(len(mock.call_args[1]), 0) self.assertIs(mock.call_args[0][0], item.values) self.assertIs(mock.call_args[0][1], item.modifiers) class TestFunctions(TestCase): def test_parse(self): self.assertEqual(parse.parse('!![]'), [parse.Item(parse.Type.brackets, [parse.Modifier.negate, parse.Modifier.negate])]) self.assertEqual(parse.parse('!![]+!+![]'), [ parse.Item(parse.Type.brackets, [parse.Modifier.negate, parse.Modifier.negate]), parse.Item(parse.Type.brackets, [parse.Modifier.negate, parse.Modifier.plus, parse.Modifier.negate]), ]) self.assertEqual(parse.parse('(!![]+!+![]+[])+(+!![]+[])'), [ parse.Item(parse.Type.group, [], [ parse.Item(parse.Type.brackets, [parse.Modifier.negate, parse.Modifier.negate]), parse.Item(parse.Type.brackets, [parse.Modifier.negate, parse.Modifier.plus, parse.Modifier.negate]), parse.Item(parse.Type.brackets, []), ]), parse.Item(parse.Type.group, [], [ parse.Item(parse.Type.brackets, [parse.Modifier.plus, parse.Modifier.negate, parse.Modifier.negate]), parse.Item(parse.Type.brackets, []), ]), ]) self.assertEqual(parse.parse('+((!![]+!+![]+[])+(+!![]+[]))'), [ parse.Item(parse.Type.group, [parse.Modifier.plus], values = [ parse.Item(parse.Type.group, [], values = [ parse.Item(parse.Type.brackets, [parse.Modifier.negate, parse.Modifier.negate]), parse.Item(parse.Type.brackets, [parse.Modifier.negate, parse.Modifier.plus, parse.Modifier.negate]), parse.Item(parse.Type.brackets, []), ]), parse.Item(parse.Type.group, [], values = [ parse.Item(parse.Type.brackets, [parse.Modifier.plus, parse.Modifier.negate, parse.Modifier.negate]), parse.Item(parse.Type.brackets, []), ]), ]), ]) def test_evaluate(self): self.assertEqual(parse.evaluate([parse.Item(parse.Type.brackets, [parse.Modifier.negate, parse.Modifier.negate])]), parse.JSBool(True)) self.assertEqual(parse.evaluate([ parse.Item(parse.Type.brackets, [parse.Modifier.negate, parse.Modifier.negate]), parse.Item(parse.Type.brackets, [parse.Modifier.negate, parse.Modifier.negate]), ]), parse.JSInt(2)) self.assertEqual(parse.evaluate([ parse.Item(parse.Type.brackets, [parse.Modifier.negate, parse.Modifier.negate]), parse.Item(parse.Type.brackets, [parse.Modifier.negate, parse.Modifier.negate]), parse.Item(parse.Type.brackets, []), ]), parse.JSString('2')) self.assertEqual(parse.evaluate([ parse.Item(parse.Type.group, [], values = [ parse.Item(parse.Type.brackets, [parse.Modifier.negate, parse.Modifier.negate]), parse.Item(parse.Type.brackets, [parse.Modifier.negate, parse.Modifier.negate]), parse.Item(parse.Type.brackets, []), ]), parse.Item(parse.Type.group, [], values = [ parse.Item(parse.Type.brackets, [parse.Modifier.plus, parse.Modifier.negate, parse.Modifier.negate]), parse.Item(parse.Type.brackets, []), ]), ]), parse.JSString('21')) def test_crack(self): for url, html, result in testdata.data: self.assertEqual(parse.crack(url, html), parse.JSInt(result))