|
| 1 | +# frozen_string_literal: true |
| 2 | + |
| 3 | +require 'repl_completion' |
| 4 | +require_relative './helper' |
| 5 | + |
| 6 | +module TestReplCompletion |
| 7 | + class ReplCompletionTest < TestCase |
| 8 | + def setup |
| 9 | + ReplCompletion.load_rbs unless ReplCompletion.rbs_loaded? |
| 10 | + end |
| 11 | + |
| 12 | + def empty_binding |
| 13 | + binding |
| 14 | + end |
| 15 | + |
| 16 | + TARGET_REGEXP = /(@@|@|\$)?[a-zA-Z_]*[!?=]?$/ |
| 17 | + |
| 18 | + def assert_completion(code, binding: empty_binding, include: nil, exclude: nil) |
| 19 | + raise ArgumentError if include.nil? && exclude.nil? |
| 20 | + candidates = ReplCompletion.analyze(code, binding).completion_candidates |
| 21 | + assert ([*include] - candidates).empty?, "Expected #{candidates} to include #{include}" if include |
| 22 | + assert (candidates & [*exclude]).empty?, "Expected #{candidates} not to include #{exclude}" if exclude |
| 23 | + end |
| 24 | + |
| 25 | + def assert_doc_namespace(code, namespace, binding: empty_binding) |
| 26 | + assert_equal namespace, ReplCompletion.analyze(code, binding).doc_namespace('') |
| 27 | + end |
| 28 | + |
| 29 | + def test_require |
| 30 | + assert_completion("require '", include: 'set') |
| 31 | + assert_completion("require 's", include: 'et') |
| 32 | + assert_completion("require_relative 'test_", include: 'repl_completion') |
| 33 | + # Incomplete double quote string is InterpolatedStringNode |
| 34 | + assert_completion('require "', include: 'set') |
| 35 | + assert_completion('require "s', include: 'et') |
| 36 | + end |
| 37 | + |
| 38 | + def test_method_block_sym |
| 39 | + assert_completion('[1].map(&:', include: 'abs') |
| 40 | + assert_completion('[:a].map(&:', exclude: 'abs') |
| 41 | + assert_completion('[1].map(&:a', include: 'bs') |
| 42 | + assert_doc_namespace('[1].map(&:abs', 'Integer#abs') |
| 43 | + end |
| 44 | + |
| 45 | + def test_symbol |
| 46 | + prefix = ':test_com' |
| 47 | + sym = :test_completion_symbol |
| 48 | + assert_completion(prefix, include: sym.inspect.delete_prefix(prefix)) |
| 49 | + end |
| 50 | + |
| 51 | + def test_call |
| 52 | + assert_completion('1.', include: 'abs') |
| 53 | + assert_completion('1.a', include: 'bs') |
| 54 | + assert_completion('ran', include: 'd') |
| 55 | + assert_doc_namespace('1.abs', 'Integer#abs') |
| 56 | + assert_doc_namespace('Integer.sqrt', 'Integer.sqrt') |
| 57 | + assert_doc_namespace('rand', 'TestReplCompletion::ReplCompletionTest#rand') |
| 58 | + assert_doc_namespace('Object::rand', 'Object.rand') |
| 59 | + end |
| 60 | + |
| 61 | + def test_lvar |
| 62 | + bind = eval('lvar = 1; binding') |
| 63 | + assert_completion('lva', binding: bind, include: 'r') |
| 64 | + assert_completion('lvar.', binding: bind, include: 'abs') |
| 65 | + assert_completion('lvar.a', binding: bind, include: 'bs') |
| 66 | + assert_completion('lvar = ""; lvar.', binding: bind, include: 'ascii_only?') |
| 67 | + assert_completion('lvar = ""; lvar.', include: 'ascii_only?') |
| 68 | + assert_doc_namespace('lvar', 'Integer', binding: bind) |
| 69 | + assert_doc_namespace('lvar.abs', 'Integer#abs', binding: bind) |
| 70 | + assert_doc_namespace('lvar = ""; lvar.ascii_only?', 'String#ascii_only?', binding: bind) |
| 71 | + end |
| 72 | + |
| 73 | + def test_const |
| 74 | + assert_completion('Ar', include: 'ray') |
| 75 | + assert_completion('::Ar', include: 'ray') |
| 76 | + assert_completion('ReplCompletion::V', include: 'ERSION') |
| 77 | + assert_completion('FooBar=1; F', include: 'ooBar') |
| 78 | + assert_completion('::FooBar=1; ::F', include: 'ooBar') |
| 79 | + assert_doc_namespace('Array', 'Array') |
| 80 | + assert_doc_namespace('Array = 1; Array', 'Integer') |
| 81 | + assert_doc_namespace('Object::Array', 'Array') |
| 82 | + assert_completion('::', include: 'Array') |
| 83 | + assert_completion('class ::', include: 'Array') |
| 84 | + assert_completion('module ReplCompletion; class T', include: ['ypes', 'racePoint']) |
| 85 | + end |
| 86 | + |
| 87 | + def test_gvar |
| 88 | + assert_completion('$', include: 'stdout') |
| 89 | + assert_completion('$s', include: 'tdout') |
| 90 | + assert_completion('$', exclude: 'foobar') |
| 91 | + assert_completion('$foobar=1; $', include: 'foobar') |
| 92 | + assert_doc_namespace('$foobar=1; $foobar', 'Integer') |
| 93 | + assert_doc_namespace('$stdout', 'IO') |
| 94 | + assert_doc_namespace('$stdout=1; $stdout', 'Integer') |
| 95 | + end |
| 96 | + |
| 97 | + def test_ivar |
| 98 | + bind = Object.new.instance_eval { @foo = 1; binding } |
| 99 | + assert_completion('@', binding: bind, include: 'foo') |
| 100 | + assert_completion('@f', binding: bind, include: 'oo') |
| 101 | + assert_completion('@bar = 1; @', include: 'bar') |
| 102 | + assert_completion('@bar = 1; @b', include: 'ar') |
| 103 | + assert_doc_namespace('@bar = 1; @bar', 'Integer') |
| 104 | + assert_doc_namespace('@foo', 'Integer', binding: bind) |
| 105 | + assert_doc_namespace('@foo = 1.0; @foo', 'Float', binding: bind) |
| 106 | + end |
| 107 | + |
| 108 | + def test_cvar |
| 109 | + bind = eval('m=Module.new; module m::M; @@foo = 1; binding; end') |
| 110 | + assert_equal(1, bind.eval('@@foo')) |
| 111 | + assert_completion('@', binding: bind, include: '@foo') |
| 112 | + assert_completion('@@', binding: bind, include: 'foo') |
| 113 | + assert_completion('@@f', binding: bind, include: 'oo') |
| 114 | + assert_doc_namespace('@@foo', 'Integer', binding: bind) |
| 115 | + assert_doc_namespace('@@foo = 1.0; @@foo', 'Float', binding: bind) |
| 116 | + assert_completion('@@bar = 1; @', include: '@bar') |
| 117 | + assert_completion('@@bar = 1; @@', include: 'bar') |
| 118 | + assert_completion('@@bar = 1; @@b', include: 'ar') |
| 119 | + assert_doc_namespace('@@bar = 1; @@bar', 'Integer') |
| 120 | + end |
| 121 | + |
| 122 | + def test_basic_object |
| 123 | + bo = BasicObject.new |
| 124 | + def bo.foo; end |
| 125 | + bo.instance_eval { @bar = 1 } |
| 126 | + bind = binding |
| 127 | + bo_self_bind = bo.instance_eval { Kernel.binding } |
| 128 | + assert_completion('bo.', binding: bind, include: 'foo') |
| 129 | + assert_completion('def bo.baz; self.', binding: bind, include: 'foo') |
| 130 | + assert_completion('[bo].first.', binding: bind, include: 'foo') |
| 131 | + assert_doc_namespace('bo', 'BasicObject', binding: bind) |
| 132 | + assert_doc_namespace('bo.__id__', 'BasicObject#__id__', binding: bind) |
| 133 | + assert_doc_namespace('v = [bo]; v', 'Array', binding: bind) |
| 134 | + assert_doc_namespace('v = [bo].first; v', 'BasicObject', binding: bind) |
| 135 | + bo_self_bind = bo.instance_eval { Kernel.binding } |
| 136 | + assert_completion('self.', binding: bo_self_bind, include: 'foo') |
| 137 | + assert_completion('@', binding: bo_self_bind, include: 'bar') |
| 138 | + assert_completion('@bar.', binding: bo_self_bind, include: 'abs') |
| 139 | + assert_doc_namespace('self.__id__', 'BasicObject#__id__', binding: bo_self_bind) |
| 140 | + assert_doc_namespace('@bar', 'Integer', binding: bo_self_bind) |
| 141 | + if RUBY_VERSION >= '3.2.0' # Needs Class#attached_object to get instance variables from singleton class |
| 142 | + assert_completion('def bo.baz; @bar.', binding: bind, include: 'abs') |
| 143 | + assert_completion('def bo.baz; @', binding: bind, include: 'bar') |
| 144 | + end |
| 145 | + end |
| 146 | + |
| 147 | + DEPRECATED_CONST = 1 |
| 148 | + deprecate_constant :DEPRECATED_CONST |
| 149 | + def test_deprecated_const_without_warning |
| 150 | + assert_deprecated_warning(/\A\z/) do |
| 151 | + assert_completion('DEPRECATED', include: '_CONST', binding: binding) |
| 152 | + assert_completion('DEPRECATED_CONST.a', include: 'bs', binding: binding) |
| 153 | + assert_doc_namespace('DEPRECATED_CONST', 'Integer', binding: binding) |
| 154 | + end |
| 155 | + end |
| 156 | + |
| 157 | + def test_sig_dir |
| 158 | + assert_doc_namespace('ReplCompletion.analyze(code, binding).completion_candidates.__id__', 'Array#__id__') |
| 159 | + assert_doc_namespace('ReplCompletion.analyze(code, binding).doc_namespace.__id__', 'String#__id__') |
| 160 | + end |
| 161 | + |
| 162 | + def test_none |
| 163 | + result = ReplCompletion.analyze('()', binding) |
| 164 | + assert_nil result |
| 165 | + end |
| 166 | + |
| 167 | + def test_repl_completion_api |
| 168 | + assert_nil ReplCompletion.rbs_load_error |
| 169 | + assert_nil ReplCompletion.last_analyze_error |
| 170 | + assert_equal true, ReplCompletion.rbs_load_started? |
| 171 | + assert_equal true, ReplCompletion.rbs_loaded? |
| 172 | + assert_nothing_raised { ReplCompletion.preload_rbs } |
| 173 | + assert_nothing_raised { ReplCompletion.load_rbs } |
| 174 | + end |
| 175 | + |
| 176 | + def test_info |
| 177 | + assert_equal "ReplCompletion: #{ReplCompletion::VERSION}, Prism: #{Prism::VERSION}, RBS: #{RBS::VERSION}", ReplCompletion.info |
| 178 | + end |
| 179 | + end |
| 180 | +end |
0 commit comments