@@ -53,8 +53,8 @@ namespace sorbet::test {
53
53
using namespace std ;
54
54
55
55
bool update;
56
- vector< string> inputs ;
57
- string output ;
56
+ string inputFileOrDir ;
57
+ string outputFileOrDir ;
58
58
59
59
// Copied from pipeline_test_runner.cc
60
60
class CFGCollectorAndTyper { // TODO(varun): Copy this over to scip_test_runner.cc
@@ -252,19 +252,27 @@ string snapshot_path(string rb_path) {
252
252
rb_path.erase (rb_path.size () - 3 , 3 );
253
253
return rb_path + " .snapshot.rb" ;
254
254
}
255
+ struct TestSettings {
256
+ optional<string> gemMetadata;
257
+ UnorderedMap</* root-relative path*/ string, FormatOptions> formatOptions;
258
+ };
255
259
256
- void updateSnapshots (const scip::Index &index, FormatOptions options , const std::filesystem::path &outputDir) {
260
+ void updateSnapshots (const scip::Index &index, const TestSettings &settings , const std::filesystem::path &outputDir) {
257
261
for (auto &doc : index.documents ()) {
258
262
auto outputFilePath = snapshot_path (doc.relative_path ());
259
263
ofstream out (outputFilePath);
260
264
if (!out.is_open ()) {
261
265
FAIL (fmt::format (" failed to open snapshot output file at {}" , outputFilePath));
262
266
}
263
- formatSnapshot (doc, options, out);
267
+ auto it = settings.formatOptions .find (doc.relative_path ());
268
+ ENFORCE (it != settings.formatOptions .end (),
269
+ fmt::format (" missing path {} as key in formatOptions map" , doc.relative_path ()));
270
+ formatSnapshot (doc, it->second , out);
264
271
}
265
272
}
266
273
267
- void compareSnapshots (const scip::Index &index, FormatOptions options, const std::filesystem::path &snapshotDir) {
274
+ void compareSnapshots (const scip::Index &index, const TestSettings &settings,
275
+ const std::filesystem::path &snapshotDir) {
268
276
for (auto &doc : index.documents ()) {
269
277
auto filePath = snapshot_path (doc.relative_path ()); // TODO: Separate out folders!
270
278
ifstream inputStream (filePath);
@@ -275,7 +283,10 @@ void compareSnapshots(const scip::Index &index, FormatOptions options, const std
275
283
input << inputStream.rdbuf ();
276
284
277
285
ostringstream out;
278
- formatSnapshot (doc, options, out);
286
+ auto it = settings.formatOptions .find (doc.relative_path ());
287
+ ENFORCE (it != settings.formatOptions .end (),
288
+ fmt::format (" missing path {} as key in formatOptions map" , doc.relative_path ()));
289
+ formatSnapshot (doc, it->second , out);
279
290
auto result = out.str ();
280
291
281
292
CHECK_EQ_DIFF (input.str (), result,
@@ -304,17 +315,11 @@ pair<optional<string>, FormatOptions> readMagicComments(string_view path) {
304
315
return {gemMetadata, options};
305
316
}
306
317
307
- TEST_CASE (" SCIPTest" ) {
308
- // FIXME(varun): Add support for multifile tests.
309
- ENFORCE (inputs.size () == 1 );
310
- Expectations test = Expectations::getExpectations (inputs[0 ]);
311
-
312
- auto [gemMetadata, formatOptions] = readMagicComments (inputs[0 ]);
313
-
318
+ void test_one_gem (Expectations &test, const TestSettings &settings) {
314
319
vector<unique_ptr<core::Error>> errors;
315
- auto inputPath = test.folder + test.basename ;
316
320
317
- auto logger = spdlog::stderr_color_mt (" fixtures: " + inputPath);
321
+ auto logger =
322
+ spdlog::stderr_color_mt (" fixtures: " + (test.isFolderTest ? test.folder + test.basename : test.folder ));
318
323
auto errorCollector = make_shared<core::ErrorCollector>();
319
324
auto errorQueue = make_shared<core::ErrorQueue>(*logger, *logger, errorCollector);
320
325
core::GlobalState gs (errorQueue);
@@ -335,7 +340,7 @@ TEST_CASE("SCIPTest") {
335
340
using Provider = pipeline::semantic_extension::SemanticExtensionProvider;
336
341
337
342
auto indexFilePath = filesystem::temp_directory_path ();
338
- indexFilePath.append (test.basename + " .scip" ); // FIXME(varun): Update for folder tests with multiple files?
343
+ indexFilePath.append (test.basename + " .scip" );
339
344
340
345
auto providers = Provider::getProviders ();
341
346
ENFORCE (providers.size () == 1 );
@@ -344,10 +349,10 @@ TEST_CASE("SCIPTest") {
344
349
cxxopts::Options options{" scip-ruby-snapshot-test" };
345
350
scipProvider->injectOptions (options);
346
351
std::vector<const char *> argv = {" scip-ruby-snapshot-test" , " --index-file" , indexFilePath.c_str ()};
347
- if (gemMetadata.has_value ()) {
352
+ if (settings. gemMetadata .has_value ()) {
348
353
argv.push_back (" --gem-metadata" );
349
- ENFORCE (!gemMetadata.value ().empty ());
350
- argv.push_back (gemMetadata.value ().data ());
354
+ ENFORCE (!settings. gemMetadata .value ().empty ());
355
+ argv.push_back (settings. gemMetadata .value ().data ());
351
356
}
352
357
argv.push_back (nullptr );
353
358
auto parseResult = options.parse (argv.size () - 1 , argv.data ());
@@ -404,14 +409,60 @@ TEST_CASE("SCIPTest") {
404
409
index.ParseFromIstream (&indexFile);
405
410
406
411
if (update) {
407
- updateSnapshots (index, formatOptions , test.folder );
412
+ updateSnapshots (index, settings , test.folder );
408
413
} else {
409
- compareSnapshots (index, formatOptions , test.folder );
414
+ compareSnapshots (index, settings , test.folder );
410
415
}
411
416
412
417
MESSAGE (" PASS" );
413
418
}
414
419
420
+ // There are several different kinds of tests that we are potentially
421
+ // interested in here:
422
+ //
423
+ // 1. Testing single files for language features.
424
+ // 2. Testing multiple files within the same Gem to see if/when defs/refs work.
425
+ // 3. Testing usage of the --gem-metadata flag, because not all Ruby Gems
426
+ // contain a .gemspec file (e.g. if it's an application), which means
427
+ // that we do not have a reliable way of inferring the name/version.
428
+ // 4. Testing usage of Gem-internal RBI files.
429
+ // 5. Testing usage of RBI files coming from external gems. This uses
430
+ // a standardized project structure with a sorbet/ folder.
431
+ // (See https://sorbet.org/docs/rbi)
432
+ // 6. Testing usage of multiple local Gems defined within the same repo.
433
+ //
434
+ // Right now, the testing is set up to handle use cases 1, 2, 3, and 4.
435
+ // It should hopefully be straightforward to extend this support for 5.
436
+ //
437
+ // For 6, I'd like to gather more information about project setups first
438
+ // (the directory layout, any expectations of ordering, how Sorbet is run)
439
+ // before setting up testing infrastructure and explicit feature support.
440
+ TEST_CASE (" SCIPTest" ) {
441
+ Expectations test = Expectations::getExpectations (inputFileOrDir);
442
+
443
+ TestSettings settings;
444
+ settings.gemMetadata = nullopt;
445
+ if (test.isFolderTest ) {
446
+ auto argsFilePath = test.folder + " scip-ruby-args.rb" ;
447
+ if (FileOps::exists (argsFilePath)) {
448
+ settings.gemMetadata = readMagicComments (argsFilePath).first ;
449
+ }
450
+ ENFORCE (test.sourceFiles .size () > 0 );
451
+ for (auto &sourceFile : test.sourceFiles ) {
452
+ auto path = test.folder + sourceFile;
453
+ auto [_, inserted] = settings.formatOptions .insert ({path, readMagicComments (path).second });
454
+ ENFORCE (inserted, " duplicate source file in Expectations struct?" );
455
+ }
456
+ } else {
457
+ ENFORCE (test.sourceFiles .size () == 1 );
458
+ auto path = test.folder + test.sourceFiles [0 ];
459
+ auto &options = settings.formatOptions [path];
460
+ std::tie (settings.gemMetadata , options) = readMagicComments (path);
461
+ }
462
+
463
+ test_one_gem (test, settings);
464
+ }
465
+
415
466
} // namespace sorbet::test
416
467
417
468
// TODO: Use string.ends_with with C++20.
@@ -435,17 +486,16 @@ int main(int argc, char *argv[]) {
435
486
" path" )(" update-snapshots" , " " );
436
487
// ("--update-snapshots", "should the snapshot files be overwritten if there are changes");
437
488
auto res = options.parse (argc, argv);
489
+ auto unmatched = res.unmatched ();
438
490
439
- for (auto &input : res.unmatched ()) {
440
- if (!ends_with (input, " .rb" )) {
441
- printf (" error: input files must have .rb extension" );
442
- return 1 ;
443
- }
491
+ if (unmatched.size () != 1 ) {
492
+ std::fprintf (stderr, " error: runner expected single input file with .rb extension or folder" );
493
+ return 1 ;
444
494
}
445
495
446
496
sorbet::test::update = res.count (" update-snapshots" ) > 0 ;
447
- sorbet::test::inputs = res. unmatched () ;
448
- sorbet::test::output = res[" output" ].as <std::string>();
497
+ sorbet::test::inputFileOrDir = unmatched[ 0 ] ;
498
+ sorbet::test::outputFileOrDir = res[" output" ].as <std::string>();
449
499
450
500
doctest::Context context (argc, argv);
451
501
return context.run ();
0 commit comments