tiny.profiling.iteration.batch
Defined in iteration.
API (8)
Actions
Public operations.
Types and contracts
Public types and contracts.
Source
Source: src/profiling/iteration/batch.zig
zig
const std = @import("std");const sys = @import("sys");const profiling = @import("../root.zig");const model = @import("model.zig");const json = profiling.json;const fingerprint = profiling.fingerprint;const maximum_receipt_bytes: usize = 4 * 1024 * 1024;pub const Marker = struct { path: []const u8, finished_unix_ns: i128,};pub const TestReceipt = struct { path: []const u8, zig_version: []const u8, started_unix_ns: i128, finished_unix_ns: i128, shard_count: u64, shard_index: u64, selected_test_count: u64, executed_test_count: u64, passed_test_count: u64, skipped_test_count: u64, failed_test_count: u64, test_ns_observed: i128, teardown_ns_observed: i128, semantic_sha256: fingerprint.Digest, pub fn semanticHex(self: TestReceipt) fingerprint.Hex { return std.fmt.bytesToHex(self.semantic_sha256, .lower); }};pub const Aggregate = struct { receipt_count: u64 = 0, selected_test_count: u64 = 0, executed_test_count: u64 = 0, passed_test_count: u64 = 0, skipped_test_count: u64 = 0, failed_test_count: u64 = 0, test_ns_observed: i128 = 0, teardown_ns_observed: i128 = 0, semantic_sha256: fingerprint.Digest, pub fn semanticHex(self: Aggregate) fingerprint.Hex { return std.fmt.bytesToHex(self.semantic_sha256, .lower); }};pub const Batch = struct { marker: Marker, receipts: []const TestReceipt, aggregate: Aggregate,};const LocatedMarker = struct { path: []const u8, finished_unix_ns: i128,};pub fn markerAt( allocator: std.mem.Allocator, directory: []const u8, token: []const u8, index: usize,) !?Marker { var markers: std.ArrayList(LocatedMarker) = .empty; const entries = try sys.fs.listDirAlloc(allocator, directory); for (entries) |entry| { if (entry.kind != .file or !std.mem.endsWith(u8, entry.name, ".json")) { continue; } const document = try loadDocument(allocator, entry.path); const object = try json.object(document); const schema = json.string(object.get("schema")) orelse return error.InvalidIterationReceipt; if (!std.mem.eql(u8, schema, model.marker_schema)) continue; try requireToken(object, token); try markers.append(allocator, .{ .path = entry.path, .finished_unix_ns = json.asI128( object.get("finished_unix_ns"), ) orelse return error.InvalidIterationMarker, }); } std.mem.sort(LocatedMarker, markers.items, {}, markerBefore); if (index >= markers.items.len) return null; const selected = markers.items[index]; return .{ .path = try allocator.dupe(u8, selected.path), .finished_unix_ns = selected.finished_unix_ns, };}pub fn load( allocator: std.mem.Allocator, directory: []const u8, token: []const u8, previous_marker_unix_ns: ?i128, marker: Marker,) !Batch { var receipts: std.ArrayList(TestReceipt) = .empty; const entries = try sys.fs.listDirAlloc(allocator, directory); for (entries) |entry| { if (entry.kind != .file or !std.mem.endsWith(u8, entry.name, ".json")) { continue; } const document = try loadDocument(allocator, entry.path); const object = try json.object(document); const schema = json.string(object.get("schema")) orelse return error.InvalidIterationReceipt; if (!std.mem.eql(u8, schema, model.test_receipt_schema)) continue; try requireToken(object, token); const finished_unix_ns = json.asI128( object.get("finished_unix_ns"), ) orelse return error.InvalidTestRunReceipt; if (finished_unix_ns > marker.finished_unix_ns or (previous_marker_unix_ns != null and finished_unix_ns <= previous_marker_unix_ns.?)) { continue; } try receipts.append( allocator, try parseTestReceipt( allocator, entry.path, object, finished_unix_ns, ), ); } std.mem.sort(TestReceipt, receipts.items, {}, receiptBefore); const summary = try aggregate(allocator, receipts.items); const owned_receipts = try receipts.toOwnedSlice(allocator); return .{ .marker = marker, .receipts = owned_receipts, .aggregate = summary, };}fn parseTestReceipt( allocator: std.mem.Allocator, path: []const u8, object: std.json.ObjectMap, finished_unix_ns: i128,) !TestReceipt { return .{ .path = try allocator.dupe(u8, path), .zig_version = try allocator.dupe( u8, json.string(object.get("zig_version")) orelse return error.InvalidTestRunReceipt, ), .started_unix_ns = json.asI128( object.get("started_unix_ns"), ) orelse return error.InvalidTestRunReceipt, .finished_unix_ns = finished_unix_ns, .shard_count = try requiredU64(object, "shard_count"), .shard_index = try requiredU64(object, "shard_index"), .selected_test_count = try requiredU64( object, "selected_test_count", ), .executed_test_count = try requiredU64( object, "executed_test_count", ), .passed_test_count = try requiredU64( object, "passed_test_count", ), .skipped_test_count = try requiredU64( object, "skipped_test_count", ), .failed_test_count = try requiredU64( object, "failed_test_count", ), .test_ns_observed = json.asI128( object.get("test_ns_observed"), ) orelse return error.InvalidTestRunReceipt, .teardown_ns_observed = json.asI128( object.get("teardown_ns_observed"), ) orelse return error.InvalidTestRunReceipt, .semantic_sha256 = try parseDigest( json.string(object.get("semantic_sha256")) orelse return error.InvalidTestRunReceipt, ), };}fn aggregate( allocator: std.mem.Allocator, receipts: []const TestReceipt,) !Aggregate { var result = Aggregate{ .semantic_sha256 = undefined, }; var digests = try allocator.alloc( fingerprint.Digest, receipts.len, ); for (receipts, 0..) |receipt, index| { result.receipt_count = try add( result.receipt_count, 1, ); result.selected_test_count = try add( result.selected_test_count, receipt.selected_test_count, ); result.executed_test_count = try add( result.executed_test_count, receipt.executed_test_count, ); result.passed_test_count = try add( result.passed_test_count, receipt.passed_test_count, ); result.skipped_test_count = try add( result.skipped_test_count, receipt.skipped_test_count, ); result.failed_test_count = try add( result.failed_test_count, receipt.failed_test_count, ); result.test_ns_observed += receipt.test_ns_observed; result.teardown_ns_observed += receipt.teardown_ns_observed; digests[index] = receipt.semantic_sha256; } std.mem.sort( fingerprint.Digest, digests, {}, digestBefore, ); var hasher = std.crypto.hash.sha2.Sha256.init(.{}); hasher.update("tiny.profiling.iteration-test-batch/v1"); for (digests) |digest| hasher.update(&digest); hasher.final(&result.semantic_sha256); return result;}fn loadDocument( allocator: std.mem.Allocator, path: []const u8,) !std.json.Value { const text = try sys.fs.readFileAlloc( allocator, path, maximum_receipt_bytes, ); return try std.json.parseFromSliceLeaky( std.json.Value, allocator, text, .{}, );}fn requireToken( object: std.json.ObjectMap, expected: []const u8,) !void { const actual = json.string(object.get("token")) orelse return error.InvalidIterationReceipt; if (!std.mem.eql(u8, actual, expected)) { return error.IterationReceiptTokenMismatch; }}fn requiredU64( object: std.json.ObjectMap, field: []const u8,) !u64 { return json.asU64(object.get(field)) orelse error.InvalidTestRunReceipt;}fn parseDigest(value: []const u8) !fingerprint.Digest { if (value.len != std.crypto.hash.sha2.Sha256.digest_length * 2) { return error.InvalidTestRunDigest; } var digest: fingerprint.Digest = undefined; _ = std.fmt.hexToBytes(&digest, value) catch return error.InvalidTestRunDigest; return digest;}fn add(left: u64, right: u64) !u64 { return std.math.add(u64, left, right) catch return error.IterationCountOverflow;}fn markerBefore( _: void, left: LocatedMarker, right: LocatedMarker,) bool { return left.finished_unix_ns < right.finished_unix_ns;}fn receiptBefore( _: void, left: TestReceipt, right: TestReceipt,) bool { if (left.started_unix_ns != right.started_unix_ns) { return left.started_unix_ns < right.started_unix_ns; } return left.shard_index < right.shard_index;}fn digestBefore( _: void, left: fingerprint.Digest, right: fingerprint.Digest,) bool { return std.mem.order(u8, &left, &right) == .lt;}test "iteration receipt batches aggregate deterministic semantics" { var arena_state = std.heap.ArenaAllocator.init(std.testing.allocator); defer arena_state.deinit(); const allocator = arena_state.allocator(); const first_digest = try parseDigest( "0000000000000000000000000000000000000000000000000000000000000001", ); const second_digest = try parseDigest( "0000000000000000000000000000000000000000000000000000000000000002", ); const receipts = [_]TestReceipt{ .{ .path = "one", .zig_version = "zig", .started_unix_ns = 1, .finished_unix_ns = 2, .shard_count = 1, .shard_index = 0, .selected_test_count = 3, .executed_test_count = 3, .passed_test_count = 3, .skipped_test_count = 0, .failed_test_count = 0, .test_ns_observed = 10, .teardown_ns_observed = 2, .semantic_sha256 = second_digest, }, .{ .path = "two", .zig_version = "zig", .started_unix_ns = 3, .finished_unix_ns = 4, .shard_count = 1, .shard_index = 0, .selected_test_count = 2, .executed_test_count = 2, .passed_test_count = 2, .skipped_test_count = 0, .failed_test_count = 0, .test_ns_observed = 20, .teardown_ns_observed = 4, .semantic_sha256 = first_digest, }, }; const result = try aggregate(allocator, &receipts); try std.testing.expectEqual(@as(u64, 5), result.selected_test_count); try std.testing.expectEqual(@as(i128, 30), result.test_ns_observed);}Source: src/profiling/iteration/root.zig:1
zig
pub const batch = @import("batch.zig");Audit
| Definitions | 9 |
|---|---|
| Public names | 9 |
| Members | 28 |
| Version | 26.7.0 |
| Revision | daab053ee433 |