Commit 373d48212f
Changed files (1)
src
Package
src/Package/Fetch.zig
@@ -1890,16 +1890,19 @@ const UnpackResult = struct {
}
};
-test "fetch tarball: fail with unable to create file" {
+test "tarball with duplicate file names" {
+ const gpa = std.testing.allocator;
var tmp = std.testing.tmpDir(.{});
defer tmp.cleanup();
const tarball_name = "package.tar";
try createTestTarball(tmp.dir, tarball_name, false);
+ const tarball_path = try std.fmt.allocPrint(gpa, "zig-cache/tmp/{s}/{s}", .{ tmp.sub_path, tarball_name });
+ defer gpa.free(tarball_path);
// Run tarball fetch, expect to fail
var fb: TestFetchBuilder = undefined;
- var fetch = try fb.build(std.testing.allocator, tmp, tarball_name);
+ var fetch = try fb.build(gpa, tmp.dir, tarball_path);
defer fb.deinit();
try std.testing.expectError(error.FetchFailed, fetch.run());
@@ -1911,16 +1914,19 @@ test "fetch tarball: fail with unable to create file" {
);
}
-test "fetch tarball: error path are excluded" {
+test "tarball with error paths excluded" {
+ const gpa = std.testing.allocator;
var tmp = std.testing.tmpDir(.{});
defer tmp.cleanup();
const tarball_name = "package.tar";
try createTestTarball(tmp.dir, tarball_name, true);
+ const tarball_path = try std.fmt.allocPrint(gpa, "zig-cache/tmp/{s}/{s}", .{ tmp.sub_path, tarball_name });
+ defer gpa.free(tarball_path);
// Run tarball fetch, should succeed
var fb: TestFetchBuilder = undefined;
- var fetch = try fb.build(std.testing.allocator, tmp, tarball_name);
+ var fetch = try fb.build(std.testing.allocator, tmp.dir, tarball_path);
defer fb.deinit();
try fetch.run();
@@ -1944,9 +1950,8 @@ const TestFetchBuilder = struct {
job_queue: Fetch.JobQueue,
fetch: Fetch,
- fn build(self: *TestFetchBuilder, allocator: std.mem.Allocator, tmp: std.testing.TmpDir, tarball_name: []const u8) !*Fetch {
- const cache_dir = try tmp.dir.makeOpenPath("zig-global-cache", .{});
- const path_or_url = try std.fmt.allocPrint(allocator, "zig-cache/tmp/{s}/{s}", .{ tmp.sub_path, tarball_name });
+ fn build(self: *TestFetchBuilder, allocator: std.mem.Allocator, cache_parent_dir: std.fs.Dir, path_or_url: []const u8) !*Fetch {
+ const cache_dir = try cache_parent_dir.makeOpenPath("zig-global-cache", .{});
try self.thread_pool.init(.{ .allocator = allocator });
self.http_client = .{ .allocator = allocator };
@@ -1993,7 +1998,6 @@ const TestFetchBuilder = struct {
}
fn deinit(self: *TestFetchBuilder) void {
- self.fetch.arena.child_allocator.free(self.fetch.location.path_or_url);
self.fetch.deinit();
self.job_queue.deinit();
self.root_prog_node.end();
@@ -2043,8 +2047,9 @@ const TestFetchBuilder = struct {
const em = errors.getErrorMessage(errors.getMessages()[0]);
try std.testing.expectEqual(1, em.count);
- try std.testing.expectEqual(notes_len, em.notes_len);
-
+ if (notes_len > 0) {
+ try std.testing.expectEqual(notes_len, em.notes_len);
+ }
var al = std.ArrayList(u8).init(std.testing.allocator);
defer al.deinit();
try errors.renderToWriter(.{ .ttyconf = .no_color }, al.writer());
@@ -2098,3 +2103,119 @@ fn createTestTarball(dir: fs.Dir, tarball_name: []const u8, with_manifest: bool)
try file.writeAll(&[_]u8{0} ** (512 - build_zig_zon.len));
}
}
+
+// Using test cases from: https://github.com/ianprime0509/pathological-packages
+// repository. Depends on existence of the FAT32 file system at /tmp/fat32.mnt
+// (look at the fat32TmpDir function below how to create it). If that folder is
+// not found test will be skipped. Folder is in FAT32 file system because it is
+// case insensitive and and does not support symlinks.
+test "pathological packages" {
+ const gpa = std.testing.allocator;
+ var buf: [128]u8 = undefined;
+
+ const urls: []const []const u8 = &.{
+ "https://github.com/ianprime0509/pathological-packages/archive/{s}.tar.gz",
+ "git+https://github.com/ianprime0509/pathological-packages#{s}",
+ };
+ const branches: []const []const u8 = &.{
+ "excluded-case-collisions",
+ "excluded-symlinks",
+ "included-case-collisions",
+ "included-symlinks",
+ };
+
+ // Expected fetched package files or error message for each combination of url/branch.
+ const expected = [_]struct {
+ files: []const []const u8 = &.{},
+ err_msg: []const u8 = "",
+ }{
+ // tar
+ .{ .files = &.{ "build.zig", "build.zig.zon" } },
+ .{ .files = &.{ "build.zig", "build.zig.zon", "main" } },
+ .{ .err_msg =
+ \\error: unable to unpack tarball
+ \\ note: unable to create file 'main': PathAlreadyExists
+ \\ note: unable to create file 'subdir/main': PathAlreadyExists
+ \\
+ },
+ .{ .err_msg =
+ \\error: unable to unpack tarball
+ \\ note: unable to create symlink from 'link' to 'main': AccessDenied
+ \\ note: unable to create symlink from 'subdir/link' to 'main': AccessDenied
+ \\
+ },
+ // git
+ .{ .files = &.{ "build.zig", "build.zig.zon" } },
+ .{ .files = &.{ "build.zig", "build.zig.zon", "main" } },
+ .{ .err_msg =
+ \\error: unable to unpack packfile
+ \\ note: unable to create file 'main': PathAlreadyExists
+ \\ note: unable to create file 'subdir/main': PathAlreadyExists
+ \\
+ },
+ .{ .err_msg =
+ \\error: unable to unpack packfile
+ \\ note: unable to create symlink from 'link' to 'main': AccessDenied
+ \\ note: unable to create symlink from 'subdir/link' to 'main': AccessDenied
+ \\
+ },
+ };
+
+ var expected_no: usize = 0;
+ inline for (urls) |url_fmt| {
+ var tmp = try fat32TmpDir();
+ defer tmp.cleanup();
+
+ for (branches) |branch| {
+ defer expected_no += 1;
+ const url = try std.fmt.bufPrint(&buf, url_fmt, .{branch});
+ // std.debug.print("fetching url: {s}\n", .{url});
+
+ var fb: TestFetchBuilder = undefined;
+ var fetch = try fb.build(gpa, tmp.dir, url);
+ defer fb.deinit();
+
+ const ex = expected[expected_no];
+ if (ex.err_msg.len > 0) {
+ try std.testing.expectError(error.FetchFailed, fetch.run());
+ try fb.expectFetchErrors(0, ex.err_msg);
+ } else {
+ try fetch.run();
+ try fb.expectPackageFiles(ex.files);
+ }
+ }
+ }
+}
+
+// Using logic from std.testing.tmpDir() to make temporary directory at specific
+// location.
+//
+// This assumes FAT32 file system in /tmp/fat32.mnt folder,
+// created with something like this:
+// $ cd /tmp && fallocate -l 1M fat32.fs && mkfs.fat -F32 fat32.fs && mkdir fat32.mnt && sudo mount -o rw,umask=0000 fat32.fs fat32.mnt
+//
+// To remove that folder:
+// $ cd /tmp && sudo umount fat32.mnt && rm -rf fat32.mnt fat32.fs
+//
+pub fn fat32TmpDir() !std.testing.TmpDir {
+ const fat32fs_path = "/tmp/fat32.mnt/";
+
+ const random_bytes_count = 12;
+ var random_bytes: [random_bytes_count]u8 = undefined;
+ std.crypto.random.bytes(&random_bytes);
+ var sub_path: [std.fs.base64_encoder.calcSize(random_bytes_count)]u8 = undefined;
+ _ = std.fs.base64_encoder.encode(&sub_path, &random_bytes);
+
+ const parent_dir = std.fs.openDirAbsolute(fat32fs_path, .{}) catch |err| switch (err) {
+ error.FileNotFound => return error.SkipZigTest,
+ else => return err,
+ };
+ const dir = parent_dir.makeOpenPath(&sub_path, .{}) catch
+ @panic("unable to make tmp dir for testing: unable to make and open the tmp dir");
+
+ return .{
+ .dir = dir,
+ .parent_dir = parent_dir,
+ .sub_path = sub_path,
+ };
+}