@descryy/runtime-test-runner 0.4.12 → 0.4.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (278) hide show
  1. package/dist/bounded-file.d.ts +18 -0
  2. package/dist/bounded-file.d.ts.map +1 -0
  3. package/dist/bounded-file.js +77 -0
  4. package/dist/bounded-file.js.map +1 -0
  5. package/dist/child-environment.d.ts +12 -0
  6. package/dist/child-environment.d.ts.map +1 -0
  7. package/dist/child-environment.js +124 -0
  8. package/dist/child-environment.js.map +1 -0
  9. package/dist/coverage-capture.d.ts +23 -0
  10. package/dist/coverage-capture.d.ts.map +1 -0
  11. package/dist/coverage-capture.js +165 -0
  12. package/dist/coverage-capture.js.map +1 -0
  13. package/dist/executed-coverage.d.ts +45 -0
  14. package/dist/executed-coverage.d.ts.map +1 -0
  15. package/dist/executed-coverage.js +143 -0
  16. package/dist/executed-coverage.js.map +1 -0
  17. package/dist/go-cover-profile.d.ts +29 -0
  18. package/dist/go-cover-profile.d.ts.map +1 -0
  19. package/dist/go-cover-profile.js +162 -0
  20. package/dist/go-cover-profile.js.map +1 -0
  21. package/dist/go-test.d.ts +29 -2
  22. package/dist/go-test.d.ts.map +1 -1
  23. package/dist/go-test.js +149 -30
  24. package/dist/go-test.js.map +1 -1
  25. package/dist/graph-mapping.d.ts +1 -0
  26. package/dist/graph-mapping.d.ts.map +1 -1
  27. package/dist/graph-mapping.js +78 -1
  28. package/dist/graph-mapping.js.map +1 -1
  29. package/dist/index.d.ts +21 -3
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js +11 -1
  32. package/dist/index.js.map +1 -1
  33. package/dist/lexers/c-family.d.ts +4 -0
  34. package/dist/lexers/c-family.d.ts.map +1 -0
  35. package/dist/lexers/c-family.js +221 -0
  36. package/dist/lexers/c-family.js.map +1 -0
  37. package/dist/lexers/chars.d.ts +7 -0
  38. package/dist/lexers/chars.d.ts.map +1 -0
  39. package/dist/lexers/chars.js +49 -0
  40. package/dist/lexers/chars.js.map +1 -0
  41. package/dist/lexers/index.d.ts +7 -0
  42. package/dist/lexers/index.d.ts.map +1 -0
  43. package/dist/lexers/index.js +5 -0
  44. package/dist/lexers/index.js.map +1 -0
  45. package/dist/lexers/python.d.ts +3 -0
  46. package/dist/lexers/python.d.ts.map +1 -0
  47. package/dist/lexers/python.js +101 -0
  48. package/dist/lexers/python.js.map +1 -0
  49. package/dist/quality/annotations.d.ts +14 -0
  50. package/dist/quality/annotations.d.ts.map +1 -0
  51. package/dist/quality/annotations.js +147 -0
  52. package/dist/quality/annotations.js.map +1 -0
  53. package/dist/quality/assertion-kinds.d.ts +18 -0
  54. package/dist/quality/assertion-kinds.d.ts.map +1 -0
  55. package/dist/quality/assertion-kinds.js +25 -0
  56. package/dist/quality/assertion-kinds.js.map +1 -0
  57. package/dist/quality/chains.d.ts +25 -0
  58. package/dist/quality/chains.d.ts.map +1 -0
  59. package/dist/quality/chains.js +129 -0
  60. package/dist/quality/chains.js.map +1 -0
  61. package/dist/quality/common.d.ts +31 -0
  62. package/dist/quality/common.d.ts.map +1 -0
  63. package/dist/quality/common.js +211 -0
  64. package/dist/quality/common.js.map +1 -0
  65. package/dist/quality/csharp-family.d.ts +9 -0
  66. package/dist/quality/csharp-family.d.ts.map +1 -0
  67. package/dist/quality/csharp-family.js +560 -0
  68. package/dist/quality/csharp-family.js.map +1 -0
  69. package/dist/quality/detect.d.ts +3 -0
  70. package/dist/quality/detect.d.ts.map +1 -0
  71. package/dist/quality/detect.js +348 -0
  72. package/dist/quality/detect.js.map +1 -0
  73. package/dist/quality/durations.d.ts +4 -0
  74. package/dist/quality/durations.d.ts.map +1 -0
  75. package/dist/quality/durations.js +177 -0
  76. package/dist/quality/durations.js.map +1 -0
  77. package/dist/quality/expressions.d.ts +7 -0
  78. package/dist/quality/expressions.d.ts.map +1 -0
  79. package/dist/quality/expressions.js +123 -0
  80. package/dist/quality/expressions.js.map +1 -0
  81. package/dist/quality/facts.d.ts +5 -0
  82. package/dist/quality/facts.d.ts.map +1 -0
  83. package/dist/quality/facts.js +183 -0
  84. package/dist/quality/facts.js.map +1 -0
  85. package/dist/quality/failure-of.d.ts +4 -0
  86. package/dist/quality/failure-of.d.ts.map +1 -0
  87. package/dist/quality/failure-of.js +233 -0
  88. package/dist/quality/failure-of.js.map +1 -0
  89. package/dist/quality/frameworks.d.ts +5 -0
  90. package/dist/quality/frameworks.d.ts.map +1 -0
  91. package/dist/quality/frameworks.js +20 -0
  92. package/dist/quality/frameworks.js.map +1 -0
  93. package/dist/quality/go-family.d.ts +6 -0
  94. package/dist/quality/go-family.d.ts.map +1 -0
  95. package/dist/quality/go-family.js +393 -0
  96. package/dist/quality/go-family.js.map +1 -0
  97. package/dist/quality/index.d.ts +10 -0
  98. package/dist/quality/index.d.ts.map +1 -0
  99. package/dist/quality/index.js +19 -0
  100. package/dist/quality/index.js.map +1 -0
  101. package/dist/quality/java-family.d.ts +12 -0
  102. package/dist/quality/java-family.d.ts.map +1 -0
  103. package/dist/quality/java-family.js +529 -0
  104. package/dist/quality/java-family.js.map +1 -0
  105. package/dist/quality/js-assertions.d.ts +11 -0
  106. package/dist/quality/js-assertions.d.ts.map +1 -0
  107. package/dist/quality/js-assertions.js +297 -0
  108. package/dist/quality/js-assertions.js.map +1 -0
  109. package/dist/quality/js-family.d.ts +3 -0
  110. package/dist/quality/js-family.d.ts.map +1 -0
  111. package/dist/quality/js-family.js +470 -0
  112. package/dist/quality/js-family.js.map +1 -0
  113. package/dist/quality/python-family.d.ts +6 -0
  114. package/dist/quality/python-family.d.ts.map +1 -0
  115. package/dist/quality/python-family.js +786 -0
  116. package/dist/quality/python-family.js.map +1 -0
  117. package/dist/quality/scope.d.ts +26 -0
  118. package/dist/quality/scope.d.ts.map +1 -0
  119. package/dist/quality/scope.js +33 -0
  120. package/dist/quality/scope.js.map +1 -0
  121. package/dist/quality/selectors.d.ts +14 -0
  122. package/dist/quality/selectors.d.ts.map +1 -0
  123. package/dist/quality/selectors.js +137 -0
  124. package/dist/quality/selectors.js.map +1 -0
  125. package/dist/quality/statuses.d.ts +7 -0
  126. package/dist/quality/statuses.d.ts.map +1 -0
  127. package/dist/quality/statuses.js +143 -0
  128. package/dist/quality/statuses.js.map +1 -0
  129. package/dist/quality/stream.d.ts +23 -0
  130. package/dist/quality/stream.d.ts.map +1 -0
  131. package/dist/quality/stream.js +194 -0
  132. package/dist/quality/stream.js.map +1 -0
  133. package/dist/quality/types.d.ts +88 -0
  134. package/dist/quality/types.d.ts.map +1 -0
  135. package/dist/quality/types.js +2 -0
  136. package/dist/quality/types.js.map +1 -0
  137. package/dist/run-timeout.d.ts +25 -0
  138. package/dist/run-timeout.d.ts.map +1 -0
  139. package/dist/run-timeout.js +52 -0
  140. package/dist/run-timeout.js.map +1 -0
  141. package/dist/runner-wrappers.d.ts +74 -0
  142. package/dist/runner-wrappers.d.ts.map +1 -0
  143. package/dist/runner-wrappers.js +604 -0
  144. package/dist/runner-wrappers.js.map +1 -0
  145. package/dist/swift-xctest.d.ts +15 -2
  146. package/dist/swift-xctest.d.ts.map +1 -1
  147. package/dist/swift-xctest.js +143 -20
  148. package/dist/swift-xctest.js.map +1 -1
  149. package/dist/test-runner-collector.d.ts +56 -6
  150. package/dist/test-runner-collector.d.ts.map +1 -1
  151. package/dist/test-runner-collector.js +265 -113
  152. package/dist/test-runner-collector.js.map +1 -1
  153. package/dist/testgen/csharp-facts.d.ts +3 -0
  154. package/dist/testgen/csharp-facts.d.ts.map +1 -0
  155. package/dist/testgen/csharp-facts.js +306 -0
  156. package/dist/testgen/csharp-facts.js.map +1 -0
  157. package/dist/testgen/csharp-profile.d.ts +14 -0
  158. package/dist/testgen/csharp-profile.d.ts.map +1 -0
  159. package/dist/testgen/csharp-profile.js +233 -0
  160. package/dist/testgen/csharp-profile.js.map +1 -0
  161. package/dist/testgen/declarations.d.ts +10 -0
  162. package/dist/testgen/declarations.d.ts.map +1 -0
  163. package/dist/testgen/declarations.js +110 -0
  164. package/dist/testgen/declarations.js.map +1 -0
  165. package/dist/testgen/facts-common.d.ts +33 -0
  166. package/dist/testgen/facts-common.d.ts.map +1 -0
  167. package/dist/testgen/facts-common.js +76 -0
  168. package/dist/testgen/facts-common.js.map +1 -0
  169. package/dist/testgen/failure.d.ts +4 -0
  170. package/dist/testgen/failure.d.ts.map +1 -0
  171. package/dist/testgen/failure.js +106 -0
  172. package/dist/testgen/failure.js.map +1 -0
  173. package/dist/testgen/frameworks.d.ts +6 -0
  174. package/dist/testgen/frameworks.d.ts.map +1 -0
  175. package/dist/testgen/frameworks.js +516 -0
  176. package/dist/testgen/frameworks.js.map +1 -0
  177. package/dist/testgen/go-facts.d.ts +11 -0
  178. package/dist/testgen/go-facts.d.ts.map +1 -0
  179. package/dist/testgen/go-facts.js +237 -0
  180. package/dist/testgen/go-facts.js.map +1 -0
  181. package/dist/testgen/go-profile.d.ts +6 -0
  182. package/dist/testgen/go-profile.d.ts.map +1 -0
  183. package/dist/testgen/go-profile.js +155 -0
  184. package/dist/testgen/go-profile.js.map +1 -0
  185. package/dist/testgen/index.d.ts +28 -0
  186. package/dist/testgen/index.d.ts.map +1 -0
  187. package/dist/testgen/index.js +21 -0
  188. package/dist/testgen/index.js.map +1 -0
  189. package/dist/testgen/java-build.d.ts +13 -0
  190. package/dist/testgen/java-build.d.ts.map +1 -0
  191. package/dist/testgen/java-build.js +250 -0
  192. package/dist/testgen/java-build.js.map +1 -0
  193. package/dist/testgen/java-facts.d.ts +3 -0
  194. package/dist/testgen/java-facts.d.ts.map +1 -0
  195. package/dist/testgen/java-facts.js +349 -0
  196. package/dist/testgen/java-facts.js.map +1 -0
  197. package/dist/testgen/java-profile.d.ts +13 -0
  198. package/dist/testgen/java-profile.d.ts.map +1 -0
  199. package/dist/testgen/java-profile.js +194 -0
  200. package/dist/testgen/java-profile.js.map +1 -0
  201. package/dist/testgen/languages.d.ts +11 -0
  202. package/dist/testgen/languages.d.ts.map +1 -0
  203. package/dist/testgen/languages.js +63 -0
  204. package/dist/testgen/languages.js.map +1 -0
  205. package/dist/testgen/lexer.d.ts +19 -0
  206. package/dist/testgen/lexer.d.ts.map +1 -0
  207. package/dist/testgen/lexer.js +257 -0
  208. package/dist/testgen/lexer.js.map +1 -0
  209. package/dist/testgen/mutant-core.d.ts +19 -0
  210. package/dist/testgen/mutant-core.d.ts.map +1 -0
  211. package/dist/testgen/mutant-core.js +39 -0
  212. package/dist/testgen/mutant-core.js.map +1 -0
  213. package/dist/testgen/mutants-lang.d.ts +6 -0
  214. package/dist/testgen/mutants-lang.d.ts.map +1 -0
  215. package/dist/testgen/mutants-lang.js +291 -0
  216. package/dist/testgen/mutants-lang.js.map +1 -0
  217. package/dist/testgen/mutants.d.ts +9 -0
  218. package/dist/testgen/mutants.d.ts.map +1 -0
  219. package/dist/testgen/mutants.js +148 -0
  220. package/dist/testgen/mutants.js.map +1 -0
  221. package/dist/testgen/paths.d.ts +32 -0
  222. package/dist/testgen/paths.d.ts.map +1 -0
  223. package/dist/testgen/paths.js +211 -0
  224. package/dist/testgen/paths.js.map +1 -0
  225. package/dist/testgen/process.d.ts +19 -0
  226. package/dist/testgen/process.d.ts.map +1 -0
  227. package/dist/testgen/process.js +89 -0
  228. package/dist/testgen/process.js.map +1 -0
  229. package/dist/testgen/profile-failure.d.ts +7 -0
  230. package/dist/testgen/profile-failure.d.ts.map +1 -0
  231. package/dist/testgen/profile-failure.js +29 -0
  232. package/dist/testgen/profile-failure.js.map +1 -0
  233. package/dist/testgen/python-facts.d.ts +11 -0
  234. package/dist/testgen/python-facts.d.ts.map +1 -0
  235. package/dist/testgen/python-facts.js +380 -0
  236. package/dist/testgen/python-facts.js.map +1 -0
  237. package/dist/testgen/python-profile.d.ts +4 -0
  238. package/dist/testgen/python-profile.d.ts.map +1 -0
  239. package/dist/testgen/python-profile.js +344 -0
  240. package/dist/testgen/python-profile.js.map +1 -0
  241. package/dist/testgen/source-facts.d.ts +30 -0
  242. package/dist/testgen/source-facts.d.ts.map +1 -0
  243. package/dist/testgen/source-facts.js +497 -0
  244. package/dist/testgen/source-facts.js.map +1 -0
  245. package/dist/testgen/stubs.d.ts +4 -0
  246. package/dist/testgen/stubs.d.ts.map +1 -0
  247. package/dist/testgen/stubs.js +66 -0
  248. package/dist/testgen/stubs.js.map +1 -0
  249. package/dist/testgen/toolchain.d.ts +23 -0
  250. package/dist/testgen/toolchain.d.ts.map +1 -0
  251. package/dist/testgen/toolchain.js +201 -0
  252. package/dist/testgen/toolchain.js.map +1 -0
  253. package/dist/testgen/types.d.ts +67 -0
  254. package/dist/testgen/types.d.ts.map +1 -0
  255. package/dist/testgen/types.js +2 -0
  256. package/dist/testgen/types.js.map +1 -0
  257. package/dist/v8-coverage.d.ts +21 -0
  258. package/dist/v8-coverage.d.ts.map +1 -0
  259. package/dist/v8-coverage.js +217 -0
  260. package/dist/v8-coverage.js.map +1 -0
  261. package/dist/vitest-reporter.d.ts +46 -13
  262. package/dist/vitest-reporter.d.ts.map +1 -1
  263. package/dist/vitest-reporter.js +119 -26
  264. package/dist/vitest-reporter.js.map +1 -1
  265. package/dist/vitest-version.d.ts +21 -0
  266. package/dist/vitest-version.d.ts.map +1 -0
  267. package/dist/vitest-version.js +98 -0
  268. package/dist/vitest-version.js.map +1 -0
  269. package/package.json +14 -8
  270. package/reporters/junit/classes/DescryJUnitListener.class +0 -0
  271. package/reporters/junit/classes/META-INF/services/org.junit.platform.launcher.TestExecutionListener +1 -0
  272. package/reporters/minitest/descry_minitest_reporter.rb +150 -0
  273. package/reporters/phpunit/DescryPhpUnitExtension.php +209 -0
  274. package/reporters/pytest/descry_pytest_plugin.py +397 -0
  275. package/reporters/rspec/descry_rspec_formatter.rb +175 -0
  276. package/reporters/xunit/bin/Release/netstandard2.0/Descry.TestLogger.deps.json +203 -0
  277. package/reporters/xunit/bin/Release/netstandard2.0/Descry.TestLogger.dll +0 -0
  278. package/reporters/xunit/bin/Release/netstandard2.0/Descry.TestLogger.pdb +0 -0
