diff --git a/Documentation/ABI/TestContent.md b/Documentation/ABI/TestContent.md index 5c29f6c7..f5c2ce46 100644 --- a/Documentation/ABI/TestContent.md +++ b/Documentation/ABI/TestContent.md @@ -21,14 +21,8 @@ the testing library are stored in dedicated platform-specific sections: | Platform | Binary Format | Section Name | |-|:-:|-| -| macOS | Mach-O | `__DATA_CONST,__swift5_tests` | -| iOS | Mach-O | `__DATA_CONST,__swift5_tests` | -| watchOS | Mach-O | `__DATA_CONST,__swift5_tests` | -| tvOS | Mach-O | `__DATA_CONST,__swift5_tests` | -| visionOS | Mach-O | `__DATA_CONST,__swift5_tests` | -| Linux | ELF | `PT_NOTE`[^1] | -| FreeBSD | ELF | `PT_NOTE`[^1] | -| Android | ELF | `PT_NOTE`[^1] | +| macOS, iOS, watchOS, tvOS, visionOS | Mach-O | `__DATA_CONST,__swift5_tests` | +| Linux, FreeBSD, Android | ELF | `PT_NOTE`[^1] | | WASI | Statically Linked | `swift5_tests` | | Windows | PE/COFF | `.sw5test` | @@ -36,13 +30,29 @@ the testing library are stored in dedicated platform-specific sections: are stored in ELF program headers of type `PT_NOTE`. Take care not to remove these program headers (for example, by invoking [`strip(1)`](https://www.man7.org/linux/man-pages/man1/strip.1.html).) -### Determining the type of test content +### Record headers Regardless of platform, all test content records created and discoverable by the -testing library start have the name `"Swift Testing"` stored in the implied -`n_name` field of their underlying ELF Notes. Each record's _type_ (stored in -the underlying ELF Note's `n_type` field) determines how the record will be -interpreted at runtime: +testing library have the following structure: + +```c +struct SWTTestContentHeader { + int32_t n_namesz; + int32_t n_descsz; + int32_t n_type; + char n_name[n_namesz]; + // ... +}; + +The size of `n_name` is dynamic and cannot be statically computed. The testing +library always generates the name `"Swift Testing"` and specifies an `n_namesz` +value of `20` (the string being null-padded to the correct length), but other +content may be present in the same section whose header content differs. For +more information about this structure such as its alignment requirements, see +the documentation for the [ELF format](https://man7.org/linux/man-pages/man5/elf.5.html). + +Each record's _kind_ (stored in the `n_type` field) determines how the record +will be interpreted at runtime: | Type Value | Interpretation | |-:|-| @@ -54,18 +64,27 @@ interpreted at runtime: -### Loading test content from a record +### Record contents -For all currently-defined record types, the header and name are followed by a -structure of the following form: +For all currently-defined record types, the header structure is immediately +followed by the actual content of the record. A test content record currently +contains an `accessor` function to load the corresponding Swift content and a +`flags` field whose value depends on the type of record. The overall structure +of a record therefore looks like: ```c struct SWTTestContent { + SWTTestContentHeader header; bool (* accessor)(void *); uint64_t flags; }; ``` +This structure may grow in the future as needed. Check the `header.n_descsz` +field to determine if there are additional fields present. Do not assume that +the size of this structure will remain fixed over time or that all discovered +test content records are the same size. + #### The accessor field The function `accessor` is a C function whose signature in Swift can be restated @@ -80,19 +99,20 @@ Swift type and returns `true`, or returns `false` if it could not generate the relevant content. On successful return, the caller is responsible for deinitializing the memory at `outValue` when done with it. -The concrete Swift type of `accessor`'s result depends on the type of record: +The concrete Swift type of the value written to `outValue` depends on the type +of record: | Type Value | Return Type | |-:|-| | < `0` | Undefined (**do not use**) | -| `0` ... `99` | `nil` | +| `0` ... `99` | Reserved (**do not use**) | | `100` | `@Sendable () async -> Test`[^2] | -| `101` | `ExitTest` (owned by caller) | +| `101` | `ExitTest` (consumed by caller) | [^2]: This signature is not the signature of `accessor`, but of the Swift - function reference it returns. This level of indirection is necessary - because loading a test or suite declaration is an asynchronous operation, - but C functions cannot be `async`. + function reference it writes to `outValue`. This level of indirection is + necessary because loading a test or suite declaration is an asynchronous + operation, but C functions cannot be `async`. #### The flags field