Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Roblox
GitHub Repository: Roblox/luau
Path: blob/master/tests/Config.test.cpp
2723 views
1
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
2
#include "Luau/Config.h"
3
#include "Luau/Frontend.h"
4
#include "Luau/LinterConfig.h"
5
#include "Luau/LuauConfig.h"
6
7
#include "Fixture.h"
8
#include "ScopedFlags.h"
9
10
#include "doctest.h"
11
12
#include <iostream>
13
#include <optional>
14
#include <stdexcept>
15
#include <string>
16
#include <utility>
17
#include <vector>
18
19
using namespace Luau;
20
21
TEST_SUITE_BEGIN("ConfigTest");
22
23
TEST_CASE("language_mode")
24
{
25
Config config;
26
auto err = parseConfig(R"({"languageMode":"strict"})", config);
27
REQUIRE(!err);
28
29
CHECK_EQ(int(Luau::Mode::Strict), int(config.mode));
30
}
31
32
TEST_CASE("disable_a_lint_rule")
33
{
34
Config config;
35
auto err = parseConfig(
36
R"(
37
{"lint": {
38
"UnknownGlobal": false,
39
}}
40
)",
41
config
42
);
43
REQUIRE(!err);
44
45
CHECK(!config.enabledLint.isEnabled(LintWarning::Code_UnknownGlobal));
46
CHECK(config.enabledLint.isEnabled(LintWarning::Code_DeprecatedGlobal));
47
}
48
49
TEST_CASE("report_a_syntax_error")
50
{
51
Config config;
52
auto err = parseConfig(
53
R"(
54
{"lint": {
55
"UnknownGlobal": "oops"
56
}}
57
)",
58
config
59
);
60
61
REQUIRE(err);
62
CHECK_EQ("In key UnknownGlobal: Bad setting 'oops'. Valid options are true and false", *err);
63
}
64
65
TEST_CASE("noinfer_is_still_allowed")
66
{
67
Config config;
68
69
ConfigOptions opts;
70
opts.compat = true;
71
72
auto err = parseConfig(R"( {"language": {"mode": "noinfer"}} )", config, opts);
73
REQUIRE(!err);
74
75
CHECK_EQ(int(Luau::Mode::NoCheck), int(config.mode));
76
}
77
78
TEST_CASE("lint_warnings_are_ordered")
79
{
80
Config root;
81
auto err = parseConfig(R"({"lint": {"*": true, "LocalShadow": false}})", root);
82
REQUIRE(!err);
83
84
Config foo = root;
85
err = parseConfig(R"({"lint": {"LocalShadow": true, "*": false}})", foo);
86
REQUIRE(!err);
87
88
CHECK(!root.enabledLint.isEnabled(LintWarning::Code_LocalShadow));
89
CHECK(root.enabledLint.isEnabled(LintWarning::Code_LocalUnused));
90
91
CHECK(!foo.enabledLint.isEnabled(LintWarning::Code_LocalShadow));
92
}
93
94
TEST_CASE("comments")
95
{
96
Config config;
97
auto err = parseConfig(
98
R"(
99
{
100
"lint": {
101
"*": false,
102
"SameLineStatement": true,
103
"FunctionUnused": true,
104
//"LocalShadow": true,
105
//"LocalUnused": true,
106
"ImportUnused": true,
107
"ImplicitReturn": true
108
}
109
}
110
)",
111
config
112
);
113
REQUIRE(!err);
114
115
CHECK(!config.enabledLint.isEnabled(LintWarning::Code_LocalShadow));
116
CHECK(config.enabledLint.isEnabled(LintWarning::Code_ImportUnused));
117
}
118
119
TEST_CASE("issue_severity")
120
{
121
Config config;
122
CHECK(!config.lintErrors);
123
CHECK(config.typeErrors);
124
125
auto err = parseConfig(
126
R"(
127
{
128
"lintErrors": true,
129
"typeErrors": false,
130
}
131
)",
132
config
133
);
134
REQUIRE(!err);
135
136
CHECK(config.lintErrors);
137
CHECK(!config.typeErrors);
138
}
139
140
TEST_CASE("extra_globals")
141
{
142
Config config;
143
auto err = parseConfig(
144
R"(
145
{
146
"globals": ["it", "__DEV__"],
147
}
148
)",
149
config
150
);
151
REQUIRE(!err);
152
153
REQUIRE(config.globals.size() == 2);
154
CHECK(config.globals[0] == "it");
155
CHECK(config.globals[1] == "__DEV__");
156
}
157
158
TEST_CASE("lint_rules_compat")
159
{
160
Config config;
161
162
ConfigOptions opts;
163
opts.compat = true;
164
165
auto err = parseConfig(
166
R"(
167
{"lint": {
168
"SameLineStatement": "enabled",
169
"FunctionUnused": "disabled",
170
"ImportUnused": "fatal",
171
}}
172
)",
173
config,
174
opts
175
);
176
REQUIRE(!err);
177
178
CHECK(config.enabledLint.isEnabled(LintWarning::Code_SameLineStatement));
179
CHECK(!config.enabledLint.isEnabled(LintWarning::Code_FunctionUnused));
180
CHECK(config.enabledLint.isEnabled(LintWarning::Code_ImportUnused));
181
CHECK(config.fatalLint.isEnabled(LintWarning::Code_ImportUnused));
182
}
183
184
TEST_SUITE_END();
185
186
TEST_SUITE_BEGIN("ConfigLuauTest");
187
188
TEST_CASE("extract_configuration")
189
{
190
std::string source = R"(
191
local config = {}
192
config.luau = {}
193
194
config.luau.languagemode = "strict"
195
config.luau.lint = {
196
["*"] = true,
197
LocalUnused = false
198
}
199
config.luau.linterrors = true
200
config.luau.typeerrors = true
201
config.luau.globals = {"expect"}
202
config.luau.aliases = {
203
src = "./src"
204
}
205
206
return config
207
)";
208
209
std::string error;
210
std::optional<ConfigTable> configTable = extractConfig(source, InterruptCallbacks{}, &error);
211
REQUIRE(configTable);
212
213
CHECK(configTable->size() == 1);
214
REQUIRE(configTable->contains("luau"));
215
ConfigTable* luau = (*configTable)["luau"].get_if<ConfigTable>();
216
REQUIRE(luau);
217
CHECK(luau->size() == 6);
218
219
REQUIRE(luau->contains("languagemode"));
220
std::string* languageMode = (*luau)["languagemode"].get_if<std::string>();
221
REQUIRE(languageMode);
222
CHECK(*languageMode == "strict");
223
224
REQUIRE(luau->contains("lint"));
225
ConfigTable* lint = (*luau)["lint"].get_if<ConfigTable>();
226
REQUIRE(lint);
227
CHECK(lint->size() == 2);
228
REQUIRE(lint->contains("*"));
229
bool* all = (*lint)["*"].get_if<bool>();
230
REQUIRE(all);
231
CHECK(*all);
232
bool* localUnused = (*lint)["LocalUnused"].get_if<bool>();
233
REQUIRE(localUnused);
234
CHECK(!(*localUnused));
235
236
REQUIRE(luau->contains("linterrors"));
237
bool* lintErrors = (*luau)["linterrors"].get_if<bool>();
238
REQUIRE(lintErrors);
239
CHECK(*lintErrors);
240
241
REQUIRE(luau->contains("typeerrors"));
242
bool* typeErrors = (*luau)["typeerrors"].get_if<bool>();
243
REQUIRE(typeErrors);
244
CHECK(*typeErrors);
245
246
REQUIRE(luau->contains("globals"));
247
ConfigTable* globalsTable = (*luau)["globals"].get_if<ConfigTable>();
248
REQUIRE(globalsTable);
249
CHECK(globalsTable->size() == 1);
250
REQUIRE(globalsTable->contains(1));
251
std::string* global = (*globalsTable)[1].get_if<std::string>();
252
REQUIRE(global);
253
CHECK(*global == "expect");
254
255
REQUIRE(luau->contains("aliases"));
256
ConfigTable* aliases = (*luau)["aliases"].get_if<ConfigTable>();
257
REQUIRE(aliases);
258
CHECK(aliases->size() == 1);
259
REQUIRE(aliases->contains("src"));
260
std::string* alias = (*aliases)["src"].get_if<std::string>();
261
REQUIRE(alias);
262
CHECK(*alias == "./src");
263
}
264
265
TEST_CASE("extract_luau_configuration")
266
{
267
std::string source = R"(
268
local config = {}
269
config.luau = {}
270
271
config.luau.languagemode = "strict"
272
config.luau.lint = {
273
["*"] = true,
274
LocalUnused = false
275
}
276
config.luau.linterrors = true
277
config.luau.typeerrors = true
278
config.luau.globals = {"expect"}
279
config.luau.aliases = {
280
src = "./src"
281
}
282
283
return config
284
)";
285
286
ConfigOptions::AliasOptions aliasOptions;
287
aliasOptions.configLocation = "/some/path";
288
aliasOptions.overwriteAliases = true;
289
290
Config config;
291
std::optional<std::string> error = extractLuauConfig(source, config, std::move(aliasOptions), InterruptCallbacks{});
292
REQUIRE(!error);
293
294
CHECK_EQ(config.mode, Mode::Strict);
295
296
for (LintWarning::Code code = static_cast<LintWarning::Code>(0); code <= LintWarning::Code::Code__Count; code = LintWarning::Code(int(code) + 1))
297
{
298
if (code == LintWarning::Code_LocalUnused)
299
CHECK(!config.enabledLint.isEnabled(code));
300
else
301
CHECK(config.enabledLint.isEnabled(code));
302
}
303
304
CHECK_EQ(config.lintErrors, true);
305
CHECK_EQ(config.typeErrors, true);
306
307
CHECK(config.globals.size() == 1);
308
CHECK(config.globals[0] == "expect");
309
310
CHECK(config.aliases.size() == 1);
311
REQUIRE(config.aliases.contains("src"));
312
CHECK(config.aliases["src"].value == "./src");
313
}
314
315
TEST_CASE("yielded_configuration")
316
{
317
std::string source = R"(
318
coroutine.yield()
319
)";
320
321
std::string error;
322
std::optional<ConfigTable> configTable = extractConfig(source, InterruptCallbacks{}, &error);
323
REQUIRE(!configTable);
324
CHECK(error == "configuration execution cannot yield");
325
}
326
327
TEST_CASE("interrupt_execution" * doctest::timeout(2))
328
{
329
std::string source = R"(
330
while true do end
331
)";
332
333
std::string error;
334
std::optional<ConfigTable> configTable = extractConfig(
335
source,
336
{
337
nullptr,
338
[](lua_State* L, int gc)
339
{
340
throw std::runtime_error("interrupted");
341
},
342
},
343
&error
344
);
345
REQUIRE(!configTable);
346
CHECK(error.find("interrupted") != std::string_view::npos);
347
}
348
349
TEST_CASE("validate_return_value")
350
{
351
std::vector<std::pair<std::string, std::string>> testCases;
352
testCases.emplace_back("", "configuration must return exactly one value");
353
testCases.emplace_back("return {}, {}", "configuration must return exactly one value");
354
testCases.emplace_back("return 'a string'", "configuration did not return a table");
355
356
for (const auto& [source, expectedError] : testCases)
357
{
358
std::string error;
359
std::optional<ConfigTable> configTable = extractConfig(source, InterruptCallbacks{}, &error);
360
REQUIRE(!configTable);
361
CHECK(error == expectedError);
362
}
363
}
364
365
TEST_SUITE_END();
366
367