1""" 2 test_domain_c 3 ~~~~~~~~~~~~~ 4 5 Tests the C Domain 6 7 :copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS. 8 :license: BSD, see LICENSE for details. 9""" 10 11import zlib 12from xml.etree import ElementTree 13 14import pytest 15 16from sphinx import addnodes 17from sphinx.addnodes import desc 18from sphinx.domains.c import DefinitionError, DefinitionParser, Symbol, _id_prefix, _max_id 19from sphinx.ext.intersphinx import load_mappings, normalize_intersphinx_mapping 20from sphinx.testing import restructuredtext 21from sphinx.testing.util import assert_node 22 23 24def parse(name, string): 25 class Config: 26 c_id_attributes = ["id_attr", 'LIGHTGBM_C_EXPORT'] 27 c_paren_attributes = ["paren_attr"] 28 parser = DefinitionParser(string, location=None, config=Config()) 29 parser.allowFallbackExpressionParsing = False 30 ast = parser.parse_declaration(name, name) 31 parser.assert_end() 32 return ast 33 34 35def _check(name, input, idDict, output, key, asTextOutput): 36 if key is None: 37 key = name 38 key += ' ' 39 if name in ('function', 'member'): 40 inputActual = input 41 outputAst = output 42 outputAsText = output 43 else: 44 inputActual = input.format(key='') 45 outputAst = output.format(key='') 46 outputAsText = output.format(key=key) 47 if asTextOutput is not None: 48 outputAsText = asTextOutput 49 50 # first a simple check of the AST 51 ast = parse(name, inputActual) 52 res = str(ast) 53 if res != outputAst: 54 print("") 55 print("Input: ", input) 56 print("Result: ", res) 57 print("Expected: ", outputAst) 58 raise DefinitionError("") 59 rootSymbol = Symbol(None, None, None, None, None) 60 symbol = rootSymbol.add_declaration(ast, docname="TestDoc", line=42) 61 parentNode = addnodes.desc() 62 signode = addnodes.desc_signature(input, '') 63 parentNode += signode 64 ast.describe_signature(signode, 'lastIsName', symbol, options={}) 65 resAsText = parentNode.astext() 66 if resAsText != outputAsText: 67 print("") 68 print("Input: ", input) 69 print("astext(): ", resAsText) 70 print("Expected: ", outputAsText) 71 raise DefinitionError("") 72 73 idExpected = [None] 74 for i in range(1, _max_id + 1): 75 if i in idDict: 76 idExpected.append(idDict[i]) 77 else: 78 idExpected.append(idExpected[i - 1]) 79 idActual = [None] 80 for i in range(1, _max_id + 1): 81 # try: 82 id = ast.get_id(version=i) 83 assert id is not None 84 idActual.append(id[len(_id_prefix[i]):]) 85 # except NoOldIdError: 86 # idActual.append(None) 87 88 res = [True] 89 for i in range(1, _max_id + 1): 90 res.append(idExpected[i] == idActual[i]) 91 92 if not all(res): 93 print("input: %s" % input.rjust(20)) 94 for i in range(1, _max_id + 1): 95 if res[i]: 96 continue 97 print("Error in id version %d." % i) 98 print("result: %s" % idActual[i]) 99 print("expected: %s" % idExpected[i]) 100 # print(rootSymbol.dump(0)) 101 raise DefinitionError("") 102 103 104def check(name, input, idDict, output=None, key=None, asTextOutput=None): 105 if output is None: 106 output = input 107 # First, check without semicolon 108 _check(name, input, idDict, output, key, asTextOutput) 109 if name != 'macro': 110 # Second, check with semicolon 111 _check(name, input + ' ;', idDict, output + ';', key, 112 asTextOutput + ';' if asTextOutput is not None else None) 113 114 115def test_expressions(): 116 def exprCheck(expr, output=None): 117 class Config: 118 c_id_attributes = ["id_attr"] 119 c_paren_attributes = ["paren_attr"] 120 parser = DefinitionParser(expr, location=None, config=Config()) 121 parser.allowFallbackExpressionParsing = False 122 ast = parser.parse_expression() 123 parser.assert_end() 124 # first a simple check of the AST 125 if output is None: 126 output = expr 127 res = str(ast) 128 if res != output: 129 print("") 130 print("Input: ", input) 131 print("Result: ", res) 132 print("Expected: ", output) 133 raise DefinitionError("") 134 displayString = ast.get_display_string() 135 if res != displayString: 136 # note: if the expression contains an anon name then this will trigger a falsely 137 print("") 138 print("Input: ", expr) 139 print("Result: ", res) 140 print("Display: ", displayString) 141 raise DefinitionError("") 142 143 # type expressions 144 exprCheck('int*') 145 exprCheck('int *const*') 146 exprCheck('int *volatile*') 147 exprCheck('int *restrict*') 148 exprCheck('int *(*)(double)') 149 exprCheck('const int*') 150 exprCheck('__int64') 151 exprCheck('unsigned __int64') 152 153 # actual expressions 154 155 # primary 156 exprCheck('true') 157 exprCheck('false') 158 ints = ['5', '0', '075', '0x0123456789ABCDEF', '0XF', '0b1', '0B1'] 159 unsignedSuffix = ['', 'u', 'U'] 160 longSuffix = ['', 'l', 'L', 'll', 'LL'] 161 for i in ints: 162 for u in unsignedSuffix: 163 for l in longSuffix: 164 expr = i + u + l 165 exprCheck(expr) 166 expr = i + l + u 167 exprCheck(expr) 168 for suffix in ['', 'f', 'F', 'l', 'L']: 169 for e in [ 170 '5e42', '5e+42', '5e-42', 171 '5.', '5.e42', '5.e+42', '5.e-42', 172 '.5', '.5e42', '.5e+42', '.5e-42', 173 '5.0', '5.0e42', '5.0e+42', '5.0e-42']: 174 expr = e + suffix 175 exprCheck(expr) 176 for e in [ 177 'ApF', 'Ap+F', 'Ap-F', 178 'A.', 'A.pF', 'A.p+F', 'A.p-F', 179 '.A', '.ApF', '.Ap+F', '.Ap-F', 180 'A.B', 'A.BpF', 'A.Bp+F', 'A.Bp-F']: 181 expr = "0x" + e + suffix 182 exprCheck(expr) 183 exprCheck('"abc\\"cba"') # string 184 # character literals 185 for p in ['', 'u8', 'u', 'U', 'L']: 186 exprCheck(p + "'a'") 187 exprCheck(p + "'\\n'") 188 exprCheck(p + "'\\012'") 189 exprCheck(p + "'\\0'") 190 exprCheck(p + "'\\x0a'") 191 exprCheck(p + "'\\x0A'") 192 exprCheck(p + "'\\u0a42'") 193 exprCheck(p + "'\\u0A42'") 194 exprCheck(p + "'\\U0001f34c'") 195 exprCheck(p + "'\\U0001F34C'") 196 197 exprCheck('(5)') 198 exprCheck('C') 199 # postfix 200 exprCheck('A(2)') 201 exprCheck('A[2]') 202 exprCheck('a.b.c') 203 exprCheck('a->b->c') 204 exprCheck('i++') 205 exprCheck('i--') 206 # unary 207 exprCheck('++5') 208 exprCheck('--5') 209 exprCheck('*5') 210 exprCheck('&5') 211 exprCheck('+5') 212 exprCheck('-5') 213 exprCheck('!5') 214 exprCheck('not 5') 215 exprCheck('~5') 216 exprCheck('compl 5') 217 exprCheck('sizeof(T)') 218 exprCheck('sizeof -42') 219 exprCheck('alignof(T)') 220 # cast 221 exprCheck('(int)2') 222 # binary op 223 exprCheck('5 || 42') 224 exprCheck('5 or 42') 225 exprCheck('5 && 42') 226 exprCheck('5 and 42') 227 exprCheck('5 | 42') 228 exprCheck('5 bitor 42') 229 exprCheck('5 ^ 42') 230 exprCheck('5 xor 42') 231 exprCheck('5 & 42') 232 exprCheck('5 bitand 42') 233 # ['==', '!='] 234 exprCheck('5 == 42') 235 exprCheck('5 != 42') 236 exprCheck('5 not_eq 42') 237 # ['<=', '>=', '<', '>'] 238 exprCheck('5 <= 42') 239 exprCheck('5 >= 42') 240 exprCheck('5 < 42') 241 exprCheck('5 > 42') 242 # ['<<', '>>'] 243 exprCheck('5 << 42') 244 exprCheck('5 >> 42') 245 # ['+', '-'] 246 exprCheck('5 + 42') 247 exprCheck('5 - 42') 248 # ['*', '/', '%'] 249 exprCheck('5 * 42') 250 exprCheck('5 / 42') 251 exprCheck('5 % 42') 252 # ['.*', '->*'] 253 # conditional 254 # TODO 255 # assignment 256 exprCheck('a = 5') 257 exprCheck('a *= 5') 258 exprCheck('a /= 5') 259 exprCheck('a %= 5') 260 exprCheck('a += 5') 261 exprCheck('a -= 5') 262 exprCheck('a >>= 5') 263 exprCheck('a <<= 5') 264 exprCheck('a &= 5') 265 exprCheck('a and_eq 5') 266 exprCheck('a ^= 5') 267 exprCheck('a xor_eq 5') 268 exprCheck('a |= 5') 269 exprCheck('a or_eq 5') 270 271 272def test_type_definitions(): 273 check('type', "{key}T", {1: "T"}) 274 275 check('type', "{key}bool *b", {1: 'b'}, key='typedef') 276 check('type', "{key}bool *const b", {1: 'b'}, key='typedef') 277 check('type', "{key}bool *const *b", {1: 'b'}, key='typedef') 278 check('type', "{key}bool *volatile *b", {1: 'b'}, key='typedef') 279 check('type', "{key}bool *restrict *b", {1: 'b'}, key='typedef') 280 check('type', "{key}bool *volatile const b", {1: 'b'}, key='typedef') 281 check('type', "{key}bool *volatile const b", {1: 'b'}, key='typedef') 282 check('type', "{key}bool *volatile const *b", {1: 'b'}, key='typedef') 283 check('type', "{key}bool b[]", {1: 'b'}, key='typedef') 284 check('type', "{key}long long int foo", {1: 'foo'}, key='typedef') 285 # test decl specs on right 286 check('type', "{key}bool const b", {1: 'b'}, key='typedef') 287 288 # from breathe#267 (named function parameters for function pointers 289 check('type', '{key}void (*gpio_callback_t)(struct device *port, uint32_t pin)', 290 {1: 'gpio_callback_t'}, key='typedef') 291 292 293def test_macro_definitions(): 294 check('macro', 'M', {1: 'M'}) 295 check('macro', 'M()', {1: 'M'}) 296 check('macro', 'M(arg)', {1: 'M'}) 297 check('macro', 'M(arg1, arg2)', {1: 'M'}) 298 check('macro', 'M(arg1, arg2, arg3)', {1: 'M'}) 299 check('macro', 'M(...)', {1: 'M'}) 300 check('macro', 'M(arg, ...)', {1: 'M'}) 301 check('macro', 'M(arg1, arg2, ...)', {1: 'M'}) 302 check('macro', 'M(arg1, arg2, arg3, ...)', {1: 'M'}) 303 # GNU extension 304 check('macro', 'M(arg1, arg2, arg3...)', {1: 'M'}) 305 with pytest.raises(DefinitionError): 306 check('macro', 'M(arg1, arg2..., arg3)', {1: 'M'}) 307 308 309def test_member_definitions(): 310 check('member', 'void a', {1: 'a'}) 311 check('member', '_Bool a', {1: 'a'}) 312 check('member', 'bool a', {1: 'a'}) 313 check('member', 'char a', {1: 'a'}) 314 check('member', 'int a', {1: 'a'}) 315 check('member', 'float a', {1: 'a'}) 316 check('member', 'double a', {1: 'a'}) 317 318 check('member', 'unsigned long a', {1: 'a'}) 319 check('member', '__int64 a', {1: 'a'}) 320 check('member', 'unsigned __int64 a', {1: 'a'}) 321 322 check('member', 'int .a', {1: 'a'}) 323 324 check('member', 'int *a', {1: 'a'}) 325 check('member', 'int **a', {1: 'a'}) 326 check('member', 'const int a', {1: 'a'}) 327 check('member', 'volatile int a', {1: 'a'}) 328 check('member', 'restrict int a', {1: 'a'}) 329 check('member', 'volatile const int a', {1: 'a'}) 330 check('member', 'restrict const int a', {1: 'a'}) 331 check('member', 'restrict volatile int a', {1: 'a'}) 332 check('member', 'restrict volatile const int a', {1: 'a'}) 333 334 check('member', 'T t', {1: 't'}) 335 336 check('member', 'int a[]', {1: 'a'}) 337 338 check('member', 'int (*p)[]', {1: 'p'}) 339 340 check('member', 'int a[42]', {1: 'a'}) 341 check('member', 'int a = 42', {1: 'a'}) 342 check('member', 'T a = {}', {1: 'a'}) 343 check('member', 'T a = {1}', {1: 'a'}) 344 check('member', 'T a = {1, 2}', {1: 'a'}) 345 check('member', 'T a = {1, 2, 3}', {1: 'a'}) 346 347 # test from issue #1539 348 check('member', 'CK_UTF8CHAR model[16]', {1: 'model'}) 349 350 check('member', 'auto int a', {1: 'a'}) 351 check('member', 'register int a', {1: 'a'}) 352 check('member', 'extern int a', {1: 'a'}) 353 check('member', 'static int a', {1: 'a'}) 354 355 check('member', 'thread_local int a', {1: 'a'}) 356 check('member', '_Thread_local int a', {1: 'a'}) 357 check('member', 'extern thread_local int a', {1: 'a'}) 358 check('member', 'thread_local extern int a', {1: 'a'}, 359 'extern thread_local int a') 360 check('member', 'static thread_local int a', {1: 'a'}) 361 check('member', 'thread_local static int a', {1: 'a'}, 362 'static thread_local int a') 363 364 check('member', 'int b : 3', {1: 'b'}) 365 366 367def test_function_definitions(): 368 check('function', 'void f()', {1: 'f'}) 369 check('function', 'void f(int)', {1: 'f'}) 370 check('function', 'void f(int i)', {1: 'f'}) 371 check('function', 'void f(int i, int j)', {1: 'f'}) 372 check('function', 'void f(...)', {1: 'f'}) 373 check('function', 'void f(int i, ...)', {1: 'f'}) 374 check('function', 'void f(struct T)', {1: 'f'}) 375 check('function', 'void f(struct T t)', {1: 'f'}) 376 check('function', 'void f(union T)', {1: 'f'}) 377 check('function', 'void f(union T t)', {1: 'f'}) 378 check('function', 'void f(enum T)', {1: 'f'}) 379 check('function', 'void f(enum T t)', {1: 'f'}) 380 381 # test from issue #1539 382 check('function', 'void f(A x[])', {1: 'f'}) 383 384 # test from issue #2377 385 check('function', 'void (*signal(int sig, void (*func)(int)))(int)', {1: 'signal'}) 386 387 check('function', 'extern void f()', {1: 'f'}) 388 check('function', 'static void f()', {1: 'f'}) 389 check('function', 'inline void f()', {1: 'f'}) 390 391 # tests derived from issue #1753 (skip to keep sanity) 392 check('function', "void f(float *q(double))", {1: 'f'}) 393 check('function', "void f(float *(*q)(double))", {1: 'f'}) 394 check('function', "void f(float (*q)(double))", {1: 'f'}) 395 check('function', "int (*f(double d))(float)", {1: 'f'}) 396 check('function', "int (*f(bool b))[5]", {1: 'f'}) 397 check('function', "void f(int *const p)", {1: 'f'}) 398 check('function', "void f(int *volatile const p)", {1: 'f'}) 399 400 # from breathe#223 401 check('function', 'void f(struct E e)', {1: 'f'}) 402 check('function', 'void f(enum E e)', {1: 'f'}) 403 check('function', 'void f(union E e)', {1: 'f'}) 404 405 # array declarators 406 check('function', 'void f(int arr[])', {1: 'f'}) 407 check('function', 'void f(int arr[*])', {1: 'f'}) 408 cvrs = ['', 'const', 'volatile', 'restrict', 'restrict volatile const'] 409 for cvr in cvrs: 410 space = ' ' if len(cvr) != 0 else '' 411 check('function', 'void f(int arr[{}*])'.format(cvr), {1: 'f'}) 412 check('function', 'void f(int arr[{}])'.format(cvr), {1: 'f'}) 413 check('function', 'void f(int arr[{}{}42])'.format(cvr, space), {1: 'f'}) 414 check('function', 'void f(int arr[static{}{} 42])'.format(space, cvr), {1: 'f'}) 415 check('function', 'void f(int arr[{}{}static 42])'.format(cvr, space), {1: 'f'}, 416 output='void f(int arr[static{}{} 42])'.format(space, cvr)) 417 check('function', 'void f(int arr[const static volatile 42])', {1: 'f'}, 418 output='void f(int arr[static volatile const 42])') 419 420 421def test_nested_name(): 422 check('struct', '{key}.A', {1: "A"}) 423 check('struct', '{key}.A.B', {1: "A.B"}) 424 check('function', 'void f(.A a)', {1: "f"}) 425 check('function', 'void f(.A.B a)', {1: "f"}) 426 427 428def test_struct_definitions(): 429 check('struct', '{key}A', {1: 'A'}) 430 431 432def test_union_definitions(): 433 check('union', '{key}A', {1: 'A'}) 434 435 436def test_enum_definitions(): 437 check('enum', '{key}A', {1: 'A'}) 438 439 check('enumerator', '{key}A', {1: 'A'}) 440 check('enumerator', '{key}A = 42', {1: 'A'}) 441 442 443def test_anon_definitions(): 444 check('struct', '@a', {1: "@a"}, asTextOutput='struct [anonymous]') 445 check('union', '@a', {1: "@a"}, asTextOutput='union [anonymous]') 446 check('enum', '@a', {1: "@a"}, asTextOutput='enum [anonymous]') 447 check('struct', '@1', {1: "@1"}, asTextOutput='struct [anonymous]') 448 check('struct', '@a.A', {1: "@a.A"}, asTextOutput='struct [anonymous].A') 449 450 451def test_initializers(): 452 idsMember = {1: 'v'} 453 idsFunction = {1: 'f'} 454 # no init 455 check('member', 'T v', idsMember) 456 check('function', 'void f(T v)', idsFunction) 457 # with '=', assignment-expression 458 check('member', 'T v = 42', idsMember) 459 check('function', 'void f(T v = 42)', idsFunction) 460 # with '=', braced-init 461 check('member', 'T v = {}', idsMember) 462 check('function', 'void f(T v = {})', idsFunction) 463 check('member', 'T v = {42, 42, 42}', idsMember) 464 check('function', 'void f(T v = {42, 42, 42})', idsFunction) 465 check('member', 'T v = {42, 42, 42,}', idsMember) 466 check('function', 'void f(T v = {42, 42, 42,})', idsFunction) 467 # TODO: designator-list 468 469 470def test_attributes(): 471 # style: C++ 472 check('member', '[[]] int f', {1: 'f'}) 473 check('member', '[ [ ] ] int f', {1: 'f'}, 474 # this will fail when the proper grammar is implemented 475 output='[[ ]] int f') 476 check('member', '[[a]] int f', {1: 'f'}) 477 # style: GNU 478 check('member', '__attribute__(()) int f', {1: 'f'}) 479 check('member', '__attribute__((a)) int f', {1: 'f'}) 480 check('member', '__attribute__((a, b)) int f', {1: 'f'}) 481 check('member', '__attribute__((optimize(3))) int f', {1: 'f'}) 482 check('member', '__attribute__((format(printf, 1, 2))) int f', {1: 'f'}) 483 # style: user-defined id 484 check('member', 'id_attr int f', {1: 'f'}) 485 # style: user-defined paren 486 check('member', 'paren_attr() int f', {1: 'f'}) 487 check('member', 'paren_attr(a) int f', {1: 'f'}) 488 check('member', 'paren_attr("") int f', {1: 'f'}) 489 check('member', 'paren_attr(()[{}][]{}) int f', {1: 'f'}) 490 with pytest.raises(DefinitionError): 491 parse('member', 'paren_attr(() int f') 492 with pytest.raises(DefinitionError): 493 parse('member', 'paren_attr([) int f') 494 with pytest.raises(DefinitionError): 495 parse('member', 'paren_attr({) int f') 496 with pytest.raises(DefinitionError): 497 parse('member', 'paren_attr([)]) int f') 498 with pytest.raises(DefinitionError): 499 parse('member', 'paren_attr((])) int f') 500 with pytest.raises(DefinitionError): 501 parse('member', 'paren_attr({]}) int f') 502 503 # position: decl specs 504 check('function', 'static inline __attribute__(()) void f()', {1: 'f'}, 505 output='__attribute__(()) static inline void f()') 506 check('function', '[[attr1]] [[attr2]] void f()', {1: 'f'}) 507 # position: declarator 508 check('member', 'int *[[attr]] i', {1: 'i'}) 509 check('member', 'int *const [[attr]] volatile i', {1: 'i'}, 510 output='int *[[attr]] volatile const i') 511 check('member', 'int *[[attr]] *i', {1: 'i'}) 512 # position: parameters 513 check('function', 'void f() [[attr1]] [[attr2]]', {1: 'f'}) 514 515 # issue michaeljones/breathe#500 516 check('function', 'LIGHTGBM_C_EXPORT int LGBM_BoosterFree(int handle)', 517 {1: 'LGBM_BoosterFree'}) 518 519# def test_print(): 520# # used for getting all the ids out for checking 521# for a in ids: 522# print(a) 523# raise DefinitionError("") 524 525 526def filter_warnings(warning, file): 527 lines = warning.getvalue().split("\n") 528 res = [l for l in lines if "domain-c" in l and "{}.rst".format(file) in l and 529 "WARNING: document isn't included in any toctree" not in l] 530 print("Filtered warnings for file '{}':".format(file)) 531 for w in res: 532 print(w) 533 return res 534 535 536def extract_role_links(app, filename): 537 t = (app.outdir / filename).read_text() 538 lis = [l for l in t.split('\n') if l.startswith("<li")] 539 entries = [] 540 for l in lis: 541 li = ElementTree.fromstring(l) 542 aList = list(li.iter('a')) 543 assert len(aList) == 1 544 a = aList[0] 545 target = a.attrib['href'].lstrip('#') 546 title = a.attrib['title'] 547 assert len(a) == 1 548 code = a[0] 549 assert code.tag == 'code' 550 text = ''.join(code.itertext()) 551 entries.append((target, title, text)) 552 return entries 553 554 555@pytest.mark.sphinx(testroot='domain-c', confoverrides={'nitpicky': True}) 556def test_build_domain_c(app, status, warning): 557 app.builder.build_all() 558 ws = filter_warnings(warning, "index") 559 assert len(ws) == 0 560 561 562@pytest.mark.sphinx(testroot='domain-c', confoverrides={'nitpicky': True}) 563def test_build_domain_c_namespace(app, status, warning): 564 app.builder.build_all() 565 ws = filter_warnings(warning, "namespace") 566 assert len(ws) == 0 567 t = (app.outdir / "namespace.html").read_text() 568 for id_ in ('NS.NSVar', 'NULLVar', 'ZeroVar', 'NS2.NS3.NS2NS3Var', 'PopVar'): 569 assert 'id="c.{}"'.format(id_) in t 570 571 572@pytest.mark.sphinx(testroot='domain-c', confoverrides={'nitpicky': True}) 573def test_build_domain_c_anon_dup_decl(app, status, warning): 574 app.builder.build_all() 575 ws = filter_warnings(warning, "anon-dup-decl") 576 assert len(ws) == 2 577 assert "WARNING: c:identifier reference target not found: @a" in ws[0] 578 assert "WARNING: c:identifier reference target not found: @b" in ws[1] 579 580 581@pytest.mark.sphinx(testroot='domain-c', confoverrides={'nitpicky': True}) 582def test_build_domain_c_semicolon(app, status, warning): 583 app.builder.build_all() 584 ws = filter_warnings(warning, "semicolon") 585 assert len(ws) == 0 586 587 588@pytest.mark.sphinx(testroot='domain-c', confoverrides={'nitpicky': True}) 589def test_build_function_param_target(app, warning): 590 # the anchor for function parameters should be the function 591 app.builder.build_all() 592 ws = filter_warnings(warning, "function_param_target") 593 assert len(ws) == 0 594 entries = extract_role_links(app, "function_param_target.html") 595 assert entries == [ 596 ('c.f', 'i', 'i'), 597 ('c.f', 'f.i', 'f.i'), 598 ] 599 600 601@pytest.mark.sphinx(testroot='domain-c', confoverrides={'nitpicky': True}) 602def test_build_ns_lookup(app, warning): 603 app.builder.build_all() 604 ws = filter_warnings(warning, "ns_lookup") 605 assert len(ws) == 0 606 607 608def _get_obj(app, queryName): 609 domain = app.env.get_domain('c') 610 for name, dispname, objectType, docname, anchor, prio in domain.get_objects(): 611 if name == queryName: 612 return (docname, anchor, objectType) 613 return (queryName, "not", "found") 614 615 616def test_cfunction(app): 617 text = (".. c:function:: PyObject* " 618 "PyType_GenericAlloc(PyTypeObject *type, Py_ssize_t nitems)") 619 doctree = restructuredtext.parse(app, text) 620 assert_node(doctree[1], addnodes.desc, desctype="function", 621 domain="c", objtype="function", noindex=False) 622 623 entry = _get_obj(app, 'PyType_GenericAlloc') 624 assert entry == ('index', 'c.PyType_GenericAlloc', 'function') 625 626 627def test_cmember(app): 628 text = ".. c:member:: PyObject* PyTypeObject.tp_bases" 629 doctree = restructuredtext.parse(app, text) 630 assert_node(doctree[1], addnodes.desc, desctype="member", 631 domain="c", objtype="member", noindex=False) 632 633 entry = _get_obj(app, 'PyTypeObject.tp_bases') 634 assert entry == ('index', 'c.PyTypeObject.tp_bases', 'member') 635 636 637def test_cvar(app): 638 text = ".. c:var:: PyObject* PyClass_Type" 639 doctree = restructuredtext.parse(app, text) 640 assert_node(doctree[1], addnodes.desc, desctype="var", 641 domain="c", objtype="var", noindex=False) 642 643 entry = _get_obj(app, 'PyClass_Type') 644 assert entry == ('index', 'c.PyClass_Type', 'member') 645 646 647def test_noindexentry(app): 648 text = (".. c:function:: void f()\n" 649 ".. c:function:: void g()\n" 650 " :noindexentry:\n") 651 doctree = restructuredtext.parse(app, text) 652 assert_node(doctree, (addnodes.index, desc, addnodes.index, desc)) 653 assert_node(doctree[0], addnodes.index, entries=[('single', 'f (C function)', 'c.f', '', None)]) 654 assert_node(doctree[2], addnodes.index, entries=[]) 655 656 657@pytest.mark.sphinx(testroot='domain-c-intersphinx', confoverrides={'nitpicky': True}) 658def test_intersphinx(tempdir, app, status, warning): 659 origSource = """\ 660.. c:member:: int _member 661.. c:var:: int _var 662.. c:function:: void _function() 663.. c:macro:: _macro 664.. c:struct:: _struct 665.. c:union:: _union 666.. c:enum:: _enum 667 668 .. c:enumerator:: _enumerator 669 670.. c:type:: _type 671.. c:function:: void _functionParam(int param) 672""" # noqa 673 inv_file = tempdir / 'inventory' 674 inv_file.write_bytes(b'''\ 675# Sphinx inventory version 2 676# Project: C Intersphinx Test 677# Version: 678# The remainder of this file is compressed using zlib. 679''' + zlib.compress(b'''\ 680_enum c:enum 1 index.html#c.$ - 681_enum._enumerator c:enumerator 1 index.html#c.$ - 682_enumerator c:enumerator 1 index.html#c._enum.$ - 683_function c:function 1 index.html#c.$ - 684_functionParam c:function 1 index.html#c.$ - 685_functionParam.param c:functionParam 1 index.html#c._functionParam - 686_macro c:macro 1 index.html#c.$ - 687_member c:member 1 index.html#c.$ - 688_struct c:struct 1 index.html#c.$ - 689_type c:type 1 index.html#c.$ - 690_union c:union 1 index.html#c.$ - 691_var c:member 1 index.html#c.$ - 692''')) # noqa 693 app.config.intersphinx_mapping = { 694 'https://localhost/intersphinx/c/': inv_file, 695 } 696 app.config.intersphinx_cache_limit = 0 697 # load the inventory and check if it's done correctly 698 normalize_intersphinx_mapping(app, app.config) 699 load_mappings(app) 700 701 app.builder.build_all() 702 ws = filter_warnings(warning, "index") 703 assert len(ws) == 0 704