diff options
Diffstat (limited to 'tests/test_domain_cpp.py')
-rw-r--r-- | tests/test_domain_cpp.py | 267 |
1 files changed, 237 insertions, 30 deletions
diff --git a/tests/test_domain_cpp.py b/tests/test_domain_cpp.py index 886fe8031..aef7e8ed4 100644 --- a/tests/test_domain_cpp.py +++ b/tests/test_domain_cpp.py @@ -5,27 +5,37 @@ Tests the C++ Domain - :copyright: Copyright 2007-2015 by the Sphinx team, see AUTHORS. + :copyright: Copyright 2007-2016 by the Sphinx team, see AUTHORS. :license: BSD, see LICENSE for details. """ +import re + from six import text_type -from util import raises +from util import raises, with_app +from sphinx import addnodes from sphinx.domains.cpp import DefinitionParser, DefinitionError, NoOldIdError +from sphinx.domains.cpp import Symbol +import sphinx.domains.cpp as cppDomain ids = [] + def parse(name, string): - parser = DefinitionParser(string) - res = getattr(parser, "parse_" + name + "_object")() + parser = DefinitionParser(string, None) + ast = parser.parse_declaration(name) if not parser.eof: print("Parsing stopped at", parser.pos) print(string) print('-'*parser.pos + '^') raise DefinitionError("") - return res + # The scopedness would usually have been set by CPPEnumObject + if name == "enum": + ast.scoped = None # simulate unscoped enum + return ast + def check(name, input, idv1output=None, idv2output=None, output=None): # first a simple check of the AST @@ -34,25 +44,28 @@ def check(name, input, idv1output=None, idv2output=None, output=None): ast = parse(name, input) res = text_type(ast) if res != output: + print("") print("Input: ", text_type(input)) print("Result: ", res) print("Expected: ", output) raise DefinitionError("") - ast.describe_signature([], 'lastIsName', None, parentScope=ast.name) - # Artificially set the prefixedName, otherwise the get_id fails. - # It would usually have been set in handle_signarue. - ast.prefixedName = ast.name + rootSymbol = Symbol(None, None, None, None, None, None) + symbol = rootSymbol.add_declaration(ast, docname="Test") + parentNode = addnodes.desc() + signode = addnodes.desc_signature(input, '') + parentNode += signode + ast.describe_signature(signode, 'lastIsName', symbol) if idv2output: idv2output = "_CPPv2" + idv2output try: idv1 = ast.get_id_v1() - assert idv1 != None + assert idv1 is not None except NoOldIdError: idv1 = None try: idv2 = ast.get_id_v2() - assert idv2 != None + assert idv2 is not None except NoOldIdError: idv2 = None if idv1 != idv1output or idv2 != idv2output: @@ -61,10 +74,33 @@ def check(name, input, idv1output=None, idv2output=None, output=None): print("result: %s %s" % (str(idv1).rjust(20), str(idv2).rjust(20))) print("expected: %s %s" % (str(idv1output).rjust(20), str(idv2output).rjust(20))) + print(rootSymbol.dump(0)) raise DefinitionError("") ids.append(ast.get_id_v2()) #print ".. %s:: %s" % (name, input) + +def test_fundamental_types(): + # see http://en.cppreference.com/w/cpp/language/types + for t, id_v2 in cppDomain._id_fundamental_v2.items(): + if t == "decltype(auto)": + continue + + def makeIdV1(): + id = t.replace(" ", "-").replace("long", "l").replace("int", "i") + id = id.replace("bool", "b").replace("char", "c") + id = id.replace("wc_t", "wchar_t").replace("c16_t", "char16_t") + id = id.replace("c32_t", "char32_t") + return "f__%s" % id + + def makeIdV2(): + id = id_v2 + if t == "std::nullptr_t": + id = "NSt9nullptr_tE" + return "1f%s" % id + check("function", "void f(%s arg)" % t, makeIdV1(), makeIdV2()) + + def test_type_definitions(): check("type", "public bool b", "b", "1b", "bool b") check("type", "bool A::b", "A::b", "N1A1bE") @@ -84,14 +120,20 @@ def test_type_definitions(): check("type", "std::function<R(A1, A2, A3)> F", "F", "1F") check("type", "std::function<R(A1, A2, A3, As...)> F", "F", "1F") check("type", "MyContainer::const_iterator", - "MyContainer::const_iterator","N11MyContainer14const_iteratorE") + "MyContainer::const_iterator", "N11MyContainer14const_iteratorE") check("type", "public MyContainer::const_iterator", "MyContainer::const_iterator", "N11MyContainer14const_iteratorE", output="MyContainer::const_iterator") # test decl specs on right check("type", "bool const b", "b", "1b") + # test name in global scope + check("type", "bool ::B::b", "B::b", "N1B1bE") + + check('type', 'A = B', None, '1A') + +def test_member_definitions(): check('member', ' const std::string & name = 42', "name__ssCR", "4name", output='const std::string &name = 42') check('member', ' const std::string & name', "name__ssCR", "4name", @@ -102,7 +144,10 @@ def test_type_definitions(): "name__std::vector:unsigned-i.l:CR", "4name", output='const std::vector<unsigned int, long> &name') check('member', 'module::myclass foo[n]', "foo__module::myclassA", "3foo") + check('member', 'int *const p', 'p__iPC', '1p') + +def test_function_definitions(): check('function', 'operator bool() const', "castto-b-operatorC", "NKcvbEv") check('function', 'A::operator bool() const', "A::castto-b-operatorC", "NK1AcvbEv") @@ -208,31 +253,39 @@ def test_type_definitions(): "A::f__doubleC", "NK1A1fEd") check("function", "void f(std::shared_ptr<int(double)> ptr)", None, "1fNSt10shared_ptrIFidEEE") + check("function", "void f(int *const p)", "f__iPC", "1fPCi") + check("function", "void f(int *volatile const p)", "f__iPVC", "1fPVCi") # TODO: make tests for functions in a template, e.g., Test<int&&()> # such that the id generation for function type types is correct. - check('class', 'public A', "A", "1A", output='A') - check('class', 'private A', "A", "1A") + check('function', 'friend std::ostream &f(std::ostream&, int)', + 'f__osR.i', '1fRNSt7ostreamEi') - check('enum', 'A', None, "1A") - check('enum', 'A : std::underlying_type<B>::type', None, "1A") - check('enum', 'A : unsigned int', None, "1A") - check('enum', 'public A', None, "1A", output='A') - check('enum', 'private A', None, "1A") + # from breathe#223 + check('function', 'void f(struct E e)', 'f__E', '1f1E') + check('function', 'void f(class E e)', 'f__E', '1f1E') + check('function', 'void f(typename E e)', 'f__E', '1f1E') + check('function', 'void f(enum E e)', 'f__E', '1f1E') + check('function', 'void f(union E e)', 'f__E', '1f1E') - check('enumerator', 'A', None, "1A") - check('enumerator', 'A = std::numeric_limits<unsigned long>::max()', - None, "1A") + # pointer to member (function) + check('function', 'void f(int C::*)', None, '1fM1Ci') + check('function', 'void f(int C::* p)', None, '1fM1Ci') + check('function', 'void f(int ::C::* p)', None, '1fM1Ci') + check('function', 'void f(int C::* const)', None, '1fKM1Ci') + check('function', 'void f(int C::* const&)', None, '1fRKM1Ci') + check('function', 'void f(int C::* volatile)', None, '1fVM1Ci') + check('function', 'void f(int C::* const volatile)', None, '1fVKM1Ci', + output='void f(int C::* volatile const)') + check('function', 'void f(int C::* volatile const)', None, '1fVKM1Ci') + check('function', 'void f(int (C::*)(float, double))', None, '1fM1CFifdE') + check('function', 'void f(int (C::* p)(float, double))', None, '1fM1CFifdE') + check('function', 'void f(int (::C::* p)(float, double))', None, '1fM1CFifdE') + check('function', 'void f(void (C::*)() const &)', None, '1fM1CKRFvvE') + check('function', 'int C::* f(int, double)', None, '1fid') + check('function', 'void f(int C::* *)', None, '1fPM1Ci') -def test_bases(): - check('class', 'A', "A", "1A") - check('class', 'A::B::C', "A::B::C", "N1A1B1CE") - check('class', 'A : B', "A", "1A") - check('class', 'A : private B', "A", "1A", output='A : B') - check('class', 'A : public B', "A", "1A") - check('class', 'A : B, C', "A", "1A") - check('class', 'A : B, protected C, D', "A", "1A") def test_operators(): check('function', 'void operator new [ ] ()', @@ -257,8 +310,162 @@ def test_operators(): check('function', 'void operator ! ()', "not-operator", "ntv", output='void operator!()') + check('function', 'void operator "" _udl()', + None, 'li4_udlv', output='void operator""_udl()') + + +def test_class_definitions(): + check('class', 'public A', "A", "1A", output='A') + check('class', 'private A', "A", "1A") + check('class', 'A final', 'A', '1A') + + # test bases + check('class', 'A', "A", "1A") + check('class', 'A::B::C', "A::B::C", "N1A1B1CE") + check('class', 'A : B', "A", "1A") + check('class', 'A : private B', "A", "1A", output='A : B') + check('class', 'A : public B', "A", "1A") + check('class', 'A : B, C', "A", "1A") + check('class', 'A : B, protected C, D', "A", "1A") + check('class', 'A : virtual private B', 'A', '1A', output='A : virtual B') + check('class', 'A : B, virtual C', 'A', '1A') + check('class', 'A : public virtual B', 'A', '1A') + check('class', 'A : B, C...', 'A', '1A') + check('class', 'A : B..., C', 'A', '1A') + + +def test_enum_definitions(): + check('enum', 'A', None, "1A") + check('enum', 'A : std::underlying_type<B>::type', None, "1A") + check('enum', 'A : unsigned int', None, "1A") + check('enum', 'public A', None, "1A", output='A') + check('enum', 'private A', None, "1A") + + check('enumerator', 'A', None, "1A") + check('enumerator', 'A = std::numeric_limits<unsigned long>::max()', + None, "1A") + + +def test_templates(): + check('class', "A<T>", None, "IE1AI1TE", output="template<> A<T>") + # first just check which objects support templating + check('class', "template<> A", None, "IE1A") + check('function', "template<> void A()", None, "IE1Av") + check('member', "template<> A a", None, "IE1a") + check('type', "template<> a = A", None, "IE1a") + raises(DefinitionError, parse, 'enum', "template<> A") + raises(DefinitionError, parse, 'enumerator', "template<> A") + # then all the real tests + check('class', "template<typename T1, typename T2> A", None, "I00E1A") + check('type', "template<> a", None, "IE1a") + + check('class', "template<typename T> A", None, "I0E1A") + check('class', "template<class T> A", None, "I0E1A") + check('class', "template<typename ...T> A", None, "IDpE1A") + check('class', "template<typename...> A", None, "IDpE1A") + check('class', "template<typename = Test> A", None, "I0E1A") + check('class', "template<typename T = Test> A", None, "I0E1A") + + check('class', "template<template<typename> typename T> A", + None, "II0E0E1A") + check('class', "template<int> A", None, "I_iE1A") + check('class', "template<int T> A", None, "I_iE1A") + check('class', "template<int... T> A", None, "I_DpiE1A") + check('class', "template<int T = 42> A", None, "I_iE1A") + check('class', "template<int = 42> A", None, "I_iE1A") + + # from breathe#218 + check('function', + "template<typename F> " + "void allow(F *f, typename func<F, B, G!=1>::type tt)", + None, "I0E5allowP1FN4funcI1F1BXG!=1EE4typeE") + + # from #2058 + check('function', + "template<typename Char, typename Traits> " + "inline std::basic_ostream<Char, Traits> &operator<<(" + "std::basic_ostream<Char, Traits> &os, " + "const c_string_view_base<const Char, Traits> &str)", + None, "I00ElsRNSt13basic_ostreamI4Char6TraitsEE" + "RK18c_string_view_baseIK4Char6TraitsE") + + #def test_print(): # # used for getting all the ids out for checking # for a in ids: # print(a) # raise DefinitionError("") + + +@with_app(testroot='domain-cpp', confoverrides={'add_function_parentheses': True}) +def test_build_domain_cpp_with_add_function_parentheses_is_True(app, status, warning): + app.builder.build_all() + + def check(spec, text, file): + pattern = '<li>%s<a .*?><code .*?><span .*?>%s</span></code></a></li>' % spec + res = re.search(pattern, text) + if not res: + print("Pattern\n\t%s\nnot found in %s" % (pattern, file)) + assert False + rolePatterns = [ + ('', 'Sphinx'), + ('', 'Sphinx::version'), + ('', 'version'), + ('', 'List'), + ('', 'MyEnum') + ] + parenPatterns = [ + ('ref function without parens ', 'paren_1\(\)'), + ('ref function with parens ', 'paren_2\(\)'), + ('ref function without parens, explicit title ', 'paren_3_title'), + ('ref function with parens, explicit title ', 'paren_4_title') + ] + + f = 'roles.html' + t = (app.outdir / f).text() + for s in rolePatterns: + check(s, t, f) + for s in parenPatterns: + check(s, t, f) + + f = 'any-role.html' + t = (app.outdir / f).text() + for s in parenPatterns: + check(s, t, f) + + +@with_app(testroot='domain-cpp', confoverrides={'add_function_parentheses': False}) +def test_build_domain_cpp_with_add_function_parentheses_is_False(app, status, warning): + app.builder.build_all() + + def check(spec, text, file): + pattern = '<li>%s<a .*?><code .*?><span .*?>%s</span></code></a></li>' % spec + res = re.search(pattern, text) + if not res: + print("Pattern\n\t%s\nnot found in %s" % (pattern, file)) + assert False + rolePatterns = [ + ('', 'Sphinx'), + ('', 'Sphinx::version'), + ('', 'version'), + ('', 'List'), + ('', 'MyEnum') + ] + parenPatterns = [ + ('ref function without parens ', 'paren_1'), + ('ref function with parens ', 'paren_2'), + ('ref function without parens, explicit title ', 'paren_3_title'), + ('ref function with parens, explicit title ', 'paren_4_title') + ] + + f = 'roles.html' + t = (app.outdir / f).text() + for s in rolePatterns: + check(s, t, f) + for s in parenPatterns: + check(s, t, f) + + f = 'any-role.html' + t = (app.outdir / f).text() + for s in parenPatterns: + check(s, t, f) |