rapydscript-ns 0.8.2 → 0.8.4
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.
- package/.agignore +1 -1
- package/.github/workflows/ci.yml +38 -38
- package/=template.pyj +5 -5
- package/CHANGELOG.md +39 -0
- package/HACKING.md +103 -103
- package/LICENSE +24 -24
- package/PYTHON_DIFFERENCES_REPORT.md +291 -0
- package/PYTHON_FEATURE_COVERAGE.md +106 -15
- package/README.md +831 -52
- package/TODO.md +4 -286
- package/add-toc-to-readme +2 -2
- package/bin/export +75 -75
- package/bin/rapydscript +70 -70
- package/bin/web-repl-export +102 -102
- package/build +2 -2
- package/language-service/index.js +4623 -0
- package/language-service/language-service.d.ts +40 -0
- package/package.json +9 -7
- package/publish.py +37 -37
- package/release/baselib-plain-pretty.js +2006 -229
- package/release/baselib-plain-ugly.js +70 -3
- package/release/compiler.js +11554 -3870
- package/release/signatures.json +31 -29
- package/session.vim +4 -4
- package/setup.cfg +2 -2
- package/src/ast.pyj +93 -1
- package/src/baselib-builtins.pyj +99 -2
- package/src/baselib-containers.pyj +107 -4
- package/src/baselib-errors.pyj +44 -0
- package/src/baselib-internal.pyj +124 -5
- package/src/baselib-itertools.pyj +97 -97
- package/src/baselib-str.pyj +32 -1
- package/src/compiler.pyj +36 -36
- package/src/errors.pyj +30 -30
- package/src/lib/aes.pyj +646 -646
- package/src/lib/collections.pyj +1 -1
- package/src/lib/copy.pyj +120 -0
- package/src/lib/elementmaker.pyj +83 -83
- package/src/lib/encodings.pyj +126 -126
- package/src/lib/gettext.pyj +569 -569
- package/src/lib/itertools.pyj +580 -580
- package/src/lib/math.pyj +193 -193
- package/src/lib/numpy.pyj +10 -10
- package/src/lib/operator.pyj +11 -11
- package/src/lib/pythonize.pyj +20 -20
- package/src/lib/random.pyj +118 -118
- package/src/lib/re.pyj +470 -470
- package/src/lib/react.pyj +74 -0
- package/src/lib/traceback.pyj +63 -63
- package/src/lib/uuid.pyj +77 -77
- package/src/monaco-language-service/analyzer.js +131 -9
- package/src/monaco-language-service/builtins.js +17 -2
- package/src/monaco-language-service/completions.js +170 -1
- package/src/monaco-language-service/diagnostics.js +25 -3
- package/src/monaco-language-service/dts.js +550 -550
- package/src/monaco-language-service/index.js +17 -0
- package/src/monaco-language-service/scope.js +3 -0
- package/src/output/classes.pyj +128 -11
- package/src/output/codegen.pyj +17 -3
- package/src/output/comments.pyj +45 -45
- package/src/output/exceptions.pyj +201 -105
- package/src/output/functions.pyj +13 -16
- package/src/output/jsx.pyj +164 -0
- package/src/output/literals.pyj +28 -2
- package/src/output/loops.pyj +0 -9
- package/src/output/modules.pyj +2 -5
- package/src/output/operators.pyj +22 -2
- package/src/output/statements.pyj +2 -2
- package/src/output/stream.pyj +1 -13
- package/src/output/treeshake.pyj +182 -182
- package/src/output/utils.pyj +72 -72
- package/src/parse.pyj +434 -114
- package/src/string_interpolation.pyj +72 -72
- package/src/tokenizer.pyj +29 -0
- package/src/unicode_aliases.pyj +576 -576
- package/src/utils.pyj +192 -192
- package/test/_import_one.pyj +37 -37
- package/test/_import_two/__init__.pyj +11 -11
- package/test/_import_two/level2/deep.pyj +4 -4
- package/test/_import_two/other.pyj +6 -6
- package/test/_import_two/sub.pyj +13 -13
- package/test/aes_vectors.pyj +421 -421
- package/test/annotations.pyj +80 -80
- package/test/baselib.pyj +4 -4
- package/test/classes.pyj +56 -17
- package/test/collections.pyj +5 -5
- package/test/decorators.pyj +77 -77
- package/test/docstrings.pyj +39 -39
- package/test/elementmaker_test.pyj +45 -45
- package/test/functions.pyj +151 -151
- package/test/generators.pyj +41 -41
- package/test/generic.pyj +370 -370
- package/test/imports.pyj +72 -72
- package/test/internationalization.pyj +73 -73
- package/test/lint.pyj +164 -164
- package/test/loops.pyj +85 -85
- package/test/numpy.pyj +734 -734
- package/test/omit_function_metadata.pyj +20 -20
- package/test/python_compat.pyj +326 -0
- package/test/python_features.pyj +129 -29
- package/test/regexp.pyj +55 -55
- package/test/repl.pyj +121 -121
- package/test/scoped_flags.pyj +76 -76
- package/test/slice.pyj +105 -0
- package/test/str.pyj +25 -0
- package/test/unit/fixtures/fibonacci_expected.js +1 -1
- package/test/unit/index.js +2296 -71
- package/test/unit/language-service-builtins.js +70 -0
- package/test/unit/language-service-bundle.js +5 -5
- package/test/unit/language-service-completions.js +180 -0
- package/test/unit/language-service-dts.js +543 -543
- package/test/unit/language-service-hover.js +455 -455
- package/test/unit/language-service-index.js +350 -0
- package/test/unit/language-service-scope.js +255 -0
- package/test/unit/language-service.js +625 -4
- package/test/unit/run-language-service.js +1 -0
- package/test/unit/web-repl.js +437 -0
- package/tools/build-language-service.js +2 -2
- package/tools/cli.js +547 -547
- package/tools/compile.js +219 -219
- package/tools/compiler.js +0 -24
- package/tools/completer.js +131 -131
- package/tools/embedded_compiler.js +251 -251
- package/tools/export.js +3 -37
- package/tools/gettext.js +185 -185
- package/tools/ini.js +65 -65
- package/tools/msgfmt.js +187 -187
- package/tools/repl.js +223 -223
- package/tools/test.js +118 -118
- package/tools/utils.js +128 -128
- package/tools/web_repl.js +95 -95
- package/try +41 -41
- package/web-repl/env.js +196 -74
- package/web-repl/index.html +163 -163
- package/web-repl/main.js +252 -254
- package/web-repl/prism.css +139 -139
- package/web-repl/prism.js +113 -113
- package/web-repl/rapydscript.js +227 -139
- package/web-repl/sha1.js +25 -25
- package/hack_demo.pyj +0 -112
- package/web-repl/language-service.js +0 -4187
|
@@ -734,6 +734,76 @@ function make_tests(BuiltinsRegistry, BuiltinInfo, HoverEngine, SignatureHelpEng
|
|
|
734
734
|
},
|
|
735
735
|
},
|
|
736
736
|
|
|
737
|
+
// ── slice builtin ─────────────────────────────────────────────────
|
|
738
|
+
|
|
739
|
+
{
|
|
740
|
+
name: "slice_registered_as_class",
|
|
741
|
+
description: "slice is registered in BuiltinsRegistry with kind='class'",
|
|
742
|
+
run: function () {
|
|
743
|
+
var reg = new BuiltinsRegistry();
|
|
744
|
+
var bi = reg.get('slice');
|
|
745
|
+
assert.ok(bi, 'slice should be registered');
|
|
746
|
+
assert.strictEqual(bi.name, 'slice');
|
|
747
|
+
assert.strictEqual(bi.kind, 'class');
|
|
748
|
+
assert.ok(Array.isArray(bi.params));
|
|
749
|
+
assert.ok(bi.doc && bi.doc.length > 0, 'slice should have a docstring');
|
|
750
|
+
},
|
|
751
|
+
},
|
|
752
|
+
|
|
753
|
+
{
|
|
754
|
+
name: "slice_hover_markdown",
|
|
755
|
+
description: "slice hover markdown includes signature and doc",
|
|
756
|
+
run: function () {
|
|
757
|
+
var reg = new BuiltinsRegistry();
|
|
758
|
+
var md = reg.getHoverMarkdown('slice');
|
|
759
|
+
assert.ok(md, 'slice should have hover markdown');
|
|
760
|
+
assert.ok(md.indexOf('slice') !== -1, 'hover should mention slice');
|
|
761
|
+
assert.ok(md.indexOf('start_or_stop') !== -1, 'hover should mention start_or_stop param');
|
|
762
|
+
},
|
|
763
|
+
},
|
|
764
|
+
|
|
765
|
+
{
|
|
766
|
+
name: "slice_in_completions",
|
|
767
|
+
description: "slice appears in completion list as a built-in class",
|
|
768
|
+
run: function () {
|
|
769
|
+
var reg = new BuiltinsRegistry();
|
|
770
|
+
var analyzer = new SourceAnalyzer(RS);
|
|
771
|
+
var engine = new CompletionEngine(analyzer, {
|
|
772
|
+
virtualFiles: {},
|
|
773
|
+
builtinNames: ['slice'],
|
|
774
|
+
builtinsRegistry: reg,
|
|
775
|
+
});
|
|
776
|
+
var list = engine.getCompletions(null, pos(1, 6), 'slice', MockKind);
|
|
777
|
+
var item = list.suggestions.find(function (s) { return s.label === 'slice'; });
|
|
778
|
+
assert.ok(item, 'slice should appear in completions');
|
|
779
|
+
assert.strictEqual(item.kind, MockKind.Class, 'slice should have Class kind');
|
|
780
|
+
},
|
|
781
|
+
},
|
|
782
|
+
|
|
783
|
+
{
|
|
784
|
+
name: "slice_signature_help",
|
|
785
|
+
description: "slice signature help returns start_or_stop, stop, step params",
|
|
786
|
+
run: function () {
|
|
787
|
+
var reg = new BuiltinsRegistry();
|
|
788
|
+
var info = reg.getSignatureInfo('slice');
|
|
789
|
+
assert.ok(info, 'slice should have signature info');
|
|
790
|
+
assert.ok(info.label.indexOf('slice') !== -1, 'label should contain slice');
|
|
791
|
+
assert.ok(info.params.length >= 1, 'should have at least 1 param');
|
|
792
|
+
assert.strictEqual(info.params[0].label, 'start_or_stop: int');
|
|
793
|
+
},
|
|
794
|
+
},
|
|
795
|
+
|
|
796
|
+
{
|
|
797
|
+
name: "slice_in_getNames",
|
|
798
|
+
description: "slice appears in BuiltinsRegistry.getNames()",
|
|
799
|
+
run: function () {
|
|
800
|
+
var reg = new BuiltinsRegistry();
|
|
801
|
+
var names = reg.getNames();
|
|
802
|
+
assert.ok(names.indexOf('slice') !== -1,
|
|
803
|
+
'slice should be in getNames(), got: ' + names.join(', '));
|
|
804
|
+
},
|
|
805
|
+
},
|
|
806
|
+
|
|
737
807
|
];
|
|
738
808
|
|
|
739
809
|
return TESTS;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* test/unit/language-service-bundle.js
|
|
3
3
|
*
|
|
4
|
-
* Verifies that the built
|
|
4
|
+
* Verifies that the built language-service/index.js can be loaded as an
|
|
5
5
|
* ES module without errors (e.g. duplicate top-level declarations).
|
|
6
6
|
*
|
|
7
7
|
* Usage:
|
|
@@ -15,11 +15,11 @@ var fs = require("fs");
|
|
|
15
15
|
var utils = require("../../tools/utils");
|
|
16
16
|
var colored = utils.safe_colored;
|
|
17
17
|
|
|
18
|
-
var bundle_path = path.join(__dirname, "../../
|
|
18
|
+
var bundle_path = path.join(__dirname, "../../language-service/index.js");
|
|
19
19
|
|
|
20
20
|
if (!fs.existsSync(bundle_path)) {
|
|
21
21
|
console.error(colored("SKIP language_service_bundle_loads", "yellow") +
|
|
22
|
-
" –
|
|
22
|
+
" – language-service/index.js not found; run: npm run build:ls");
|
|
23
23
|
process.exit(0);
|
|
24
24
|
}
|
|
25
25
|
|
|
@@ -41,7 +41,7 @@ import(bundle_url).then(function (mod) {
|
|
|
41
41
|
|
|
42
42
|
// Test 1: module loads without parse/runtime errors (implicit — we got here)
|
|
43
43
|
pass("language_service_bundle_loads",
|
|
44
|
-
"
|
|
44
|
+
"language-service/index.js loads as an ES module without errors");
|
|
45
45
|
|
|
46
46
|
// Test 2: registerRapydScript is exported
|
|
47
47
|
try {
|
|
@@ -75,7 +75,7 @@ import(bundle_url).then(function (mod) {
|
|
|
75
75
|
|
|
76
76
|
}).catch(function (e) {
|
|
77
77
|
console.log(colored("FAIL language_service_bundle_loads", "red") +
|
|
78
|
-
" \u2013
|
|
78
|
+
" \u2013 language-service/index.js failed to load as an ES module");
|
|
79
79
|
console.log(" " + (e.message || String(e)));
|
|
80
80
|
console.log("");
|
|
81
81
|
console.log(colored("1 test(s) failed.", "red"));
|
|
@@ -1097,6 +1097,186 @@ function make_tests(CompletionEngine, detect_context, SourceAnalyzer, DtsRegistr
|
|
|
1097
1097
|
},
|
|
1098
1098
|
},
|
|
1099
1099
|
|
|
1100
|
+
// ── Return-type inference ─────────────────────────────────────────
|
|
1101
|
+
|
|
1102
|
+
{
|
|
1103
|
+
name: "return_type_local_fn_list_annotation",
|
|
1104
|
+
description: "def fn() -> [str]: — call result completions include list members",
|
|
1105
|
+
run: function () {
|
|
1106
|
+
var builtins_mod = require(path.join(__dirname, '../../src/monaco-language-service/builtins.js'));
|
|
1107
|
+
var engine = make_engine({}, [], null, builtins_mod);
|
|
1108
|
+
var analyzer = new SourceAnalyzer(RS);
|
|
1109
|
+
var scopeMap = analyzer.analyze([
|
|
1110
|
+
"def getAllServers() -> [str]:",
|
|
1111
|
+
" return []",
|
|
1112
|
+
"pass",
|
|
1113
|
+
].join("\n"), {});
|
|
1114
|
+
|
|
1115
|
+
// fn().l — call_result context
|
|
1116
|
+
var list = engine.getCompletions(scopeMap, pos(3, 18), "getAllServers().", MockKind);
|
|
1117
|
+
assert_has(list, 'length', 'list.length via call_result return type');
|
|
1118
|
+
assert_has(list, 'append', 'list.append via call_result return type');
|
|
1119
|
+
},
|
|
1120
|
+
},
|
|
1121
|
+
|
|
1122
|
+
{
|
|
1123
|
+
name: "return_type_local_fn_dot_var",
|
|
1124
|
+
description: "x = fn() where fn() -> [str] — x. completions include list members",
|
|
1125
|
+
run: function () {
|
|
1126
|
+
var builtins_mod = require(path.join(__dirname, '../../src/monaco-language-service/builtins.js'));
|
|
1127
|
+
var engine = make_engine({}, [], null, builtins_mod);
|
|
1128
|
+
var analyzer = new SourceAnalyzer(RS);
|
|
1129
|
+
var scopeMap = analyzer.analyze([
|
|
1130
|
+
"def getAllServers() -> [str]:",
|
|
1131
|
+
" return []",
|
|
1132
|
+
"servers = getAllServers()",
|
|
1133
|
+
"pass",
|
|
1134
|
+
].join("\n"), {});
|
|
1135
|
+
|
|
1136
|
+
var list = engine.getCompletions(scopeMap, pos(4, 9), "servers.", MockKind);
|
|
1137
|
+
assert_has(list, 'length', 'list.length on var assigned from annotated fn');
|
|
1138
|
+
assert_has(list, 'append', 'list.append on var assigned from annotated fn');
|
|
1139
|
+
},
|
|
1140
|
+
},
|
|
1141
|
+
|
|
1142
|
+
{
|
|
1143
|
+
name: "return_type_imported_fn_call_result",
|
|
1144
|
+
description: "getAllServers().l — imported fn with -> [str] annotation suggests list members",
|
|
1145
|
+
run: function () {
|
|
1146
|
+
var builtins_mod = require(path.join(__dirname, '../../src/monaco-language-service/builtins.js'));
|
|
1147
|
+
var servers_src = [
|
|
1148
|
+
"def getAllServers() -> [str]:",
|
|
1149
|
+
" return []",
|
|
1150
|
+
].join("\n");
|
|
1151
|
+
var engine = make_engine({ servers: servers_src }, [], null, builtins_mod);
|
|
1152
|
+
var analyzer = new SourceAnalyzer(RS);
|
|
1153
|
+
var scopeMap = analyzer.analyze([
|
|
1154
|
+
"from servers import getAllServers",
|
|
1155
|
+
"pass",
|
|
1156
|
+
].join("\n"), {});
|
|
1157
|
+
|
|
1158
|
+
// getAllServers().l — call_result context
|
|
1159
|
+
var list = engine.getCompletions(scopeMap, pos(2, 18), "getAllServers().", MockKind);
|
|
1160
|
+
assert_has(list, 'length', 'list.length via cross-file return type (call_result)');
|
|
1161
|
+
assert_has(list, 'append', 'list.append via cross-file return type (call_result)');
|
|
1162
|
+
},
|
|
1163
|
+
},
|
|
1164
|
+
|
|
1165
|
+
{
|
|
1166
|
+
name: "return_type_imported_fn_dot_var",
|
|
1167
|
+
description: "x = getAllServers() from import — x. includes list members",
|
|
1168
|
+
run: function () {
|
|
1169
|
+
var builtins_mod = require(path.join(__dirname, '../../src/monaco-language-service/builtins.js'));
|
|
1170
|
+
var servers_src = [
|
|
1171
|
+
"def getAllServers() -> [str]:",
|
|
1172
|
+
" return []",
|
|
1173
|
+
].join("\n");
|
|
1174
|
+
var engine = make_engine({ servers: servers_src }, [], null, builtins_mod);
|
|
1175
|
+
var analyzer = new SourceAnalyzer(RS);
|
|
1176
|
+
var scopeMap = analyzer.analyze([
|
|
1177
|
+
"from servers import getAllServers",
|
|
1178
|
+
"servers = getAllServers()",
|
|
1179
|
+
"pass",
|
|
1180
|
+
].join("\n"), {});
|
|
1181
|
+
|
|
1182
|
+
var list = engine.getCompletions(scopeMap, pos(3, 10), "servers.", MockKind);
|
|
1183
|
+
assert_has(list, 'length', 'list.length on var from cross-file fn');
|
|
1184
|
+
assert_has(list, 'append', 'list.append on var from cross-file fn');
|
|
1185
|
+
},
|
|
1186
|
+
},
|
|
1187
|
+
|
|
1188
|
+
// ── Inferred return type for imported functions (no annotation) ───────
|
|
1189
|
+
|
|
1190
|
+
{
|
|
1191
|
+
name: "return_type_imported_fn_inferred_call_result",
|
|
1192
|
+
description: "imported fn with no annotation, return [] — call result completions include list members",
|
|
1193
|
+
run: function () {
|
|
1194
|
+
var builtins_mod = require(path.join(__dirname, '../../src/monaco-language-service/builtins.js'));
|
|
1195
|
+
var servers_src = [
|
|
1196
|
+
"def getAllServers():",
|
|
1197
|
+
" return []",
|
|
1198
|
+
].join("\n");
|
|
1199
|
+
var engine = make_engine({ servers: servers_src }, [], null, builtins_mod);
|
|
1200
|
+
var analyzer = new SourceAnalyzer(RS);
|
|
1201
|
+
var scopeMap = analyzer.analyze([
|
|
1202
|
+
"from servers import getAllServers",
|
|
1203
|
+
"pass",
|
|
1204
|
+
].join("\n"), {});
|
|
1205
|
+
|
|
1206
|
+
var list = engine.getCompletions(scopeMap, pos(2, 18), "getAllServers().", MockKind);
|
|
1207
|
+
assert_has(list, 'length', 'list.length via inferred cross-file return type (call_result)');
|
|
1208
|
+
assert_has(list, 'append', 'list.append via inferred cross-file return type (call_result)');
|
|
1209
|
+
},
|
|
1210
|
+
},
|
|
1211
|
+
|
|
1212
|
+
{
|
|
1213
|
+
name: "return_type_imported_fn_inferred_dot_var",
|
|
1214
|
+
description: "x = fn() from import, fn returns [] — x. includes list members",
|
|
1215
|
+
run: function () {
|
|
1216
|
+
var builtins_mod = require(path.join(__dirname, '../../src/monaco-language-service/builtins.js'));
|
|
1217
|
+
var servers_src = [
|
|
1218
|
+
"def getAllServers():",
|
|
1219
|
+
" return []",
|
|
1220
|
+
].join("\n");
|
|
1221
|
+
var engine = make_engine({ servers: servers_src }, [], null, builtins_mod);
|
|
1222
|
+
var analyzer = new SourceAnalyzer(RS);
|
|
1223
|
+
var scopeMap = analyzer.analyze([
|
|
1224
|
+
"from servers import getAllServers",
|
|
1225
|
+
"servers = getAllServers()",
|
|
1226
|
+
"pass",
|
|
1227
|
+
].join("\n"), {});
|
|
1228
|
+
|
|
1229
|
+
var list = engine.getCompletions(scopeMap, pos(3, 10), "servers.", MockKind);
|
|
1230
|
+
assert_has(list, 'length', 'list.length on var from inferred cross-file fn');
|
|
1231
|
+
assert_has(list, 'append', 'list.append on var from inferred cross-file fn');
|
|
1232
|
+
},
|
|
1233
|
+
},
|
|
1234
|
+
|
|
1235
|
+
{
|
|
1236
|
+
name: "return_type_imported_fn_inferred_dict",
|
|
1237
|
+
description: "imported fn with return {} — call result completions include dict members",
|
|
1238
|
+
run: function () {
|
|
1239
|
+
var builtins_mod = require(path.join(__dirname, '../../src/monaco-language-service/builtins.js'));
|
|
1240
|
+
var utils_src = [
|
|
1241
|
+
"def get_config():",
|
|
1242
|
+
" return {}",
|
|
1243
|
+
].join("\n");
|
|
1244
|
+
var engine = make_engine({ utils: utils_src }, [], null, builtins_mod);
|
|
1245
|
+
var analyzer = new SourceAnalyzer(RS);
|
|
1246
|
+
var scopeMap = analyzer.analyze([
|
|
1247
|
+
"from utils import get_config",
|
|
1248
|
+
"pass",
|
|
1249
|
+
].join("\n"), {});
|
|
1250
|
+
|
|
1251
|
+
var list = engine.getCompletions(scopeMap, pos(2, 14), "get_config().", MockKind);
|
|
1252
|
+
assert_has(list, 'keys', 'dict.keys via inferred cross-file return type');
|
|
1253
|
+
assert_has(list, 'values', 'dict.values via inferred cross-file return type');
|
|
1254
|
+
},
|
|
1255
|
+
},
|
|
1256
|
+
|
|
1257
|
+
{
|
|
1258
|
+
name: "return_type_imported_fn_inferred_consistent_branches",
|
|
1259
|
+
description: "imported fn with multiple return [] branches — type still inferred as list",
|
|
1260
|
+
run: function () {
|
|
1261
|
+
var builtins_mod = require(path.join(__dirname, '../../src/monaco-language-service/builtins.js'));
|
|
1262
|
+
var utils_src = [
|
|
1263
|
+
"def get_items(flag):",
|
|
1264
|
+
" if flag:",
|
|
1265
|
+
" return []",
|
|
1266
|
+
" return []",
|
|
1267
|
+
].join("\n");
|
|
1268
|
+
var engine = make_engine({ utils: utils_src }, [], null, builtins_mod);
|
|
1269
|
+
var analyzer = new SourceAnalyzer(RS);
|
|
1270
|
+
var scopeMap = analyzer.analyze([
|
|
1271
|
+
"from utils import get_items",
|
|
1272
|
+
"pass",
|
|
1273
|
+
].join("\n"), {});
|
|
1274
|
+
|
|
1275
|
+
var list = engine.getCompletions(scopeMap, pos(2, 12), "get_items().", MockKind);
|
|
1276
|
+
assert_has(list, 'append', 'list.append via inferred multi-branch import');
|
|
1277
|
+
},
|
|
1278
|
+
},
|
|
1279
|
+
|
|
1100
1280
|
];
|
|
1101
1281
|
|
|
1102
1282
|
return TESTS;
|