zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

blob 6b9c19c4 (13295B) - Raw


      1 const std = @import("../std.zig");
      2 const os = std.os;
      3 const testing = std.testing;
      4 const expect = testing.expect;
      5 const expectEqual = testing.expectEqual;
      6 const io = std.io;
      7 const fs = std.fs;
      8 const mem = std.mem;
      9 const elf = std.elf;
     10 const File = std.fs.File;
     11 const Thread = std.Thread;
     12 
     13 const a = std.testing.allocator;
     14 
     15 const builtin = @import("builtin");
     16 const AtomicRmwOp = builtin.AtomicRmwOp;
     17 const AtomicOrder = builtin.AtomicOrder;
     18 const tmpDir = std.testing.tmpDir;
     19 const Dir = std.fs.Dir;
     20 const ArenaAllocator = std.heap.ArenaAllocator;
     21 
     22 test "fstatat" {
     23     // enable when `fstat` and `fstatat` are implemented on Windows
     24     if (builtin.os.tag == .windows) return error.SkipZigTest;
     25 
     26     var tmp = tmpDir(.{});
     27     defer tmp.cleanup();
     28 
     29     // create dummy file
     30     const contents = "nonsense";
     31     try tmp.dir.writeFile("file.txt", contents);
     32 
     33     // fetch file's info on the opened fd directly
     34     const file = try tmp.dir.openFile("file.txt", .{});
     35     const stat = try os.fstat(file.handle);
     36     defer file.close();
     37 
     38     // now repeat but using `fstatat` instead
     39     const flags = if (builtin.os.tag == .wasi) 0x0 else os.AT_SYMLINK_NOFOLLOW;
     40     const statat = try os.fstatat(tmp.dir.fd, "file.txt", flags);
     41     expectEqual(stat, statat);
     42 }
     43 
     44 test "readlink" {
     45     if (builtin.os.tag == .wasi) return error.SkipZigTest;
     46 
     47     // First, try relative paths
     48     {
     49         var cwd = fs.cwd();
     50         try cwd.writeFile("file.txt", "nonsense");
     51         try os.symlink("file.txt", "symlinked");
     52 
     53         var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;
     54         const given = try os.readlink("symlinked", buffer[0..]);
     55         expect(mem.eql(u8, "file.txt", given));
     56 
     57         try cwd.deleteFile("file.txt");
     58         try cwd.deleteFile("symlinked");
     59     }
     60 
     61     // Next, let's try fully-qualified paths
     62     {
     63         var tmp = tmpDir(.{});
     64         // defer tmp.cleanup();
     65 
     66         // create file
     67         try tmp.dir.writeFile("file.txt", "nonsense");
     68 
     69         // get paths
     70         // TODO: use Dir's realpath function once that exists
     71         var arena = ArenaAllocator.init(testing.allocator);
     72         defer arena.deinit();
     73 
     74         const base_path = blk: {
     75             const relative_path = try fs.path.join(&arena.allocator, &[_][]const u8{ "zig-cache", "tmp", tmp.sub_path[0..] });
     76             break :blk try fs.realpathAlloc(&arena.allocator, relative_path);
     77         };
     78         const target_path = try fs.path.join(&arena.allocator, &[_][]const u8{ base_path, "file.txt" });
     79         const symlink_path = try fs.path.join(&arena.allocator, &[_][]const u8{ base_path, "symlinked" });
     80         std.debug.warn("\ntarget_path={}\n", .{target_path});
     81         std.debug.warn("symlink_path={}\n", .{symlink_path});
     82 
     83         // create symbolic link by path
     84         try os.symlink(target_path, symlink_path);
     85 
     86         // now, read the link and verify
     87         var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;
     88         const given = try os.readlink(symlink_path, buffer[0..]);
     89         expect(mem.eql(u8, symlink_path, given));
     90     }
     91 }
     92 
     93 test "readlinkat" {
     94     // enable when `readlinkat` and `symlinkat` are implemented on Windows
     95     if (builtin.os.tag == .windows) return error.SkipZigTest;
     96 
     97     var tmp = tmpDir(.{});
     98     defer tmp.cleanup();
     99 
    100     // create file
    101     try tmp.dir.writeFile("file.txt", "nonsense");
    102 
    103     // create a symbolic link
    104     try os.symlinkat("file.txt", tmp.dir.fd, "link");
    105 
    106     // read the link
    107     var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;
    108     const read_link = try os.readlinkat(tmp.dir.fd, "link", buffer[0..]);
    109     expect(mem.eql(u8, "file.txt", read_link));
    110 }
    111 
    112 fn testThreadIdFn(thread_id: *Thread.Id) void {
    113     thread_id.* = Thread.getCurrentId();
    114 }
    115 
    116 test "std.Thread.getCurrentId" {
    117     if (builtin.single_threaded) return error.SkipZigTest;
    118 
    119     var thread_current_id: Thread.Id = undefined;
    120     const thread = try Thread.spawn(&thread_current_id, testThreadIdFn);
    121     const thread_id = thread.handle();
    122     thread.wait();
    123     if (Thread.use_pthreads) {
    124         expect(thread_current_id == thread_id);
    125     } else if (builtin.os.tag == .windows) {
    126         expect(Thread.getCurrentId() != thread_current_id);
    127     } else {
    128         // If the thread completes very quickly, then thread_id can be 0. See the
    129         // documentation comments for `std.Thread.handle`.
    130         expect(thread_id == 0 or thread_current_id == thread_id);
    131     }
    132 }
    133 
    134 test "spawn threads" {
    135     if (builtin.single_threaded) return error.SkipZigTest;
    136 
    137     var shared_ctx: i32 = 1;
    138 
    139     const thread1 = try Thread.spawn({}, start1);
    140     const thread2 = try Thread.spawn(&shared_ctx, start2);
    141     const thread3 = try Thread.spawn(&shared_ctx, start2);
    142     const thread4 = try Thread.spawn(&shared_ctx, start2);
    143 
    144     thread1.wait();
    145     thread2.wait();
    146     thread3.wait();
    147     thread4.wait();
    148 
    149     expect(shared_ctx == 4);
    150 }
    151 
    152 fn start1(ctx: void) u8 {
    153     return 0;
    154 }
    155 
    156 fn start2(ctx: *i32) u8 {
    157     _ = @atomicRmw(i32, ctx, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
    158     return 0;
    159 }
    160 
    161 test "cpu count" {
    162     if (builtin.os.tag == .wasi) return error.SkipZigTest;
    163 
    164     const cpu_count = try Thread.cpuCount();
    165     expect(cpu_count >= 1);
    166 }
    167 
    168 test "thread local storage" {
    169     if (builtin.single_threaded) return error.SkipZigTest;
    170     const thread1 = try Thread.spawn({}, testTls);
    171     const thread2 = try Thread.spawn({}, testTls);
    172     testTls({});
    173     thread1.wait();
    174     thread2.wait();
    175 }
    176 
    177 threadlocal var x: i32 = 1234;
    178 fn testTls(context: void) void {
    179     if (x != 1234) @panic("bad start value");
    180     x += 1;
    181     if (x != 1235) @panic("bad end value");
    182 }
    183 
    184 test "getrandom" {
    185     var buf_a: [50]u8 = undefined;
    186     var buf_b: [50]u8 = undefined;
    187     try os.getrandom(&buf_a);
    188     try os.getrandom(&buf_b);
    189     // If this test fails the chance is significantly higher that there is a bug than
    190     // that two sets of 50 bytes were equal.
    191     expect(!mem.eql(u8, &buf_a, &buf_b));
    192 }
    193 
    194 test "getcwd" {
    195     if (builtin.os.tag == .wasi) return error.SkipZigTest;
    196 
    197     // at least call it so it gets compiled
    198     var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;
    199     _ = os.getcwd(&buf) catch undefined;
    200 }
    201 
    202 test "sigaltstack" {
    203     if (builtin.os.tag == .windows or builtin.os.tag == .wasi) return error.SkipZigTest;
    204 
    205     var st: os.stack_t = undefined;
    206     try os.sigaltstack(null, &st);
    207     // Setting a stack size less than MINSIGSTKSZ returns ENOMEM
    208     st.ss_flags = 0;
    209     st.ss_size = 1;
    210     testing.expectError(error.SizeTooSmall, os.sigaltstack(&st, null));
    211 }
    212 
    213 // If the type is not available use void to avoid erroring out when `iter_fn` is
    214 // analyzed
    215 const dl_phdr_info = if (@hasDecl(os, "dl_phdr_info")) os.dl_phdr_info else c_void;
    216 
    217 const IterFnError = error{
    218     MissingPtLoadSegment,
    219     MissingLoad,
    220     BadElfMagic,
    221     FailedConsistencyCheck,
    222 };
    223 
    224 fn iter_fn(info: *dl_phdr_info, size: usize, counter: *usize) IterFnError!void {
    225     // Count how many libraries are loaded
    226     counter.* += @as(usize, 1);
    227 
    228     // The image should contain at least a PT_LOAD segment
    229     if (info.dlpi_phnum < 1) return error.MissingPtLoadSegment;
    230 
    231     // Quick & dirty validation of the phdr pointers, make sure we're not
    232     // pointing to some random gibberish
    233     var i: usize = 0;
    234     var found_load = false;
    235     while (i < info.dlpi_phnum) : (i += 1) {
    236         const phdr = info.dlpi_phdr[i];
    237 
    238         if (phdr.p_type != elf.PT_LOAD) continue;
    239 
    240         const reloc_addr = info.dlpi_addr + phdr.p_vaddr;
    241         // Find the ELF header
    242         const elf_header = @intToPtr(*elf.Ehdr, reloc_addr - phdr.p_offset);
    243         // Validate the magic
    244         if (!mem.eql(u8, elf_header.e_ident[0..4], "\x7fELF")) return error.BadElfMagic;
    245         // Consistency check
    246         if (elf_header.e_phnum != info.dlpi_phnum) return error.FailedConsistencyCheck;
    247 
    248         found_load = true;
    249         break;
    250     }
    251 
    252     if (!found_load) return error.MissingLoad;
    253 }
    254 
    255 test "dl_iterate_phdr" {
    256     if (builtin.os.tag == .windows or builtin.os.tag == .wasi or builtin.os.tag == .macosx)
    257         return error.SkipZigTest;
    258 
    259     var counter: usize = 0;
    260     try os.dl_iterate_phdr(&counter, IterFnError, iter_fn);
    261     expect(counter != 0);
    262 }
    263 
    264 test "gethostname" {
    265     if (builtin.os.tag == .windows or builtin.os.tag == .wasi)
    266         return error.SkipZigTest;
    267 
    268     var buf: [os.HOST_NAME_MAX]u8 = undefined;
    269     const hostname = try os.gethostname(&buf);
    270     expect(hostname.len != 0);
    271 }
    272 
    273 test "pipe" {
    274     if (builtin.os.tag == .windows or builtin.os.tag == .wasi)
    275         return error.SkipZigTest;
    276 
    277     var fds = try os.pipe();
    278     expect((try os.write(fds[1], "hello")) == 5);
    279     var buf: [16]u8 = undefined;
    280     expect((try os.read(fds[0], buf[0..])) == 5);
    281     testing.expectEqualSlices(u8, buf[0..5], "hello");
    282     os.close(fds[1]);
    283     os.close(fds[0]);
    284 }
    285 
    286 test "argsAlloc" {
    287     var args = try std.process.argsAlloc(std.testing.allocator);
    288     std.process.argsFree(std.testing.allocator, args);
    289 }
    290 
    291 test "memfd_create" {
    292     // memfd_create is linux specific.
    293     if (builtin.os.tag != .linux) return error.SkipZigTest;
    294     const fd = std.os.memfd_create("test", 0) catch |err| switch (err) {
    295         // Related: https://github.com/ziglang/zig/issues/4019
    296         error.SystemOutdated => return error.SkipZigTest,
    297         else => |e| return e,
    298     };
    299     defer std.os.close(fd);
    300     expect((try std.os.write(fd, "test")) == 4);
    301     try std.os.lseek_SET(fd, 0);
    302 
    303     var buf: [10]u8 = undefined;
    304     const bytes_read = try std.os.read(fd, &buf);
    305     expect(bytes_read == 4);
    306     expect(mem.eql(u8, buf[0..4], "test"));
    307 }
    308 
    309 test "mmap" {
    310     if (builtin.os.tag == .windows or builtin.os.tag == .wasi)
    311         return error.SkipZigTest;
    312 
    313     var tmp = tmpDir(.{});
    314     defer tmp.cleanup();
    315 
    316     // Simple mmap() call with non page-aligned size
    317     {
    318         const data = try os.mmap(
    319             null,
    320             1234,
    321             os.PROT_READ | os.PROT_WRITE,
    322             os.MAP_ANONYMOUS | os.MAP_PRIVATE,
    323             -1,
    324             0,
    325         );
    326         defer os.munmap(data);
    327 
    328         testing.expectEqual(@as(usize, 1234), data.len);
    329 
    330         // By definition the data returned by mmap is zero-filled
    331         testing.expect(mem.eql(u8, data, &[_]u8{0x00} ** 1234));
    332 
    333         // Make sure the memory is writeable as requested
    334         std.mem.set(u8, data, 0x55);
    335         testing.expect(mem.eql(u8, data, &[_]u8{0x55} ** 1234));
    336     }
    337 
    338     const test_out_file = "os_tmp_test";
    339     // Must be a multiple of 4096 so that the test works with mmap2
    340     const alloc_size = 8 * 4096;
    341 
    342     // Create a file used for testing mmap() calls with a file descriptor
    343     {
    344         const file = try tmp.dir.createFile(test_out_file, .{});
    345         defer file.close();
    346 
    347         const stream = file.outStream();
    348 
    349         var i: u32 = 0;
    350         while (i < alloc_size / @sizeOf(u32)) : (i += 1) {
    351             try stream.writeIntNative(u32, i);
    352         }
    353     }
    354 
    355     // Map the whole file
    356     {
    357         const file = try tmp.dir.openFile(test_out_file, .{});
    358         defer file.close();
    359 
    360         const data = try os.mmap(
    361             null,
    362             alloc_size,
    363             os.PROT_READ,
    364             os.MAP_PRIVATE,
    365             file.handle,
    366             0,
    367         );
    368         defer os.munmap(data);
    369 
    370         var mem_stream = io.fixedBufferStream(data);
    371         const stream = mem_stream.inStream();
    372 
    373         var i: u32 = 0;
    374         while (i < alloc_size / @sizeOf(u32)) : (i += 1) {
    375             testing.expectEqual(i, try stream.readIntNative(u32));
    376         }
    377     }
    378 
    379     // Map the upper half of the file
    380     {
    381         const file = try tmp.dir.openFile(test_out_file, .{});
    382         defer file.close();
    383 
    384         const data = try os.mmap(
    385             null,
    386             alloc_size / 2,
    387             os.PROT_READ,
    388             os.MAP_PRIVATE,
    389             file.handle,
    390             alloc_size / 2,
    391         );
    392         defer os.munmap(data);
    393 
    394         var mem_stream = io.fixedBufferStream(data);
    395         const stream = mem_stream.inStream();
    396 
    397         var i: u32 = alloc_size / 2 / @sizeOf(u32);
    398         while (i < alloc_size / @sizeOf(u32)) : (i += 1) {
    399             testing.expectEqual(i, try stream.readIntNative(u32));
    400         }
    401     }
    402 
    403     try tmp.dir.deleteFile(test_out_file);
    404 }
    405 
    406 test "getenv" {
    407     if (builtin.os.tag == .windows) {
    408         expect(os.getenvW(&[_:0]u16{ 'B', 'O', 'G', 'U', 'S', 0x11, 0x22, 0x33, 0x44, 0x55 }) == null);
    409     } else {
    410         expect(os.getenvZ("BOGUSDOESNOTEXISTENVVAR") == null);
    411     }
    412 }
    413 
    414 test "fcntl" {
    415     if (builtin.os.tag == .windows or builtin.os.tag == .wasi)
    416         return error.SkipZigTest;
    417 
    418     var tmp = tmpDir(.{});
    419     defer tmp.cleanup();
    420 
    421     const test_out_file = "os_tmp_test";
    422 
    423     const file = try tmp.dir.createFile(test_out_file, .{});
    424     defer {
    425         file.close();
    426         tmp.dir.deleteFile(test_out_file) catch {};
    427     }
    428 
    429     // Note: The test assumes createFile opens the file with O_CLOEXEC
    430     {
    431         const flags = try os.fcntl(file.handle, os.F_GETFD, 0);
    432         expect((flags & os.FD_CLOEXEC) != 0);
    433     }
    434     {
    435         _ = try os.fcntl(file.handle, os.F_SETFD, 0);
    436         const flags = try os.fcntl(file.handle, os.F_GETFD, 0);
    437         expect((flags & os.FD_CLOEXEC) == 0);
    438     }
    439     {
    440         _ = try os.fcntl(file.handle, os.F_SETFD, os.FD_CLOEXEC);
    441         const flags = try os.fcntl(file.handle, os.F_GETFD, 0);
    442         expect((flags & os.FD_CLOEXEC) != 0);
    443     }
    444 }