Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

37 changes: 20 additions & 17 deletions Sources/DevTesting/Documentation.docc/UsingRandomValueGeneration.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ automatically logs the random seeds used in each test, allowing you to reproduce
exactly.

The random value generation system integrates seamlessly with Swift Testing, automatically logging
seeds both to the console and as test attachments. This means that when a test fails, you have
multiple ways to capture and reuse the seed that caused the failure, making debugging much more
straightforward than with traditional random testing approaches.
seeds both to Apple’s Unified Logging system and as test attachments. This means that when a test
fails, you have multiple ways to capture and reuse the seed that caused the failure, making
debugging much more straightforward than with traditional random testing approaches.

In this guide, we’ll explore how to use DevTesting’s random value generation effectively in your
test suites, from basic random data creation to advanced patterns for testing complex scenarios with
Expand Down Expand Up @@ -51,10 +51,10 @@ and add the required property:
}
}

The ``makeRandomNumberGenerator()`` function creates a seeded random number generator and
automatically logs the seed for reproducibility. Each time your test runs, it will use a different
seed (based on the current time), giving you varied test data while maintaining the ability to
reproduce any specific run.
The ``RandomValueGenerating/makeRandomNumberGenerator(seed:)`` function creates a seeded random
number generator and automatically logs the seed for reproducibility. Each time your test runs, it
will use a different seed (based on the current time), giving you varied test data while maintaining
the ability to reproduce any specific run.


### The required property
Expand Down Expand Up @@ -167,8 +167,9 @@ DevTesting includes generators for common Foundation types:
let complexURL = randomURL(includeFragment: true, includeQueryItems: true)
}

The ``randomURL()`` function creates realistic URLs with random schemes, hosts, paths, and
optionally query parameters and fragments.
The ``RandomValueGenerating/randomURL(includeFragment:includeQueryItems:)`` function creates
realistic URLs with random schemes, hosts, paths, and optionally query parameters and
fragments.


### Collections
Expand All @@ -190,8 +191,9 @@ Work with collections and enumerations easily:
let emptyResult = randomElement(in: [Int]()) // nil
}

Note that both ``randomElement(in:)`` and ``randomCase(of:)`` return optionals because they return
`nil` when called on empty collections.
Note that both ``RandomValueGenerating/randomElement(in:)`` and
``RandomValueGenerating/randomCase(of:)`` return optionals because they return `nil` when called on
empty collections.


### Collection generation
Expand Down Expand Up @@ -232,12 +234,13 @@ seeds and provides multiple ways to reproduce test failures.

### Automatic seed logging

Every time you create a random number generator with ``makeRandomNumberGenerator()``, DevTesting
automatically logs the seed being used. This logging happens in two places:
Every time you create a random number generator with
``RandomValueGenerating/makeRandomNumberGenerator(seed:)``, DevTesting automatically logs the seed
being used. This logging happens in two places:

1. **Log output**: The seed is logged using the Unified Logging system. It is logged with
subsystem `"DevTesting"` and category `"randomization"`. This output is logged to Xcode’s
console.
console, and is also accessible via the Console app and the `log` command line tool.

MyTestsTests.MyTests.testSomething(): Using random seed 4636473893658426368

Expand All @@ -252,7 +255,7 @@ you’re debugging interactively or analyzing results in a CI system.
### Setting custom seeds

When you need to reproduce a specific test failure, you can override the automatic seed generation
by setting the ``randomSeed`` property:
by setting the ``RandomValueGenerating/randomSeed`` property:

