| ... | @@ -1890,16 +1890,19 @@ const UnpackResult = struct { | ... | @@ -1890,16 +1890,19 @@ const UnpackResult = struct { |
| 1890 | } | 1890 | } |
| 1891 | }; | 1891 | }; |
| 1892 | | 1892 | |
| 1893 | test "fetch tarball: fail with unable to create file" { | 1893 | test "tarball with duplicate file names" { |
| | 1894 | const gpa = std.testing.allocator; |
| 1894 | var tmp = std.testing.tmpDir(.{}); | 1895 | var tmp = std.testing.tmpDir(.{}); |
| 1895 | defer tmp.cleanup(); | 1896 | defer tmp.cleanup(); |
| 1896 | | 1897 | |
| 1897 | const tarball_name = "package.tar"; | 1898 | const tarball_name = "package.tar"; |
| 1898 | try createTestTarball(tmp.dir, tarball_name, false); | 1899 | try createTestTarball(tmp.dir, tarball_name, false); |
| | 1900 | const tarball_path = try std.fmt.allocPrint(gpa, "zig-cache/tmp/{s}/{s}", .{ tmp.sub_path, tarball_name }); |
| | 1901 | defer gpa.free(tarball_path); |
| 1899 | | 1902 | |
| 1900 | // Run tarball fetch, expect to fail | 1903 | // Run tarball fetch, expect to fail |
| 1901 | var fb: TestFetchBuilder = undefined; | 1904 | var fb: TestFetchBuilder = undefined; |
| 1902 | var fetch = try fb.build(std.testing.allocator, tmp, tarball_name); | 1905 | var fetch = try fb.build(gpa, tmp.dir, tarball_path); |
| 1903 | defer fb.deinit(); | 1906 | defer fb.deinit(); |
| 1904 | try std.testing.expectError(error.FetchFailed, fetch.run()); | 1907 | try std.testing.expectError(error.FetchFailed, fetch.run()); |
| 1905 | | 1908 | |
| ... | @@ -1911,16 +1914,19 @@ test "fetch tarball: fail with unable to create file" { | ... | @@ -1911,16 +1914,19 @@ test "fetch tarball: fail with unable to create file" { |
| 1911 | ); | 1914 | ); |
| 1912 | } | 1915 | } |
| 1913 | | 1916 | |
| 1914 | test "fetch tarball: error path are excluded" { | 1917 | test "tarball with error paths excluded" { |
| | 1918 | const gpa = std.testing.allocator; |
| 1915 | var tmp = std.testing.tmpDir(.{}); | 1919 | var tmp = std.testing.tmpDir(.{}); |
| 1916 | defer tmp.cleanup(); | 1920 | defer tmp.cleanup(); |
| 1917 | | 1921 | |
| 1918 | const tarball_name = "package.tar"; | 1922 | const tarball_name = "package.tar"; |
| 1919 | try createTestTarball(tmp.dir, tarball_name, true); | 1923 | try createTestTarball(tmp.dir, tarball_name, true); |
| | 1924 | const tarball_path = try std.fmt.allocPrint(gpa, "zig-cache/tmp/{s}/{s}", .{ tmp.sub_path, tarball_name }); |
| | 1925 | defer gpa.free(tarball_path); |
| 1920 | | 1926 | |
| 1921 | // Run tarball fetch, should succeed | 1927 | // Run tarball fetch, should succeed |
| 1922 | var fb: TestFetchBuilder = undefined; | 1928 | var fb: TestFetchBuilder = undefined; |
| 1923 | var fetch = try fb.build(std.testing.allocator, tmp, tarball_name); | 1929 | var fetch = try fb.build(std.testing.allocator, tmp.dir, tarball_path); |
| 1924 | defer fb.deinit(); | 1930 | defer fb.deinit(); |
| 1925 | try fetch.run(); | 1931 | try fetch.run(); |
| 1926 | | 1932 | |
| ... | @@ -1944,9 +1950,8 @@ const TestFetchBuilder = struct { | ... | @@ -1944,9 +1950,8 @@ const TestFetchBuilder = struct { |
| 1944 | job_queue: Fetch.JobQueue, | 1950 | job_queue: Fetch.JobQueue, |
| 1945 | fetch: Fetch, | 1951 | fetch: Fetch, |
| 1946 | | 1952 | |
| 1947 | fn build(self: *TestFetchBuilder, allocator: std.mem.Allocator, tmp: std.testing.TmpDir, tarball_name: []const u8) !*Fetch { | 1953 | fn build(self: *TestFetchBuilder, allocator: std.mem.Allocator, cache_parent_dir: std.fs.Dir, path_or_url: []const u8) !*Fetch { |
| 1948 | const cache_dir = try tmp.dir.makeOpenPath("zig-global-cache", .{}); | 1954 | const cache_dir = try cache_parent_dir.makeOpenPath("zig-global-cache", .{}); |
| 1949 | const path_or_url = try std.fmt.allocPrint(allocator, "zig-cache/tmp/{s}/{s}", .{ tmp.sub_path, tarball_name }); | | |
| 1950 | | 1955 | |
| 1951 | try self.thread_pool.init(.{ .allocator = allocator }); | 1956 | try self.thread_pool.init(.{ .allocator = allocator }); |
| 1952 | self.http_client = .{ .allocator = allocator }; | 1957 | self.http_client = .{ .allocator = allocator }; |
| ... | @@ -1993,7 +1998,6 @@ const TestFetchBuilder = struct { | ... | @@ -1993,7 +1998,6 @@ const TestFetchBuilder = struct { |
| 1993 | } | 1998 | } |
| 1994 | | 1999 | |
| 1995 | fn deinit(self: *TestFetchBuilder) void { | 2000 | fn deinit(self: *TestFetchBuilder) void { |
| 1996 | self.fetch.arena.child_allocator.free(self.fetch.location.path_or_url); | | |
| 1997 | self.fetch.deinit(); | 2001 | self.fetch.deinit(); |
| 1998 | self.job_queue.deinit(); | 2002 | self.job_queue.deinit(); |
| 1999 | self.root_prog_node.end(); | 2003 | self.root_prog_node.end(); |
| ... | @@ -2043,8 +2047,9 @@ const TestFetchBuilder = struct { | ... | @@ -2043,8 +2047,9 @@ const TestFetchBuilder = struct { |
| 2043 | | 2047 | |
| 2044 | const em = errors.getErrorMessage(errors.getMessages()[0]); | 2048 | const em = errors.getErrorMessage(errors.getMessages()[0]); |
| 2045 | try std.testing.expectEqual(1, em.count); | 2049 | try std.testing.expectEqual(1, em.count); |
| 2046 | try std.testing.expectEqual(notes_len, em.notes_len); | 2050 | if (notes_len > 0) { |
| 2047 | | 2051 | try std.testing.expectEqual(notes_len, em.notes_len); |
| | 2052 | } |
| 2048 | var al = std.ArrayList(u8).init(std.testing.allocator); | 2053 | var al = std.ArrayList(u8).init(std.testing.allocator); |
| 2049 | defer al.deinit(); | 2054 | defer al.deinit(); |
| 2050 | try errors.renderToWriter(.{ .ttyconf = .no_color }, al.writer()); | 2055 | try errors.renderToWriter(.{ .ttyconf = .no_color }, al.writer()); |
| ... | @@ -2098,3 +2103,119 @@ fn createTestTarball(dir: fs.Dir, tarball_name: []const u8, with_manifest: bool) | ... | @@ -2098,3 +2103,119 @@ fn createTestTarball(dir: fs.Dir, tarball_name: []const u8, with_manifest: bool) |
| 2098 | try file.writeAll(&[_]u8{0} ** (512 - build_zig_zon.len)); | 2103 | try file.writeAll(&[_]u8{0} ** (512 - build_zig_zon.len)); |
| 2099 | } | 2104 | } |
| 2100 | } | 2105 | } |
| | 2106 | |
| | 2107 | // Using test cases from: https://github.com/ianprime0509/pathological-packages |
| | 2108 | // repository. Depends on existence of the FAT32 file system at /tmp/fat32.mnt |
| | 2109 | // (look at the fat32TmpDir function below how to create it). If that folder is |
| | 2110 | // not found test will be skipped. Folder is in FAT32 file system because it is |
| | 2111 | // case insensitive and and does not support symlinks. |
| | 2112 | test "pathological packages" { |
| | 2113 | const gpa = std.testing.allocator; |
| | 2114 | var buf: [128]u8 = undefined; |
| | 2115 | |
| | 2116 | const urls: []const []const u8 = &.{ |
| | 2117 | "https://github.com/ianprime0509/pathological-packages/archive/{s}.tar.gz", |
| | 2118 | "git+https://github.com/ianprime0509/pathological-packages#{s}", |
| | 2119 | }; |
| | 2120 | const branches: []const []const u8 = &.{ |
| | 2121 | "excluded-case-collisions", |
| | 2122 | "excluded-symlinks", |
| | 2123 | "included-case-collisions", |
| | 2124 | "included-symlinks", |
| | 2125 | }; |
| | 2126 | |
| | 2127 | // Expected fetched package files or error message for each combination of url/branch. |
| | 2128 | const expected = [_]struct { |
| | 2129 | files: []const []const u8 = &.{}, |
| | 2130 | err_msg: []const u8 = "", |
| | 2131 | }{ |
| | 2132 | // tar |
| | 2133 | .{ .files = &.{ "build.zig", "build.zig.zon" } }, |
| | 2134 | .{ .files = &.{ "build.zig", "build.zig.zon", "main" } }, |
| | 2135 | .{ .err_msg = |
| | 2136 | \\error: unable to unpack tarball |
| | 2137 | \\ note: unable to create file 'main': PathAlreadyExists |
| | 2138 | \\ note: unable to create file 'subdir/main': PathAlreadyExists |
| | 2139 | \\ |
| | 2140 | }, |
| | 2141 | .{ .err_msg = |
| | 2142 | \\error: unable to unpack tarball |
| | 2143 | \\ note: unable to create symlink from 'link' to 'main': AccessDenied |
| | 2144 | \\ note: unable to create symlink from 'subdir/link' to 'main': AccessDenied |
| | 2145 | \\ |
| | 2146 | }, |
| | 2147 | // git |
| | 2148 | .{ .files = &.{ "build.zig", "build.zig.zon" } }, |
| | 2149 | .{ .files = &.{ "build.zig", "build.zig.zon", "main" } }, |
| | 2150 | .{ .err_msg = |
| | 2151 | \\error: unable to unpack packfile |
| | 2152 | \\ note: unable to create file 'main': PathAlreadyExists |
| | 2153 | \\ note: unable to create file 'subdir/main': PathAlreadyExists |
| | 2154 | \\ |
| | 2155 | }, |
| | 2156 | .{ .err_msg = |
| | 2157 | \\error: unable to unpack packfile |
| | 2158 | \\ note: unable to create symlink from 'link' to 'main': AccessDenied |
| | 2159 | \\ note: unable to create symlink from 'subdir/link' to 'main': AccessDenied |
| | 2160 | \\ |
| | 2161 | }, |
| | 2162 | }; |
| | 2163 | |
| | 2164 | var expected_no: usize = 0; |
| | 2165 | inline for (urls) |url_fmt| { |
| | 2166 | var tmp = try fat32TmpDir(); |
| | 2167 | defer tmp.cleanup(); |
| | 2168 | |
| | 2169 | for (branches) |branch| { |
| | 2170 | defer expected_no += 1; |
| | 2171 | const url = try std.fmt.bufPrint(&buf, url_fmt, .{branch}); |
| | 2172 | // std.debug.print("fetching url: {s}\n", .{url}); |
| | 2173 | |
| | 2174 | var fb: TestFetchBuilder = undefined; |
| | 2175 | var fetch = try fb.build(gpa, tmp.dir, url); |
| | 2176 | defer fb.deinit(); |
| | 2177 | |
| | 2178 | const ex = expected[expected_no]; |
| | 2179 | if (ex.err_msg.len > 0) { |
| | 2180 | try std.testing.expectError(error.FetchFailed, fetch.run()); |
| | 2181 | try fb.expectFetchErrors(0, ex.err_msg); |
| | 2182 | } else { |
| | 2183 | try fetch.run(); |
| | 2184 | try fb.expectPackageFiles(ex.files); |
| | 2185 | } |
| | 2186 | } |
| | 2187 | } |
| | 2188 | } |
| | 2189 | |
| | 2190 | // Using logic from std.testing.tmpDir() to make temporary directory at specific |
| | 2191 | // location. |
| | 2192 | // |
| | 2193 | // This assumes FAT32 file system in /tmp/fat32.mnt folder, |
| | 2194 | // created with something like this: |
| | 2195 | // $ 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 |
| | 2196 | // |
| | 2197 | // To remove that folder: |
| | 2198 | // $ cd /tmp && sudo umount fat32.mnt && rm -rf fat32.mnt fat32.fs |
| | 2199 | // |
| | 2200 | pub fn fat32TmpDir() !std.testing.TmpDir { |
| | 2201 | const fat32fs_path = "/tmp/fat32.mnt/"; |
| | 2202 | |
| | 2203 | const random_bytes_count = 12; |
| | 2204 | var random_bytes: [random_bytes_count]u8 = undefined; |
| | 2205 | std.crypto.random.bytes(&random_bytes); |
| | 2206 | var sub_path: [std.fs.base64_encoder.calcSize(random_bytes_count)]u8 = undefined; |
| | 2207 | _ = std.fs.base64_encoder.encode(&sub_path, &random_bytes); |
| | 2208 | |
| | 2209 | const parent_dir = std.fs.openDirAbsolute(fat32fs_path, .{}) catch |err| switch (err) { |
| | 2210 | error.FileNotFound => return error.SkipZigTest, |
| | 2211 | else => return err, |
| | 2212 | }; |
| | 2213 | const dir = parent_dir.makeOpenPath(&sub_path, .{}) catch |
| | 2214 | @panic("unable to make tmp dir for testing: unable to make and open the tmp dir"); |
| | 2215 | |
| | 2216 | return .{ |
| | 2217 | .dir = dir, |
| | 2218 | .parent_dir = parent_dir, |
| | 2219 | .sub_path = sub_path, |
| | 2220 | }; |
| | 2221 | } |