| ... | @@ -1584,13 +1584,6 @@ pub fn openSelfExe() OpenSelfExeError!File { | ... | @@ -1584,13 +1584,6 @@ pub fn openSelfExe() OpenSelfExeError!File { |
| 1584 | return openFileAbsoluteZ(buf[0..self_exe_path.len :0].ptr, .{}); | 1584 | return openFileAbsoluteZ(buf[0..self_exe_path.len :0].ptr, .{}); |
| 1585 | } | 1585 | } |
| 1586 | | 1586 | |
| 1587 | test "openSelfExe" { | | |
| 1588 | switch (builtin.os.tag) { | | |
| 1589 | .linux, .macosx, .ios, .windows, .freebsd, .dragonfly => (try openSelfExe()).close(), | | |
| 1590 | else => return error.SkipZigTest, // Unsupported OS. | | |
| 1591 | } | | |
| 1592 | } | | |
| 1593 | | | |
| 1594 | pub const SelfExePathError = os.ReadLinkError || os.SysCtlError; | 1587 | pub const SelfExePathError = os.ReadLinkError || os.SysCtlError; |
| 1595 | | 1588 | |
| 1596 | /// `selfExePath` except allocates the result on the heap. | 1589 | /// `selfExePath` except allocates the result on the heap. |
| ... | @@ -1678,163 +1671,9 @@ test "" { | ... | @@ -1678,163 +1671,9 @@ test "" { |
| 1678 | _ = copyFileAbsolute; | 1671 | _ = copyFileAbsolute; |
| 1679 | _ = updateFileAbsolute; | 1672 | _ = updateFileAbsolute; |
| 1680 | _ = Dir.copyFile; | 1673 | _ = Dir.copyFile; |
| | 1674 | _ = @import("fs/test.zig"); |
| 1681 | _ = @import("fs/path.zig"); | 1675 | _ = @import("fs/path.zig"); |
| 1682 | _ = @import("fs/file.zig"); | 1676 | _ = @import("fs/file.zig"); |
| 1683 | _ = @import("fs/get_app_data_dir.zig"); | 1677 | _ = @import("fs/get_app_data_dir.zig"); |
| 1684 | _ = @import("fs/watch.zig"); | 1678 | _ = @import("fs/watch.zig"); |
| 1685 | } | 1679 | } |
| 1686 | | | |
| 1687 | const FILE_LOCK_TEST_SLEEP_TIME = 5 * std.time.millisecond; | | |
| 1688 | | | |
| 1689 | test "open file with exclusive nonblocking lock twice" { | | |
| 1690 | const dir = cwd(); | | |
| 1691 | const filename = "file_nonblocking_lock_test.txt"; | | |
| 1692 | | | |
| 1693 | const file1 = try dir.createFile(filename, .{ .lock = .Exclusive, .lock_nonblocking = true }); | | |
| 1694 | defer file1.close(); | | |
| 1695 | | | |
| 1696 | const file2 = dir.createFile(filename, .{ .lock = .Exclusive, .lock_nonblocking = true }); | | |
| 1697 | std.debug.assert(std.meta.eql(file2, error.WouldBlock)); | | |
| 1698 | | | |
| 1699 | dir.deleteFile(filename) catch |err| switch (err) { | | |
| 1700 | error.FileNotFound => {}, | | |
| 1701 | else => return err, | | |
| 1702 | }; | | |
| 1703 | } | | |
| 1704 | | | |
| 1705 | test "open file with lock twice, make sure it wasn't open at the same time" { | | |
| 1706 | if (builtin.single_threaded) return; | | |
| 1707 | | | |
| 1708 | const filename = "file_lock_test.txt"; | | |
| 1709 | | | |
| 1710 | var contexts = [_]FileLockTestContext{ | | |
| 1711 | .{ .filename = filename, .create = true, .lock = .Exclusive }, | | |
| 1712 | .{ .filename = filename, .create = true, .lock = .Exclusive }, | | |
| 1713 | }; | | |
| 1714 | try run_lock_file_test(&contexts); | | |
| 1715 | | | |
| 1716 | // Check for an error | | |
| 1717 | var was_error = false; | | |
| 1718 | for (contexts) |context, idx| { | | |
| 1719 | if (context.err) |err| { | | |
| 1720 | was_error = true; | | |
| 1721 | std.debug.warn("\nError in context {}: {}\n", .{ idx, err }); | | |
| 1722 | } | | |
| 1723 | } | | |
| 1724 | if (was_error) builtin.panic("There was an error in contexts", null); | | |
| 1725 | | | |
| 1726 | std.debug.assert(!contexts[0].overlaps(&contexts[1])); | | |
| 1727 | | | |
| 1728 | cwd().deleteFile(filename) catch |err| switch (err) { | | |
| 1729 | error.FileNotFound => {}, | | |
| 1730 | else => return err, | | |
| 1731 | }; | | |
| 1732 | } | | |
| 1733 | | | |
| 1734 | test "create file, lock and read from multiple process at once" { | | |
| 1735 | if (builtin.single_threaded) return; | | |
| 1736 | | | |
| 1737 | const filename = "file_read_lock_test.txt"; | | |
| 1738 | const filedata = "Hello, world!\n"; | | |
| 1739 | | | |
| 1740 | try std.fs.cwd().writeFile(filename, filedata); | | |
| 1741 | | | |
| 1742 | var contexts = [_]FileLockTestContext{ | | |
| 1743 | .{ .filename = filename, .create = false, .lock = .Shared }, | | |
| 1744 | .{ .filename = filename, .create = false, .lock = .Shared }, | | |
| 1745 | .{ .filename = filename, .create = false, .lock = .Exclusive }, | | |
| 1746 | }; | | |
| 1747 | | | |
| 1748 | try run_lock_file_test(&contexts); | | |
| 1749 | | | |
| 1750 | var was_error = false; | | |
| 1751 | for (contexts) |context, idx| { | | |
| 1752 | if (context.err) |err| { | | |
| 1753 | was_error = true; | | |
| 1754 | std.debug.warn("\nError in context {}: {}\n", .{ idx, err }); | | |
| 1755 | } | | |
| 1756 | } | | |
| 1757 | if (was_error) builtin.panic("There was an error in contexts", null); | | |
| 1758 | | | |
| 1759 | std.debug.assert(contexts[0].overlaps(&contexts[1])); | | |
| 1760 | std.debug.assert(!contexts[2].overlaps(&contexts[0])); | | |
| 1761 | std.debug.assert(!contexts[2].overlaps(&contexts[1])); | | |
| 1762 | if (contexts[0].bytes_read.? != filedata.len) { | | |
| 1763 | std.debug.warn("\n bytes_read: {}, expected: {} \n", .{ contexts[0].bytes_read, filedata.len }); | | |
| 1764 | } | | |
| 1765 | std.debug.assert(contexts[0].bytes_read.? == filedata.len); | | |
| 1766 | std.debug.assert(contexts[1].bytes_read.? == filedata.len); | | |
| 1767 | | | |
| 1768 | cwd().deleteFile(filename) catch |err| switch (err) { | | |
| 1769 | error.FileNotFound => {}, | | |
| 1770 | else => return err, | | |
| 1771 | }; | | |
| 1772 | } | | |
| 1773 | | | |
| 1774 | const FileLockTestContext = struct { | | |
| 1775 | filename: []const u8, | | |
| 1776 | pid: if (builtin.os.tag == .windows) ?void else ?std.os.pid_t = null, | | |
| 1777 | | | |
| 1778 | // use file.createFile | | |
| 1779 | create: bool, | | |
| 1780 | // the type of lock to use | | |
| 1781 | lock: File.Lock, | | |
| 1782 | | | |
| 1783 | // Output variables | | |
| 1784 | err: ?(File.OpenError || std.os.ReadError) = null, | | |
| 1785 | start_time: u64 = 0, | | |
| 1786 | end_time: u64 = 0, | | |
| 1787 | bytes_read: ?usize = null, | | |
| 1788 | | | |
| 1789 | fn overlaps(self: *const @This(), other: *const @This()) bool { | | |
| 1790 | return (self.start_time < other.end_time) and (self.end_time > other.start_time); | | |
| 1791 | } | | |
| 1792 | | | |
| 1793 | fn run(ctx: *@This()) void { | | |
| 1794 | var file: File = undefined; | | |
| 1795 | if (ctx.create) { | | |
| 1796 | file = cwd().createFile(ctx.filename, .{ .lock = ctx.lock }) catch |err| { | | |
| 1797 | ctx.err = err; | | |
| 1798 | return; | | |
| 1799 | }; | | |
| 1800 | } else { | | |
| 1801 | file = cwd().openFile(ctx.filename, .{ .lock = ctx.lock }) catch |err| { | | |
| 1802 | ctx.err = err; | | |
| 1803 | return; | | |
| 1804 | }; | | |
| 1805 | } | | |
| 1806 | defer file.close(); | | |
| 1807 | | | |
| 1808 | ctx.start_time = std.time.milliTimestamp(); | | |
| 1809 | | | |
| 1810 | if (!ctx.create) { | | |
| 1811 | var buffer: [100]u8 = undefined; | | |
| 1812 | ctx.bytes_read = 0; | | |
| 1813 | while (true) { | | |
| 1814 | const amt = file.read(buffer[0..]) catch |err| { | | |
| 1815 | ctx.err = err; | | |
| 1816 | return; | | |
| 1817 | }; | | |
| 1818 | if (amt == 0) break; | | |
| 1819 | ctx.bytes_read.? += amt; | | |
| 1820 | } | | |
| 1821 | } | | |
| 1822 | | | |
| 1823 | std.time.sleep(FILE_LOCK_TEST_SLEEP_TIME); | | |
| 1824 | | | |
| 1825 | ctx.end_time = std.time.milliTimestamp(); | | |
| 1826 | } | | |
| 1827 | }; | | |
| 1828 | | | |
| 1829 | fn run_lock_file_test(contexts: []FileLockTestContext) !void { | | |
| 1830 | var threads = std.ArrayList(*std.Thread).init(std.testing.allocator); | | |
| 1831 | defer { | | |
| 1832 | for (threads.toSlice()) |thread| { | | |
| 1833 | thread.wait(); | | |
| 1834 | } | | |
| 1835 | threads.deinit(); | | |
| 1836 | } | | |
| 1837 | for (contexts) |*ctx, idx| { | | |
| 1838 | try threads.append(try std.Thread.spawn(ctx, FileLockTestContext.run)); | | |
| 1839 | } | | |
| 1840 | } | | |