Skip to content

Commit 6627249

Browse files
committed
add test
1 parent 8036e2e commit 6627249

6 files changed

Lines changed: 1102 additions & 0 deletions

File tree

test/repl_completion/helper.rb

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# frozen_string_literal: true
2+
3+
require 'test/unit'
4+
require 'core_assertions'
5+
6+
module TestReplCompletion
7+
class TestCase < Test::Unit::TestCase
8+
include Test::Unit::CoreAssertions
9+
end
10+
end
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
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
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# frozen_string_literal: true
2+
3+
require 'repl_completion'
4+
require_relative './helper'
5+
6+
module TestReplCompletion
7+
class RequirePathsTest < TestCase
8+
def test_require_paths
9+
assert_include ReplCompletion::RequirePaths.require_completions('repl_com'), 'repl_completion'
10+
assert_include ReplCompletion::RequirePaths.require_completions('repl_com'), 'repl_completion/version'
11+
assert_equal ['repl_completion/version'], ReplCompletion::RequirePaths.require_completions('repl_completion/vers')
12+
end
13+
14+
def test_require_relative_paths
15+
assert_include ReplCompletion::RequirePaths.require_relative_completions('test_re', binding), 'test_require_paths'
16+
assert_include ReplCompletion::RequirePaths.require_relative_completions('../repl_', binding), '../repl_completion/test_require_paths'
17+
root_path_binding = eval('binding', binding, File.join(__dir__, '../../Gemfile'), 1)
18+
assert_not_include ReplCompletion::RequirePaths.require_relative_completions('li', binding), 'lib/repl_completion'
19+
assert_include ReplCompletion::RequirePaths.require_relative_completions('li', root_path_binding), 'lib/repl_completion'
20+
# Incrementally complete deep path
21+
assert_include ReplCompletion::RequirePaths.require_relative_completions('li', root_path_binding), 'lib/repl_completion/'
22+
assert_not_include ReplCompletion::RequirePaths.require_relative_completions('li', root_path_binding), 'lib/repl_completion/version'
23+
assert_include ReplCompletion::RequirePaths.require_relative_completions('lib/', root_path_binding), 'lib/repl_completion/version'
24+
end
25+
end
26+
end

test/repl_completion/test_scope.rb

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
# frozen_string_literal: true
2+
3+
require 'repl_completion'
4+
require_relative './helper'
5+
6+
module TestReplCompletion
7+
class ScopeTest < TestCase
8+
A, B, C, D, E, F, G, H, I, J, K = ('A'..'K').map do |name|
9+
klass = Class.new
10+
klass.define_singleton_method(:inspect) { name }
11+
ReplCompletion::Types::InstanceType.new(klass)
12+
end
13+
14+
def assert_type(expected_types, type)
15+
assert_equal [*expected_types].map(&:klass).to_set, type.types.map(&:klass).to_set
16+
end
17+
18+
def table(*local_variable_names)
19+
local_variable_names.to_h { [_1, ReplCompletion::Types::NIL] }
20+
end
21+
22+
def base_scope
23+
ReplCompletion::RootScope.new(binding, Object.new, [])
24+
end
25+
26+
def test_lvar
27+
scope = ReplCompletion::Scope.new base_scope, table('a')
28+
scope['a'] = A
29+
assert_equal A, scope['a']
30+
end
31+
32+
def test_conditional
33+
scope = ReplCompletion::Scope.new base_scope, table('a')
34+
scope.conditional do |sub_scope|
35+
sub_scope['a'] = A
36+
end
37+
assert_type [A, ReplCompletion::Types::NIL], scope['a']
38+
end
39+
40+
def test_branch
41+
scope = ReplCompletion::Scope.new base_scope, table('a', 'b', 'c', 'd')
42+
scope['c'] = A
43+
scope['d'] = B
44+
scope.run_branches(
45+
-> { _1['a'] = _1['c'] = _1['d'] = C },
46+
-> { _1['a'] = _1['b'] = _1['d'] = D },
47+
-> { _1['a'] = _1['b'] = _1['d'] = E },
48+
-> { _1['a'] = _1['b'] = _1['c'] = F; _1.terminate }
49+
)
50+
assert_type [C, D, E], scope['a']
51+
assert_type [ReplCompletion::Types::NIL, D, E], scope['b']
52+
assert_type [A, C], scope['c']
53+
assert_type [C, D, E], scope['d']
54+
end
55+
56+
def test_scope_local_variables
57+
scope1 = ReplCompletion::Scope.new base_scope, table('a', 'b')
58+
scope2 = ReplCompletion::Scope.new scope1, table('b', 'c'), trace_lvar: false
59+
scope3 = ReplCompletion::Scope.new scope2, table('c', 'd')
60+
scope4 = ReplCompletion::Scope.new scope2, table('d', 'e')
61+
assert_empty base_scope.local_variables
62+
assert_equal %w[a b], scope1.local_variables.sort
63+
assert_equal %w[b c], scope2.local_variables.sort
64+
assert_equal %w[b c d], scope3.local_variables.sort
65+
assert_equal %w[b c d e], scope4.local_variables.sort
66+
end
67+
68+
def test_nested_scope
69+
scope = ReplCompletion::Scope.new base_scope, table('a', 'b', 'c')
70+
scope['a'] = A
71+
scope['b'] = A
72+
scope['c'] = A
73+
sub_scope = ReplCompletion::Scope.new scope, { 'c' => B }
74+
assert_type A, sub_scope['a']
75+
76+
assert_type A, sub_scope['b']
77+
assert_type B, sub_scope['c']
78+
sub_scope['a'] = C
79+
sub_scope.conditional { _1['b'] = C }
80+
sub_scope['c'] = C
81+
assert_type C, sub_scope['a']
82+
assert_type [A, C], sub_scope['b']
83+
assert_type C, sub_scope['c']
84+
scope.update sub_scope
85+
assert_type C, scope['a']
86+
assert_type [A, C], scope['b']
87+
assert_type A, scope['c']
88+
end
89+
90+
def test_break
91+
scope = ReplCompletion::Scope.new base_scope, table('a')
92+
scope['a'] = A
93+
breakable_scope = ReplCompletion::Scope.new scope, { ReplCompletion::Scope::BREAK_RESULT => nil }
94+
breakable_scope.conditional do |sub|
95+
sub['a'] = B
96+
assert_type [B], sub['a']
97+
sub.terminate_with ReplCompletion::Scope::BREAK_RESULT, C
98+
sub['a'] = C
99+
assert_type [C], sub['a']
100+
end
101+
assert_type [A], breakable_scope['a']
102+
breakable_scope[ReplCompletion::Scope::BREAK_RESULT] = D
103+
breakable_scope.merge_jumps
104+
assert_type [C, D], breakable_scope[ReplCompletion::Scope::BREAK_RESULT]
105+
scope.update breakable_scope
106+
assert_type [A, B], scope['a']
107+
end
108+
end
109+
end

0 commit comments

Comments
 (0)