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 withKnownIssue comments to known Issues #1014

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

Conversation

aroben
Copy link

@aroben aroben commented Mar 11, 2025

Add withKnownIssue comments to known Issues

Motivation:

The Comment passed to withKnownIssue() is currently only used when a known issue does not occur. When a known issue does occur, it is marked isKnown but does not contain any record of the withKnownIssue() comment, making it harder to understand why the issue was considered to be known, especially if there are multiple nested withKnownIssue() calls.

Modifications:

When an issue is marked "known" by a withKnownIssue() call, the recorded issue will now have a new knownIssueContext property containing the comment passed to withKnownIssue() (if any). This comment will be included in the messages array of the ABI.EncodedEvent that represents the issue.

If the issue is recorded within multiple nested withKnownIssue() calls, knownIssueContext corresponds to the innermost matching call.

Checklist:

  • Code and documentation should follow the style of the Style Guide.
  • If public symbols are renamed or modified, DocC references should be updated.

@grynspan grynspan added bug 🪲 Something isn't working issue-handling Related to Issue handling within the testing library labels Mar 11, 2025
@grynspan grynspan added this to the Swift 6.2 milestone Mar 11, 2025
@aroben
Copy link
Author

aroben commented Mar 11, 2025

@swift-ci test

### Motivation:

The Comment passed to withKnownIssue() is currently only used when a
known issue does not occur. When a known issue does occur, it is marked
isKnown but does not contain any record of the withKnownIssue() comment,
making it harder to understand why the issue was considered to be known,
especially if there are multiple nested withKnownIssue() calls.

### Modifications:

When an issue is marked "known" by a `withKnownIssue()` call, the
recorded issue will now have multiple comments in its `comments` array:

1. The comment passed to `withKnownIssue()`, if any
2. The Issue's own comment(s)

If the issue is recorded within multiple nested `withKnownIssue()`
calls, only the comment of the innermost matching call is added to the
Issue.

### Checklist:

