diff --git a/exe/rbs b/exe/rbs index 5bc440ec3e..3366f324fc 100755 --- a/exe/rbs +++ b/exe/rbs @@ -4,4 +4,4 @@ $LOAD_PATH << File.join(__dir__, "../lib") require "rbs" require "rbs/cli" -RBS::CLI.new(stdout: STDOUT, stderr: STDERR).run(ARGV.dup) +exit RBS::CLI.new(stdout: STDOUT, stderr: STDERR).run(ARGV.dup) diff --git a/lib/rbs/cli.rb b/lib/rbs/cli.rb index 2706d86792..c59fe35ea0 100644 --- a/lib/rbs/cli.rb +++ b/lib/rbs/cli.rb @@ -136,10 +136,12 @@ def run(args) case command when :version stdout.puts opts.ver + 0 when *COMMANDS __send__ :"run_#{command}", args, options else stdout.puts opts.help + 0 end end @@ -191,6 +193,8 @@ def run_ast(args, options) stdout.print JSON.generate(decls) stdout.flush + + 0 end def run_list(args, options) @@ -254,6 +258,8 @@ def run_list(args, options) stdout.puts "#{name} (interface)" end end + + 0 end def run_ancestors(args, options) @@ -279,7 +285,7 @@ def run_ancestors(args, options) unless args.size == 1 stdout.puts "Expected one argument." - return + return 1 end loader = options.loader() @@ -317,6 +323,8 @@ def run_ancestors(args, options) else stdout.puts "Cannot find class: #{type_name}" end + + 0 end def run_methods(args, options) @@ -344,7 +352,7 @@ def run_methods(args, options) unless args.size == 1 stdout.puts "Expected one argument." - return + return 1 end loader = options.loader() @@ -373,6 +381,8 @@ def run_methods(args, options) else stdout.puts "Cannot find class: #{type_name}" end + + 0 end def run_method(args, options) @@ -398,7 +408,7 @@ def run_method(args, options) unless args.size == 2 stdout.puts "Expected two arguments, but given #{args.size}." - return + return 1 end loader = options.loader() @@ -410,7 +420,7 @@ def run_method(args, options) unless env.module_name?(type_name) stdout.puts "Cannot find class: #{type_name}" - return + return 1 end definition = case kind @@ -426,7 +436,7 @@ def run_method(args, options) unless method stdout.puts "Cannot find method: #{method_name}" - return + return 1 end stdout.puts "#{type_name}#{kind == :instance ? "#" : "."}#{method_name}" @@ -440,6 +450,8 @@ def run_method(args, options) stdout.puts format(" %s %-#{length_max}s at %s", separator, type, type.location) separator = "|" end + + 0 end def run_validate(args, options) @@ -469,7 +481,7 @@ def run_constant(args, options) unless args.size == 1 stdout.puts "Expected one argument." - return + return 1 end loader = options.loader() @@ -502,6 +514,8 @@ def run_constant(args, options) else stdout.puts " => [no constant]" end + + 0 end def run_paths(args, options) @@ -544,6 +558,8 @@ def run_paths(args, options) stdout.puts "#{dir} (#{kind_of[dir]}, library, name=#{source.name})" end end + + 0 end def run_prototype(args, options) @@ -647,6 +663,8 @@ def autoload(name, path) writer = Writer.new(out: stdout) writer.write decls + + 0 else stdout.puts < do @@ -827,6 +845,8 @@ def run_prototype_file(format, args) writer = Writer.new(out: stdout) writer.write parser.decls end + + 0 end def run_vendor(args, options) @@ -880,6 +900,8 @@ def run_vendor(args, options) stdout.puts " Copying RBS files..." vendorer.copy! + + 0 end def run_parse(args, options) @@ -927,7 +949,11 @@ def run_parse(args, options) syntax_error = true end - exit 1 if syntax_error + if syntax_error + 1 + else + 0 + end end def run_annotate(args, options) @@ -975,6 +1001,8 @@ def run_annotate(args, options) annotator.annotate_file(path, preserve: preserve) end end + + 0 end def test_opt options @@ -1028,7 +1056,7 @@ def run_test(args, options) if args.length.zero? stdout.puts opts.help - exit 1 + return 1 end # @type var env_hash: Hash[String, String?] @@ -1044,11 +1072,12 @@ def run_test(args, options) # @type var out: String # @type var err: String + # @type var status: Process::Status out, err, status = __skip__ = Open3.capture3(env_hash, *args) stdout.print(out) stderr.print(err) - status + status.to_i end def run_collection(args, options) @@ -1073,7 +1102,7 @@ def run_collection(args, options) when 'init' if config_path.exist? puts "#{config_path} already exists" - exit 1 + return 1 end config_path.write(<<~'YAML') @@ -1101,15 +1130,17 @@ def run_collection(args, options) when 'clean' unless lock_path.exist? puts "#{lock_path} should exist to clean" - exit 1 + return 1 end Collection::Cleaner.new(lockfile_path: lock_path) when 'help', 'hel', 'he', 'h' puts opts.help else puts opts.help - exit 1 + return 1 end + + 0 end def collection_options(args) @@ -1171,7 +1202,7 @@ def run_subtract(args, _) *minuend_paths, subtrahend_path = args unless subtrahend_path stdout.puts opts.help - exit 1 + return 1 end subtrahend_paths << subtrahend_path else @@ -1180,7 +1211,7 @@ def run_subtract(args, _) if minuend_paths.empty? stdout.puts opts.help - exit 1 + return 1 end subtrahend = Environment.new.tap do |env| @@ -1213,10 +1244,12 @@ def run_subtract(args, _) end end end + + 0 end def run_diff(argv, library_options) - Diff.new(argv: argv, library_options: library_options, stdout: stdout, stderr: stderr).run + Diff.new(stdout: stdout, stderr: stderr).run(argv: argv, library_options: library_options) end end end diff --git a/lib/rbs/cli/diff.rb b/lib/rbs/cli/diff.rb index 43c8d4ba6c..f60954b763 100644 --- a/lib/rbs/cli/diff.rb +++ b/lib/rbs/cli/diff.rb @@ -3,13 +3,14 @@ module RBS class CLI class Diff - def initialize(argv:, library_options:, stdout: $stdout, stderr: $stderr) - @format = nil + def initialize(stdout: $stdout, stderr: $stderr) @stdout = stdout @stderr = stderr + end - # @type var type_name: String? - type_name = nil + def run(argv:, library_options:) + format = nil #: String? + type_name = nil #: String? library_options = library_options before_path = [] #: Array[String] after_path = [] #: Array[String] @@ -32,7 +33,7 @@ def initialize(argv:, library_options:, stdout: $stdout, stderr: $stderr) # Confirmation of methods related to Time class added by including stdlib/time $ rbs diff --format diff --type-name Time --after stdlib/time HELP - o.on("--format NAME") { |arg| @format = arg } + o.on("--format NAME") { |arg| format = arg } o.on("--type-name NAME") { |arg| type_name = arg } o.on("--before DIR") { |arg| before_path << arg } o.on("--after DIR") { |arg| after_path << arg } @@ -40,28 +41,28 @@ def initialize(argv:, library_options:, stdout: $stdout, stderr: $stderr) end opt.parse!(argv) - unless @format && type_name && ["markdown", "diff"].include?(@format) + unless format && type_name && ["markdown", "diff"].include?(format) @stderr.puts opt.banner - exit 1 + return 1 end - @diff = RBS::Diff.new( + diff = RBS::Diff.new( type_name: TypeName.parse(type_name).absolute!, library_options: library_options, after_path: after_path, before_path: before_path, detail: detail, ) - end - def run - public_send("run_#{@format}") + public_send("run_#{format}", diff) + + 0 end - def run_diff + def run_diff(diff) first = true io = RBS::CLI::ColoredIO.new(stdout: @stdout) - @diff.each_diff do |before, after| + diff.each_diff do |before, after| io.puts if !first io.puts_red "- #{before}" io.puts_green "+ #{after}" @@ -69,10 +70,10 @@ def run_diff end end - def run_markdown + def run_markdown(diff) @stdout.puts "| before | after |" @stdout.puts "| --- | --- |" - @diff.each_diff do |before, after| + diff.each_diff do |before, after| before.gsub!("|", "\\|") after.gsub!("|", "\\|") @stdout.puts "| `#{before}` | `#{after}` |" diff --git a/lib/rbs/cli/validate.rb b/lib/rbs/cli/validate.rb index d7feb02f8a..7ba52665a9 100644 --- a/lib/rbs/cli/validate.rb +++ b/lib/rbs/cli/validate.rb @@ -21,10 +21,18 @@ def add(error) finish if @limit == 1 end + def try(&block) + catch(:finish) do |tag| + @tag = tag + yield + finish() + end + end + def finish if @errors.empty? if @exit_error && @has_syntax_error - exit 1 + throw @tag, 1 else # success end @@ -32,8 +40,10 @@ def finish @errors.each do |error| RBS.logger.error(build_message(error)) end - exit 1 + throw @tag, 1 end + + 0 end private @@ -81,14 +91,14 @@ def initialize(args:, options:) end def run - validate_class_module_definition - validate_class_module_alias_definition - validate_interface - validate_constant - validate_global - validate_type_alias - - @errors.finish + @errors.try do + validate_class_module_definition + validate_class_module_alias_definition + validate_interface + validate_constant + validate_global + validate_type_alias + end end private diff --git a/sig/cli.rbs b/sig/cli.rbs index d6ce90e2f8..6dcd5bfdfd 100644 --- a/sig/cli.rbs +++ b/sig/cli.rbs @@ -46,41 +46,41 @@ module RBS def has_parser?: () -> bool - def run: (Array[String] args) -> void + def run: (Array[String] args) -> Integer - def run_ast: (Array[String], LibraryOptions) -> void + def run_ast: (Array[String], LibraryOptions) -> Integer - def run_list: (Array[String], LibraryOptions) -> void + def run_list: (Array[String], LibraryOptions) -> Integer - def run_ancestors: (Array[String], LibraryOptions) -> void + def run_ancestors: (Array[String], LibraryOptions) -> Integer - def run_methods: (Array[String], LibraryOptions) -> void + def run_methods: (Array[String], LibraryOptions) -> Integer - def run_method: (Array[String], LibraryOptions) -> void + def run_method: (Array[String], LibraryOptions) -> Integer - def run_validate: (Array[String], LibraryOptions) -> void + def run_validate: (Array[String], LibraryOptions) -> Integer - def run_constant: (Array[String], LibraryOptions) -> void + def run_constant: (Array[String], LibraryOptions) -> Integer - def run_paths: (Array[String], LibraryOptions) -> void + def run_paths: (Array[String], LibraryOptions) -> Integer - def run_prototype: (Array[String], LibraryOptions) -> void + def run_prototype: (Array[String], LibraryOptions) -> Integer - def run_prototype_file: (String format, Array[String]) -> void + def run_prototype_file: (String format, Array[String]) -> Integer - def run_vendor: (Array[String], LibraryOptions) -> void + def run_vendor: (Array[String], LibraryOptions) -> Integer - def run_parse: (Array[String], LibraryOptions) -> void + def run_parse: (Array[String], LibraryOptions) -> Integer - def run_test: (Array[String], LibraryOptions) -> void + def run_test: (Array[String], LibraryOptions) -> Integer - def run_collection: (Array[String], LibraryOptions) -> void + def run_collection: (Array[String], LibraryOptions) -> Integer - def run_annotate: (Array[String], top) -> void + def run_annotate: (Array[String], top) -> Integer - def run_subtract: (Array[String], top) -> void + def run_subtract: (Array[String], top) -> Integer - def run_diff: (Array[String], LibraryOptions) -> void + def run_diff: (Array[String], LibraryOptions) -> Integer def test_opt: (LibraryOptions) -> String? diff --git a/sig/cli/diff.rbs b/sig/cli/diff.rbs index a01edbd150..b5a8bb7727 100644 --- a/sig/cli/diff.rbs +++ b/sig/cli/diff.rbs @@ -1,21 +1,15 @@ module RBS class CLI class Diff - @format: String @stdout: _IO @stderr: _IO - @diff: RBS::Diff - def initialize: ( - argv: Array[String], - library_options: RBS::CLI::LibraryOptions, - ?stdout: _IO, - ?stderr: _IO, - ) -> void + def initialize: (?stdout: _IO, ?stderr: _IO) -> void - def run: () -> void - def run_diff: () -> void - def run_markdown: () -> void + def run: (library_options: RBS::CLI::LibraryOptions, argv: Array[String]) -> Integer + + def run_diff: (RBS::Diff) -> void + def run_markdown: (RBS::Diff) -> void end end end diff --git a/sig/cli/validate.rbs b/sig/cli/validate.rbs index 65909e73da..790a97c8c9 100644 --- a/sig/cli/validate.rbs +++ b/sig/cli/validate.rbs @@ -7,11 +7,20 @@ module RBS @has_syntax_error: bool @errors: Array[BaseError] + # The tag that will be thrown in #finish method + @tag: top + def initialize: (limit: Integer?, exit_error: boolish) -> void def add: (BaseError) -> void - def finish: () -> void + # Throws the `@tag` with 0 or 1 + # + # Must be called from the block passed to `#try` method. + # + def finish: () -> Integer + + def try: () { () -> void } -> Integer private @@ -25,7 +34,7 @@ module RBS def initialize: (args: Array[String], options: LibraryOptions) -> void - def run: () -> void + def run: () -> Integer private diff --git a/test/rbs/cli_test.rb b/test/rbs/cli_test.rb index 04b5830c06..69cea0a285 100644 --- a/test/rbs/cli_test.rb +++ b/test/rbs/cli_test.rb @@ -15,6 +15,18 @@ def stderr @stderr ||= StringIO.new end + def assert_cli_success(exit_status = nil, &) + exit_status ||= yield + assert_instance_of Integer, exit_status + assert_predicate exit_status, :zero?, "Expected CLI to succeed, but it failed with status: #{exit_status.inspect}" + end + + def refute_cli_success(exit_status = nil, &) + exit_status ||= yield + assert_instance_of Integer, exit_status + assert_predicate exit_status, :nonzero?, "Expected CLI to succeed, but it failed with status: #{exit_status.inspect}" + end + # Run `rbs collection` with fresh bundler environment # # You need this method to test `rbs collection` features. @@ -52,7 +64,7 @@ def bundle_install(*gems) stdout, stderr, status = Bundler.with_unbundled_env do gems << 'prism' unless gems.include?('prism') - + gems = gems.map do |gem| if gem == :gemspec "gemspec" @@ -91,7 +103,9 @@ def with_cli def test_ast with_cli do |cli| - cli.run(%w(ast)) + assert_cli_success do + cli.run(%w(ast)) + end # Outputs a JSON JSON.parse stdout.string @@ -100,7 +114,9 @@ def test_ast def test_no_stdlib_option with_cli do |cli| - cli.run(%w(--no-stdlib ast)) + assert_cli_success do + cli.run(%w(--no-stdlib ast)) + end assert_equal '[]', stdout.string end @@ -108,28 +124,34 @@ def test_no_stdlib_option def test_list with_cli do |cli| - cli.run(%w(-r pathname list)) + assert_cli_success { cli.run(%w(-r pathname list)) } assert_match %r{^::Pathname \(class\)$}, stdout.string assert_match %r{^::Kernel \(module\)$}, stdout.string assert_match %r{^::_Each \(interface\)$}, stdout.string end with_cli do |cli| - cli.run(%w(-r pathname list --class)) + assert_cli_success do + cli.run(%w(-r pathname list --class)) + end assert_match %r{^::Pathname \(class\)$}, stdout.string refute_match %r{^::Kernel \(module\)$}, stdout.string refute_match %r{^::_Each \(interface\)$}, stdout.string end with_cli do |cli| - cli.run(%w(-r pathname list --module)) + assert_cli_success do + cli.run(%w(-r pathname list --module)) + end refute_match %r{^::Pathname \(class\)$}, stdout.string assert_match %r{^::Kernel \(module\)$}, stdout.string refute_match %r{^::_Each \(interface\)$}, stdout.string end with_cli do |cli| - cli.run(%w(-r pathname list --interface)) + assert_cli_success do + cli.run(%w(-r pathname list --interface)) + end refute_match %r{^::Pathname \(class\)$}, stdout.string refute_match %r{^::Kernel \(module\)$}, stdout.string assert_match %r{^::_Each \(interface\)$}, stdout.string @@ -145,7 +167,9 @@ module Bar = Kernel Dir.chdir(dir) do with_cli do |cli| - cli.run(%w(-I. list)) + assert_cli_success do + cli.run(%w(-I. list)) + end assert_match %r{^::Foo \(class alias\)$}, stdout.string assert_match %r{^::Bar \(module alias\)$}, stdout.string @@ -156,7 +180,9 @@ module Bar = Kernel def test_ancestors with_cli do |cli| - cli.run(%w(ancestors ::Set)) + assert_cli_success do + cli.run(%w(ancestors ::Set)) + end assert_equal <<-EOF, stdout.string ::Set[A] ::Enumerable[A] @@ -167,7 +193,9 @@ def test_ancestors end with_cli do |cli| - cli.run(%w(ancestors --instance ::Set)) + assert_cli_success do + cli.run(%w(ancestors --instance ::Set)) + end assert_equal <<-EOF, stdout.string ::Set[A] ::Enumerable[A] @@ -178,7 +206,10 @@ def test_ancestors end with_cli do |cli| - cli.run(%w(ancestors --singleton ::Set)) + assert_cli_success do + cli.run(%w(ancestors --singleton ::Set)) + end + assert_equal <<-EOF, stdout.string singleton(::Set) singleton(::Object) @@ -202,7 +233,9 @@ class Bar Dir.chdir(dir) do with_cli do |cli| - cli.run(%w(-I. ancestors ::Foo)) + assert_cli_success do + cli.run(%w(-I. ancestors ::Foo)) + end assert_equal <<~EOF, stdout.string ::String @@ -218,9 +251,9 @@ class Bar def test_methods with_cli do |cli| - cli.run(%w(methods ::Set)) - cli.run(%w(methods --instance ::Set)) - cli.run(%w(methods --singleton ::Set)) + assert_cli_success { cli.run(%w(methods ::Set)) } + assert_cli_success { cli.run(%w(methods --instance ::Set)) } + assert_cli_success { cli.run(%w(methods --singleton ::Set)) } end Dir.mktmpdir do |dir| @@ -233,12 +266,16 @@ module Bar = Kernel Dir.chdir(dir) do with_cli do |cli| - cli.run(%w(-I. methods ::Foo)) + assert_cli_success do + cli.run(%w(-I. methods ::Foo)) + end assert_match %r{^puts \(private\)$}, stdout.string end with_cli do |cli| - cli.run(%w(-I. methods --singleton ::Bar)) + assert_cli_success do + cli.run(%w(-I. methods --singleton ::Bar)) + end assert_match %r{^puts \(public\)$}, stdout.string end end @@ -247,7 +284,7 @@ module Bar = Kernel def test_method with_cli do |cli| - cli.run(%w(method ::Object yield_self)) + assert_cli_success { cli.run(%w(method ::Object yield_self)) } assert_includes stdout.string, '::Object#yield_self' assert_includes stdout.string, 'defined_in: ::Kernel' assert_includes stdout.string, 'implementation: ::Kernel' @@ -269,12 +306,12 @@ module Bar = Kernel Dir.chdir(dir) do with_cli do |cli| - cli.run(%w(-I. method ::Foo puts)) + assert_cli_success { cli.run(%w(-I. method ::Foo puts)) } assert_match %r{^::Foo#puts$}, stdout.string end with_cli do |cli| - cli.run(%w(-I. method --singleton ::Bar puts)) + assert_cli_success { cli.run(%w(-I. method --singleton ::Bar puts)) } assert_match %r{^::Bar\.puts$}, stdout.string end end @@ -284,12 +321,12 @@ module Bar = Kernel def test_validate with_cli do |cli| - cli.run(%w(--log-level=info validate)) + assert_cli_success cli.run(%w(--log-level=info validate)) assert_match(/Validating/, stdout.string) end with_cli do |cli| - cli.run(%w(--log-level=warn validate --silent)) + assert_cli_success cli.run(%w(--log-level=warn validate --silent)) assert_match(/`--silent` option is deprecated because it's silent by default\. You can use --log-level option of rbs command to display more information\.$/, stdout.string) end end @@ -302,7 +339,7 @@ class Hello::World end RBS - assert_raises SystemExit do + refute_cli_success do cli.run(["-I", dir, "validate"]) end @@ -318,7 +355,7 @@ def test_validate_no_type_found_error_2 Hello::World: Integer RBS - assert_raises SystemExit do + refute_cli_success do cli.run(["-I", dir, "validate"]) end @@ -334,7 +371,7 @@ def test_validate_no_type_found_error_3 type Hello::t = Integer RBS - assert_raises SystemExit do + refute_cli_success do cli.run(["-I", dir, "validate"]) end @@ -355,7 +392,7 @@ module Bar[B] end RBS - assert_raises SystemExit do + refute_cli_success do cli.run(["-I", dir, "validate"]) end @@ -372,7 +409,7 @@ class Foo[A < _Each[B], B < _Foo[A]] end RBS - assert_raises SystemExit do + refute_cli_success do cli.run(["-I", dir, "validate"]) end @@ -395,7 +432,7 @@ class B end RBS - assert_raises SystemExit do + refute_cli_success do cli.run(["-I", dir, "validate"]) end @@ -412,7 +449,7 @@ def test_validate_with_cyclic_type_parameter_bound_3 end RBS - assert_raises SystemExit do + refute_cli_success do cli.run(["-I", dir, "validate"]) end @@ -432,7 +469,7 @@ def bar: [X < _Foo[Y], Y < _Bar[Z], Z < _Baz[X]] () -> void end RBS - assert_raises SystemExit do + refute_cli_success do cli.run(["-I", dir, "validate"]) end @@ -473,7 +510,9 @@ module Baz : A, _C end RBS - cli.run(["-I", dir, "validate"]) + assert_cli_success do + cli.run(["-I", dir, "validate"]) + end end end end @@ -539,8 +578,7 @@ module X7 : A[String, untyped] end RBS - - assert_raises SystemExit do + refute_cli_success do cli.run(["-I", dir.to_s, "validate"]) end @@ -572,7 +610,9 @@ class B[S = self] type t[T = self] = untyped RBS - cli.run(["-I", dir, "validate"]) + assert_cli_success do + cli.run(["-I", dir, "validate"]) + end assert_include stdout.string, "/a.rbs:1:13...1:17: `self` type is not allowed in this context (RBS::WillSyntaxError)\n" assert_include stdout.string, "/a.rbs:4:12...4:16: `self` type is not allowed in this context (RBS::WillSyntaxError)\n" @@ -598,7 +638,7 @@ class B[A, B = A, C = B?] type t[A, B = A, C = B?] = untyped RBS - assert_raises SystemExit do + refute_cli_success do cli.run(["-I", dir, "validate"]) end @@ -620,7 +660,9 @@ def bar: (void) -> void end RBS - cli.run(["-I", dir, "--log-level=warn", "validate"]) + assert_cli_success do + cli.run(["-I", dir, "--log-level=warn", "validate"]) + end assert_include stdout.string, "a.rbs:2:11...2:25: `void` type is only allowed in return type or generics parameter (RBS::WillSyntaxError)" assert_include stdout.string, "a.rbs:3:11...3:25: `void` type is only allowed in return type or generics parameter (RBS::WillSyntaxError)" @@ -638,7 +680,9 @@ def bar: (void) -> void end RBS - cli.run(["-I", dir, "--log-level=warn", "validate", "--fail-fast"]) + assert_cli_success do + cli.run(["-I", dir, "--log-level=warn", "validate", "--fail-fast"]) + end assert_include stdout.string, "a.rbs:2:11...2:25: `void` type is only allowed in return type or generics parameter (RBS::WillSyntaxError)" assert_include stdout.string, "a.rbs:3:11...3:25: `void` type is only allowed in return type or generics parameter (RBS::WillSyntaxError)" end @@ -655,7 +699,7 @@ def bar: (void) -> void end RBS - assert_raises SystemExit do + refute_cli_success do cli.run(["-I", dir, "--log-level=warn", "validate", "--exit-error-on-syntax-error"]) end assert_include stdout.string, "a.rbs:2:11...2:25: `void` type is only allowed in return type or generics parameter (RBS::WillSyntaxError)" @@ -674,7 +718,7 @@ def bar: (void) -> void end RBS - assert_raises SystemExit do + refute_cli_success do cli.run(["-I", dir, "--log-level=warn", "validate", "--fail-fast", "--exit-error-on-syntax-error"]) end assert_include stdout.string, "a.rbs:2:11...2:25: `void` type is only allowed in return type or generics parameter (RBS::WillSyntaxError)" @@ -685,7 +729,7 @@ def bar: (void) -> void def test_validate_multiple_with_many_errors with_cli do |cli| - assert_raise SystemExit do + refute_cli_success do cli.run(%w(--log-level=warn -I test/multiple_error.rbs validate)) end assert_include(stdout.string, "`void` type is only allowed in return type or generics parameter") @@ -715,7 +759,7 @@ def test_validate_multiple_with_many_errors def test_validate_multiple_fail_fast with_cli do |cli| - assert_raise SystemExit do + refute_cli_success do cli.run(%w(--log-level=warn -I test/multiple_error.rbs validate --fail-fast)) end assert_include(stdout.string, "test/multiple_error.rbs:2:11...2:25: `void` type is only allowed in return type or generics parameter (RBS::WillSyntaxError)") @@ -726,7 +770,7 @@ def test_validate_multiple_fail_fast def test_validate_multiple_fail_fast_and_exit_error_on_syntax_error with_cli do |cli| - assert_raise SystemExit do + refute_cli_success do cli.run(%w(--log-level=warn -I test/multiple_error.rbs validate --fail-fast --exit-error-on-syntax-error)) end assert_include(stdout.string, "test/multiple_error.rbs:2:11...2:25: `void` type is only allowed in return type or generics parameter (RBS::WillSyntaxError)") @@ -780,12 +824,16 @@ class Foo Dir.mktmpdir do |dir| (Pathname(dir) + 'a.rbs').write(rbs) - cli.run(["-I", dir, "validate"]) + assert_cli_success do + cli.run(["-I", dir, "validate"]) + end assert_match(/void|self|instance|class/, stdout.string) - cli.run(["-I", dir, "validate", "--no-exit-error-on-syntax-error"]) - assert_raises SystemExit do + assert_cli_success do + cli.run(["-I", dir, "validate", "--no-exit-error-on-syntax-error"]) + end + refute_cli_success do cli.run(["-I", dir, "validate", "--exit-error-on-syntax-error"]) end end @@ -802,7 +850,7 @@ def voice: [X] { () -> X } -> String end RBS - cli.run(["-I", dir, "validate"]) + assert_cli_success cli.run(["-I", dir, "validate"]) end end end @@ -816,7 +864,7 @@ def void: () -> _Void end RBS - assert_raises SystemExit do + refute_cli_success do cli.run(["-I", dir, "validate"]) end assert_match %r{a.rbs:2:18...2:23: Could not find _Void \(.*RBS::NoTypeFoundError.*\)}, stdout.string @@ -833,7 +881,7 @@ def void: () -> voida end RBS - assert_raises SystemExit do + refute_cli_success do cli.run(["-I", dir, "validate"]) end assert_match %r{a.rbs:2:18...2:23: Could not find voida \(.*RBS::NoTypeFoundError.*\)}, stdout.string @@ -843,20 +891,20 @@ def void: () -> voida def test_constant with_cli do |cli| - cli.run(%w(constant Pathname)) - cli.run(%w(constant --context File IO)) + assert_cli_success cli.run(%w(constant Pathname)) + assert_cli_success cli.run(%w(constant --context File IO)) end end def test_version with_cli do |cli| - cli.run(%w(version)) + assert_cli_success cli.run(%w(version)) end end def test_paths with_cli do |cli| - cli.run(%w(-r pathname -I no-such-dir paths)) + assert_cli_success cli.run(%w(-r pathname -I no-such-dir paths)) assert_match %r{/core \(dir, core\)$}, stdout.string assert_match %r{/stdlib/pathname/0 \(dir, library, name=pathname\)$}, stdout.string assert_match %r{^no-such-dir \(absent\)$}, stdout.string @@ -867,7 +915,7 @@ def test_paths_with_gem omit unless has_gem?("rbs-amber") with_cli do |cli| - cli.run(%w(-r rbs-amber paths)) + assert_cli_success cli.run(%w(-r rbs-amber paths)) assert_match %r{/core \(dir, core\)$}, stdout.string assert_match %r{/sig \(dir, library, name=rbs-amber\)$}, stdout.string end @@ -877,7 +925,7 @@ def test_vendor Dir.mktmpdir do |d| Dir.chdir(d) do with_cli do |cli| - cli.run(%w(vendor --vendor-dir=dir1)) + assert_cli_success cli.run(%w(vendor --vendor-dir=dir1)) assert_predicate Pathname(d) + "dir1/core", :directory? end @@ -891,7 +939,7 @@ def test_vendor_gem Dir.mktmpdir do |d| Dir.chdir(d) do with_cli do |cli| - cli.run(%w(-r rbs-amber vendor --vendor-dir=dir1)) + assert_cli_success cli.run(%w(-r rbs-amber vendor --vendor-dir=dir1)) assert_predicate Pathname(d) + "dir1/rbs-amber-1.0.0", :directory? end @@ -919,7 +967,7 @@ def foo: () -> void RBS with_cli do |cli| - assert_raises(SystemExit) { cli.run(%W(parse #{dir})) } + refute_cli_success { cli.run(%W(parse #{dir})) } assert_equal [ "#{dir}/semantics_error.rbs:2:10...2:11: Syntax error: expected a token `pCOLON`, token=`.` (pDOT) (RBS::ParsingError)", @@ -937,10 +985,10 @@ def foo: () -> void def test_parse_e with_cli do |cli| - cli.run(['parse', '-e', 'class C end']) + assert_cli_success cli.run(['parse', '-e', 'class C end']) assert_empty stdout.string - assert_raises(SystemExit) { cli.run(['parse', '-e', 'class C en']) } + refute_cli_success { cli.run(['parse', '-e', 'class C en']) } assert_equal [ "-e:1:8...1:10: Syntax error: unexpected token for class/module declaration member, token=`en` (tLIDENT) (RBS::ParsingError)", "", @@ -952,10 +1000,10 @@ def test_parse_e def test_parse_type with_cli do |cli| - cli.run(['parse', '--type', '-e', 'bool']) + assert_cli_success cli.run(['parse', '--type', '-e', 'bool']) assert_empty stdout.string - assert_raises(SystemExit) { cli.run(['parse', '--type', '-e', '?']) } + refute_cli_success { cli.run(['parse', '--type', '-e', '?']) } assert_equal [ "-e:1:0...1:1: Syntax error: unexpected token for simple type, token=`?` (pQUESTION) (RBS::ParsingError)", "", @@ -967,10 +1015,10 @@ def test_parse_type def test_parse_method_type with_cli do |cli| - cli.run(['parse', '--method-type', '-e', '() -> void']) + assert_cli_success cli.run(['parse', '--method-type', '-e', '() -> void']) assert_empty stdout.string - assert_raises(SystemExit) { cli.run(['parse', '--method-type', '-e', '()']) } + refute_cli_success cli.run(['parse', '--method-type', '-e', '()']) assert_equal [ "-e:1:2...1:2: Syntax error: expected a token `pARROW`, token=`` (pEOF) (RBS::ParsingError)", "", @@ -987,13 +1035,8 @@ def cli.has_parser? false end - assert_raises SystemExit do - cli.run(%w(prototype rb)) - end - - assert_raises SystemExit do - cli.run(%w(prototype rbi)) - end + refute_cli_success cli.run(%w(prototype rb)) + refute_cli_success cli.run(%w(prototype rbi)) assert_equal "Not supported on this interpreter (ruby).\n", stdout.string.lines[0] assert_equal "Not supported on this interpreter (ruby).\n", stdout.string.lines[1] @@ -1035,7 +1078,7 @@ module C Dir.chdir(dir) do with_cli do |cli| - cli.run(%w(prototype rb --out_dir=sig lib Gemfile)) + assert_cli_success cli.run(%w(prototype rb --out_dir=sig lib Gemfile)) assert_equal <<-EOM, cli.stdout.string Processing `lib`... @@ -1074,7 +1117,7 @@ module A Dir.chdir(dir) do with_cli do |cli| - cli.run(%w(prototype rb --out_dir=sig --base_dir=lib test/a_test.rb)) + assert_cli_success cli.run(%w(prototype rb --out_dir=sig --base_dir=lib test/a_test.rb)) assert_equal <<-EOM, cli.stdout.string Processing `test/a_test.rb`... @@ -1094,13 +1137,13 @@ def test_prototype_batch_syntax_error (dir + "lib").mkdir (dir + "lib/a.rb").write(<<-RUBY) -class A < <%= @superclass %> +class A < {{SUPER_CLASS}} end RUBY Dir.chdir(dir) do with_cli do |cli| - cli.run(%w(prototype rb --out_dir=sig lib)) + assert_cli_success cli.run(%w(prototype rb --out_dir=sig lib)) assert_equal <<-EOM, cli.stdout.string Processing `lib`... @@ -1121,7 +1164,7 @@ def test_prototype__runtime__todo begin old = $stderr $stderr = cli.stderr - cli.run(%w(prototype runtime --todo ::Object)) + assert_cli_success cli.run(%w(prototype runtime --todo ::Object)) ensure $stderr = old end @@ -1155,9 +1198,9 @@ def foo: () -> void # `exit` is a common shell built-in command. assert_rbs_test_no_errors(cli, dir, %w(--target ::Foo exit)) - assert_raises(SystemExit) { cli.run(%w(test)) } - assert_raises(SystemExit) { cli.run(%W(-I #{dir} test)) } - assert_raises(SystemExit) { cli.run(%W(-I #{dir} test --target ::Foo)) } + refute_cli_success cli.run(%w(test)) + refute_cli_success cli.run(%W(-I #{dir} test)) + refute_cli_success cli.run(%W(-I #{dir} test --target ::Foo)) end end end @@ -1568,7 +1611,7 @@ def x: () -> untyped RBS with_cli do |cli| - cli.run(['subtract', minuend.to_s, subtrahend.to_s]) + assert_cli_success cli.run(['subtract', minuend.to_s, subtrahend.to_s]) assert_empty stderr.string assert_equal <<~RBS, stdout.string use A::B @@ -1608,7 +1651,9 @@ def y: () -> untyped RBS with_cli do |cli| - cli.run(['subtract', minuend.to_s, '--subtrahend', subtrahend_1.to_s, '--subtrahend', subtrahend_2.to_s]) + assert_cli_success { + cli.run(['subtract', minuend.to_s, '--subtrahend', subtrahend_1.to_s, '--subtrahend', subtrahend_2.to_s]) + } assert_empty stderr.string assert_equal <<~RBS, stdout.string use A::B @@ -1641,7 +1686,7 @@ def x: () -> untyped RBS with_cli do |cli| - cli.run(['subtract', '--write', minuend.to_s, subtrahend.to_s]) + assert_cli_success cli.run(['subtract', '--write', minuend.to_s, subtrahend.to_s]) assert_empty stderr.string assert_empty stdout.string assert_equal minuend.read, <<~RBS @@ -1674,7 +1719,7 @@ def x: () -> untyped RBS with_cli do |cli| - cli.run(['subtract', '--write', minuend.to_s, subtrahend.to_s]) + assert_cli_success cli.run(['subtract', '--write', minuend.to_s, subtrahend.to_s]) assert_empty stderr.string assert_empty stdout.string refute_predicate minuend, :exist? @@ -1684,7 +1729,9 @@ def x: () -> untyped def assert_rbs_test_no_errors cli, dir, arg_array args = ['-I', dir.to_s, 'test', *arg_array] - assert_instance_of Process::Status, cli.run(args) + exit_status = cli.run(args) + assert_instance_of Integer, exit_status + assert_predicate exit_status, :zero? end def mktmp_diff_case @@ -1730,7 +1777,9 @@ class Foo def test_diff_markdown mktmp_diff_case do |dir1, dir2| with_cli do |cli| - cli.run(['diff', '--format', 'markdown', '--type-name', 'Foo', '--before', dir1.to_s, '--after', dir2.to_s]) + assert_cli_success { + cli.run(['diff', '--format', 'markdown', '--type-name', 'Foo', '--before', dir1.to_s, '--after', dir2.to_s]) + } assert_equal <<~MARKDOWN, stdout.string | before | after | @@ -1747,7 +1796,9 @@ def test_diff_markdown def test_diff_diff mktmp_diff_case do |dir1, dir2| with_cli do |cli| - cli.run(['diff', '--format', 'diff', '--type-name', 'Foo', '--before', dir1.to_s, '--after', dir2.to_s]) + assert_cli_success do + cli.run(['diff', '--format', 'diff', '--type-name', 'Foo', '--before', dir1.to_s, '--after', dir2.to_s]) + end assert_equal <<~DIFF, stdout.string - def qux: (untyped) -> untyped