@@ -0,0 +1,786 @@
1
+ import { argsOf, parseChain } from "./chains.js";
2
+ import { allChains, chainsInRange, spanSetOf } from "./common.js";
3
+ import { durationMs } from "./durations.js";
4
+ import { isNullLiteralRange } from "./expressions.js";
5
+ import { EXISTENCE, negate, SNAPSHOT, STRONG, STRONG_THROWS, STRONG_URL, STRONG_VALUE, strongerOf, strongStatus, WEAK, weakerOf } from "./assertion-kinds.js";
6
+ import { selectorFromBy, selectorFromCall } from "./selectors.js";
7
+ import { isStatusSubject, isUrlSubject, statusFromTokens } from "./statuses.js";
8
+ import { endLineOf, isIdent, isPunct, lineOf, splitTopLevel, stringValue, textAt, textOf } from "./stream.js";
9
+ const CHAIN_OPTIONS = { generics: false, sameLine: true };
10
+ function buildLayout(stream) {
11
+ const toks = stream.toks;
12
+ const stmtStart = new Int32Array(toks.length).fill(0);
13
+ const stmtId = new Int32Array(toks.length).fill(0);
14
+ const starts = [];
15
+ let depth = 0;
16
+ let current = 0;
17
+ for (let index = 0; index < toks.length; index += 1) {
18
+ const token = toks[index];
19
+ const previous = toks[index - 1];
20
+ let begins = index === 0;
21
+ if (!begins && depth === 0) {
22
+ const continued = previous.kind === "punctuator" && previous.text === "\\";
23
+ if (!continued && endLineOf(previous) < token.line)
24
+ begins = true;
25
+ else if (previous.kind === "punctuator" && previous.text === ";")
26
+ begins = true;
27
+ }
28
+ if (begins) {
29
+ current = index;
30
+ starts.push(index);
31
+ }
32
+ stmtStart[index] = current;
33
+ stmtId[index] = starts.length - 1;
34
+ if (token.kind === "punctuator") {
35
+ if (token.text === "(" || token.text === "[" || token.text === "{")
36
+ depth += 1;
37
+ else if (token.text === ")" || token.text === "]" || token.text === "}")
38
+ depth = Math.max(0, depth - 1);
39
+ }
40
+ }
41
+ return { stmtStart, starts, stmtId };
42
+ }
43
+ function statementEnd(stream, layout, start) {
44
+ const id = layout.stmtId[start];
45
+ const next = layout.starts[id + 1];
46
+ return next === undefined ? stream.toks.length - 1 : next - 1;
47
+ }
48
+ function blockEnd(stream, layout, start) {
49
+ const headerEnd = statementEnd(stream, layout, start);
50
+ if (textAt(stream, headerEnd) !== ":")
51
+ return headerEnd;
52
+ const indent = stream.toks[layout.stmtStart[start]].column;
53
+ let index = headerEnd + 1;
54
+ while (index < stream.toks.length) {
55
+ if (layout.stmtStart[index] === index && stream.toks[index].column <= indent)
56
+ break;
57
+ index += 1;
58
+ }
59
+ return index - 1;
60
+ }
61
+ function bodyIndent(stream, layout, start) {
62
+ const headerEnd = statementEnd(stream, layout, start);
63
+ if (textAt(stream, headerEnd) !== ":")
64
+ return null;
65
+ const first = stream.toks[headerEnd + 1];
66
+ return first === undefined ? null : first.column;
67
+ }
68
+ function decoratorsBefore(stream, layout, declarationStart) {
69
+ const found = [];
70
+ let cursor = declarationStart;
71
+ while (cursor > 0) {
72
+ const previous = cursor - 1;
73
+ const start = layout.stmtStart[previous];
74
+ if (!isPunct(stream, start, "@"))
75
+ break;
76
+ found.push({ start, end: statementEnd(stream, layout, start) });
77
+ cursor = start;
78
+ }
79
+ return found;
80
+ }
81
+ function chainOfDecorator(stream, decorator) {
82
+ if (stream.toks[decorator.start + 1]?.kind !== "identifier")
83
+ return null;
84
+ return parseChain(stream, decorator.start + 1, CHAIN_OPTIONS);
85
+ }
86
+ const SKIP_NAMES = new Set(["skip", "skipif", "skipIf", "skipUnless", "skip_if", "skip_unless", "skip_on", "skipTest"]);
87
+ function decoratorSkips(stream, decorators) {
88
+ for (const decorator of decorators) {
89
+ const chain = chainOfDecorator(stream, decorator);
90
+ if (chain === null)
91
+ continue;
92
+ if (chain.segments.some((segment) => SKIP_NAMES.has(segment.name)))
93
+ return true;
94
+ }
95
+ return false;
96
+ }
97
+ const WEAK_CALLS = new Set(["len", "bool"]);
98
+ const EXISTENCE_CALLS = new Set(["hasattr", "exists", "isfile", "isdir", "is_file", "is_dir", "isexists", "path_exists"]);
99
+ function snapshotReference(stream, from, last) {
100
+ for (let index = from; index <= last; index += 1) {
101
+ const token = stream.toks[index];
102
+ if (token.kind === "identifier" && (token.text === "snapshot" || token.text === "match_snapshot" || token.text === "matches_snapshot"))
103
+ return true;
104
+ }
105
+ return false;
106
+ }
107
+ function stripParentheses(stream, from, last) {
108
+ let start = from;
109
+ let end = last;
110
+ while (end > start && isPunct(stream, start, "(") && stream.match[start] === end) {
111
+ start += 1;
112
+ end -= 1;
113
+ }
114
+ return [start, end];
115
+ }
116
+ function splitOnKeyword(stream, from, last, keyword) {
117
+ const parts = [];
118
+ let start = from;
119
+ let index = from;
120
+ while (index <= last) {
121
+ const token = stream.toks[index];
122
+ if (token.kind === "punctuator" && (token.text === "(" || token.text === "[" || token.text === "{") && stream.match[index] > index) {
123
+ index = stream.match[index] + 1;
124
+ continue;
125
+ }
126
+ if (token.kind === "identifier" && token.text === keyword && index > start) {
127
+ parts.push([start, index - 1]);
128
+ start = index + 1;
129
+ }
130
+ index += 1;
131
+ }
132
+ if (start <= last)
133
+ parts.push([start, last]);
134
+ return parts;
135
+ }
136
+ const COMPARISON_PUNCTUATION = new Set(["==", "!=", "<", ">", "<=", ">="]);
137
+ function findComparison(stream, from, last) {
138
+ let index = from;
139
+ while (index <= last) {
140
+ const token = stream.toks[index];
141
+ if (token.kind === "punctuator") {
142
+ if ((token.text === "(" || token.text === "[" || token.text === "{") && stream.match[index] > index) {
143
+ index = stream.match[index] + 1;
144
+ continue;
145
+ }
146
+ if (COMPARISON_PUNCTUATION.has(token.text) && index > from && index < last)
147
+ return { operator: token.text, left: [from, index - 1], right: [index + 1, last] };
148
+ }
149
+ else if (token.kind === "identifier" && index > from) {
150
+ if (token.text === "in" && index < last)
151
+ return { operator: "in", left: [from, index - 1], right: [index + 1, last] };
152
+ if (token.text === "not" && isIdent(stream, index + 1, "in") && index + 1 < last)
153
+ return { operator: "not in", left: [from, index - 1], right: [index + 2, last] };
154
+ if (token.text === "is" && index < last) {
155
+ if (isIdent(stream, index + 1, "not") && index + 1 < last)
156
+ return { operator: "is not", left: [from, index - 1], right: [index + 2, last] };
157
+ return { operator: "is", left: [from, index - 1], right: [index + 1, last] };
158
+ }
159
+ }
160
+ index += 1;
161
+ }
162
+ return null;
163
+ }
164
+ function comparesWithNone(stream, from, last) {
165
+ const [start, end] = stripParentheses(stream, from, last);
166
+ const comparison = findComparison(stream, start, end);
167
+ if (comparison === null || (comparison.operator !== "is" && comparison.operator !== "==" && comparison.operator !== "is not" && comparison.operator !== "!="))
168
+ return false;
169
+ return isNullLiteralRange(stream, comparison.left[0], comparison.left[1]) || isNullLiteralRange(stream, comparison.right[0], comparison.right[1]);
170
+ }
171
+ function bareCallName(stream, from, last) {
172
+ if (stream.toks[from]?.kind !== "identifier")
173
+ return null;
174
+ const chain = parseChain(stream, from, CHAIN_OPTIONS);
175
+ if (chain.end !== last)
176
+ return null;
177
+ const finalSegment = chain.segments[chain.segments.length - 1];
178
+ return { name: finalSegment.name, wholeCall: finalSegment.open >= 0 };
179
+ }
180
+ const DEPTH_LIMIT = 48;
181
+ export function classifyPythonExpression(stream, fromIndex, lastIndex, depth = 0) {
182
+ if (depth > DEPTH_LIMIT)
183
+ return STRONG;
184
+ const [from, last] = stripParentheses(stream, fromIndex, lastIndex);
185
+ if (last < from)
186
+ return WEAK;
187
+ const disjuncts = splitOnKeyword(stream, from, last, "or");
188
+ if (disjuncts.length > 1)
189
+ return disjuncts.map((part) => classifyPythonExpression(stream, part[0], part[1], depth + 1)).reduce(weakerOf);
190
+ const conjuncts = splitOnKeyword(stream, from, last, "and");
191
+ if (conjuncts.length > 1)
192
+ return conjuncts.map((part) => classifyPythonExpression(stream, part[0], part[1], depth + 1)).reduce(strongerOf);
193
+ if (isIdent(stream, from, "not") && !isIdent(stream, from + 1, "in") && from < last) {
194
+ if (comparesWithNone(stream, from + 1, last))
195
+ return WEAK;
196
+ return negate(classifyPythonExpression(stream, from + 1, last, depth + 1));
197
+ }
198
+ const comparison = findComparison(stream, from, last);
199
+ if (comparison !== null) {
200
+ const { operator, left, right } = comparison;
201
+ if (operator === "==") {
202
+ if (snapshotReference(stream, left[0], left[1]) || snapshotReference(stream, right[0], right[1]))
203
+ return SNAPSHOT;
204
+ if (isStatusSubject(stream, left[0], left[1])) {
205
+ const value = statusFromTokens(stream, right[0], right[1]);
206
+ if (value !== null)
207
+ return strongStatus(value);
208
+ }
209
+ if (isStatusSubject(stream, right[0], right[1])) {
210
+ const value = statusFromTokens(stream, left[0], left[1]);
211
+ if (value !== null)
212
+ return strongStatus(value);
213
+ }
214
+ if (isUrlSubject(stream, left[0], left[1]) || isUrlSubject(stream, right[0], right[1]))
215
+ return STRONG_URL;
216
+ return STRONG_VALUE;
217
+ }
218
+ if (operator === "!=" || operator === "is not")
219
+ return isNullLiteralRange(stream, left[0], left[1]) || isNullLiteralRange(stream, right[0], right[1]) ? WEAK : STRONG;
220
+ if (operator === "not in")
221
+ return STRONG;
222
+ return STRONG_VALUE;
223
+ }
224
+ const call = bareCallName(stream, from, last);
225
+ if (call !== null) {
226
+ if (!call.wholeCall)
227
+ return WEAK;
228
+ if (WEAK_CALLS.has(call.name))
229
+ return WEAK;
230
+ if (EXISTENCE_CALLS.has(call.name))
231
+ return EXISTENCE;
232
+ if (call.name === "snapshot" || call.name === "match_snapshot")
233
+ return SNAPSHOT;
234
+ return STRONG;
235
+ }
236
+ if (from === last && stream.toks[from].kind === "identifier")
237
+ return WEAK;
238
+ return STRONG;
239
+ }
240
+ const UNITTEST_THROWS = /^assert(Raises|RaisesRegex|Raises_regex|Throws)$/;
241
+ const UNITTEST_NEGATED = /^assert(Not|IsNot)/;
242
+ function classifyUnittest(stream, segment) {
243
+ const name = segment.name;
244
+ const args = argsOf(stream, segment);
245
+ if (UNITTEST_THROWS.test(name))
246
+ return STRONG_THROWS;
247
+ if (/^assertMatch.*Snapshot$/.test(name) || name === "assertSnapshot")
248
+ return SNAPSHOT;
249
+ if (name === "assertTrue" || name === "assertFalse") {
250
+ if (args[0] === undefined)
251
+ return null;
252
+ if (name === "assertFalse" && comparesWithNone(stream, args[0][0], args[0][1]))
253
+ return WEAK;
254
+ const inner = classifyPythonExpression(stream, args[0][0], args[0][1]);
255
+ return name === "assertFalse" ? negate(inner) : inner;
256
+ }
257
+ if (name === "assertIsNotNone")
258
+ return WEAK;
259
+ if (UNITTEST_NEGATED.test(name)) {
260
+ const operands = args.slice(0, 2);
261
+ if (operands.length < 2)
262
+ return WEAK;
263
+ return operands.some((operand) => isNullLiteralRange(stream, operand[0], operand[1])) ? WEAK : STRONG;
264
+ }
265
+ if (name === "assertEqual" || name === "assertEquals" || name === "assertDictEqual" || name === "assertListEqual") {
266
+ if (args.length >= 2) {
267
+ for (const [subject, other] of [[args[0], args[1]], [args[1], args[0]]]) {
268
+ if (isStatusSubject(stream, subject[0], subject[1])) {
269
+ const value = statusFromTokens(stream, other[0], other[1]);
270
+ if (value !== null)
271
+ return strongStatus(value);
272
+ }
273
+ }
274
+ if (isUrlSubject(stream, args[0][0], args[0][1]) || isUrlSubject(stream, args[1][0], args[1][1]))
275
+ return STRONG_URL;
276
+ }
277
+ return STRONG_VALUE;
278
+ }
279
+ if (name === "assertContains" || name === "assertRedirects") {
280
+ for (let index = segment.open + 1; index < segment.close; index += 1) {
281
+ if (isIdent(stream, index, "status_code") && isPunct(stream, index + 1, "=")) {
282
+ const value = statusFromTokens(stream, index + 2, index + 2);
283
+ if (value !== null)
284
+ return strongStatus(value);
285
+ }
286
+ }
287
+ return STRONG_VALUE;
288
+ }
289
+ return STRONG_VALUE;
290
+ }
291
+ const MOCK_STRONG = new Set(["assert_called_with", "assert_called_once_with", "assert_any_call", "assert_has_calls", "assert_awaited_with", "assert_awaited_once_with", "assert_not_called", "assert_called_once", "assert_awaited_once", "assert_not_awaited", "assert_has_awaits", "assert_any_await"]);
292
+ const MOCK_WEAK = new Set(["assert_called", "assert_awaited"]);
293
+ function pythonCallAssertion(stream, chain) {
294
+ const segments = chain.segments;
295
+ for (let index = 0; index < segments.length; index += 1) {
296
+ const segment = segments[index];
297
+ if (segment.open < 0)
298
+ continue;
299
+ if (segment.name === "raises" && index >= 1 && segments[index - 1].name === "pytest")
300
+ return { nameIndex: segment.nameIndex, classified: STRONG_THROWS };
301
+ if ((segment.name === "warns" || segment.name === "deprecated_call") && index >= 1 && segments[index - 1].name === "pytest")
302
+ return { nameIndex: segment.nameIndex, classified: STRONG };
303
+ if (/^assert[A-Z]/.test(segment.name) && index >= 1) {
304
+ const classified = classifyUnittest(stream, segment);
305
+ if (classified !== null)
306
+ return { nameIndex: segment.nameIndex, classified };
307
+ }
308
+ if (segment.name.startsWith("assert_") && index >= 1) {
309
+ if (segment.name === "assert_match" && /snapshot/i.test(segments[index - 1].name))
310
+ return { nameIndex: segment.nameIndex, classified: SNAPSHOT };
311
+ if (MOCK_STRONG.has(segment.name))
312
+ return { nameIndex: segment.nameIndex, classified: STRONG };
313
+ if (MOCK_WEAK.has(segment.name))
314
+ return { nameIndex: segment.nameIndex, classified: WEAK };
315
+ return { nameIndex: segment.nameIndex, classified: STRONG_VALUE };
316
+ }
317
+ if ((segment.name === "assert_match_snapshot" || segment.name === "match_snapshot") && index === 0)
318
+ return { nameIndex: segment.nameIndex, classified: SNAPSHOT };
319
+ }
320
+ return null;
321
+ }
322
+ function stringArg(stream, segment, position) {
323
+ const range = argsOf(stream, segment)[position];
324
+ if (range === undefined || range[0] !== range[1])
325
+ return null;
326
+ return stringValue(stream.toks[range[0]]);
327
+ }
328
+ function dotted(target) {
329
+ const cut = target.lastIndexOf(".");
330
+ if (cut <= 0 || cut === target.length - 1)
331
+ return null;
332
+ return { module: target.slice(0, cut), symbol: target.slice(cut + 1) };
333
+ }
334
+ const MOCK_FACTORIES = new Set(["MagicMock", "Mock", "AsyncMock", "PropertyMock", "NonCallableMock", "NonCallableMagicMock", "create_autospec"]);
335
+ const NETWORK_HEADS = new Set(["responses", "requests_mock", "httpretty", "respx", "aioresponses", "vcr", "HTTPretty", "httpx_mock", "pook"]);
336
+ function pythonMocksOf(stream, chain) {
337
+ const segments = chain.segments;
338
+ const found = [];
339
+ const first = segments[0];
340
+ for (let index = 0; index < segments.length; index += 1) {
341
+ const segment = segments[index];
342
+ if (index === 0 && NETWORK_HEADS.has(segment.name) && segments.length >= 2 && segments[1].name !== "") {
343
+ found.push({ nameIndex: segments[1].nameIndex, kind: "network", module: null, symbol: null });
344
+ continue;
345
+ }
346
+ if (segment.open < 0 && segment.name !== "patch")
347
+ continue;
348
+ if (segment.name === "patch") {
349
+ const member = segments[index + 1];
350
+ if (member !== undefined && (member.name === "dict" || member.name === "stopall" || member.name === "stop"))
351
+ continue;
352
+ if (member !== undefined && member.name === "object" && member.open >= 0) {
353
+ found.push({ nameIndex: member.nameIndex, kind: "function", module: null, symbol: stringArg(stream, member, 1) });
354
+ continue;
355
+ }
356
+ if (member !== undefined && member.name === "multiple" && member.open >= 0) {
357
+ found.push({ nameIndex: member.nameIndex, kind: "function", module: null, symbol: null });
358
+ continue;
359
+ }
360
+ if (segment.open >= 0) {
361
+ const target = stringArg(stream, segment, 0);
362
+ const split = target === null ? null : dotted(target);
363
+ if (split !== null)
364
+ found.push({ nameIndex: segment.nameIndex, kind: "function", module: split.module, symbol: split.symbol });
365
+ else
366
+ found.push({ nameIndex: segment.nameIndex, kind: target === null ? "function" : "module", module: target, symbol: null });
367
+ }
368
+ continue;
369
+ }
370
+ if (segment.name === "setattr" && index >= 1 && /monkeypatch|^mp$/i.test(segments[index - 1].name)) {
371
+ const target = stringArg(stream, segment, 0);
372
+ const split = target === null ? null : dotted(target);
373
+ if (split !== null)
374
+ found.push({ nameIndex: segment.nameIndex, kind: "function", module: split.module, symbol: split.symbol });
375
+ else
376
+ found.push({ nameIndex: segment.nameIndex, kind: "function", module: null, symbol: stringArg(stream, segment, 1) });
377
+ continue;
378
+ }
379
+ if ((segment.name === "spy" || segment.name === "stub") && index >= 1 && segments[index - 1].name === "mocker") {
380
+ found.push({ nameIndex: segment.nameIndex, kind: "function", module: null, symbol: segment.name === "spy" ? stringArg(stream, segment, 1) : null });
381
+ continue;
382
+ }
383
+ if (MOCK_FACTORIES.has(segment.name) && segment.open >= 0) {
384
+ found.push({ nameIndex: segment.nameIndex, kind: "function", module: null, symbol: null });
385
+ continue;
386
+ }
387
+ if (segment.name === "freeze_time" && segment.open >= 0) {
388
+ found.push({ nameIndex: segment.nameIndex, kind: "timer", module: null, symbol: null });
389
+ continue;
390
+ }
391
+ if (segment.name === "travel" && index >= 1 && segments[index - 1].name === "time_machine" && segment.open >= 0) {
392
+ found.push({ nameIndex: segment.nameIndex, kind: "timer", module: null, symbol: null });
393
+ continue;
394
+ }
395
+ if (segment.name === "use_cassette" && segment.open >= 0)
396
+ found.push({ nameIndex: segment.nameIndex, kind: "network", module: null, symbol: null });
397
+ if (segment.name === "aioresponses" && segment.open >= 0 && index === 0)
398
+ found.push({ nameIndex: segment.nameIndex, kind: "network", module: null, symbol: null });
399
+ }
400
+ if (first.name === "freezegun" && segments[1]?.name === "freeze_time" && found.length === 0)
401
+ found.push({ nameIndex: segments[1].nameIndex, kind: "timer", module: null, symbol: null });
402
+ return found;
403
+ }
404
+ const SLEEP_RECEIVERS = new Set(["time", "asyncio", "trio", "anyio", "gevent", "eventlet"]);
405
+ function pythonSleepOf(stream, chain) {
406
+ const segments = chain.segments;
407
+ const last = segments[segments.length - 1];
408
+ if (last.name !== "sleep" || last.open < 0)
409
+ return null;
410
+ if (segments.length > 1 && !SLEEP_RECEIVERS.has(segments[segments.length - 2].name))
411
+ return null;
412
+ const range = argsOf(stream, last)[0];
413
+ return { nameIndex: last.nameIndex, ms: range === undefined ? null : durationMs(stream, range[0], range[1], 1000) };
414
+ }
415
+ function pythonSelectors(stream, chain) {
416
+ const found = [];
417
+ const segments = chain.segments;
418
+ const head = segments[0].name;
419
+ for (let index = 0; index < segments.length; index += 1) {
420
+ const segment = segments[index];
421
+ if (segment.open < 0 || segment.name === "")
422
+ continue;
423
+ const args = argsOf(stream, segment);
424
+ const normalised = segment.name.toLowerCase().replace(/_/g, "");
425
+ if ((normalised === "findelement" || normalised === "findelements") && args.length >= 2) {
426
+ const locator = args[0];
427
+ const value = args[1];
428
+ const byName = locator[1] - locator[0] === 2 && isIdent(stream, locator[0], "By") && isPunct(stream, locator[0] + 1, ".") ? textAt(stream, locator[1]) : null;
429
+ const text = value[0] === value[1] ? stringValue(stream.toks[value[0]]) : null;
430
+ if (byName !== null && text !== null) {
431
+ const candidate = selectorFromBy(byName, text);
432
+ if (candidate !== null)
433
+ found.push({ nameIndex: segment.nameIndex, text: candidate.text, stability: candidate.stability });
434
+ }
435
+ continue;
436
+ }
437
+ const first = args[0];
438
+ const text = first !== undefined && first[0] === first[1] ? stringValue(stream.toks[first[0]]) : null;
439
+ if (text === null)
440
+ continue;
441
+ const candidate = selectorFromCall(segment.name, index > 0 ? segments[index - 1].name : null, text, { head, allowFind: false });
442
+ if (candidate !== null)
443
+ found.push({ nameIndex: segment.nameIndex, text: candidate.text, stability: candidate.stability });
444
+ }
445
+ return found;
446
+ }
447
+ const HELPER_EXCLUDED = new Set([
448
+ "print", "len", "range", "str", "int", "float", "bool", "list", "dict", "set", "tuple", "frozenset", "sorted", "reversed", "enumerate", "zip", "map", "filter", "min", "max", "sum", "any", "all", "abs", "round", "isinstance", "issubclass", "hasattr", "getattr", "setattr", "delattr", "type", "repr", "open", "iter", "next", "id", "hash", "format", "bytes", "bytearray", "callable", "vars", "dir", "super", "object", "input", "eval", "exec", "compile", "divmod", "pow", "chr", "ord", "hex", "oct", "bin", "slice", "memoryview", "property", "staticmethod", "classmethod", "globals", "locals", "ascii", "complex", "if", "while", "for", "return", "assert", "lambda", "yield", "await", "not", "and", "or", "in", "is", "with", "print_",
449
+ "patch", "MagicMock", "Mock", "AsyncMock", "call", "mock_open", "sentinel", "PropertyMock", "create_autospec", "freeze_time", "sleep", "skip", "fixture", "param", "raises", "approx", "fail", "parametrize", "mark", "snapshot", "responses", "aioresponses",
450
+ ]);
451
+ function pythonHelperOf(stream, chain) {
452
+ const segments = chain.segments;
453
+ if (segments.length !== 1)
454
+ return null;
455
+ const only = segments[0];
456
+ if (only.open < 0 || HELPER_EXCLUDED.has(only.name))
457
+ return null;
458
+ if (/^[A-Z]/.test(only.name) || /(Error|Exception|Warning)$/.test(only.name))
459
+ return null;
460
+ const previous = stream.toks[chain.head - 1];
461
+ if (previous !== undefined && previous.kind === "identifier" && (previous.text === "def" || previous.text === "class" || previous.text === "import" || previous.text === "from" || previous.text === "as"))
462
+ return null;
463
+ return only.name;
464
+ }
465
+ function collectDefinitions(stream, layout) {
466
+ const definitions = [];
467
+ for (const start of layout.starts) {
468
+ let index = start;
469
+ if (isIdent(stream, index, "async") && isIdent(stream, index + 1, "def"))
470
+ index += 1;
471
+ const token = stream.toks[index];
472
+ if (token === undefined || token.kind !== "identifier" || (token.text !== "def" && token.text !== "class"))
473
+ continue;
474
+ const name = stream.toks[index + 1];
475
+ if (name === undefined || name.kind !== "identifier")
476
+ continue;
477
+ definitions.push({ start, defIndex: index, nameIndex: index + 1, name: name.text, kind: token.text });
478
+ }
479
+ return definitions;
480
+ }
481
+ const HOOK_FUNCTIONS = {
482
+ setUp: { kind: "setup", scope: "each" },
483
+ asyncSetUp: { kind: "setup", scope: "each" },
484
+ tearDown: { kind: "teardown", scope: "each" },
485
+ asyncTearDown: { kind: "teardown", scope: "each" },
486
+ setup_method: { kind: "setup", scope: "each" },
487
+ teardown_method: { kind: "teardown", scope: "each" },
488
+ setup_function: { kind: "setup", scope: "each" },
489
+ teardown_function: { kind: "teardown", scope: "each" },
490
+ setUpClass: { kind: "setup", scope: "all" },
491
+ tearDownClass: { kind: "teardown", scope: "all" },
492
+ setUpModule: { kind: "setup", scope: "all" },
493
+ tearDownModule: { kind: "teardown", scope: "all" },
494
+ setup_class: { kind: "setup", scope: "all" },
495
+ teardown_class: { kind: "teardown", scope: "all" },
496
+ setup_module: { kind: "setup", scope: "all" },
497
+ teardown_module: { kind: "teardown", scope: "all" },
498
+ };
499
+ const ASSIGNMENT_TOKENS = new Set(["=", "+=", "-=", "*=", "/=", "//=", "%=", "**=", "@=", "&=", "|=", "^=", ">>=", "<<=", ":="]);
500
+ const MUTATORS = new Set(["append", "extend", "update", "add", "remove", "pop", "clear", "insert", "setdefault", "discard", "popitem", "sort", "reverse", "put", "put_nowait"]);
501
+ function fixtureInfo(stream, decorators) {
502
+ for (const decorator of decorators) {
503
+ const chain = chainOfDecorator(stream, decorator);
504
+ if (chain === null)
505
+ continue;
506
+ const fixture = chain.segments.find((segment) => segment.name === "fixture");
507
+ if (fixture === undefined)
508
+ continue;
509
+ let autouse = false;
510
+ let scope = "each";
511
+ if (fixture.open >= 0) {
512
+ for (let index = fixture.open + 1; index < fixture.close; index += 1) {
513
+ if (isIdent(stream, index, "autouse") && isPunct(stream, index + 1, "=") && isIdent(stream, index + 2, "True"))
514
+ autouse = true;
515
+ if (isIdent(stream, index, "scope") && isPunct(stream, index + 1, "=")) {
516
+ const value = stringValue(stream.toks[index + 2]);
517
+ if (value !== null && value !== "function")
518
+ scope = "all";
519
+ }
520
+ }
521
+ }
522
+ return { autouse, scope };
523
+ }
524
+ return null;
525
+ }
526
+ function containsYield(stream, from, to) {
527
+ for (let index = from; index <= to; index += 1)
528
+ if (isIdent(stream, index, "yield"))
529
+ return true;
530
+ return false;
531
+ }
532
+ function nestedFunctionSpans(stream, layout, definitions, first, last) {
533
+ const spans = [];
534
+ const enclosingEnds = [];
535
+ for (const candidate of definitions) {
536
+ if (candidate.kind !== "def" || candidate.defIndex < first || candidate.defIndex > last)
537
+ continue;
538
+ while (enclosingEnds.length > 0 && enclosingEnds[enclosingEnds.length - 1] < candidate.start)
539
+ enclosingEnds.pop();
540
+ const end = blockEnd(stream, layout, candidate.start);
541
+ if (enclosingEnds.length > 0)
542
+ spans.push([candidate.defIndex, end]);
543
+ enclosingEnds.push(end);
544
+ }
545
+ return spanSetOf(spans);
546
+ }
547
+ export function extractPython(scope) {
548
+ const { stream, first, last, out } = scope;
549
+ const layout = buildLayout(stream);
550
+ const definitions = collectDefinitions(stream, layout);
551
+ const chains = allChains(stream, CHAIN_OPTIONS);
552
+ const rangeChains = chainsInRange(chains, first, last);
553
+ let nestedFunctions = null;
554
+ for (let index = first; index <= last; index += 1) {
555
+ const token = stream.toks[index];
556
+ if (token.kind !== "identifier" || token.text !== "assert")
557
+ continue;
558
+ const previous = stream.toks[index - 1];
559
+ const startsStatement = layout.stmtStart[index] === index || (previous !== undefined && previous.kind === "punctuator" && previous.text === ":");
560
+ if (!startsStatement)
561
+ continue;
562
+ const end = statementEnd(stream, layout, layout.stmtStart[index]);
563
+ if (end <= index)
564
+ continue;
565
+ const parts = splitTopLevel(stream, index + 1, end);
566
+ const expression = parts[0];
567
+ if (expression === undefined)
568
+ continue;
569
+ const classified = classifyPythonExpression(stream, expression[0], expression[1]);
570
+ out.assertions.push({ line: token.line, strength: classified.strength, expects: classified.expects, text: textOf(stream, index, end) });
571
+ }
572
+ for (const chain of rangeChains) {
573
+ const assertion = pythonCallAssertion(stream, chain);
574
+ if (assertion !== null)
575
+ out.assertions.push({ line: lineOf(stream, assertion.nameIndex), strength: assertion.classified.strength, expects: assertion.classified.expects, text: textOf(stream, chain.head, chain.end) });
576
+ for (const mock of pythonMocksOf(stream, chain))
577
+ out.mocks.push({ line: lineOf(stream, mock.nameIndex), module: mock.module, symbol: mock.symbol, kind: mock.kind });
578
+ const sleep = pythonSleepOf(stream, chain);
579
+ if (sleep !== null) {
580
+ nestedFunctions ??= nestedFunctionSpans(stream, layout, definitions, first, last);
581
+ if (!nestedFunctions.contains(chain.head))
582
+ out.sleeps.push({ line: lineOf(stream, sleep.nameIndex), ms: sleep.ms });
583
+ }
584
+ for (const selector of pythonSelectors(stream, chain))
585
+ out.selectors.push({ line: lineOf(stream, selector.nameIndex), text: selector.text, stability: selector.stability });
586
+ const helper = pythonHelperOf(stream, chain);
587
+ if (helper !== null)
588
+ out.helperCalls.push({ line: lineOf(stream, chain.head), name: helper });
589
+ }
590
+ const subject = definitions.find((definition) => definition.kind === "def" && definition.start >= first && definition.start <= last);
591
+ const enclosingClasses = (subject === undefined ? definitions.filter((d) => d.kind === "class" && d.start >= first && d.start <= last).slice(0, 1) : definitions.filter((d) => d.kind === "class" && d.start < subject.start && blockEnd(stream, layout, d.start) >= subject.start));
592
+ const decorated = [];
593
+ if (subject !== undefined)
594
+ decorated.push(decoratorsBefore(stream, layout, subject.start));
595
+ for (const owner of enclosingClasses)
596
+ decorated.push(decoratorsBefore(stream, layout, owner.start));
597
+ for (const decorators of decorated) {
598
+ if (decoratorSkips(stream, decorators))
599
+ out.skipped = true;
600
+ for (const decorator of decorators) {
601
+ const chain = chainOfDecorator(stream, decorator);
602
+ if (chain === null)
603
+ continue;
604
+ for (const mock of pythonMocksOf(stream, chain))
605
+ out.mocks.push({ line: lineOf(stream, mock.nameIndex), module: mock.module, symbol: mock.symbol, kind: mock.kind });
606
+ }
607
+ }
608
+ if (subject !== undefined) {
609
+ const indent = bodyIndent(stream, layout, subject.start);
610
+ const end = blockEnd(stream, layout, subject.start);
611
+ for (const chain of chains) {
612
+ if (chain.head <= subject.defIndex || chain.head > end)
613
+ continue;
614
+ if (layout.stmtStart[chain.head] !== chain.head || stream.toks[chain.head].column !== indent)
615
+ continue;
616
+ const names = chain.segments.map((segment) => segment.name);
617
+ const unconditional = (names[0] === "pytest" && names[1] === "skip") || (names[0] === "self" && names[1] === "skipTest") || (names[0] === "unittest" && names[1] === "skip");
618
+ if (unconditional)
619
+ out.skipped = true;
620
+ }
621
+ }
622
+ for (const start of layout.starts) {
623
+ if (stream.toks[start].column !== 1)
624
+ continue;
625
+ if (!isIdent(stream, start, "pytestmark"))
626
+ continue;
627
+ const end = statementEnd(stream, layout, start);
628
+ for (let index = start; index <= end; index += 1)
629
+ if (SKIP_NAMES.has(textAt(stream, index)))
630
+ out.skipped = true;
631
+ }
632
+ for (const definition of definitions) {
633
+ if (definition.kind !== "def")
634
+ continue;
635
+ const hook = HOOK_FUNCTIONS[definition.name];
636
+ if (hook !== undefined) {
637
+ out.hooks.push({ line: lineOf(stream, definition.defIndex), kind: hook.kind, scope: hook.scope });
638
+ continue;
639
+ }
640
+ const decorators = decoratorsBefore(stream, layout, definition.start);
641
+ const fixture = fixtureInfo(stream, decorators);
642
+ if (fixture === null)
643
+ continue;
644
+ const end = blockEnd(stream, layout, definition.start);
645
+ const line = lineOf(stream, definition.defIndex);
646
+ if (containsYield(stream, definition.defIndex, end)) {
647
+ out.hooks.push({ line, kind: "setup", scope: fixture.scope });
648
+ out.hooks.push({ line, kind: "teardown", scope: fixture.scope });
649
+ }
650
+ else if (fixture.autouse)
651
+ out.hooks.push({ line, kind: "setup", scope: fixture.scope });
652
+ }
653
+ for (const chain of chains) {
654
+ const lastSegment = chain.segments[chain.segments.length - 1];
655
+ if (lastSegment.open < 0)
656
+ continue;
657
+ if (lastSegment.name === "addCleanup" || lastSegment.name === "addfinalizer" || lastSegment.name === "addAsyncCleanup")
658
+ out.hooks.push({ line: lineOf(stream, chain.head), kind: "teardown", scope: "each" });
659
+ if (lastSegment.name === "addClassCleanup")
660
+ out.hooks.push({ line: lineOf(stream, chain.head), kind: "teardown", scope: "all" });
661
+ }
662
+ collectPythonSharedState(scope, layout, definitions);
663
+ }
664
+ function collectPythonSharedState(scope, layout, definitions) {
665
+ const { stream, first, last, out } = scope;
666
+ if (first > last)
667
+ return;
668
+ const moduleNames = new Set();
669
+ const classNames = new Set();
670
+ const classAttributes = new Set();
671
+ const assignmentTarget = (start) => {
672
+ const token = stream.toks[start];
673
+ if (token === undefined || token.kind !== "identifier")
674
+ return null;
675
+ const next = stream.toks[start + 1];
676
+ if (next === undefined || next.kind !== "punctuator")
677
+ return null;
678
+ if (next.text === "=" || (next.text === ":" && isPunct(stream, start + 3, "=")) || (next.text === ":" && stream.toks[start + 2]?.kind === "identifier" && isPunct(stream, start + 3, "=")))
679
+ return token.text;
680
+ return null;
681
+ };
682
+ for (const start of layout.starts) {
683
+ if (stream.toks[start].column !== 1)
684
+ continue;
685
+ const name = assignmentTarget(start);
686
+ if (name !== null)
687
+ moduleNames.add(name);
688
+ }
689
+ for (const definition of definitions) {
690
+ if (definition.kind !== "class")
691
+ continue;
692
+ classNames.add(definition.name);
693
+ const indent = bodyIndent(stream, layout, definition.start);
694
+ const end = blockEnd(stream, layout, definition.start);
695
+ if (indent === null)
696
+ continue;
697
+ for (const start of layout.starts) {
698
+ if (start <= definition.defIndex || start > end || stream.toks[start].column !== indent)
699
+ continue;
700
+ const name = assignmentTarget(start);
701
+ if (name !== null)
702
+ classAttributes.add(name);
703
+ }
704
+ }
705
+ if (moduleNames.size === 0 && classAttributes.size === 0)
706
+ return;
707
+ const globals = new Set();
708
+ const parameters = new Set();
709
+ const locals = new Set();
710
+ for (let index = first; index <= last; index += 1) {
711
+ const token = stream.toks[index];
712
+ if (token.kind === "identifier" && token.text === "global" && layout.stmtStart[index] === index) {
713
+ const end = statementEnd(stream, layout, index);
714
+ for (let cursor = index + 1; cursor <= end; cursor += 1)
715
+ if (stream.toks[cursor].kind === "identifier")
716
+ globals.add(stream.toks[cursor].text);
717
+ }
718
+ if (token.kind === "identifier" && token.text === "def" && isPunct(stream, index + 2, "(")) {
719
+ const close = stream.match[index + 2];
720
+ for (let cursor = index + 3; cursor < close; cursor += 1) {
721
+ const parameter = stream.toks[cursor];
722
+ const before = stream.toks[cursor - 1];
723
+ if (parameter.kind === "identifier" && before.kind === "punctuator" && (before.text === "(" || before.text === "," || before.text === "*" || before.text === "**"))
724
+ parameters.add(parameter.text);
725
+ }
726
+ }
727
+ }
728
+ for (let index = first; index <= last; index += 1) {
729
+ const token = stream.toks[index];
730
+ if (token.kind !== "identifier")
731
+ continue;
732
+ if (layout.stmtStart[index] === index && assignmentTarget(index) === token.text && !globals.has(token.text))
733
+ locals.add(token.text);
734
+ if (isIdent(stream, index - 1, "for") || isIdent(stream, index - 1, "as"))
735
+ locals.add(token.text);
736
+ }
737
+ const qualifierNames = new Set([...classNames, "cls"]);
738
+ const record = (index, name) => {
739
+ out.sharedState.push({ line: lineOf(stream, index), name });
740
+ };
741
+ const isAssignment = (index) => {
742
+ const token = stream.toks[index];
743
+ return token !== undefined && token.kind === "punctuator" && ASSIGNMENT_TOKENS.has(token.text);
744
+ };
745
+ for (let index = first; index <= last; index += 1) {
746
+ const token = stream.toks[index];
747
+ if (token.kind !== "identifier")
748
+ continue;
749
+ const previous = stream.toks[index - 1];
750
+ const owner = previous !== undefined && previous.kind === "punctuator" && previous.text === "." ? stream.toks[index - 2] : undefined;
751
+ if (owner === undefined) {
752
+ if (!moduleNames.has(token.text) || parameters.has(token.text))
753
+ continue;
754
+ const chain = parseChain(stream, index, CHAIN_OPTIONS);
755
+ if (chain.segments[0].open >= 0)
756
+ continue;
757
+ const lastSegment = chain.segments[chain.segments.length - 1];
758
+ const bare = chain.end === index;
759
+ const assigned = isAssignment(chain.end + 1);
760
+ const mutatorCall = chain.segments.length > 1 && lastSegment.open >= 0 && MUTATORS.has(lastSegment.name);
761
+ if (bare && assigned) {
762
+ if (globals.has(token.text))
763
+ record(index, token.text);
764
+ }
765
+ else if ((mutatorCall || (!bare && assigned)) && !locals.has(token.text))
766
+ record(index, token.text);
767
+ continue;
768
+ }
769
+ if (owner.kind !== "identifier")
770
+ continue;
771
+ const viaSelf = owner.text === "self";
772
+ if (!viaSelf && !qualifierNames.has(owner.text))
773
+ continue;
774
+ if (!classAttributes.has(token.text))
775
+ continue;
776
+ const chain = parseChain(stream, index, CHAIN_OPTIONS);
777
+ if (chain.segments[0].open >= 0)
778
+ continue;
779
+ const lastSegment = chain.segments[chain.segments.length - 1];
780
+ const assigned = isAssignment(chain.end + 1);
781
+ const mutatorCall = chain.segments.length > 1 && lastSegment.open >= 0 && MUTATORS.has(lastSegment.name);
782
+ if (viaSelf ? mutatorCall || (chain.end > index && assigned) : mutatorCall || assigned)
783
+ record(index, token.text);
784
+ }
785
+ }
786
+ //# sourceMappingURL=python-family.js.map