- [x] Code and documentation should follow the style of the [Style Guide](https://github.com/apple/swift-testing/blob/main/Documentation/StyleGuide.md).
- [x] If public symbols are renamed or modified, DocC references should be updated.
@aroben aroben force-pushed the known-issue-comments branch from cf99d11 to c6cbd3d Compare March 11, 2025 18:17
@aroben
Copy link
Author

aroben commented Mar 11, 2025

@swift-ci test

2 similar comments
@aroben
Copy link
Author

aroben commented Mar 11, 2025

@swift-ci test

@aroben
Copy link
Author

aroben commented Mar 11, 2025

@swift-ci test

@aroben
Copy link
Author

aroben commented Mar 12, 2025

@stmontgomery I took a crack at putting the known-issue data into a separate stored property and converting isKnown to a computed property. Some questions:

  1. I left a setter in place for isKnown to try to preserve ABI compatibility. Does this seem important to you?
  2. What do you think about the names Issue.knownIssueContext and Issue.KnownIssueContext?
  3. I replaced EncodedIssue.isKnown with a new EncodedIssue.knownIssueContext and made KnownIssueContext conform to Codable. Does that seem like the right approach? Do I need to bump the ABI version or do anything else to handle compatibility?

@aroben
Copy link
Author

aroben commented Mar 12, 2025

@swift-ci test

@aroben
Copy link
Author

aroben commented Mar 12, 2025

@swift-ci test

@aroben
Copy link
Author

aroben commented Mar 12, 2025

@swift-ci test

@aroben aroben requested a review from stmontgomery March 12, 2025 18:01
Copy link
Contributor

@grynspan grynspan left a comment

Choose a reason for hiding this comment

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

This is not the direction I'd go with this change.

Copy link
Author

@aroben aroben left a comment

Choose a reason for hiding this comment

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

This is not the direction I'd go with this change.

@grynspan For reference, I've pushed my initial version of this PR here: main...aroben:swift-testing:known-issue-comments-v1

In that take, I just prepended the known-issue comment to Issue.comments and left it at that. As you saw, @stmontgomery encouraged me to pull it out into its own struct to make the difference between the Issue's comment and the known-issue comment explicit.

Maybe you two can sync up and agree on an approach here? I'm happy to do it either way.

@grynspan grynspan modified the milestones: Swift 6.2, Swift 6.x Apr 6, 2025
@aroben
Copy link
Author

aroben commented Apr 7, 2025

After discussion with @grynspan and @stmontgomery I've reverted the ABI changes. Instead, we append the known issue comment (if any) to ABI.EncodedEvent.messages for the .issueRecorded event.

@aroben
Copy link
Author

aroben commented Apr 7, 2025

@swift-ci please test

@aroben
Copy link
Author

aroben commented Apr 7, 2025

I wanted to see if the known issue comment now appears in swift test's output, so I added a new suite to this repo:

struct TryIt {
  @Test func knownIssueTest() async throws {
    withKnownIssue("known issue comment") {
      Issue.record("issue comment")
    }
  }

  @Test func knownIssueTest_throws() async throws {
    withKnownIssue("known issue comment") {
      struct TheError: Error {}
      throw TheError()
    }
  }
}

Here's what I got:

$ SWT_SF_SYMBOLS_ENABLED=no xcrun swift test --filter TryIt      
Building for debugging...
[1/1] Write swift-version--1EBDF3DDE0C0C582.txt
Build complete! (0.16s)
Test Suite 'Selected tests' started at 2025-04-07 10:48:10.677.
Test Suite 'swift-testingPackageTests.xctest' started at 2025-04-07 10:48:10.678.
Test Suite 'swift-testingPackageTests.xctest' passed at 2025-04-07 10:48:10.678.
	 Executed 0 tests, with 0 failures (0 unexpected) in 0.000 (0.000) seconds
Test Suite 'Selected tests' passed at 2025-04-07 10:48:10.678.
	 Executed 0 tests, with 0 failures (0 unexpected) in 0.000 (0.001) seconds
◇ Test run started.
↳ Testing Library Version: feb96cf762e5732f8702e1dc9755fa2908bfb5ba (modified)
◇ Suite TryIt started.
◇ Test knownIssueTest_throws() started.
◇ Test knownIssueTest() started.
✘ Test knownIssueTest() recorded a known issue at EventRecorderTests.swift:660:19: Issue recorded
↳ issue comment
↳ known issue comment
✘ Test knownIssueTest() passed after 0.001 seconds with 1 known issue.
✘ Test knownIssueTest_throws() recorded a known issue at EventRecorderTests.swift:665:19: Caught error: TheError()
↳ known issue comment
↳ known issue comment
✘ Test knownIssueTest_throws() passed after 0.001 seconds with 1 known issue.
✘ Suite TryIt passed after 0.001 seconds with 2 known issues.
✘ Test run with 2 tests passed after 0.001 seconds with 2 known issues.

This looks decent, but you can see that known issue comment is duplicated for knownIssueTest_throws(). This is because _matchError puts the known issue comment directly in the Issue.comments array, but then we also include it in Issue.knownIssueContext, so we end up with two copies of the comment.

Here are some ways to handle this:

  1. Leave it as-is
  2. Remove the known issue comment from Issue.comments, leaving it only in Issue.knownIssueContext.
    a. This seems right from a modeling perspective, but will in practice remove the known issue comment from Xcode's inline error annotation UI
  3. Omit the known issue comment from Issue.knownIssueContext for thrown errors
    a. This feels bad, though I'm not sure it has any practical downsides.
  4. Teach HumanReadableOutputRecorder to somehow detect this case and avoid the duplication
    a. We should be able to detect this reliably: if case .errorCaught = issue.kind, issue.isKnown

I'm leaning toward 4 (and would add a test for it of course).

@aroben
Copy link
Author

aroben commented Apr 7, 2025

I'm leaning toward 4 (and would add a test for it of course).

I implemented this.

@aroben
Copy link
Author

aroben commented Apr 7, 2025

@swift-ci please test

@@ -454,7 +454,11 @@ extension Event.HumanReadableOutputRecorder {
}
additionalMessages += _formattedComments(issue.comments)
if let knownIssueComment = issue.knownIssueContext?.comment {
additionalMessages.append(_formattedComment(knownIssueComment))
if case .errorCaught = issue.kind {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you explain why this specific case is being singled out?

Copy link
Author

Choose a reason for hiding this comment

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

Pushed an updated comment, how does it look to you?

Copy link
Contributor

Choose a reason for hiding this comment

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

Seems like the correct fix might be to remove the comment from _matchError() so that nothing needs to be special-cased?

Copy link
Contributor

Choose a reason for hiding this comment

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

Note for compatibility, we would want to clear the comments property of Issue in _matchError() after calling the matcher function as there could be code out there that inspects it:

withKnownIssue {
  ...
} matching: { issue in
  issue.comments.first?.contains("abc") ?? false
}

(Something like that.)

Copy link
Author

@aroben aroben Apr 7, 2025

Choose a reason for hiding this comment

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

Seems like the correct fix might be to remove the comment from _matchError() so that nothing needs to be special-cased?

We certainly could, but that has a downside that I mentioned in #1014 (comment):

  1. Remove the known issue comment from Issue.comments, leaving it only in Issue.knownIssueContext.
    a. This seems right from a modeling perspective, but will in practice remove the known issue comment from Xcode's inline error annotation UI

Copy link
Author

Choose a reason for hiding this comment

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

Yes, but I'm trying not to introduce any regressions along the way.

Copy link
Contributor

Choose a reason for hiding this comment

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

I appreciate that :) And that's a good goal—but we also need to be mindful of creating technical debt for ourselves.

Copy link
Author

Choose a reason for hiding this comment

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

OK, so do you think I should take approach 2 despite the regression in Xcode and the weirdness of Issue.comments changing after the matcher runs?

Copy link
Author

Choose a reason for hiding this comment

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

We discussed this and decided to remove the comment entirely from _matchError. This has a slight risk of causing incompatibilities because the matcher function will no longer see the comment there, but it's actually kind of weird that the matcher sees the comment anyway since it is always the same as the comment passed to withKnownIssue:

@Test func knownIssueCommentPassedToIssueMatcher() throws {
    struct E: Error {}
    try withKnownIssue("Known Issue Comment") {
        throw E()
    } matching: { issue in
        print("Comments passed to matcher:", issue.comments)
        // prints: Comments passed to matcher: ["Known Issue Comment"]
        // which seems unhelpful since we can see the comment a few lines up in the withKnownIssue call
        return true
    }
}```

Copy link
Contributor

Choose a reason for hiding this comment

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

I agree we should stop including the comment passed to withKnownIssue (if any) in the comments of the Issue constructed in _matchError().

@aroben
Copy link
Author

aroben commented Apr 7, 2025

@swift-ci please test

Copy link
Contributor

@stmontgomery stmontgomery left a comment

Choose a reason for hiding this comment

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

A few nitpicky formatting comments I saw as I caught up with the latest changes

/// - Returns: A formatted string representing `comments`, or `nil` if there
/// are none.
/// - Returns: An array of formatted messages representing `comments`, or an
/// empty array if there are none.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
/// empty array if there are none.
/// empty array if there are none.

/// - Returns: A known issue context containing information about the known
/// issue, if the issue is considered "known" by this known issue scope or any
/// ancestor scope, or `nil` otherwise.
typealias Matcher = @Sendable (Issue) -> Issue.KnownIssueContext?
Copy link
Contributor

Choose a reason for hiding this comment

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

Explicitly declare issue so that the - Parameters: entry refers to it:

Suggested change
typealias Matcher = @Sendable (Issue) -> Issue.KnownIssueContext?
typealias Matcher = @Sendable (_ issue: Issue) -> Issue.KnownIssueContext?

Comment on lines +22 to +24
/// - Returns: A known issue context containing information about the known
/// issue, if the issue is considered "known" by this known issue scope or any
/// ancestor scope, or `nil` otherwise.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
/// - Returns: A known issue context containing information about the known
/// issue, if the issue is considered "known" by this known issue scope or any
/// ancestor scope, or `nil` otherwise.
///
/// - Returns: A known issue context containing information about the known
/// issue, if the issue is considered "known" by this known issue scope or any
/// ancestor scope, or `nil` otherwise.

@@ -454,7 +454,11 @@ extension Event.HumanReadableOutputRecorder {
}
additionalMessages += _formattedComments(issue.comments)
if let knownIssueComment = issue.knownIssueContext?.comment {
additionalMessages.append(_formattedComment(knownIssueComment))
if case .errorCaught = issue.kind {
Copy link
Contributor

Choose a reason for hiding this comment

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

I agree we should stop including the comment passed to withKnownIssue (if any) in the comments of the Issue constructed in _matchError().

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug 🪲 Something isn't working issue-handling Related to Issue handling within the testing library
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants