Skip to content

Record issues generated within exit tests. #697

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

Merged
merged 9 commits into from
Sep 20, 2024

Conversation

grynspan
Copy link
Contributor

@grynspan grynspan commented Sep 17, 2024

This PR introduces a "back channel" file handle to exit tests, allowing us to record issues that occur within exit test bodies. For example:

await #expect(exitsWith: .failure) {
  let context = try #require(possiblyMissingContext)
  ...
}

In this example, if the call to try #require() finds nil, it will record an issue, but that issue today will be lost because there's no mechanism to forward the issue back to the parent process hosting the exit test. This PR fixes that!

Issues are converted to JSON using the same schema we use for event handling, then written over a pipe back to the parent process where they are decoded. This decoding is lossy, so there will be further refinement needed here to try to preserve more information about the recorded issues. That said, "it's got good bones" right?

On Darwin, Linux, and FreeBSD, the pipe's write end is allowed to survive into the child process (i.e. no FD_CLOEXEC). On Windows, the equivalent is to tell CreateProcessW() to explicitly inherit a HANDLE. The identity of this file descriptor or handle is passed to the child process via environment variable. The child process then parses the file descriptor or handle out of the environment and converts it back to a FileHandle that is then connected to an instance of Configuration with an event handler set, and off we go.

Because we can now report these issues back to the parent process, I've removed the compile-time diagnostic in the #expect(exitsWith:) macro implementation that we emit when we see a nested #expect() or #require() 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 enhancement New feature or request exit-tests ☠️ Work related to exit tests issue-handling Related to Issue handling within the testing library labels Sep 17, 2024
@grynspan grynspan self-assigned this Sep 17, 2024
@grynspan
Copy link
Contributor Author

@swift-ci test

6 similar comments
@grynspan
Copy link
Contributor Author

@swift-ci test

@grynspan
Copy link
Contributor Author

@swift-ci test

@grynspan
Copy link
Contributor Author

@swift-ci test

@grynspan
Copy link
Contributor Author

@swift-ci test

@grynspan
Copy link
Contributor Author

@swift-ci test

@grynspan
Copy link
Contributor Author

@swift-ci test

@grynspan grynspan force-pushed the jgrynspan/exit-test-back-channel branch from 4b4ec43 to 32b58ed Compare September 17, 2024 03:41
@grynspan
Copy link
Contributor Author

@swift-ci test

1 similar comment
@grynspan
Copy link
Contributor Author

@swift-ci test

@grynspan grynspan force-pushed the jgrynspan/exit-test-back-channel branch from c783c17 to ff61fc6 Compare September 17, 2024 12:23
@grynspan
Copy link
Contributor Author

@swift-ci test

2 similar comments
@grynspan
Copy link
Contributor Author

@swift-ci test

@grynspan
Copy link
Contributor Author

@swift-ci test

@grynspan grynspan requested a review from compnerd September 17, 2024 13:33
@grynspan
Copy link
Contributor Author

@swift-ci test Linux

@grynspan grynspan force-pushed the jgrynspan/exit-test-back-channel branch from b291707 to 324f6b8 Compare September 17, 2024 13:48
@grynspan
Copy link
Contributor Author

@swift-ci test

@grynspan grynspan force-pushed the jgrynspan/exit-test-back-channel branch 2 times, most recently from 6845910 to c635ea3 Compare September 17, 2024 13:52
grynspan added a commit that referenced this pull request Sep 17, 2024
This PR moves the logic that implements JSON Lines support (i.e. the code that
strips newlines from JSON generated by Foundation) out of EntryPoint.swift so
that it can be used by other callers.

This change was originally part of #697. I'm splitting it out into its own PR to
make that one (both really) easier to read and understand.
@grynspan
Copy link
Contributor Author

@swift-ci test

1 similar comment
@grynspan
Copy link
Contributor Author

@swift-ci test

@grynspan grynspan added this to the Swift 6.1 milestone Sep 17, 2024
@grynspan
Copy link
Contributor Author

@swift-ci test

This PR introduces a "back channel" file handle to exit tests, allowing us to
record issues that occur within exit test bodies. For example:

```swift
await #expect(exitsWith: .failure) {
  let context = try #require(possiblyMissingContext)
  ...
}
```

In this example, if the call to `try #require()` finds `nil`, it will record an
issue, but that issue today will be lost because there's no mechanism to forward
the issue back to the parent process hosting the exit test. This PR fixes that!

Issues are converted to JSON using the same schema we use for event handling,
then written over a pipe back to the parent process where they are decoded. This
decoding is lossy, so there will be further refinement needed here to try to
preserve more information about the recorded issues. That said, "it's got good
bones" right?

On Darwin, Linux, and FreeBSD, the pipe's write end is allowed to survive into
the child process (i.e. no `FD_CLOEXEC`). On Windows, the equivalent is to tell
`CreateProcessW()` to explicitly inherit a `HANDLE`. The identity of this file
descriptor or handle is passed to the child process via environment variable.
The child process then parses the file descriptor or handle out of the
environment and converts it back to a `FileHandle` that is then connected to an
instance of `Configuration` with an event handler set, and off we go.

Because we can now report these issues back to the parent process, I've removed
the compile-time diagnostic in the `#expect(exitsWith:)` macro implementation
that we emit when we see a nested `#expect()` or `#require()` call.
…nction so you can't break things by calling it twice in the same process
…y applies to our own entry point and third parties would need to supply their own configuration/etc. anyway
@grynspan grynspan force-pushed the jgrynspan/exit-test-back-channel branch from a03037b to ec89f02 Compare September 19, 2024 21:02
@grynspan
Copy link
Contributor Author

@swift-ci test

1 similar comment
@grynspan
Copy link
Contributor Author

@swift-ci test

@grynspan
Copy link
Contributor Author

@swift-ci test

@grynspan
Copy link
Contributor Author

@swift-ci test

message.symbol == .details ? Comment(rawValue: message.text) : nil
}
let sourceContext = SourceContext(
backtrace: nil, // `issue._backtrace` will have the wrong address space.
Copy link
Contributor

@stmontgomery stmontgomery Sep 20, 2024

Choose a reason for hiding this comment

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

Can we file an issue to follow up here and eventually propagate and preserve some of the symbolicated address of the backtrace? I know the specific numeric addresses will not be useful, but if the backtrace was symbolicated, it would be great to propagate symbol names. But I assume that will require a bit more effort and isn't in scope here

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's something we can do once we have a proper symbolication mechanism rather than the POC that's in the repo now.

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.

Nice!

@grynspan grynspan merged commit 9d1bb5f into main Sep 20, 2024
3 checks passed
@grynspan grynspan deleted the jgrynspan/exit-test-back-channel branch September 20, 2024 18:25
grynspan added a commit that referenced this pull request Sep 26, 2024
#718)

Hot on the heels of #697…

If an exit test records an issue of kind `.errorCaught()`, we currently
treat it the same as any other issue and translate it to a flat
`.unconditional` issue in the parent process:

```swift
await #expect(exitsWith: .failure) {
  Issue.record(NoSuchWeaselError()) // unconditional issue recorded
}
```

This PR distinguishes the `.errorCaught` issue kind and records an
`.errorCaught` issue in the parent process. Fidelity of errors across a
process boundary is weak, but we encode a minimal representation of the
error that should be good enough for most uses. If you need
`withKnownIssue()`, or `#expect(throws:)`, you can call them _inside_
the exit test.

### 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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request exit-tests ☠️ Work related to exit tests 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