import Foundation import XCTest @testable import DocsKit final class NativePerformanceProbeTests: XCTestCase { func testTypingProbe() async throws { let mode = ProcessInfo.processInfo.environment["DOCS_PROBE_MODE"] ?? "fresh" let limit = Int(ProcessInfo.processInfo.environment["DOCS_PROBE_COUNT"] ?? "100")! let replica = CRDTReplica(guid: "performance-probe") _ = try await replica.open(update: Data()) _ = try await replica.command(["op": .string("insert_element"), "at": .number(0), "type": .string("paragraph")]) let first = try await replica.presentation(source: 0, selection: NSRange(location: 0, length: 0)) var generation = try XCTUnwrap(first?.0) let clock = ContinuousClock() let begin = clock.now var totalBytes = 0, maximumBytes = 0 let batchSize = mode == "batch" ? 100 : 1 var inserted = 0 while inserted < limit { let count = min(batchSize, limit - inserted) let (update, _) = try await replica.command(["op": .string("display_edit"), "generation": .number(Double(generation)), "at": .number(Double(inserted)), "length": .number(0), "text": .string(String(repeating: "a", count: count))]) totalBytes += update.count; maximumBytes = max(maximumBytes, update.count) inserted += count await replica.retainGenerations([generation]) if mode != "retained" { let next = try await replica.presentation(source: generation, selection: NSRange(location: inserted, length: 0), allowReuse: mode == "reuse") generation = try XCTUnwrap(next?.0) } if [100, 1000, 5000].contains(inserted) { let elapsed = begin.duration(to: clock.now) let (snapshot, projection) = try await replica.command(["op": .string("snapshot")]) let (clientsData, _) = try await replica.command(["op": .string("clients")]) let clients = try JSONDecoder().decode([UInt64].self, from: clientsData) XCTAssertEqual(projection.children.map(\.plainText).joined().count, inserted) print("PROBE mode=\(mode) count=\(inserted) elapsed=\(elapsed) snapshot=\(snapshot.count) updateTotal=\(totalBytes) updateMax=\(maximumBytes) clients=\(clients.count)") fflush(stdout) } } await replica.close() } }