zeno-mobile-runner 0.2.1 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +39 -0
- package/FEATURES.md +1 -1
- package/README.md +1 -1
- package/build.zig +10 -0
- package/build.zig.zon +2 -2
- package/clients/kotlin/README.md +1 -1
- package/clients/kotlin/build.gradle.kts +1 -1
- package/clients/python/pyproject.toml +1 -1
- package/clients/rust/Cargo.lock +1 -1
- package/clients/rust/Cargo.toml +1 -1
- package/clients/typescript/package.json +1 -1
- package/docs/protocol-fixtures/core-session.responses.jsonl +1 -1
- package/docs/protocol.md +10 -10
- package/package.json +3 -1
- package/prebuilds/darwin-arm64/zmr +0 -0
- package/prebuilds/darwin-x64/zmr +0 -0
- package/prebuilds/linux-arm64/zmr +0 -0
- package/prebuilds/linux-x64/zmr +0 -0
- package/scripts/create-react-native-expo-demo-app.sh +11 -13
- package/shims/ios/ZMRShim.swift +40 -12
- package/shims/ios/ZMRShimUITestCase.swift +135 -15
- package/src/android.zig +10 -9
- package/src/android_emulator.zig +22 -11
- package/src/android_screen_recording.zig +11 -7
- package/src/bundle.zig +10 -9
- package/src/bundle_redaction.zig +29 -28
- package/src/bundle_tar.zig +15 -12
- package/src/cli_devices.zig +7 -3
- package/src/cli_discover.zig +7 -3
- package/src/cli_doctor.zig +7 -3
- package/src/cli_draft.zig +51 -47
- package/src/cli_explore.zig +7 -3
- package/src/cli_import.zig +8 -4
- package/src/cli_info.zig +13 -6
- package/src/cli_init.zig +9 -5
- package/src/cli_inspect.zig +8 -4
- package/src/cli_run.zig +22 -16
- package/src/cli_serve.zig +3 -3
- package/src/cli_trace.zig +25 -12
- package/src/cli_validate.zig +8 -4
- package/src/command.zig +81 -99
- package/src/config.zig +2 -1
- package/src/config_diagnostics.zig +2 -1
- package/src/config_paths.zig +2 -1
- package/src/doctor.zig +8 -7
- package/src/doctor_hints.zig +1 -1
- package/src/errors.zig +5 -5
- package/src/importer.zig +8 -7
- package/src/ios.zig +98 -19
- package/src/ios_devices.zig +6 -5
- package/src/ios_lifecycle.zig +4 -4
- package/src/ios_shim.zig +12 -0
- package/src/json_rpc.zig +39 -40
- package/src/json_rpc_methods.zig +8 -8
- package/src/json_rpc_observation.zig +9 -8
- package/src/json_rpc_params.zig +1 -1
- package/src/json_rpc_trace.zig +22 -21
- package/src/main.zig +19 -10
- package/src/mcp.zig +28 -19
- package/src/mcp_trace.zig +30 -29
- package/src/report.zig +39 -36
- package/src/report_html.zig +5 -4
- package/src/runner.zig +2 -1
- package/src/runner_actions.zig +20 -17
- package/src/runner_diagnostics.zig +4 -4
- package/src/runner_events.zig +55 -51
- package/src/runner_native.zig +21 -19
- package/src/runner_waits.zig +46 -41
- package/src/scaffold.zig +25 -24
- package/src/scenario.zig +4 -3
- package/src/stdio.zig +129 -0
- package/src/trace.zig +34 -26
- package/src/trace_summary.zig +3 -2
- package/src/trace_summary_diagnostic.zig +15 -13
- package/src/validation.zig +5 -4
- package/src/version.zig +1 -1
package/src/cli_trace.zig
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const std = @import("std");
|
|
2
|
+
const stdio = @import("stdio.zig");
|
|
2
3
|
|
|
3
4
|
const bundle = @import("bundle.zig");
|
|
4
5
|
const report = @import("report.zig");
|
|
@@ -36,11 +37,11 @@ pub fn parseReportArgs(args: []const []const u8) !ReportArgs {
|
|
|
36
37
|
index += 1;
|
|
37
38
|
junit_path = if (index < args.len) args[index] else return error.MissingJUnitOutput;
|
|
38
39
|
} else if (std.mem.startsWith(u8, arg, "--")) {
|
|
39
|
-
return error.
|
|
40
|
+
return error.unknownFlag;
|
|
40
41
|
} else if (input_path == null) {
|
|
41
42
|
input_path = arg;
|
|
42
43
|
} else {
|
|
43
|
-
return error.
|
|
44
|
+
return error.unknownFlag;
|
|
44
45
|
}
|
|
45
46
|
}
|
|
46
47
|
if (input_path == null) return error.MissingReportInput;
|
|
@@ -60,7 +61,7 @@ pub fn parseExplainArgs(args: []const []const u8) !ExplainArgs {
|
|
|
60
61
|
} else if (parsed.trace_dir == null) {
|
|
61
62
|
parsed.trace_dir = arg;
|
|
62
63
|
} else {
|
|
63
|
-
return error.
|
|
64
|
+
return error.unknownFlag;
|
|
64
65
|
}
|
|
65
66
|
}
|
|
66
67
|
if (parsed.trace_dir == null) return error.MissingTraceDir;
|
|
@@ -85,11 +86,11 @@ pub fn parseExportArgs(args: []const []const u8) !ExportArgs {
|
|
|
85
86
|
redact = true;
|
|
86
87
|
omit_screenshots = true;
|
|
87
88
|
} else if (std.mem.startsWith(u8, arg, "--")) {
|
|
88
|
-
return error.
|
|
89
|
+
return error.unknownFlag;
|
|
89
90
|
} else if (trace_dir == null) {
|
|
90
91
|
trace_dir = arg;
|
|
91
92
|
} else {
|
|
92
|
-
return error.
|
|
93
|
+
return error.unknownFlag;
|
|
93
94
|
}
|
|
94
95
|
}
|
|
95
96
|
if (trace_dir == null) return error.MissingTraceDir;
|
|
@@ -102,32 +103,40 @@ pub fn parseExportArgs(args: []const []const u8) !ExportArgs {
|
|
|
102
103
|
};
|
|
103
104
|
}
|
|
104
105
|
|
|
105
|
-
pub fn runReport(allocator: std.mem.Allocator, args: *std.process.
|
|
106
|
+
pub fn runReport(allocator: std.mem.Allocator, args: *std.process.Args.Iterator) !void {
|
|
106
107
|
var raw_args = std.ArrayList([]const u8).empty;
|
|
107
108
|
defer raw_args.deinit(allocator);
|
|
108
109
|
while (args.next()) |arg| try raw_args.append(allocator, arg);
|
|
109
110
|
|
|
110
111
|
const parsed = try parseReportArgs(raw_args.items);
|
|
112
|
+
var stdout_io: stdio.Output = .{};
|
|
113
|
+
stdout_io.init(.stdout());
|
|
114
|
+
defer stdout_io.deinit();
|
|
115
|
+
const stdout = stdout_io.writer();
|
|
116
|
+
|
|
111
117
|
try report.writeHtmlReport(allocator, parsed.input_path, parsed.out_path.?);
|
|
112
|
-
try
|
|
118
|
+
try stdout.print("wrote {s}\n", .{parsed.out_path.?});
|
|
113
119
|
if (parsed.junit_path) |junit_path| {
|
|
114
120
|
try report.writeJUnitReport(allocator, parsed.input_path, junit_path);
|
|
115
|
-
try
|
|
121
|
+
try stdout.print("wrote {s}\n", .{junit_path});
|
|
116
122
|
}
|
|
117
123
|
}
|
|
118
124
|
|
|
119
|
-
pub fn runExplain(allocator: std.mem.Allocator, args: *std.process.
|
|
125
|
+
pub fn runExplain(allocator: std.mem.Allocator, args: *std.process.Args.Iterator) !void {
|
|
120
126
|
var raw_args = std.ArrayList([]const u8).empty;
|
|
121
127
|
defer raw_args.deinit(allocator);
|
|
122
128
|
while (args.next()) |arg| try raw_args.append(allocator, arg);
|
|
123
129
|
|
|
124
130
|
const parsed = try parseExplainArgs(raw_args.items);
|
|
125
|
-
|
|
131
|
+
var stdout_io: stdio.Output = .{};
|
|
132
|
+
stdout_io.init(.stdout());
|
|
133
|
+
defer stdout_io.deinit();
|
|
134
|
+
const stdout = stdout_io.writer();
|
|
126
135
|
if (parsed.json) return try report.writeTraceExplanationJson(allocator, parsed.trace_dir.?, stdout);
|
|
127
136
|
try report.writeTraceExplanation(allocator, parsed.trace_dir.?, stdout);
|
|
128
137
|
}
|
|
129
138
|
|
|
130
|
-
pub fn runExport(allocator: std.mem.Allocator, args: *std.process.
|
|
139
|
+
pub fn runExport(allocator: std.mem.Allocator, args: *std.process.Args.Iterator) !void {
|
|
131
140
|
var raw_args = std.ArrayList([]const u8).empty;
|
|
132
141
|
defer raw_args.deinit(allocator);
|
|
133
142
|
while (args.next()) |arg| try raw_args.append(allocator, arg);
|
|
@@ -137,5 +146,9 @@ pub fn runExport(allocator: std.mem.Allocator, args: *std.process.ArgIterator) !
|
|
|
137
146
|
.redact = parsed.redact,
|
|
138
147
|
.omit_screenshots = parsed.omit_screenshots,
|
|
139
148
|
});
|
|
140
|
-
|
|
149
|
+
var stdout_io: stdio.Output = .{};
|
|
150
|
+
stdout_io.init(.stdout());
|
|
151
|
+
defer stdout_io.deinit();
|
|
152
|
+
const stdout = stdout_io.writer();
|
|
153
|
+
try stdout.print("wrote {s}\n", .{parsed.out_path.?});
|
|
141
154
|
}
|
package/src/cli_validate.zig
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const std = @import("std");
|
|
2
|
+
const stdio = @import("stdio.zig");
|
|
2
3
|
|
|
3
4
|
const cli_output = @import("cli_output.zig");
|
|
4
5
|
const validation = @import("validation.zig");
|
|
@@ -15,11 +16,11 @@ pub fn parseArgs(args: []const []const u8) !ParsedArgs {
|
|
|
15
16
|
if (std.mem.eql(u8, arg, "--json")) {
|
|
16
17
|
json = true;
|
|
17
18
|
} else if (std.mem.startsWith(u8, arg, "--")) {
|
|
18
|
-
return error.
|
|
19
|
+
return error.unknownFlag;
|
|
19
20
|
} else if (path == null) {
|
|
20
21
|
path = arg;
|
|
21
22
|
} else {
|
|
22
|
-
return error.
|
|
23
|
+
return error.unknownFlag;
|
|
23
24
|
}
|
|
24
25
|
}
|
|
25
26
|
return ParsedArgs{
|
|
@@ -28,7 +29,7 @@ pub fn parseArgs(args: []const []const u8) !ParsedArgs {
|
|
|
28
29
|
};
|
|
29
30
|
}
|
|
30
31
|
|
|
31
|
-
pub fn run(allocator: std.mem.Allocator, args: *std.process.
|
|
32
|
+
pub fn run(allocator: std.mem.Allocator, args: *std.process.Args.Iterator) !void {
|
|
32
33
|
var raw_args = std.ArrayList([]const u8).empty;
|
|
33
34
|
defer raw_args.deinit(allocator);
|
|
34
35
|
while (args.next()) |arg| try raw_args.append(allocator, arg);
|
|
@@ -37,7 +38,10 @@ pub fn run(allocator: std.mem.Allocator, args: *std.process.ArgIterator) !void {
|
|
|
37
38
|
const result = try validation.validateFile(allocator, parsed.path);
|
|
38
39
|
defer result.deinit(allocator);
|
|
39
40
|
|
|
40
|
-
|
|
41
|
+
var stdout_io: stdio.Output = .{};
|
|
42
|
+
stdout_io.init(.stdout());
|
|
43
|
+
defer stdout_io.deinit();
|
|
44
|
+
const stdout = stdout_io.writer();
|
|
41
45
|
if (parsed.json) {
|
|
42
46
|
try cli_output.writeValidationJson(stdout, parsed.path, result);
|
|
43
47
|
} else {
|
package/src/command.zig
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const std = @import("std");
|
|
2
|
-
const
|
|
2
|
+
const stdio = @import("stdio.zig");
|
|
3
3
|
|
|
4
4
|
pub const ExecResult = struct {
|
|
5
5
|
stdout: []u8,
|
|
@@ -15,7 +15,7 @@ pub const ExecResult = struct {
|
|
|
15
15
|
pub fn ensureSuccess(self: ExecResult) !void {
|
|
16
16
|
if (self.timed_out) return error.CommandTimedOut;
|
|
17
17
|
switch (self.term) {
|
|
18
|
-
.
|
|
18
|
+
.exited => |code| if (code == 0) return,
|
|
19
19
|
else => {},
|
|
20
20
|
}
|
|
21
21
|
return error.CommandFailed;
|
|
@@ -27,10 +27,10 @@ pub fn run(
|
|
|
27
27
|
argv: []const []const u8,
|
|
28
28
|
max_output_bytes: usize,
|
|
29
29
|
) !ExecResult {
|
|
30
|
-
const result = try std.process.
|
|
31
|
-
.allocator = allocator,
|
|
30
|
+
const result = try std.process.run(allocator, stdio.io(), .{
|
|
32
31
|
.argv = argv,
|
|
33
|
-
.
|
|
32
|
+
.stdout_limit = .limited(max_output_bytes),
|
|
33
|
+
.stderr_limit = .limited(max_output_bytes),
|
|
34
34
|
});
|
|
35
35
|
return .{
|
|
36
36
|
.stdout = result.stdout,
|
|
@@ -56,59 +56,24 @@ pub fn runWithInputTimeout(
|
|
|
56
56
|
max_output_bytes: usize,
|
|
57
57
|
timeout_ms: u64,
|
|
58
58
|
) !ExecResult {
|
|
59
|
-
var child = std.process.
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
defer
|
|
66
|
-
var stderr = std.ArrayList(u8).empty;
|
|
67
|
-
defer stderr.deinit(allocator);
|
|
68
|
-
|
|
69
|
-
try child.spawn();
|
|
70
|
-
errdefer _ = child.kill() catch {};
|
|
71
|
-
|
|
72
|
-
var done = std.Thread.ResetEvent{};
|
|
73
|
-
var timed_out = std.atomic.Value(bool).init(false);
|
|
74
|
-
const use_timeout = timeout_ms > 0;
|
|
75
|
-
const killer = if (use_timeout)
|
|
76
|
-
try std.Thread.spawn(.{}, timeoutKiller, .{
|
|
77
|
-
child.id,
|
|
78
|
-
&done,
|
|
79
|
-
&timed_out,
|
|
80
|
-
std.math.mul(u64, timeout_ms, std.time.ns_per_ms) catch std.math.maxInt(u64),
|
|
81
|
-
})
|
|
82
|
-
else
|
|
83
|
-
null;
|
|
59
|
+
var child = try std.process.spawn(stdio.io(), .{
|
|
60
|
+
.argv = argv,
|
|
61
|
+
.stdin = .pipe,
|
|
62
|
+
.stdout = .pipe,
|
|
63
|
+
.stderr = .pipe,
|
|
64
|
+
});
|
|
65
|
+
defer if (child.id != null) child.kill(stdio.io());
|
|
84
66
|
|
|
85
67
|
if (child.stdin) |stdin_file| {
|
|
86
|
-
|
|
87
|
-
stdin_file.
|
|
68
|
+
var stdin_buffer: [8192]u8 = undefined;
|
|
69
|
+
var stdin_writer = stdin_file.writerStreaming(stdio.io(), &stdin_buffer);
|
|
70
|
+
try stdin_writer.interface.writeAll(stdin);
|
|
71
|
+
try stdin_writer.interface.flush();
|
|
72
|
+
stdin_file.close(stdio.io());
|
|
88
73
|
child.stdin = null;
|
|
89
74
|
}
|
|
90
75
|
|
|
91
|
-
|
|
92
|
-
_ = child.kill() catch {};
|
|
93
|
-
done.set();
|
|
94
|
-
if (killer) |thread| thread.join();
|
|
95
|
-
return err;
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
const term = child.wait() catch |err| {
|
|
99
|
-
done.set();
|
|
100
|
-
if (killer) |thread| thread.join();
|
|
101
|
-
return err;
|
|
102
|
-
};
|
|
103
|
-
done.set();
|
|
104
|
-
if (killer) |thread| thread.join();
|
|
105
|
-
|
|
106
|
-
return .{
|
|
107
|
-
.stdout = try stdout.toOwnedSlice(allocator),
|
|
108
|
-
.stderr = try stderr.toOwnedSlice(allocator),
|
|
109
|
-
.term = term,
|
|
110
|
-
.timed_out = timed_out.load(.acquire),
|
|
111
|
-
};
|
|
76
|
+
return try collectSpawnedOutput(allocator, &child, max_output_bytes, timeoutForMs(timeout_ms));
|
|
112
77
|
}
|
|
113
78
|
|
|
114
79
|
pub fn runWithTimeout(
|
|
@@ -119,64 +84,81 @@ pub fn runWithTimeout(
|
|
|
119
84
|
) !ExecResult {
|
|
120
85
|
if (timeout_ms == 0) return run(allocator, argv, max_output_bytes);
|
|
121
86
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
87
|
+
const result = std.process.run(allocator, stdio.io(), .{
|
|
88
|
+
.argv = argv,
|
|
89
|
+
.stdout_limit = .limited(max_output_bytes),
|
|
90
|
+
.stderr_limit = .limited(max_output_bytes),
|
|
91
|
+
.timeout = timeoutForMs(timeout_ms),
|
|
92
|
+
}) catch |err| switch (err) {
|
|
93
|
+
error.Timeout => return timedOutResult(allocator),
|
|
94
|
+
else => |actual| return actual,
|
|
95
|
+
};
|
|
96
|
+
return .{
|
|
97
|
+
.stdout = result.stdout,
|
|
98
|
+
.stderr = result.stderr,
|
|
99
|
+
.term = result.term,
|
|
100
|
+
.timed_out = false,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
134
103
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
104
|
+
fn collectSpawnedOutput(
|
|
105
|
+
allocator: std.mem.Allocator,
|
|
106
|
+
child: *std.process.Child,
|
|
107
|
+
max_output_bytes: usize,
|
|
108
|
+
timeout: std.Io.Timeout,
|
|
109
|
+
) !ExecResult {
|
|
110
|
+
var multi_reader_buffer: std.Io.File.MultiReader.Buffer(2) = undefined;
|
|
111
|
+
var multi_reader: std.Io.File.MultiReader = undefined;
|
|
112
|
+
multi_reader.init(allocator, stdio.io(), multi_reader_buffer.toStreams(), &.{ child.stdout.?, child.stderr.? });
|
|
113
|
+
defer multi_reader.deinit();
|
|
114
|
+
|
|
115
|
+
const stdout_reader = multi_reader.reader(0);
|
|
116
|
+
const stderr_reader = multi_reader.reader(1);
|
|
117
|
+
|
|
118
|
+
while (multi_reader.fill(64, timeout)) |_| {
|
|
119
|
+
if (stdout_reader.buffered().len > max_output_bytes) return error.StreamTooLong;
|
|
120
|
+
if (stderr_reader.buffered().len > max_output_bytes) return error.StreamTooLong;
|
|
121
|
+
} else |err| switch (err) {
|
|
122
|
+
error.EndOfStream => {},
|
|
123
|
+
error.Timeout => {
|
|
124
|
+
child.kill(stdio.io());
|
|
125
|
+
return timedOutResult(allocator);
|
|
126
|
+
},
|
|
127
|
+
else => |actual| return actual,
|
|
128
|
+
}
|
|
139
129
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
done.set();
|
|
143
|
-
killer.join();
|
|
144
|
-
return err;
|
|
145
|
-
};
|
|
130
|
+
try multi_reader.checkAnyError();
|
|
131
|
+
const term = try child.wait(stdio.io());
|
|
146
132
|
|
|
147
|
-
const
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
};
|
|
152
|
-
done.set();
|
|
153
|
-
killer.join();
|
|
133
|
+
const stdout_slice = try multi_reader.toOwnedSlice(0);
|
|
134
|
+
errdefer allocator.free(stdout_slice);
|
|
135
|
+
const stderr_slice = try multi_reader.toOwnedSlice(1);
|
|
136
|
+
errdefer allocator.free(stderr_slice);
|
|
154
137
|
|
|
155
138
|
return .{
|
|
156
|
-
.stdout =
|
|
157
|
-
.stderr =
|
|
139
|
+
.stdout = stdout_slice,
|
|
140
|
+
.stderr = stderr_slice,
|
|
158
141
|
.term = term,
|
|
159
|
-
.timed_out =
|
|
142
|
+
.timed_out = false,
|
|
160
143
|
};
|
|
161
144
|
}
|
|
162
145
|
|
|
163
|
-
fn
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
done.timedWait(timeout_ns) catch {
|
|
170
|
-
timed_out.store(true, .release);
|
|
171
|
-
killChildId(child_id);
|
|
146
|
+
fn timedOutResult(allocator: std.mem.Allocator) !ExecResult {
|
|
147
|
+
return .{
|
|
148
|
+
.stdout = try allocator.dupe(u8, ""),
|
|
149
|
+
.stderr = try allocator.dupe(u8, ""),
|
|
150
|
+
.term = .{ .unknown = 0 },
|
|
151
|
+
.timed_out = true,
|
|
172
152
|
};
|
|
173
153
|
}
|
|
174
154
|
|
|
175
|
-
fn
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
155
|
+
fn timeoutForMs(timeout_ms: u64) std.Io.Timeout {
|
|
156
|
+
if (timeout_ms == 0) return .none;
|
|
157
|
+
const timeout_ns = std.math.mul(u64, timeout_ms, std.time.ns_per_ms) catch std.math.maxInt(u64);
|
|
158
|
+
return .{ .duration = .{
|
|
159
|
+
.raw = std.Io.Duration.fromNanoseconds(@intCast(timeout_ns)),
|
|
160
|
+
.clock = .awake,
|
|
161
|
+
} };
|
|
180
162
|
}
|
|
181
163
|
|
|
182
164
|
pub fn escapeAdbInputText(allocator: std.mem.Allocator, text: []const u8) ![]const u8 {
|
package/src/config.zig
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const std = @import("std");
|
|
2
|
+
const stdio = @import("stdio.zig");
|
|
2
3
|
const config_diagnostics = @import("config_diagnostics.zig");
|
|
3
4
|
|
|
4
5
|
pub const PlatformConfig = struct {
|
|
@@ -92,7 +93,7 @@ pub const Config = struct {
|
|
|
92
93
|
};
|
|
93
94
|
|
|
94
95
|
pub fn parseFile(allocator: std.mem.Allocator, path: []const u8) !Config {
|
|
95
|
-
const content = try
|
|
96
|
+
const content = try stdio.readFileAlloc(allocator, path, 1024 * 1024);
|
|
96
97
|
defer allocator.free(content);
|
|
97
98
|
return try parseSlice(allocator, content);
|
|
98
99
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
const std = @import("std");
|
|
2
|
+
const stdio = @import("stdio.zig");
|
|
2
3
|
|
|
3
4
|
pub fn errorFieldPathForFile(allocator: std.mem.Allocator, path: []const u8, err: anyerror) !?[]const u8 {
|
|
4
|
-
const content =
|
|
5
|
+
const content = stdio.readFileAlloc(allocator, path, 1024 * 1024) catch return null;
|
|
5
6
|
defer allocator.free(content);
|
|
6
7
|
return try errorFieldPathForSlice(allocator, content, err);
|
|
7
8
|
}
|
package/src/config_paths.zig
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
const std = @import("std");
|
|
2
|
+
const stdio = @import("stdio.zig");
|
|
2
3
|
const config = @import("config.zig");
|
|
3
4
|
|
|
4
5
|
pub const default_path = ".zmr/config.json";
|
|
5
6
|
|
|
6
7
|
pub fn loadIfPresent(allocator: std.mem.Allocator, explicit_path: ?[]const u8) !?config.Config {
|
|
7
8
|
if (explicit_path) |path| return try config.parseFile(allocator, path);
|
|
8
|
-
|
|
9
|
+
stdio.access(default_path) catch |err| switch (err) {
|
|
9
10
|
error.FileNotFound => return null,
|
|
10
11
|
else => return err,
|
|
11
12
|
};
|
package/src/doctor.zig
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const std = @import("std");
|
|
2
|
+
const stdio = @import("stdio.zig");
|
|
2
3
|
const android = @import("android.zig");
|
|
3
4
|
const command = @import("command.zig");
|
|
4
5
|
const config = @import("config.zig");
|
|
@@ -137,7 +138,7 @@ pub fn run(allocator: std.mem.Allocator, options: Options) ![]Check {
|
|
|
137
138
|
}
|
|
138
139
|
|
|
139
140
|
fn checkPath(allocator: std.mem.Allocator, name: []const u8, path: []const u8) !Check {
|
|
140
|
-
|
|
141
|
+
stdio.accessWithOptions(path, .{ .read = true }) catch |err| {
|
|
141
142
|
return .{
|
|
142
143
|
.name = try allocator.dupe(u8, name),
|
|
143
144
|
.status = .missing,
|
|
@@ -347,9 +348,9 @@ fn physicalStateBreakdown(allocator: std.mem.Allocator, devices: []const types.D
|
|
|
347
348
|
}
|
|
348
349
|
}
|
|
349
350
|
|
|
350
|
-
var parts =
|
|
351
|
-
defer parts.deinit(
|
|
352
|
-
const writer = parts.writer
|
|
351
|
+
var parts: std.Io.Writer.Allocating = .init(allocator);
|
|
352
|
+
defer parts.deinit();
|
|
353
|
+
const writer = &parts.writer;
|
|
353
354
|
var wrote = false;
|
|
354
355
|
if (disconnected > 0) {
|
|
355
356
|
try writer.print("disconnected={d}", .{disconnected});
|
|
@@ -372,7 +373,7 @@ fn physicalStateBreakdown(allocator: std.mem.Allocator, devices: []const types.D
|
|
|
372
373
|
}
|
|
373
374
|
|
|
374
375
|
if (!wrote) return try allocator.dupe(u8, "");
|
|
375
|
-
return try std.fmt.allocPrint(allocator, " ({s})", .{parts.
|
|
376
|
+
return try std.fmt.allocPrint(allocator, " ({s})", .{parts.writer.buffered()});
|
|
376
377
|
}
|
|
377
378
|
|
|
378
379
|
pub fn checkCommand(allocator: std.mem.Allocator, name: []const u8, argv: []const []const u8) !Check {
|
|
@@ -387,7 +388,7 @@ pub fn checkCommand(allocator: std.mem.Allocator, name: []const u8, argv: []cons
|
|
|
387
388
|
};
|
|
388
389
|
defer result.deinit(allocator);
|
|
389
390
|
|
|
390
|
-
if (result.term == .
|
|
391
|
+
if (result.term == .exited and result.term.exited == 0) {
|
|
391
392
|
return .{
|
|
392
393
|
.name = try allocator.dupe(u8, name),
|
|
393
394
|
.status = .ok,
|
|
@@ -395,7 +396,7 @@ pub fn checkCommand(allocator: std.mem.Allocator, name: []const u8, argv: []cons
|
|
|
395
396
|
};
|
|
396
397
|
}
|
|
397
398
|
|
|
398
|
-
const code = if (result.term == .
|
|
399
|
+
const code = if (result.term == .exited) result.term.exited else 255;
|
|
399
400
|
return .{
|
|
400
401
|
.name = try allocator.dupe(u8, name),
|
|
401
402
|
.status = .warning,
|
package/src/doctor_hints.zig
CHANGED
|
@@ -19,7 +19,7 @@ pub fn hintForCheck(allocator: std.mem.Allocator, name: []const u8, status: Stat
|
|
|
19
19
|
if (status == .ok) return null;
|
|
20
20
|
const hint =
|
|
21
21
|
if (std.mem.eql(u8, name, "zig"))
|
|
22
|
-
"Install Zig 0.
|
|
22
|
+
"Install Zig 0.16.0 or newer, ensure it is on PATH, then run zmr doctor again."
|
|
23
23
|
else if (std.mem.eql(u8, name, "adb"))
|
|
24
24
|
"Install Android SDK Platform Tools, ensure adb is on PATH, then run adb devices."
|
|
25
25
|
else if (std.mem.eql(u8, name, "android-devices"))
|
package/src/errors.zig
CHANGED
|
@@ -21,8 +21,8 @@ pub fn classify(err: anyerror) PublicError {
|
|
|
21
21
|
error.MissingXcrunPath => .{ .code = "cli.missing_xcrun_path", .message = "missing xcrun path" },
|
|
22
22
|
error.MissingZigPath => .{ .code = "cli.missing_zig_path", .message = "missing zig path" },
|
|
23
23
|
error.MissingPlatform => .{ .code = "cli.missing_platform", .message = "missing platform" },
|
|
24
|
-
error.
|
|
25
|
-
error.
|
|
24
|
+
error.unknownCommand => .{ .code = "cli.unknown_command", .message = "unknown command" },
|
|
25
|
+
error.unknownFlag => .{ .code = "cli.unknown_flag", .message = "unknown flag" },
|
|
26
26
|
error.MissingParam => .{ .code = "cli.missing_param", .message = "missing parameter" },
|
|
27
27
|
error.FileNotFound => .{ .code = "scenario.file_not_found", .message = "scenario file was not found" },
|
|
28
28
|
error.SemanticSnapshotMissing => .{ .code = "draft.no_snapshot", .message = "no semantic snapshot was found in the trace" },
|
|
@@ -36,9 +36,9 @@ pub fn classify(err: anyerror) PublicError {
|
|
|
36
36
|
error.StepMustBeObject,
|
|
37
37
|
error.StepMissingAction,
|
|
38
38
|
error.StepActionMustBeString,
|
|
39
|
-
error.
|
|
40
|
-
error.
|
|
41
|
-
error.
|
|
39
|
+
error.unknownAction,
|
|
40
|
+
error.unknownScenarioAction,
|
|
41
|
+
error.unknownScrollDirection,
|
|
42
42
|
error.StepMissingUrl,
|
|
43
43
|
error.StepMissingText,
|
|
44
44
|
error.StepMissingX1,
|
package/src/importer.zig
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const std = @import("std");
|
|
2
|
+
const stdio = @import("stdio.zig");
|
|
2
3
|
const importer_json = @import("importer_json.zig");
|
|
3
4
|
const model = @import("importer_model.zig");
|
|
4
5
|
|
|
@@ -13,20 +14,20 @@ pub fn importFlowYamlFile(
|
|
|
13
14
|
) !ImportResult {
|
|
14
15
|
if (!options.force and fileExists(out_path)) return error.ImportOutputExists;
|
|
15
16
|
|
|
16
|
-
const content = try
|
|
17
|
+
const content = try stdio.readFileAlloc(allocator, source_path, 4 * 1024 * 1024);
|
|
17
18
|
defer allocator.free(content);
|
|
18
19
|
|
|
19
20
|
var imported = try parseFlowYamlSlice(allocator, content, options);
|
|
20
21
|
defer imported.deinit(allocator);
|
|
21
22
|
|
|
22
23
|
if (std.fs.path.dirname(out_path)) |dir| {
|
|
23
|
-
if (dir.len > 0) try std.
|
|
24
|
+
if (dir.len > 0) try std.Io.Dir.cwd().createDirPath(stdio.io(), dir);
|
|
24
25
|
}
|
|
25
26
|
|
|
26
|
-
var file = try std.
|
|
27
|
-
defer file.close();
|
|
27
|
+
var file = try std.Io.Dir.cwd().createFile(stdio.io(), out_path, .{ .truncate = true });
|
|
28
|
+
defer file.close(stdio.io());
|
|
28
29
|
var write_buffer: [8192]u8 = undefined;
|
|
29
|
-
var file_writer = file.
|
|
30
|
+
var file_writer = file.writerStreaming(stdio.io(), &write_buffer);
|
|
30
31
|
try importer_json.writeScenarioJson(&file_writer.interface, imported);
|
|
31
32
|
try file_writer.interface.flush();
|
|
32
33
|
|
|
@@ -39,7 +40,7 @@ pub fn importFlowYamlFile(
|
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
fn fileExists(path: []const u8) bool {
|
|
42
|
-
|
|
43
|
+
stdio.access(path) catch return false;
|
|
43
44
|
return true;
|
|
44
45
|
}
|
|
45
46
|
|
|
@@ -59,7 +60,7 @@ fn parseFlowYamlSlice(allocator: std.mem.Allocator, content: []const u8, options
|
|
|
59
60
|
defer lines.deinit(allocator);
|
|
60
61
|
var split = std.mem.splitScalar(u8, content, '\n');
|
|
61
62
|
while (split.next()) |line| {
|
|
62
|
-
try lines.append(allocator, std.mem.
|
|
63
|
+
try lines.append(allocator, std.mem.trimEnd(u8, line, "\r"));
|
|
63
64
|
}
|
|
64
65
|
|
|
65
66
|
var in_commands = false;
|