Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add prototype for custom test reporter for minitest #3187

Open
wants to merge 15 commits into
base: main
Choose a base branch
from

Conversation

andyw8
Copy link
Contributor

@andyw8 andyw8 commented Feb 12, 2025

Addresses #3176

This adds a custom reporter for use with the upcoming changes planned for the Test Explorer UI. It emits the test progress as a series of events, for easy consumption by the VS Code extension.

I have a version for test/unit in progress separately.

No tests yet, but I will add once things become more concrete.

Copy link

graphite-app bot commented Feb 12, 2025

How to use the Graphite Merge Queue

Add the label graphite-merge to this PR to add it to the merge queue.

You must have a Graphite account in order to use the merge queue. Sign up using this link.

An organization admin has enabled the Graphite Merge Queue in this repository.

Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue.

@andyw8 andyw8 force-pushed the andyw8/custom-test-reporter branch from 9e0d218 to 93c2d6d Compare February 12, 2025 14:57
@andyw8 andyw8 changed the title Prototyping for custom test reporter Prototyping for custom test reporters Feb 12, 2025
@andyw8 andyw8 force-pushed the andyw8/custom-test-reporter branch 2 times, most recently from 58ddebf to c31bee6 Compare February 14, 2025 18:32
end

sig { params(result: Minitest::Result).void }
def record_pass(result)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is some duplication between the various record_ methods, but I want to understand what's actually required before cleaning this up. We'll know better once integrated with the extension.

@andyw8 andyw8 force-pushed the andyw8/custom-test-reporter branch 3 times, most recently from 94fb924 to b8fd1ab Compare February 14, 2025 18:52
@andyw8 andyw8 changed the title Prototyping for custom test reporters Add prototype for custom test reporter for minitest Feb 14, 2025
@andyw8 andyw8 marked this pull request as ready for review February 14, 2025 18:54
@andyw8 andyw8 requested a review from a team as a code owner February 14, 2025 18:54
@andyw8 andyw8 added chore Chore task enhancement New feature or request server This pull request should be included in the server gem's release notes and removed chore Chore task labels Feb 14, 2025
extend T::Sig

sig { params(class_name: String, test_name: String, file: String).void }
def before_test(class_name:, test_name:, file:)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the methods in this class should just take in id (or full_name) and leave the assembling to each test framework's addons. For example, I may use test example/group's location as the rspec addon's test id first.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does before_test mean? If this means when we're starting to run it, should this be named start_test?

@andyw8 andyw8 force-pushed the andyw8/custom-test-reporter branch from b8fd1ab to 30a66c9 Compare February 14, 2025 19:27
full_name: full_name,
file: file,
}
puts result.to_json
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test outputs and messages may include line breaks, so we need to use JSON RPC to be able to parse this accurately from the client side.

Suggested change
puts result.to_json
json_message = result.to_json
$stdout.write("Content-Length: #{json_message.bytesize}\r\n\r\n#{json_message}")

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When we call to to_json it will escape the line breaks.

Re. the client/server split: does this feature need to involve the server at all? If we're only targetting VS Code, then could the extension watch the output directly?

lib/ruby_lsp/test_reporting.rb Outdated Show resolved Hide resolved
extend T::Sig

sig { params(class_name: String, test_name: String, file: String).void }
def before_test(class_name:, test_name:, file:)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does before_test mean? If this means when we're starting to run it, should this be named start_test?

require "json"

module RubyLsp
class TestReporting
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This class has no state. Should these just be class methods instead? Also, I think it's clearer to call this a TestReporter.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've changed the name.

Also I am now passing in an IO instance to allow for easier testing and flexibility.

params(
class_name: String,
test_name: String,
type: T.untyped, # TODO: what type should this be?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is type here? What will we use it for in the extension?

Copy link
Contributor Author

@andyw8 andyw8 Feb 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've seen two variants so far:

  • A Minitest::Assertion failure
  • A Minitest::UnexpectedError, e.g. if something in the test raises

I thought it may be useful to distingish these in the UI, similar to how the progress reporter shows E vs F.

lib/minitest/reporters/ruby_lsp_reporter.rb Outdated Show resolved Hide resolved
test_name: result.name,
type: result.failure.class.name,
message: result.failure.message,
file: result.source_location[0],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These record events seem to have access to the file path without having to resort to Module.const_source_location. Is there some other event, like start_suite that we can remember the file path ahead of time?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a before_suite which takes a Minitest::Reporters::Suite, but that doesn't have file information:

https://github.com/minitest-reporters/minitest-reporters/blob/265ff4b40d5827e84d7e902b808fbee860b61221/lib/minitest/reporters/base_reporter.rb#L3C11-L3C16

We could propose adding this in minitest-reporters.

@andyw8 andyw8 force-pushed the andyw8/custom-test-reporter branch from a82f421 to c71eb8b Compare February 18, 2025 16:01
lib/ruby_lsp/test_reporting.rb Outdated Show resolved Hide resolved

sig { params(test: Minitest::Test).returns(String) }
def file_for_class_name(test)
T.must(Kernel.const_source_location(test.class_name)).first
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a headsup that even in minitest/test-unit it's possible to define test classes dynamically, which make their source location path a bit different, like:

class Foo
  class_eval <<~RUBY
    class FooTest < Test::Unit::TestCase
      def test_foo
        puts "========================================="
        puts Kernel.const_source_location("Foo::FooTest")
        puts "========================================="
        fail
      end
    end
  RUBY
end
=========================================
(eval at /Users/hung-wulo/src/github.com/Shopify/rdoc/test/rdoc/test_rdoc_comment.rb:483)
1
=========================================

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pushed an update to handle that. Still need to think about what we do in the UI.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request server This pull request should be included in the server gem's release notes
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants