अपने Mac पर यादृच्छिक Swift परीक्षण इनपुट फिर से चलाने के लिए, हर परीक्षण केस के लिए नए जनरेटर में स्पष्ट सीड दें। जनरेटर, इनपुट मैपिंग और ड्रॉ क्रम को स्थिर रखें, फिर हर विफलता के साथ सीड दर्ज करें।

यह वॉकथ्रू एक छोटा स्थानीय Swift पैकेज बनाता है, जो एक कृत्रिम अंकगणितीय गणना की जाँच करता है। इसमें Swift Testing का पूरा उदाहरण और XCTest का विकल्प दिया गया है। सभी परीक्षण डेटा स्थानीय गणनाओं से आता है; पैकेज की कोई बाहरी निर्भरता नहीं है।

सीड वास्तव में किस चीज़ को दोहराने योग्य बनाती है?

बदलाव के दो अलग स्रोत हैं: परीक्षण द्वारा बनाए गए मान और वह क्रम जिसमें परीक्षण रनर परीक्षणों को शेड्यूल करता है। एप्लिकेशन द्वारा निर्धारित सीड पहले स्रोत को तभी नियंत्रित करती है, जब हर जनरेट किया गया मान संबंधित जनरेटर से आता हो। Apple के दस्तावेज़ बताते हैं कि सामान्य रैंडम ऑपरेशन डिफ़ॉल्ट रूप से स्वतः सीड किए गए SystemRandomNumberGenerator का उपयोग करते हैं। इसके दस्तावेज़ित इनिशियलाइज़र में कोई सीड स्वीकार नहीं की जाती। SystemRandomNumberGenerator

Xcode परीक्षणों के बीच निर्भरताएँ उजागर करने के लिए यादृच्छिक निष्पादन क्रम भी देता है। इसकी टेस्ट-प्लान संबंधी दस्तावेज़ी सामग्री वर्णानुक्रम या यादृच्छिक निष्पादन क्रम का वर्णन करती है, लेकिन उस सेटिंग के लिए उपयोगकर्ता द्वारा दी गई रीप्ले सीड का उल्लेख नहीं करती। इसलिए यहाँ समर्थित तरीका स्वतंत्र परीक्षणों के भीतर स्पष्ट रूप से सीड किया गया इनपुट जनरेटर है। इससे रनर की शेड्यूलिंग को दोहराने की गारंटी नहीं मिलती। परीक्षणों को टेस्ट प्लान में व्यवस्थित करके कोड आकलन बेहतर बनाना

किसी अस्थिर परीक्षण को बदलने से पहले इस निर्णय-सहायिका का उपयोग करें:

रन के बीच क्या बदलता है? क्या नियंत्रित करें
जनरेट किए गए संख्यात्मक इनपुट सीड, जनरेटर कार्यान्वयन, इनपुट मैपिंग और ड्रॉ क्रम
किसी दूसरे परीक्षण के चलने के बाद परिणाम परिवर्तनशील स्थिति और प्रति-परीक्षण इनिशियलाइज़ेशन
केवल समवर्ती निष्पादन के दौरान परिणाम साझा परिवर्तनशील संसाधन और एक-दूसरे पर चढ़ते कार्य
टूलचेन अपडेट के बाद इनपुट इनपुट-मैपिंग कार्यान्वयन और दर्ज किए गए वास्तविक इनपुट

Swift, RandomNumberGenerator के ज़रिए कस्टम जनरेटरों का स्पष्ट समर्थन करता है। इसमें आवश्यक ऑपरेशन एक म्यूटेटिंग next() है, जो UInt64 लौटाता है। Swift प्रोजेक्ट के स्रोत-दस्तावेज़ों में परीक्षण के लिए दोहराने योग्य क्रम उपलब्ध कराना, सीड वाले जनरेटर देने का एक कारण बताया गया है। Random.swift

छोटा स्थानीय परीक्षण पैकेज बनाएँ

Xcode और उपलब्ध Swift 6 या बाद के टूलचेन वाला Mac उपयोग करें। Terminal में चुने गए कमांड-लाइन टूलचेन की जाँच करें, फिर एक नई डायरेक्टरी बनाएँ और लाइब्रेरी पैकेज शुरू करें:

swift --version
mkdir SeedReplay
cd SeedReplay
swift package init --type library

Swift प्रोजेक्ट के इंटीग्रेशन परीक्षण swift package init --type library के ज़रिए लाइब्रेरी-पैकेज बनाना परखते हैं। Swift 6 में Swift Testing सीधे शामिल है, इसलिए इस उदाहरण को import Testing के लिए किसी पैकेज निर्भरता की ज़रूरत नहीं है। Swift Package Manager उपलब्ध होने पर Swift Testing और XCTest, दोनों प्रकार के परीक्षण चलाता है। Swift पैकेज इनिशियलाइज़ेशन इंटीग्रेशन टेस्ट, Swift 6 की घोषणा

पैकेज मैनिफ़ेस्ट और लाइब्रेरी स्रोत को वैसे ही रखें। बनाई गई नमूना परीक्षण फ़ाइल को नीचे दी गई परीक्षण फ़ाइलों से बदलें; सभी फ़ाइलें Tests/SeedReplayTests के भीतर हों। इस प्रदर्शन को स्व-निहित रखने के लिए अंकगणितीय फ़ंक्शन परीक्षण-सहायता फ़ाइल में रखा गया है। किसी मौजूदा प्रोजेक्ट में इसके बजाय वास्तविक परीक्षणाधीन फ़ंक्शन इस्तेमाल करें।

सीड और इनपुट-जनरेशन अनुबंध तय करें

Tests/SeedReplayTests/ReplaySupport.swift बनाएँ और उसमें यह पूरी सामग्री रखें:

import Foundation

// Local synthetic test-data generator.
// Mutable value state: give each test case its own instance.
// Its output sequence is part of this example's replay contract.
struct ReplayGenerator: RandomNumberGenerator {
    private var state: UInt64

    init(seed: UInt64) {
        state = seed
    }

    mutating func next() -> UInt64 {
        state &+= 0x9E3779B97F4A7C15
        var value = state
        value = (value ^ (value >> 30)) &* 0xBF58476D1CE4E5B9
        value = (value ^ (value >> 27)) &* 0x94D049BB133111EB
        return value ^ (value >> 31)
    }
}

enum ReplayConfigurationError: Error {
    case invalidSeed(String)
}

func replaySeeds() throws -> [UInt64] {
    guard let text = ProcessInfo.processInfo.environment["TEST_SEED"] else {
        return [0, 42, 2026]
    }

    guard !text.isEmpty,
          text.utf8.allSatisfy({ $0 >= 48 && $0 <= 57 }),
          let seed = UInt64(text) else {
        throw ReplayConfigurationError.invalidSeed(text)
    }

    return [seed]
}

func generatedInputs(seed: UInt64) -> [Int] {
    var generator = ReplayGenerator(seed: seed)
    return (0..<256).map { _ in
        // Exactly one draw per input; retain the upper ten bits.
        Int(generator.next() >> 54)
    }
}

func triangularNumber(_ n: Int) -> Int {
    precondition((0...1023).contains(n))
    return n * (n + 1) / 2
}

func referenceTotal(_ n: Int) -> Int {
    precondition((0...1023).contains(n))
    return (0...n).reduce(0, +)
}

कॉन्फ़िगरेशन शून्य सहित दशमलव UInt64 स्वीकार करता है। अगर TEST_SEED सेट नहीं है, तो तीन स्पष्ट सीड चुनी जाती हैं; खाली, ऋणात्मक, गैर-संख्यात्मक या सीमा से बड़ी वैल्यू पर त्रुटि आती है। इसलिए गलत टाइप की गई सीड चुपचाप कोई अलग रन नहीं चुन सकती।

जनरेटर स्पष्ट रैपिंग अंकगणित का उपयोग करता है। हर जनरेट किए गए इनपुट के लिए एक आउटपुट के ऊपरी दस बिट लिए जाते हैं, जिससे 0 से 1023 तक के मान मिलते हैं। यह मैपिंग स्रोत कोड में जानबूझकर स्थिर रखी गई है। अंकगणित छोटा रहता है: सबसे बड़ा अपेक्षित परिणाम 523776 है। यह सीमित कृत्रिम इनपुट रेसिपी है; इससे सांख्यिकीय परीक्षण कवरेज का कोई दावा नहीं किया जाता।

यहाँ Int.random(in:using:) से क्यों बचें? यह API दिए गए जनरेटर का समर्थन करती है और किसी स्थिर टूलचेन के साथ रीप्ले करने में उपयोगी है। हालाँकि, Apple स्पष्ट रूप से चेतावनी देता है कि इसकी मान-निर्माण एल्गोरिदम Swift के अलग-अलग संस्करणों में बदल सकती है, भले ही जनरेटर वही अंतर्निहित क्रम बनाए। इस उदाहरण की छोटी मैपिंग पर अपना नियंत्रण रखने से यह खास निर्भरता हट जाती है। Int.random(in:using:)

Swift Testing संस्करण चलाएँ और रीप्ले करें

Tests/SeedReplayTests/SeedReplayTests.swift बनाएँ:

import Testing

@Test(arguments: try replaySeeds())
func triangularTotals(seed: UInt64) {
    print("triangularTotals seed=\(seed) recipe=upper10-v1 count=256")

    for (index, n) in generatedInputs(seed: seed).enumerated() {
        let actual = triangularNumber(n)
        let expected = referenceTotal(n)

        #expect(
            actual == expected,
            "seed=\(seed) case=\(index) n=\(n) actual=\(actual) expected=\(expected)"
        )

        if actual != expected {
            return
        }
    }
}

@Test
func replayRecipeIsStable() {
    var generator = ReplayGenerator(seed: 0)
    let firstWord = generator.next()
    #expect(firstWord == 0xE220A8397B1DCDAF)

    let firstInputs = Array(generatedInputs(seed: 0).prefix(3))
    #expect(firstInputs == [904, 441, 27])
}

पैरामीटरयुक्त परीक्षण हर सीड को अलग परीक्षण केस बनाता है। Apple के दस्तावेज़ों के मुताबिक arguments: कलेक्शन के तत्वों को परीक्षण आमंत्रणों तक पहुँचाता है और थ्रो करने वाले एक्सप्रेशन स्वीकार करता है। इससे परीक्षण मॉडल में सीड का चयन स्पष्ट रहता है। स्पष्ट लूप इंडेक्स उस सीड के भीतर एक-एक जनरेट किए गए इनपुट की पहचान करता है। पैरामीटरयुक्त परीक्षण लागू करना

डिफ़ॉल्ट कलेक्शन चलाएँ, फिर एक सीड को दो बार रीप्ले करें:

swift test
TEST_SEED=42 swift test
TEST_SEED=42 swift test

स्रोत अपरिवर्तित रहने पर इन दोनों सीड वाले रन में समान संख्यात्मक इनपुट बनने चाहिए। सीड 42 के लिए पहले तीन इनपुट 759, 163 और 285 हैं। परीक्षण उस सीड के लिए 256 इनपुट जाँचता है। अलग स्थिरता परीक्षण जानबूझकर सीड शून्य पर स्थिर रहता है: यह चुनी गई अंकगणितीय-परीक्षण सीड से स्वतंत्र रूप से जनरेटर और मैपिंग को जाँचता है।

दिए गए कार्यान्वयन में सभी assertions सफल होने चाहिए। शोध परिवेश में Swift उपलब्ध नहीं था, इसलिए Swift फ़ाइलों को संकलित करने के बजाय निरीक्षण किया गया। एक स्वतंत्र Python गणना ने तीन डिफ़ॉल्ट सीडों के लिए सभी 768 अंकगणितीय तुलनाएँ और दिखाए गए शुरुआती मान जाँचे; यह जाँच Swift पैकेज को संकलित करने का विकल्प नहीं है।

जानबूझकर की गई गणना की त्रुटि को रीप्ले करने का अभ्यास करें

डायग्नोस्टिक प्रक्रिया देखने के लिए, triangularNumber(_:) में रिटर्न एक्सप्रेशन को अस्थायी रूप से इस लेबल वाले अंश से बदलें:

// Temporary replacement inside triangularNumber(_:).
return n * n / 2

TEST_SEED=42 swift test फिर से चलाएँ। पहला जनरेट किया गया इनपुट 759 है। बदला गया एक्सप्रेशन 288040 देता है; संदर्भ कुल 288420 है। assertion संदेश सीड 42, केस इंडेक्स 0 और इनपुट 759 दिखाता है। पहली दर्ज की गई विसंगति के बाद जल्दी रिटर्न करने से डायग्नोस्टिक केंद्रित रहता है।

return n * (n + 1) / 2 बहाल करें और वही कमांड फिर से चलाएँ। सुधार का मूल्यांकन करते समय सीड स्थिर रखें। किसी जनरेट किए गए इनपुट से उपयोगी दोष सामने आने पर, उस वास्तविक इनपुट को नाम वाले रिग्रेशन परीक्षण में भी सुरक्षित रखें; जनरेशन रेसिपी बाद में बदल जाए, तब भी वह समझने योग्य रहता है।

XCTest पर यही जीवनचक्र लागू करें

XCTest-आधारित टार्गेट के लिए ReplaySupport.swift रखें और Swift Testing फ़ाइल की जगह नीचे दी गई पूरी परीक्षण फ़ाइल इस्तेमाल करें। इसका नाम Tests/SeedReplayTests/SeedReplayXCTests.swift रखें:

import XCTest

final class SeedReplayXCTests: XCTestCase {
    func testTriangularTotals() throws {
        for seed in try replaySeeds() {
            print("testTriangularTotals seed=\(seed) recipe=upper10-v1 count=256")

            for (index, n) in generatedInputs(seed: seed).enumerated() {
                let actual = triangularNumber(n)
                let expected = referenceTotal(n)

                XCTAssertEqual(
                    actual,
                    expected,
                    "seed=\(seed) case=\(index) n=\(n)"
                )

                if actual != expected {
                    return
                }
            }
        }
    }

    func testReplayRecipeIsStable() {
        var generator = ReplayGenerator(seed: 0)
        XCTAssertEqual(generator.next(), 0xE220A8397B1DCDAF)
        XCTAssertEqual(
            Array(generatedInputs(seed: 0).prefix(3)),
            [904, 441, 27]
        )
    }
}

वही Terminal कमांड लागू होते हैं। generatedInputs(seed:) का हर कॉल एक नया जनरेटर बनाता है, इसलिए कोई परीक्षण दूसरे परीक्षण की रैंडम स्ट्रीम से मान नहीं लेता। परीक्षण के बाद जनरेटर की स्थिति साफ़ करने की ज़रूरत नहीं होती।

अगर मौजूदा XCTest फ़िक्स्चर जनरेटर को प्रॉपर्टी के रूप में रखता है, तो हर परीक्षण विधि से पहले उसे रीसेट करें। Apple प्रति-परीक्षण इनिशियलाइज़ेशन के लिए इंस्टेंस सेटअप विधियों और पूरी क्लास में साझा स्थिति के लिए क्लास सेटअप का दस्तावेज़ देता है। जब परीक्षणों को स्वतंत्र रूप से रीप्ले करना हो, तब परिवर्तनशील रैंडम स्ट्रीम प्रति-परीक्षण इनिशियलाइज़ेशन में रखनी चाहिए। अगले परीक्षण की स्थिति टीयरडाउन में तैयार करना भरोसेमंद नहीं है, क्योंकि क्रैश के बाद टीयरडाउन चलने की गारंटी नहीं होती। अपने परीक्षणों में स्थिति सेटअप और टीयरडाउन करें

Xcode शेड्यूलिंग को इनपुट रीप्ले से अलग रखें

