diff options
author | Rogan Creswick <creswick@galois.com> | 2012-03-30 17:07:02 -0700 |
---|---|---|
committer | Rogan Creswick <creswick@galois.com> | 2012-03-30 17:07:02 -0700 |
commit | f6ab6622aab00fe7c2f4c3dc41f786ebbe0f0d73 (patch) | |
tree | 870111038542cd27153e1396ebdc063573249689 /tools/addon-sdk-1.5/python-lib/cuddlefish/tests/test_apiparser.py |
initial revision
Diffstat (limited to 'tools/addon-sdk-1.5/python-lib/cuddlefish/tests/test_apiparser.py')
-rw-r--r-- | tools/addon-sdk-1.5/python-lib/cuddlefish/tests/test_apiparser.py | 538 |
1 files changed, 538 insertions, 0 deletions
diff --git a/tools/addon-sdk-1.5/python-lib/cuddlefish/tests/test_apiparser.py b/tools/addon-sdk-1.5/python-lib/cuddlefish/tests/test_apiparser.py new file mode 100644 index 0000000..c24f5c3 --- /dev/null +++ b/tools/addon-sdk-1.5/python-lib/cuddlefish/tests/test_apiparser.py @@ -0,0 +1,538 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +import os +import unittest +from cuddlefish.docs.apiparser import parse_hunks, ParseError + +tests_path = os.path.abspath(os.path.dirname(__file__)) +static_files_path = os.path.join(tests_path, "static-files") + +class ParserTests(unittest.TestCase): + def pathname(self, filename): + return os.path.join(static_files_path, "docs", filename) + + def parse_text(self, text): + return list(parse_hunks(text)) + + def parse(self, pathname): + return self.parse_text(open(pathname).read()) + + def test_parser(self): + parsed = self.parse(self.pathname("APIsample.md")) + #for i,h in enumerate(parsed): + # print i, h + self.assertEqual(parsed[0], + ("version", 4)) + self.assertEqual(parsed[1], + ("markdown", """\ +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.0. If a copy of the MPL was not distributed with this + - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> + +# Title # + +Some text here + +""")) + + self.assertEqual(parsed[2][0], "api-json") + p_test = parsed[2][1] + self.assertEqual(p_test["name"], "test") + self.assertEqual(p_test["type"], "function") + self.assertEqual(p_test["signature"], "test(argOne, argTwo, \ +argThree, options)") + self.assertEqual(p_test["description"], + "This is a function which does nothing in \ +particular.") + r = p_test["returns"] + self.assertEqual(r["datatype"], "object") + self.assertEqual(r["description"], "") + self.assertEqual(len(r["props"]), 2) + self.assertEqual(r["props"][0]["datatype"], "string") + self.assertEqual(r["props"][0]["description"], "First string") + self.assertEqual(r["props"][1]["datatype"], "url") + self.assertEqual(r["props"][1]["description"], "First URL") + + self.assertEqual(p_test["params"][0], + {"name": "argOne", + "required": True, + "datatype": "string", + "description": "This is the first argument.", + "line_number": 15, + }) + + self.assertEqual(p_test["params"][1], + {"name": "argTwo", + "required": False, + "datatype": "bool", + "description": "This is the second argument.", + "line_number": 16, + }) + + self.assertEqual(p_test["params"][2], + {"name": "argThree", + "required": False, + "default": "default", + "datatype": "uri", + "line_number": 17, + "description": """\ +This is the third and final argument. And this is +a test of the ability to do multiple lines of +text.""", + }) + p3 = p_test["params"][3] + self.assertEqual(p3["name"], "options") + self.assertEqual(p3["required"], False) + self.failIf("type" in p3) + self.assertEqual(p3["description"], "Options Bag") + self.assertEqual(p3["props"][0], + {"name": "style", + "required": False, + "datatype": "string", + "description": "Some style information.", + "line_number": 22, + }) + self.assertEqual(p3["props"][1], + {"name": "secondToLastOption", + "required": False, + "default": "True", + "datatype": "bool", + "description": "The last property.", + "line_number": 23, + }) + self.assertEqual(p3["props"][2]["name"], "lastOption") + self.assertEqual(p3["props"][2]["required"], False) + self.assertEqual(p3["props"][2]["datatype"], "uri") + self.assertEqual(p3["props"][2]["description"], """\ +And this time we have +A multiline description +Written as haiku""") + + self.assertEqual(parsed[3][0], "markdown") + self.assertEqual(parsed[3][1], "\n\nThis text appears between the \ +API blocks.\n\n") + + self.assertEqual(parsed[4][0], "api-json") + p_test = parsed[4][1] + + expected = {'line_number': 32, + 'name': 'append', + 'params': [{'props':[{'line_number': 37, + 'required': False, + 'datatype': 'uri', + 'name': 'icon', + 'description': 'The HREF of an icon to show as the \ +method of accessing your features slideBar'}, + {'line_number': 38, + 'required': False, + 'datatype': 'string/xml', + 'name': 'html', + 'description': 'The content of the feature, either \ +as an HTML string,\nor an E4X document fragment.'}, + {'line_number': 41, + 'required': False, + 'datatype': 'uri', + 'name': 'url', + 'description': 'The url to load into the content area \ +of the feature'}, + {'line_number': 42, + 'required': False, + 'datatype': 'int', + 'name': 'width', + 'description': 'Width of the content area and the \ +selected slide size'}, + {'line_number': 43, + 'required': False, + 'datatype': 'bool', + 'name': 'persist', + 'description': 'Default slide behavior when being \ +selected as follows:\nIf true: blah; If false: double blah.'}, + {'line_number': 46, + 'required': False, + 'datatype': 'bool', + 'name': 'autoReload', + 'description': 'Automatically reload content on \ +select'}, + {'line_number': 47, + 'required': False, + 'datatype': 'function', + 'name': 'onClick', + 'description': 'Callback when the icon is \ +clicked'}, + {'line_number': 48, + 'required': False, + 'datatype': 'function', + 'name': 'onSelect', + 'description': 'Callback when the feature is selected'}, + {'line_number': 49, + 'required': False, + 'datatype': 'function', + 'name': 'onReady', + 'description': + 'Callback when featured is loaded'}], + 'line_number': 35, + 'required': True, + 'name': 'options', + 'description': 'Pass in all of your options here.'}], + 'signature': 'append(options)', + 'type': 'function', + 'description': 'This is a list of options to specify modifications to your \ +slideBar instance.'} + self.assertEqual(p_test, expected) + + self.assertEqual(parsed[6][0], "api-json") + p_test = parsed[6][1] + self.assertEqual(p_test["name"], "cool-func.dot") + self.assertEqual(p_test["signature"], "cool-func.dot(howMuch, double, \ +options, onemore, options2)") + self.assertEqual(p_test["returns"]["description"], + """\ +A value telling you just how cool you are. +A boa-constructor! +This description can go on for a while, and can even contain +some **realy** fancy things. Like `code`, or even +~~~~{.javascript} +// Some code! +~~~~""") + self.assertEqual(p_test["params"][2]["props"][0], + {"name": "callback", + "required": True, + "datatype": "function", + "line_number": 67, + "description": "The callback", + }) + self.assertEqual(p_test["params"][2]["props"][1], + {"name": "random", + "required": False, + "datatype": "bool", + "line_number": 68, + "description": "Do something random?", + }) + + p_test = parsed[8][1] + self.assertEqual(p_test["signature"],"random()") + + # tests for classes + #1) empty class + p_test = parsed[10][1] + self.assertEqual(len(p_test), 4) + self.assertEqual(p_test["name"], "empty-class") + self.assertEqual(p_test["description"], "This class contains nothing.") + self.assertEqual(p_test["type"], "class") + # 2) class with just one ctor + p_test = parsed[12][1] + self.assertEqual(len(p_test), 5) + self.assertEqual(p_test["name"], "only-one-ctor") + self.assertEqual(p_test["description"], "This class contains only \ +one constructor.") + self.assertEqual(p_test["type"], "class") + constructors = p_test["constructors"] + self.assertEqual(len(constructors), 1) + self._test_class_constructor(constructors[0], "one-constructor") + # 3) class with 2 ctors + p_test = parsed[14][1] + self.assertEqual(len(p_test), 5) + self.assertEqual(p_test["name"], "two-ctors") + self.assertEqual(p_test["description"], "This class contains two \ +constructors.") + self.assertEqual(p_test["type"], "class") + constructors = p_test["constructors"] + self.assertEqual(len(constructors), 2) + self._test_class_constructor(constructors[0], "one-constructor") + self._test_class_constructor(constructors[1], "another-constructor") + # 4) class with ctor + method + p_test = parsed[16][1] + self.assertEqual(len(p_test), 6) + self.assertEqual(p_test["name"], "ctor-and-method") + self.assertEqual(p_test["description"], "This class contains one \ +constructor and one method.") + self.assertEqual(p_test["type"], "class") + constructors = p_test["constructors"] + self.assertEqual(len(constructors), 1) + self._test_class_constructor(constructors[0], "one-constructor") + methods = p_test["methods"] + self.assertEqual(len(methods), 1) + self._test_class_method(methods[0]) + # 5) class with ctor + method + property + p_test = parsed[18][1] + self.assertEqual(len(p_test), 8) + self.assertEqual(p_test["name"], "ctor-method-prop-event") + self.assertEqual(p_test["description"], "This class contains one \ +constructor, one method, one property and an event.") + self.assertEqual(p_test["type"], "class") + constructors = p_test["constructors"] + self.assertEqual(len(constructors), 1) + self._test_class_constructor(constructors[0], "one-constructor") + methods = p_test["methods"] + self.assertEqual(len(methods), 1) + self._test_class_method(methods[0]) + properties = p_test["properties"] + self.assertEqual(len(properties), 1) + self._test_class_property(properties[0]) + events = p_test["events"] + self.assertEqual(len(events), 1) + self._test_class_event(events[0]) + + self.assertEqual(parsed[-1][0], "markdown") + self.assertEqual(parsed[-1][1], "\n\nSome more text here, \ +at the end of the file.\n\n") + + def _test_class_constructor(self, constructor, name): + self.assertEqual(constructor["type"], "constructor") + self.assertEqual(constructor["name"], name) + params = constructor["params"] + self.assertEqual(len(params), 1) + self.assertEqual(params[0]["name"], "options") + self.assertEqual(params[0]["description"], "An object-bag of goodies.") + + def _test_class_method(self, method): + self.assertEqual(method["type"], "method") + self.assertEqual(method["name"], "a-method") + self.assertEqual(method["description"], "Does things.") + params = method["params"] + self.assertEqual(len(params), 1) + self.assertEqual(params[0]["name"], "options") + self.assertEqual(params[0]["description"], "An argument.") + + def _test_class_property(self, prop): + self.assertEqual(prop["type"], "property") + self.assertEqual(prop["name"], "a-property") + self.assertEqual(prop["description"], "Represents stuff.") + self.assertEqual(prop["datatype"], "bool") + + def _test_class_event(self, event): + self.assertEqual(event["type"], "event") + self.assertEqual(event["name"], "message") + self.assertEqual(event["description"], "Event emitted when the \ +content script sends a message to the add-on.") + arguments = event["arguments"] + self.assertEqual(len(arguments), 1) + argument = arguments[0] + self.assertEqual(argument["datatype"], "JSON") + self.assertEqual(argument["description"], "The message itself as a \ +JSON-serialized object.") + + def test_missing_return_propname(self): + md = '''\ +<api name="test"> +@method +This is a function which does nothing in particular. +@returns {object} + @prop {string} First string, but the property name is missing + @prop {url} First URL, same problem +@param argOne {string} This is the first argument. +</api> +''' + self.assertRaises(ParseError, self.parse_text, md) + + def test_missing_return_proptype(self): + md = '''\ +<api name="test"> +@method +This is a function which does nothing in particular. +@returns {object} + @prop untyped It is an error to omit the type of a return property. +@param argOne {string} This is the first argument. +@param [argTwo=True] {bool} This is the second argument. +</api> +''' + self.assertRaises(ParseError, self.parse_text, md) + + def test_return_propnames(self): + md = '''\ +<api name="test"> +@method +This is a function which does nothing in particular. +@returns {object} + @prop firststring {string} First string. + @prop [firsturl] {url} First URL, not always provided. +@param argOne {string} This is the first argument. +@param [argTwo=True] {bool} This is the second argument. +</api> +''' + parsed = self.parse_text(md) + r = parsed[1][1]["returns"] + self.assertEqual(r["props"][0]["name"], "firststring") + self.assertEqual(r["props"][0], + {"name": "firststring", + "datatype": "string", + "description": "First string.", + "required": True, + "line_number": 5, # 1-indexed + }) + self.assertEqual(r["props"][1], + {"name": "firsturl", + "datatype": "url", + "description": "First URL, not always provided.", + "required": False, + "line_number": 6, + }) + + def test_return_description_1(self): + md = '''\ +<api name="test"> +@method +This is a function which does nothing in particular. +@returns {object} A one-line description. + @prop firststring {string} First string. + @prop [firsturl] {url} First URL, not always provided. +@param argOne {string} This is the first argument. +@param [argTwo=True] {bool} This is the second argument. +</api> +''' + parsed = self.parse_text(md) + r = parsed[1][1]["returns"] + self.assertEqual(r["description"], "A one-line description.") + + def test_return_description_2(self): + md = '''\ +<api name="test"> +@method +This is a function which does nothing in particular. +@returns {object} A six-line description + which is consistently indented by two spaces + except for this line + and preserves the following empty line + + from which a two-space indentation will be removed. + @prop firststring {string} First string. + @prop [firsturl] {url} First URL, not always provided. +@param argOne {string} This is the first argument. +@param [argTwo=True] {bool} This is the second argument. +</api> +''' + parsed = self.parse_text(md) + r = parsed[1][1]["returns"] + self.assertEqual(r["description"], + "A six-line description\n" + "which is consistently indented by two spaces\n" + " except for this line\n" + "and preserves the following empty line\n" + "\n" + "from which a two-space indentation will be removed.") + + def test_return_description_3(self): + md = '''\ +<api name="test"> +@method +This is a function which does nothing in particular. +@returns A one-line untyped description. +@param argOne {string} This is the first argument. +@param [argTwo=True] {bool} This is the second argument. +</api> +''' + parsed = self.parse_text(md) + r = parsed[1][1]["returns"] + self.assertEqual(r["description"], "A one-line untyped description.") + + # if the return value was supposed to be an array, the correct syntax + # would not have any @prop tags: + # @returns {array} + # Array consists of two elements, a string and a url... + + def test_return_array(self): + md = '''\ +<api name="test"> +@method +This is a function which returns an array. +@returns {array} + Array consists of two elements, a string and a url. +@param argOne {string} This is the first argument. +@param [argTwo=True] {bool} This is the second argument. +</api> +''' + parsed = self.parse_text(md) + r = parsed[1][1]["returns"] + self.assertEqual(r["description"], + "Array consists of two elements, a string and a url.") + + def test_bad_default_on_required_parameter(self): + md = '''\ +<api name="test"> +@method +This is a function which does nothing in particular. +@returns something +@param argOne=ILLEGAL {string} Mandatory parameters do not take defaults. +@param [argTwo=Chicago] {string} This is the second argument. +</api> +''' + self.assertRaises(ParseError, self.parse_text, md) + + def test_missing_apitype(self): + md = '''\ +<api name="test"> +Sorry, you must have a @method or something before the description. +Putting it after the description is not good enough +@method +@returns something +</api> +''' + self.assertRaises(ParseError, self.parse_text, md) + + def test_missing_param_propname(self): + md = '''\ +<api name="test"> +@method +This is a function which does nothing in particular. +@param p1 {object} This is a parameter. + @prop {string} Oops, props must have a name. +</api> +''' + self.assertRaises(ParseError, self.parse_text, md) + + def test_missing_param_proptype(self): + md = '''\ +<api name="test"> +@method +This is a function which does nothing in particular. +@param p1 {object} This is a parameter. + @prop name Oops, props must have a type. +</api> +''' + self.assertRaises(ParseError, self.parse_text, md) + + def test_property(self): + md = '''\ +<api name="test"> +@property {foo} +An object property named test of type foo. +</api> +''' + parsed = self.parse_text(md) + self.assertEqual(parsed[1][0], 'api-json') + actual_api_json_obj = parsed[1][1] + expected_api_json_obj = { + 'line_number': 1, + 'datatype': 'foo', + 'type': 'property', + 'name': 'test', + 'description': "An object property named test of type foo." + } + self.assertEqual(actual_api_json_obj, expected_api_json_obj) + + def test_property_no_type(self): + md = '''\ +<api name="test"> +@property +This property needs to specify a type! +</api> +''' + self.assertRaises(ParseError, self.parse_text, md) + + def test_missing_api_closing_tag(self): + md = '''\ +<api name="test"> +@class +This is a class with a missing closing tag. +<api name="doStuff" +@method +This method does stuff. +</api> +''' + self.assertRaises(ParseError, self.parse_text, md) + +if __name__ == "__main__": + unittest.main() |