Class: RubyLsp::Listeners::TestStyle
- Inherits:
-
TestDiscovery
- Object
- TestDiscovery
- RubyLsp::Listeners::TestStyle
- Includes:
- Requests::Support::Common
- Defined in:
- lib/ruby_lsp/listeners/test_style.rb
Constant Summary collapse
- MINITEST_REPORTER_PATH =
: String
File.("../test_reporters/minitest_reporter.rb", __dir__)
- TEST_UNIT_REPORTER_PATH =
: String
File.("../test_reporters/test_unit_reporter.rb", __dir__)
- BASE_COMMAND =
begin Bundler.with_unbundled_env { Bundler.default_lockfile } "bundle exec ruby" rescue Bundler::GemfileNotFound "ruby" end
- COMMAND =
: String
"#{BASE_COMMAND} -r#{MINITEST_REPORTER_PATH} -r#{TEST_UNIT_REPORTER_PATH}"- ACCESS_MODIFIERS =
[:public, :private, :protected].freeze
Constants inherited from TestDiscovery
RubyLsp::Listeners::TestDiscovery::DYNAMIC_REFERENCE_MARKER
Class Method Summary collapse
-
.resolve_test_commands(items) ⇒ Object
Resolves the minimal set of commands required to execute the requested tests : (Array[Hash[Symbol, untyped]]) -> Array.
Instance Method Summary collapse
-
#initialize(response_builder, global_state, dispatcher, uri) ⇒ TestStyle
constructor
: (ResponseBuilders::TestCollection, GlobalState, Prism::Dispatcher, URI::Generic) -> void.
-
#on_call_node_enter(node) ⇒ Object
: (Prism::CallNode node) -> void.
-
#on_call_node_leave(node) ⇒ Object
: (Prism::CallNode node) -> void.
-
#on_class_node_enter(node) ⇒ Object
: (Prism::ClassNode node) -> void.
-
#on_class_node_leave(node) ⇒ Object
: (Prism::ClassNode node) -> void.
-
#on_def_node_enter(node) ⇒ Object
: (Prism::DefNode node) -> void.
-
#on_module_node_enter(node) ⇒ Object
: (Prism::ModuleNode node) -> void.
-
#on_module_node_leave(node) ⇒ Object
: (Prism::ModuleNode node) -> void.
Methods included from Requests::Support::Common
#categorized_markdown_from_index_entries, #constant_name, #create_code_lens, #each_constant_path_part, #kind_for_entry, #markdown_from_index_entries, #namespace_constant_name, #not_in_dependencies?, #range_from_location, #range_from_node, #self_receiver?
Constructor Details
#initialize(response_builder, global_state, dispatcher, uri) ⇒ TestStyle
: (ResponseBuilders::TestCollection, GlobalState, Prism::Dispatcher, URI::Generic) -> void
159 160 161 162 163 164 165 166 167 168 169 170 171 172 |
# File 'lib/ruby_lsp/listeners/test_style.rb', line 159 def initialize(response_builder, global_state, dispatcher, uri) super(response_builder, global_state, uri) @framework = :minitest #: Symbol @parent_stack = [@response_builder] #: Array[(Requests::Support::TestItem | ResponseBuilders::TestCollection)?] register_events( dispatcher, :on_class_node_enter, :on_def_node_enter, :on_call_node_enter, :on_call_node_leave, ) end |
Class Method Details
.resolve_test_commands(items) ⇒ Object
Resolves the minimal set of commands required to execute the requested tests : (Array[Hash[Symbol, untyped]]) -> Array
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
# File 'lib/ruby_lsp/listeners/test_style.rb', line 10 def resolve_test_commands(items) # A nested hash of file_path => test_group => { tags: [], examples: [test_example] } to ensure we build the # minimum amount of commands needed to execute the requested tests. This is only used for specific examples # where we will need more complex regexes to execute it all at the same time aggregated_tests = Hash.new do |hash, key| hash[key] = Hash.new do |inner_h, inner_k| inner_h[inner_k] = { tags: Set.new, examples: [] } end end # Full files are paths that should be executed as a whole e.g.: an entire test file or directory full_files = [] queue = items.dup until queue.empty? item = queue.shift #: as !nil = Set.new(item[:tags]) next unless .include?("framework:minitest") || .include?("framework:test_unit") children = item[:children] uri = URI(item[:uri]) path = uri.full_path next unless path if .include?("test_dir") if children.empty? full_files.concat( Dir.glob( "#{path}/**/{*_test,test_*,*_spec}.rb", File::Constants::FNM_EXTGLOB | File::Constants::FNM_PATHNAME, ).map! { |p| Shellwords.escape(p) }, ) end elsif .include?("test_file") full_files << Shellwords.escape(path) if children.empty? elsif .include?("test_group") # If all of the children of the current test group are other groups, then there's no need to add it to the # aggregated examples unless children.any? && children.all? { |child| child[:tags].include?("test_group") } aggregated_tests[path][item[:id]] = { tags: , examples: [] } end else class_name, method_name = item[:id].split("#") aggregated_tests[path][class_name][:examples] << method_name aggregated_tests[path][class_name][:tags].merge() end queue.concat(children) unless children.empty? end commands = [] aggregated_tests.each do |file_path, groups_and_examples| # Separate groups into Minitest and Test Unit. You can have both frameworks in the same file, but you cannot # have a group belongs to both at the same time minitest_groups, test_unit_groups = groups_and_examples.partition do |_, info| info[:tags].include?("framework:minitest") end if minitest_groups.any? commands << handle_minitest_groups(file_path, minitest_groups) end if test_unit_groups.any? commands.concat(handle_test_unit_groups(file_path, test_unit_groups)) end end unless full_files.empty? specs, tests = full_files.partition { |path| spec?(path) } commands << "#{COMMAND} -Itest -e \"ARGV.each { |f| require f }\" #{tests.join(" ")}" if tests.any? commands << "#{COMMAND} -Ispec -e \"ARGV.each { |f| require f }\" #{specs.join(" ")}" if specs.any? end commands end |
Instance Method Details
#on_call_node_enter(node) ⇒ Object
: (Prism::CallNode node) -> void
238 239 240 241 242 243 |
# File 'lib/ruby_lsp/listeners/test_style.rb', line 238 def on_call_node_enter(node) # rubocop:disable RubyLsp/UseRegisterWithHandlerMethod name = node.name return unless ACCESS_MODIFIERS.include?(name) @visibility_stack << name end |
#on_call_node_leave(node) ⇒ Object
: (Prism::CallNode node) -> void
246 247 248 249 250 251 252 |
# File 'lib/ruby_lsp/listeners/test_style.rb', line 246 def on_call_node_leave(node) # rubocop:disable RubyLsp/UseRegisterWithHandlerMethod name = node.name return unless ACCESS_MODIFIERS.include?(name) return unless node.arguments&.arguments @visibility_stack.pop end |
#on_class_node_enter(node) ⇒ Object
: (Prism::ClassNode node) -> void
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 |
# File 'lib/ruby_lsp/listeners/test_style.rb', line 175 def on_class_node_enter(node) # rubocop:disable RubyLsp/UseRegisterWithHandlerMethod with_test_ancestor_tracking(node) do |name, ancestors| @framework = :test_unit if ancestors.include?("Test::Unit::TestCase") if @framework == :test_unit || non_declarative_minitest?(ancestors, name) test_item = Requests::Support::TestItem.new( name, name, @uri, range_from_node(node), framework: @framework, ) last_test_group.add(test_item) @response_builder.add_code_lens(test_item) @parent_stack << test_item else @parent_stack << nil end end end |
#on_class_node_leave(node) ⇒ Object
: (Prism::ClassNode node) -> void
198 199 200 201 |
# File 'lib/ruby_lsp/listeners/test_style.rb', line 198 def on_class_node_leave(node) # rubocop:disable RubyLsp/UseRegisterWithHandlerMethod @parent_stack.pop super end |
#on_def_node_enter(node) ⇒ Object
: (Prism::DefNode node) -> void
216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 |
# File 'lib/ruby_lsp/listeners/test_style.rb', line 216 def on_def_node_enter(node) # rubocop:disable RubyLsp/UseRegisterWithHandlerMethod return if @visibility_stack.last != :public name = node.name.to_s return unless name.start_with?("test_") current_group_name = RubyIndexer::Index.actual_nesting(@nesting, nil).join("::") parent = @parent_stack.last return unless parent.is_a?(Requests::Support::TestItem) example_item = Requests::Support::TestItem.new( "#{current_group_name}##{name}", name, @uri, range_from_node(node), framework: @framework, ) parent.add(example_item) @response_builder.add_code_lens(example_item) end |
#on_module_node_enter(node) ⇒ Object
: (Prism::ModuleNode node) -> void
204 205 206 207 |
# File 'lib/ruby_lsp/listeners/test_style.rb', line 204 def on_module_node_enter(node) # rubocop:disable RubyLsp/UseRegisterWithHandlerMethod @parent_stack << nil super end |
#on_module_node_leave(node) ⇒ Object
: (Prism::ModuleNode node) -> void
210 211 212 213 |
# File 'lib/ruby_lsp/listeners/test_style.rb', line 210 def on_module_node_leave(node) # rubocop:disable RubyLsp/UseRegisterWithHandlerMethod @parent_stack.pop super end |