इस पैकेज को Xcode से चलाने के लिए उसका Package.swift खोलें। टेस्ट प्लान वाले प्रोजेक्ट में Product > Scheme > Edit Test Plan चुनें, संबंधित कॉन्फ़िगरेशन चुनें, फिर Environment Variables के तहत दशमलव वैल्यू 42 के साथ TEST_SEED जोड़ें। Product > Test से चलाएँ और परीक्षण आउटपुट में प्रिंट हुई सीड की पुष्टि करें। Apple परीक्षण-प्लान परिवेश कॉन्फ़िगरेशन और परीक्षण चलाने से पहले सक्रिय प्लान चुनने का दस्तावेज़ देता है। परीक्षणों को टेस्ट प्लान में व्यवस्थित करके कोड आकलन बेहतर बनाना

Terminal और Xcode के बीच स्विच करने पर प्रिंट हुई सीड की पुष्टि करें। किसी एक Terminal कमांड को दिया गया वेरिएबल, Xcode टेस्ट-प्लान सेटिंग नहीं होता। लॉग लाइन बताती है कि परीक्षण प्रक्रिया ने वास्तव में कौन-सा कॉन्फ़िगरेशन पढ़ा।

Swift Testing डिफ़ॉल्ट रूप से परीक्षणों को समानांतर चलाता है। इसका .serialized ट्रेट किसी पैरामीटरयुक्त परीक्षण के केस या किसी सुइट के परीक्षणों को क्रम से चला सकता है, लेकिन असंबंधित परीक्षणों को नियंत्रित नहीं करता। Apple वैश्विक समानांतरता बंद करने के लिए swift test --no-parallel का भी दस्तावेज़ देता है। इन दोनों में से कोई भी इनपुट सीड नहीं देता। इस उदाहरण को क्रमबद्धता की ज़रूरत नहीं है, क्योंकि हर केस का अपना जनरेटर और संख्यात्मक इनपुट होता है। परीक्षणों को क्रम से या समानांतर चलाना

अगर कोई परीक्षण अकेले सफल होता है, लेकिन समान जनरेट किए गए इनपुट के बावजूद दूसरे परीक्षणों के साथ विफल होता है, तो साझा स्थिति की जाँच करें। समानांतर काम की पड़ताल में क्रमिक निष्पादन मदद कर सकता है, लेकिन सीड दूसरे परीक्षण की परिवर्तनशील ऑब्जेक्ट को रीसेट नहीं कर सकती और न ही पूरे समवर्ती शेड्यूल को दोहरा सकती है।

विफल सीड के साथ क्या सुरक्षित रखना चाहिए?

इनपुट क्रम को फिर से बनाने के लिए पर्याप्त जानकारी सुरक्षित रखें:

  • परीक्षण का नाम और दशमलव सीड।
  • जनरेटर और मैपिंग का संस्करण, यहाँ upper10-v1।
  • केस इंडेक्स और assertion संदेश में दिया गया वास्तविक इनपुट।
  • स्रोत संशोधन, Swift संस्करण और चुना गया Xcode संस्करण।
  • इनपुट की संख्या और रैंडम ड्रॉ के उपभोग के तरीके में हुए बदलाव।

इस उदाहरण में, लूप से पहले जनरेटर का एक ड्रॉ जोड़ने पर उसके बाद के सभी इनपुट बदल जाएँगे। शिफ्ट की मात्रा बदलने से मैपिंग बदलती है। इनपुट की संख्या बढ़ाने पर मौजूदा शुरुआती क्रम तभी बरकरार रहता है, जब जनरेटर और मैपिंग अपरिवर्तित हों। ये सभी परिणाम कोड के रीप्ले अनुबंध से निकलते हैं।

नियमित रन के लिए कुछ स्थिर सीड रखें, खोज के दौरान अतिरिक्त स्पष्ट सीड आज़माएँ और दोष उजागर करने वाले वास्तविक इनपुट को रिग्रेशन टेस्ट के लिए सुरक्षित रखें। उपयोगी रीप्ले वही मान और ज्ञात शुरुआती स्थिति से शुरू होता है; सीड भरोसेमंद इसलिए बनती है क्योंकि परीक्षण नियंत्रित करता है कि उसका उपयोग कैसे हो।

स्रोत