struct MyTests: RandomValueGenerating {
var randomNumberGenerator = makeRandomNumberGenerator()
Expand Down Expand Up @@ -283,8 +286,8 @@ When a test fails due to random inputs, follow these steps to reproduce and debu
1. **Find the seed**: Look for the seed in your test output or check the test attachments for the
`randomSeed_*.txt` file.

2. **Set the seed**: Use the ``randomSeed`` property in your test to force the same sequence of
random values:
2. **Set the seed**: Use the ``RandomValueGenerating/randomSeed`` property in your test to force
the same sequence of random values:

@Test
mutating func testThatFailed() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ extension BinaryFloatingPoint where RawSignificand: FixedWidthInteger {
/// Returns a random value in the half-open range that almost always can be printed precisely in decimal.
///
/// This function is useful when generating random values that need to be serialized as strings, e.g., in a JSON
/// payload or property list and converted back to binary floating points. It works by attempting to produce a
/// random value in the range as an integer plus a multiple of 1/256. Given this, if `range`’s width is sufficiently
/// small, this function may not work. In that case, it will return a value within the range, but not necessarily
/// printable.
/// payload or property list, and converted back to binary floating points. It works by attempting to produce a
/// random integer value in the range, and then adds a multiple of 1/256. Given this, if `range`’s width is
/// sufficiently small, this function may not work. In that case, it will return a value within the range, but not
/// necessarily printable.
///
/// - Parameters:
/// - range: The range in which to create a random value. `range` must not be empty, and should generally be
Expand All @@ -38,10 +38,10 @@ extension BinaryFloatingPoint where RawSignificand: FixedWidthInteger {
/// Returns a random value in the closed range that almost always can be printed precisely in decimal.
///
/// This function is useful when generating random values that need to be serialized as strings, e.g., in a JSON
/// payload or property list and converted back to binary floating points. It works by attempting to produce a
/// random value in the range as an integer plus a multiple of 1/256. Given this, if `range`’s width is sufficiently
/// small, this function may not work. In that case, it will return a value within the range, but not necessarily
/// printable.
/// payload or property list, and converted back to binary floating points. It works by attempting to produce a
/// random integer value in the range, and then adds a multiple of 1/256. Given this, if `range`’s width is
/// sufficiently small, this function may not work. In that case, it will return a value within the range, but not
/// necessarily printable.
///
/// - Parameters:
/// - range: The range in which to create a random value. `range` must not be empty, and should generally be
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ private let randomizationLogger = Logger(subsystem: "DevTesting", category: "ran
/// }
///
/// Importantly, ``makeRandomNumberGenerator(seed:)`` and ``randomSeed``’s setter log the seed (using subsystem
/// `DevTesting` and category `randomization`), which appear in Xcode’s output and can be queried via the command-line
///
/// `DevTesting` and category `randomization`), which appear in Xcode’s output and can be queried via the command-line.
/// The seed value is also added as an attachment to the current test.
public protocol RandomValueGenerating {
/// The random number generator that the type uses to generate values.
var randomNumberGenerator: SeedableRandomNumberGenerator { get set }
Expand All @@ -49,7 +49,9 @@ public protocol RandomValueGenerating {
extension RandomValueGenerating {
/// Creates and returns a new seedable random number generator with the specified seed.
///
/// Logs the seed to the standard output device before returning.
/// Logs the seed to a Unified Logger whose subsystem is `DevTesting` and whose category is `randomization`. This
/// value appear in Xcode’s output and can be viewed using the Console app or queried via the command-line. The seed
/// value is also added as an attachment to the current test.
///
/// - Parameter seed: The seed with which to initialize the random number generator. By default, this is a value
/// based on the the current time.
Expand All @@ -63,7 +65,9 @@ extension RandomValueGenerating {

/// The random number generator’s seed.
///
/// This property’s setter logs the new seed to the standard output device.
/// This property’s setter logs the seed to a Unified Logger whose subsystem is `DevTesting` and whose category is
/// `randomization`. This value appear in Xcode’s output and can be viewed using the Console app or queried via the
/// command-line. The seed value is also added as an attachment to the current test.
public var randomSeed: UInt64 {
get {
return randomNumberGenerator.seed
Expand All @@ -76,7 +80,9 @@ extension RandomValueGenerating {
}


/// Outputs the current seed value to the standard output device.
/// Logs the specified seed to the randomization logger and adds it as an attachment to the current test.
///
/// - Parameter seed: The seed to log.
private static func logSeed(_ seed: UInt64) {
let fullyQualifiedTestName = Test.current.map { (test) in
"\(test.id.moduleName).\(test.id.nameComponents.joined(separator: "."))"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import os

/// An efficient, thread-safe pseudo-random number generator that can be seeded to produce repeatable results.
///
/// This type is a Swift translation version of Sebastiano Vigna’s [public domain C implementation of
/// This type is a Swift translation of Sebastiano Vigna’s [public domain C implementation of
/// xorshiro128++](https://prng.di.unimi.it/xoroshiro128plusplus.c), though that may change in the future. See [Vigna’s
/// PRNG shootout](https://prng.di.unimi.it) for more information about the xoroshiro128++.
public struct SeedableRandomNumberGenerator: RandomNumberGenerator, Sendable {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ extension URLComponents {
/// The URL components are generated as follows:
///
/// - The scheme is `"https"`
/// - The host is of the form `subdomain<XXXXX>.domain<XXXXX>.<tld>`. Here, _<XXXXX>_ are five random
/// alphanumeric characters, and _<tld>_ is one of _com_, _edu_, _gov_, _net_, or _org_.
/// - The host is of the form `subdomain«XXXXX».domain«YYYYY».«tld»`. Here, _«XXXXX»_ and _«YYYYY»_ are strings of
/// five random alphanumeric characters, and _«tld»_ is one of _com_, _edu_, _gov_, _net_, or _org_.
/// - The path contains 1–5 components, each of which are random alphanumeric strings between 1–5 characters long.
/// - If the function includes fragments, the fragment is a random alphanumeric string between 3–5 characters
/// long.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@

import Foundation

enum UninhabitedEnum: CaseIterable {
// Intentionally empty
}
enum UninhabitedEnum: CaseIterable {}


enum InhabitedEnum: CaseIterable {
Expand Down
Loading