diff --git a/foundry.toml b/foundry.toml index 6afc1c3a..c82919a1 100644 --- a/foundry.toml +++ b/foundry.toml @@ -5,6 +5,7 @@ libs = ['lib'] ffi = true fs_permissions = [ { access = "read", path = "./out"}, + { access = "read", path = "./test/_modules" }, { access = "read", path = "./test/fixtures/fs/read" }, { access = "read-write", path = "./test/fixtures/fs/write" }, { access = "read-write", path = "./test/fixtures/fe/output" } diff --git a/src/_modules/Error.sol b/src/_modules/Error.sol index 0a1376fe..04394f67 100644 --- a/src/_modules/Error.sol +++ b/src/_modules/Error.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.13; import {Pointer} from "./Pointer.sol"; -import {LibResultPointer, ResultType} from "./Result.sol"; +import {LibResultPointer, ResultType, StringResult, BytesResult, BoolResult} from "./Result.sol"; type Error is bytes32; @@ -105,6 +105,18 @@ library LibError { (,, bytes memory data) = decode(err); return abi.decode(data, (string)); } + + function toStringResult(Error self) internal pure returns (StringResult) { + return StringResult.wrap(Pointer.unwrap(self.toPointer())); + } + + function toBytesResult(Error self) internal pure returns (BytesResult) { + return BytesResult.wrap(Pointer.unwrap(self.toPointer())); + } + + function toBoolResult(Error self) internal pure returns (BoolResult) { + return BoolResult.wrap(Pointer.unwrap(self.toPointer())); + } } using LibError for Error global; diff --git a/src/_modules/Fe.sol b/src/_modules/Fe.sol index dcc05dbc..a144e630 100644 --- a/src/_modules/Fe.sol +++ b/src/_modules/Fe.sol @@ -5,6 +5,7 @@ import "./Commands.sol"; import "./Strings.sol"; import "./Fs.sol"; import {formatError} from "../_utils/formatError.sol"; +import {BoolResult, BytesResult} from "./Result.sol"; struct Fe { string compilerPath; @@ -80,13 +81,9 @@ library fe { /// @dev Obtains the bytecode of a compiled contract with `contractName`. /// @param contractName The name of the contract from which to retrive the bytecode. - function getBytecode(Fe memory self, string memory contractName) internal view returns (bytes memory) { + function getBytecode(Fe memory self, string memory contractName) internal view returns (BytesResult) { string memory path = string.concat(self.outputDir, "/", contractName, "/", contractName, ".bin"); - if (!fs.fileExists(path)) { - revert(_formatError("getBytecode(Fe,string))", "Contract not found")); - } - return fs.readFileBinary(path); } diff --git a/src/_modules/Fs.sol b/src/_modules/Fs.sol index 64a81587..922f2336 100644 --- a/src/_modules/Fs.sol +++ b/src/_modules/Fs.sol @@ -2,6 +2,12 @@ pragma solidity >=0.8.13 <0.9.0; import "./Vulcan.sol"; +import {Pointer} from "./Pointer.sol"; +import {ResultType, Ok, StringResult, BoolResult, BytesResult, EmptyResult, LibResultPointer} from "./Result.sol"; +import {LibError, Error} from "./Error.sol"; +import {removeSelector} from "../_utils/removeSelector.sol"; + +type FsMetadataResult is bytes32; struct FsMetadata { bool isDir; @@ -14,125 +20,338 @@ struct FsMetadata { } library fs { + using LibError for *; + using FsErrors for Error; + /// @dev Reads the file on `path` and returns its content as a `string`. /// @param path The path to the file. /// @return The content of the file as `string`. - function readFile(string memory path) internal view returns (string memory) { - return vulcan.hevm.readFile(path); + function readFile(string memory path) internal view returns (StringResult) { + try vulcan.hevm.readFile(path) returns (string memory content) { + return Ok(content); + } catch Error(string memory reason) { + return FsErrors.FailedToRead(reason).toStringResult(); + } catch (bytes memory reason) { + return FsErrors.FailedToRead(abi.decode(removeSelector(reason), (string))).toStringResult(); + } } /// @dev Reads the file on `path` and returns its content as a `bytes`. /// @param path The path to the file. /// @return The content of the file as `bytes`. - function readFileBinary(string memory path) internal view returns (bytes memory) { - return vulcan.hevm.readFileBinary(path); + function readFileBinary(string memory path) internal view returns (BytesResult) { + try vulcan.hevm.readFileBinary(path) returns (bytes memory content) { + return Ok(content); + } catch Error(string memory reason) { + return FsErrors.FailedToRead(reason).toBytesResult(); + } catch (bytes memory reason) { + return FsErrors.FailedToRead(abi.decode(removeSelector(reason), (string))).toBytesResult(); + } } /// @dev Obtains the current project's root. /// @return The current project's root. - function projectRoot() internal view returns (string memory) { - return vulcan.hevm.projectRoot(); + function projectRoot() internal view returns (StringResult) { + try vulcan.hevm.projectRoot() returns (string memory path) { + return Ok(path); + } catch Error(string memory reason) { + return FsErrors.FailedToGetProjectRoot(reason).toStringResult(); + } catch (bytes memory reason) { + return FsErrors.FailedToGetProjectRoot(abi.decode(removeSelector(reason), (string))).toStringResult(); + } } /// @dev Obtains the metadata of the specified file or directory. /// @param fileOrDir The path to the file or directory. /// @return data The metadata of the file or directory. - function metadata(string memory fileOrDir) internal view returns (FsMetadata memory data) { - Hevm.FsMetadata memory md = vulcan.hevm.fsMetadata(fileOrDir); - assembly { - data := md + function metadata(string memory fileOrDir) internal view returns (FsMetadataResult) { + try vulcan.hevm.fsMetadata(fileOrDir) returns (Hevm.FsMetadata memory md) { + FsMetadata memory data; + assembly { + data := md + } + + return Ok(data); + } catch Error(string memory reason) { + return FsErrors.FailedToReadMetadata(reason).toFsMetadataResult(); + } catch (bytes memory reason) { + return FsErrors.FailedToReadMetadata(abi.decode(removeSelector(reason), (string))).toFsMetadataResult(); } } /// @dev Reads the next line of the file on `path`. /// @param path The path to the file. /// @return The line that was read. - function readLine(string memory path) internal view returns (string memory) { - return vulcan.hevm.readLine(path); + function readLine(string memory path) internal view returns (StringResult) { + try vulcan.hevm.readLine(path) returns (string memory line) { + return Ok(line); + } catch Error(string memory reason) { + return FsErrors.FailedToReadLine(reason).toStringResult(); + } catch (bytes memory reason) { + return FsErrors.FailedToReadLine(abi.decode(removeSelector(reason), (string))).toStringResult(); + } } /// @dev Modifies the content of the file on `path` with `data`. /// @param path The path to the file. /// @param data The new content of the file. - function writeFile(string memory path, string memory data) internal { - vulcan.hevm.writeFile(path, data); + function writeFile(string memory path, string memory data) internal returns (EmptyResult) { + try vulcan.hevm.writeFile(path, data) { + return Ok(); + } catch Error(string memory reason) { + return FsErrors.FailedToWrite(reason).toEmptyResult(); + } catch (bytes memory reason) { + return FsErrors.FailedToWrite(abi.decode(removeSelector(reason), (string))).toEmptyResult(); + } } /// @dev Modifies the content of the file on `path` with `data`. /// @param path The path to the file. /// @param data The new content of the file. - function writeFileBinary(string memory path, bytes memory data) internal { - vulcan.hevm.writeFileBinary(path, data); + function writeFileBinary(string memory path, bytes memory data) internal returns (EmptyResult) { + try vulcan.hevm.writeFileBinary(path, data) { + return Ok(); + } catch Error(string memory reason) { + return FsErrors.FailedToWrite(reason).toEmptyResult(); + } catch (bytes memory reason) { + return FsErrors.FailedToWrite(abi.decode(removeSelector(reason), (string))).toEmptyResult(); + } } /// @dev Adds a new line to the file on `path`. /// @param path The path to the file. /// @param data The content of the new line. - function writeLine(string memory path, string memory data) internal { - vulcan.hevm.writeLine(path, data); + function writeLine(string memory path, string memory data) internal returns (EmptyResult) { + try vulcan.hevm.writeLine(path, data) { + return Ok(); + } catch Error(string memory reason) { + return FsErrors.FailedToWriteLine(reason).toEmptyResult(); + } catch (bytes memory reason) { + return FsErrors.FailedToWriteLine(abi.decode(removeSelector(reason), (string))).toEmptyResult(); + } } /// @dev Resets the state of the file on `path`. /// @param path The path to the file. - function closeFile(string memory path) internal { - vulcan.hevm.closeFile(path); + function closeFile(string memory path) internal returns (EmptyResult) { + try vulcan.hevm.closeFile(path) { + return Ok(); + } catch Error(string memory reason) { + return FsErrors.FailedToCloseFile(reason).toEmptyResult(); + } catch (bytes memory reason) { + return FsErrors.FailedToCloseFile(abi.decode(removeSelector(reason), (string))).toEmptyResult(); + } } /// @dev Deletes the file on `path`. /// @param path The path to the file. - function removeFile(string memory path) internal { - vulcan.hevm.removeFile(path); + function removeFile(string memory path) internal returns (EmptyResult) { + try vulcan.hevm.removeFile(path) { + return Ok(); + } catch Error(string memory reason) { + return FsErrors.FailedToRemoveFile(reason).toEmptyResult(); + } catch (bytes memory reason) { + return FsErrors.FailedToRemoveFile(abi.decode(removeSelector(reason), (string))).toEmptyResult(); + } } /// @dev Copies a file from `origin` to `target`. /// @param origin The file to copy. /// @param target The destination of the copied data. - function copyFile(string memory origin, string memory target) internal { - writeFileBinary(target, readFileBinary(origin)); + function copyFile(string memory origin, string memory target) internal returns (EmptyResult) { + BytesResult readResult = readFileBinary(origin); + + if (readResult.isError()) { + return readResult.toError().toEmptyResult(); + } + + EmptyResult writeResult = writeFileBinary(target, readResult.toValue()); + + if (writeResult.isError()) { + return writeResult; + } + + return Ok(); } /// @dev Moves a file from `origin` to `target`. /// @param origin The file to be moved. /// @param target The destination of the data. - function moveFile(string memory origin, string memory target) internal { - copyFile(origin, target); - removeFile(origin); + function moveFile(string memory origin, string memory target) internal returns (EmptyResult) { + EmptyResult copyResult = copyFile(origin, target); + + if (copyResult.isError()) { + return copyResult; + } + + EmptyResult removeResult = removeFile(origin); + + if (removeResult.isError()) { + return removeResult; + } + + return Ok(); } /// @dev Checks if a file or directory exists. /// @param path The file or directory to check. /// @return Whether the file on `path` exists or not. - function fileExists(string memory path) internal view returns (bool) { - try vulcan.hevm.fsMetadata(path) { - return true; + function fileExists(string memory path) internal view returns (BoolResult) { + try vulcan.hevm.readFile(path) { + return Ok(true); } catch Error(string memory) { - return false; + return Ok(false); } catch (bytes memory reason) { bytes4 selector = 0x0bc44503; - string memory errorMessage = string.concat( - "The path \"", string.concat(path, "\" is not allowed to be accessed for read operations.") - ); + string memory errorMessage = + string.concat("The path \"", path, "\" is not allowed to be accessed for read operations."); bytes32 errorHash = keccak256(abi.encodeWithSelector(selector, errorMessage)); if (keccak256(reason) == errorHash) { - assembly { - revert(add(reason, 32), mload(reason)) - } + return FsErrors.Forbidden(errorMessage).toBoolResult(); } - return false; + return Ok(false); } } /// @dev Obtains the creation code from an artifact file located at `path` /// @param path The file or directory to check. /// @return The creation bytecode. - function getCode(string memory path) internal view returns (bytes memory) { - return vulcan.hevm.getCode(path); + function getCode(string memory path) internal view returns (BytesResult) { + try vulcan.hevm.getCode(path) returns (bytes memory code) { + return Ok(code); + } catch Error(string memory reason) { + return FsErrors.FailedToGetCode(reason).toBytesResult(); + } catch (bytes memory reason) { + return FsErrors.FailedToGetCode(abi.decode(removeSelector(reason), (string))).toBytesResult(); + } } /// @dev Obtains the deployed code from an artifact file located at `path` /// @param path The file or directory to check. /// @return The deployed bytecode. - function getDeployedCode(string memory path) internal view returns (bytes memory) { - return vulcan.hevm.getDeployedCode(path); + function getDeployedCode(string memory path) internal view returns (BytesResult) { + try vulcan.hevm.getDeployedCode(path) returns (bytes memory code) { + return Ok(code); + } catch Error(string memory reason) { + return FsErrors.FailedToGetCode(reason).toBytesResult(); + } catch (bytes memory reason) { + return FsErrors.FailedToGetCode(abi.decode(removeSelector(reason), (string))).toBytesResult(); + } } } + +library FsErrors { + using LibError for *; + + function FailedToRead(string memory reason) internal pure returns (Error) { + string memory message = string.concat("Failed to read file: \"", reason, "\""); + return FailedToRead.encodeError(message, reason); + } + + function FailedToReadLine(string memory reason) internal pure returns (Error) { + string memory message = string.concat("Failed to read line: \"", reason, "\""); + return FailedToReadLine.encodeError(message, reason); + } + + function FailedToReadMetadata(string memory reason) internal pure returns (Error) { + string memory message = string.concat("Failed to read metadata: \"", reason, "\""); + return FailedToReadMetadata.encodeError(message, reason); + } + + function FailedToGetProjectRoot(string memory reason) internal pure returns (Error) { + string memory message = string.concat("Failed to get project root: \"", reason, "\""); + return FailedToGetProjectRoot.encodeError(message, reason); + } + + function Forbidden(string memory reason) internal pure returns (Error) { + string memory message = string.concat("Not enough permissions to access file: \"", reason, "\""); + return Forbidden.encodeError(message, reason); + } + + function FailedToWrite(string memory reason) internal pure returns (Error) { + string memory message = string.concat("Failed to write file: \"", reason, "\""); + return FailedToWrite.encodeError(message, reason); + } + + function FailedToWriteLine(string memory reason) internal pure returns (Error) { + string memory message = string.concat("Failed to write line: \"", reason, "\""); + return FailedToWriteLine.encodeError(message, reason); + } + + function FailedToCloseFile(string memory reason) internal pure returns (Error) { + string memory message = string.concat("Failed to close file: \"", reason, "\""); + return FailedToCloseFile.encodeError(message, reason); + } + + function FailedToRemoveFile(string memory reason) internal pure returns (Error) { + string memory message = string.concat("Failed to remove file: \"", reason, "\""); + return FailedToRemoveFile.encodeError(message, reason); + } + + function FailedToGetCode(string memory reason) internal pure returns (Error) { + string memory message = string.concat("Failed to get code: \"", reason, "\""); + return FailedToGetCode.encodeError(message, reason); + } + + function toFsMetadataResult(Error self) internal pure returns (FsMetadataResult) { + return FsMetadataResult.wrap(Pointer.unwrap(self.toPointer())); + } + + function toEmptyResult(Error self) internal pure returns (EmptyResult) { + return EmptyResult.wrap(Pointer.unwrap(self.toPointer())); + } +} + +library LibFsMetadataPointer { + function asFsMetadata(Pointer self) internal pure returns (FsMetadata memory metadata) { + bytes32 memoryAddr = self.asBytes32(); + + assembly { + metadata := memoryAddr + } + } +} + +library LibFsMetadataResult { + using LibFsMetadataPointer for Pointer; + + function isOk(FsMetadataResult self) internal pure returns (bool) { + return LibResultPointer.isOk(self.toPointer()); + } + + function isError(FsMetadataResult self) internal pure returns (bool) { + return LibResultPointer.isError(self.toPointer()); + } + + function unwrap(FsMetadataResult self) internal pure returns (FsMetadata memory val) { + return LibResultPointer.unwrap(self.toPointer()).asFsMetadata(); + } + + function expect(FsMetadataResult self, string memory err) internal pure returns (FsMetadata memory) { + return LibResultPointer.expect(self.toPointer(), err).asFsMetadata(); + } + + function toError(FsMetadataResult self) internal pure returns (Error) { + return LibResultPointer.toError(self.toPointer()); + } + + function toValue(FsMetadataResult self) internal pure returns (FsMetadata memory val) { + (, Pointer ptr) = LibResultPointer.decode(self.toPointer()); + + return ptr.asFsMetadata(); + } + + function toPointer(FsMetadataResult self) internal pure returns (Pointer) { + return Pointer.wrap(FsMetadataResult.unwrap(self)); + } +} + +function Ok(FsMetadata memory value) pure returns (FsMetadataResult) { + bytes32 _value; + assembly { + _value := value + } + return FsMetadataResult.wrap(Pointer.unwrap(ResultType.Ok.encode(_value))); +} + +using LibFsMetadataResult for FsMetadataResult global; diff --git a/src/_modules/Result.sol b/src/_modules/Result.sol index 507e1778..7d692ce9 100644 --- a/src/_modules/Result.sol +++ b/src/_modules/Result.sol @@ -15,6 +15,10 @@ type BytesResult is bytes32; type StringResult is bytes32; +type BoolResult is bytes32; + +type EmptyResult is bytes32; + library LibResultPointer { function decode(Pointer self) internal pure returns (ResultType, Pointer) { bytes memory data; @@ -161,6 +165,64 @@ library LibStringResult { } } +library LibBoolResult { + function isOk(BoolResult self) internal pure returns (bool) { + return LibResultPointer.isOk(self.toPointer()); + } + + function isError(BoolResult self) internal pure returns (bool) { + return LibResultPointer.isError(self.toPointer()); + } + + function unwrap(BoolResult self) internal pure returns (bool val) { + return LibResultPointer.unwrap(self.toPointer()).asBool(); + } + + function expect(BoolResult self, string memory err) internal pure returns (bool) { + return LibResultPointer.expect(self.toPointer(), err).asBool(); + } + + function toError(BoolResult self) internal pure returns (Error) { + return LibResultPointer.toError(Pointer.wrap(BoolResult.unwrap(self))); + } + + function toValue(BoolResult self) internal pure returns (bool val) { + (, Pointer ptr) = LibResultPointer.decode(self.toPointer()); + + return ptr.asBool(); + } + + function toPointer(BoolResult self) internal pure returns (Pointer) { + return Pointer.wrap(BoolResult.unwrap(self)); + } +} + +library LibEmptyResult { + function isOk(EmptyResult self) internal pure returns (bool) { + return LibResultPointer.isOk(self.toPointer()); + } + + function isError(EmptyResult self) internal pure returns (bool) { + return LibResultPointer.isError(self.toPointer()); + } + + function unwrap(EmptyResult self) internal pure { + LibResultPointer.unwrap(self.toPointer()); + } + + function expect(EmptyResult self, string memory err) internal pure { + LibResultPointer.expect(self.toPointer(), err); + } + + function toError(EmptyResult self) internal pure returns (Error) { + return LibResultPointer.toError(self.toPointer()); + } + + function toPointer(EmptyResult self) internal pure returns (Pointer) { + return Pointer.wrap(EmptyResult.unwrap(self)); + } +} + library LibResultType { function encode(ResultType _type, bytes32 _data) internal pure returns (Pointer result) { bytes memory data = abi.encode(_type, _data); @@ -190,7 +252,21 @@ function Ok(string memory value) pure returns (StringResult) { return StringResult.wrap(Pointer.unwrap(ResultType.Ok.encode(_value))); } +function Ok(bool value) pure returns (BoolResult) { + bytes32 _value; + assembly { + _value := value + } + return BoolResult.wrap(Pointer.unwrap(ResultType.Ok.encode(_value))); +} + +function Ok() pure returns (EmptyResult) { + return EmptyResult.wrap(Pointer.unwrap(ResultType.Ok.encode(bytes32(0)))); +} + using LibStringResult for StringResult global; using LibBytesResult for BytesResult global; +using LibBoolResult for BoolResult global; +using LibEmptyResult for EmptyResult global; using LibBytes32Result for Bytes32Result global; using LibResultType for ResultType global; diff --git a/src/test.sol b/src/test.sol index 4a3a29bc..0eb715dc 100644 --- a/src/test.sol +++ b/src/test.sol @@ -11,7 +11,7 @@ import {env} from "./_modules/Env.sol"; import {events} from "./_modules/Events.sol"; import {expect} from "./_modules/Expect.sol"; import {forks, Fork} from "./_modules/Forks.sol"; -import {fs, FsMetadata} from "./_modules/Fs.sol"; +import {fs, FsMetadata, FsMetadataResult, FsErrors} from "./_modules/Fs.sol"; import {gas} from "./_modules/Gas.sol"; import {huff, Huffc} from "./_modules/Huff.sol"; import {InvariantsBase, invariants} from "./_modules/Invariants.sol"; @@ -26,7 +26,7 @@ import {println} from "./_utils/println.sol"; import {bound} from "./_utils/bound.sol"; import {formatError} from "./_utils/formatError.sol"; import {removeSelector} from "./_utils/removeSelector.sol"; -import {Ok} from "./_modules/Result.sol"; +import {Ok, StringResult, BoolResult, BytesResult, EmptyResult} from "./_modules/Result.sol"; import {Error} from "./_modules/Error.sol"; // @dev Main entry point to Vulcan tests diff --git a/test/_modules/Fe.t.sol b/test/_modules/Fe.t.sol index 05ec1ed7..c1815327 100644 --- a/test/_modules/Fe.t.sol +++ b/test/_modules/Fe.t.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.8.13 <0.9.0; -import {Test, expect, commands, Command, fe, Fe, fs, println, strings} from "../../src/test.sol"; +import {Test, expect, commands, Command, fe, Fe, fs, println, strings, StringResult} from "../../src/test.sol"; contract FeTest is Test { function testToCommandAllSet() external { @@ -39,9 +39,9 @@ contract FeTest is Test { true ).build(); - string memory result = fs.readFile("./test/fixtures/fe/output/A/A.bin"); + StringResult result = fs.readFile("./test/fixtures/fe/output/A/A.bin"); - expect(bytes(result).length).toBeGreaterThan(0); + expect(bytes(result.unwrap()).length).toBeGreaterThan(0); } function testCompileAndRead() external { @@ -51,6 +51,6 @@ contract FeTest is Test { string memory expectedBytecode = "600180600c6000396000f3fe00"; - expect(string(feCmd.getBytecode("A"))).toEqual(expectedBytecode); + expect(string(feCmd.getBytecode("A").toValue())).toEqual(expectedBytecode); } } diff --git a/test/_modules/Fs.t.sol b/test/_modules/Fs.t.sol index 8398dd66..3c7a20f1 100644 --- a/test/_modules/Fs.t.sol +++ b/test/_modules/Fs.t.sol @@ -1,6 +1,20 @@ pragma solidity >=0.8.13 <0.9.0; -import {Test, expect, console, fs, FsMetadata, commands} from "../../src/test.sol"; +import { + Test, + expect, + console, + fs, + FsMetadata, + commands, + StringResult, + BoolResult, + BytesResult, + EmptyResult, + FsMetadataResult, + Error, + FsErrors +} from "../../src/test.sol"; contract FsTest is Test { string constant HELLO_WORLD = "./test/fixtures/fs/read/hello_world.txt"; @@ -8,106 +22,150 @@ contract FsTest is Test { string constant BINARY_TEST_FILE = "./test/fixtures/fs/write/test_binary.txt"; function testItCanReadAFile() external { - string memory output = fs.readFile(HELLO_WORLD); + StringResult output = fs.readFile(HELLO_WORLD); - expect(output).toEqual("Hello, World!\n"); + expect(output.unwrap()).toEqual("Hello, World!\n"); + } + + function testItCanCheckIfAFileExists() external { + BoolResult shouldExists = fs.fileExists("./test/_modules/Fs.t.sol"); + BoolResult shouldNotExist = fs.fileExists("./test/_modules/Fsssss.t.sol"); + BoolResult shouldBeError = fs.fileExists("/tmp/klsajdflksjadfrlkjasdf"); + + expect(shouldExists.isError()).toBeFalse(); + expect(shouldNotExist.isError()).toBeFalse(); + expect(shouldBeError.isError()).toBeTrue(); + + expect(shouldExists.toValue()).toBeTrue(); + expect(shouldNotExist.toValue()).toBeFalse(); + + Error err = shouldBeError.toError(); + (, string memory message,) = err.decode(); + + expect(message).toEqual( + "Not enough permissions to access file: \"The path \"/tmp/klsajdflksjadfrlkjasdf\" is not allowed to be accessed for read operations.\"" + ); } function testItCanReadAFileAsBinary() external { - bytes memory output = fs.readFileBinary(HELLO_WORLD); + BytesResult result = fs.readFileBinary(HELLO_WORLD); + + expect(result.isOk()).toBeTrue(); - expect(output).toEqual(bytes("Hello, World!\n")); + expect(result.toValue()).toEqual(bytes("Hello, World!\n")); } function testItCanWriteAFile() external { string memory content = "Writing from a test"; - fs.writeFile(TEXT_TEST_FILE, content); + EmptyResult writeResult = fs.writeFile(TEXT_TEST_FILE, content); + + expect(writeResult.isOk()).toBeTrue(); + + StringResult readResult = fs.readFile(TEXT_TEST_FILE); - expect(fs.readFile(TEXT_TEST_FILE)).toEqual(content); + expect(readResult.isOk()).toBeTrue(); + + expect(readResult.toValue()).toEqual(content); } function testItCanWriteAFileAsBinary() external { bytes memory content = bytes("Writing from a test using bytes"); - fs.writeFileBinary(BINARY_TEST_FILE, content); + EmptyResult writeResult = fs.writeFileBinary(BINARY_TEST_FILE, content); + + expect(writeResult.isOk()).toBeTrue(); + + BytesResult readResult = fs.readFileBinary(BINARY_TEST_FILE); - expect(fs.readFileBinary(BINARY_TEST_FILE)).toEqual(content); + expect(readResult.isOk()).toBeTrue(); + + expect(readResult.toValue()).toEqual(content); } function testItCanRemoveFiles() external { string memory path = "./test/fixtures/fs/write/temp.txt"; - fs.writeFile(path, string("Should be removed")); + EmptyResult writeResult = fs.writeFile(path, string("Should be removed")); + + expect(writeResult.isOk()).toBeTrue(); - fs.removeFile(path); + EmptyResult removeResult = fs.removeFile(path); - expect(fs.fileExists(path)).toBeFalse(); + expect(removeResult.isOk()).toBeTrue(); + + expect(fs.fileExists(path).toValue()).toBeFalse(); } function testItCanCopyAFile() external { string memory path = "./test/fixtures/fs/write/hello_world_copy.txt"; - fs.copyFile(HELLO_WORLD, path); + EmptyResult copyResult = fs.copyFile(HELLO_WORLD, path); + + expect(copyResult.isOk()).toBeTrue(); - expect(fs.readFile(path)).toEqual("Hello, World!\n"); + expect(fs.readFile(path).toValue()).toEqual("Hello, World!\n"); - fs.removeFile(path); + expect(fs.removeFile(path).isOk()).toBeTrue(); } function testItCanMoveAfile() external { string memory path = "./test/fixtures/fs/write/hello_world.txt"; string memory newPath = "./test/fixtures/fs/write/new_hello_world.txt"; - fs.copyFile(HELLO_WORLD, path); - fs.moveFile(path, newPath); + expect(fs.copyFile(HELLO_WORLD, path).isOk()).toBeTrue(); + expect(fs.moveFile(path, newPath).isOk()).toBeTrue(); - expect(fs.readFile(newPath)).toEqual("Hello, World!\n"); + expect(fs.readFile(newPath).toValue()).toEqual("Hello, World!\n"); - expect(fs.fileExists(path)).toBeFalse(); + expect(fs.fileExists(path).toValue()).toBeFalse(); - fs.removeFile(newPath); + expect(fs.removeFile(newPath).isOk()).toBeTrue(); } function testItCanReadLines() external { string memory content = "Lorem\nipsum\ndolor\nsit\n"; string memory path = "./test/fixtures/fs/write/test_read_lines.txt"; - fs.writeFile(path, content); + expect(fs.writeFile(path, content).isOk()).toBeTrue(); - expect(fs.readLine(path)).toEqual("Lorem"); - expect(fs.readLine(path)).toEqual("ipsum"); - expect(fs.readLine(path)).toEqual("dolor"); - expect(fs.readLine(path)).toEqual("sit"); + expect(fs.readLine(path).toValue()).toEqual("Lorem"); + expect(fs.readLine(path).toValue()).toEqual("ipsum"); + expect(fs.readLine(path).toValue()).toEqual("dolor"); + expect(fs.readLine(path).toValue()).toEqual("sit"); - fs.removeFile(path); + expect(fs.removeFile(path).isOk()).toBeTrue(); } function testItCanWriteLines() external { string memory content = "Lorem\nipsum\ndolor\nsit\n"; string memory path = "./test/fixtures/fs/write/test_write_lines.txt"; - fs.writeFile(path, content); + expect(fs.writeFile(path, content).isOk()).toBeTrue(); - fs.writeLine(path, string("amet")); + expect(fs.writeLine(path, string("amet")).isOk()).toBeTrue(); - expect(fs.readLine(path)).toEqual("Lorem"); - expect(fs.readLine(path)).toEqual("ipsum"); - expect(fs.readLine(path)).toEqual("dolor"); - expect(fs.readLine(path)).toEqual("sit"); - expect(fs.readLine(path)).toEqual("amet"); + expect(fs.readLine(path).toValue()).toEqual("Lorem"); + expect(fs.readLine(path).toValue()).toEqual("ipsum"); + expect(fs.readLine(path).toValue()).toEqual("dolor"); + expect(fs.readLine(path).toValue()).toEqual("sit"); + expect(fs.readLine(path).toValue()).toEqual("amet"); - fs.removeFile(path); + expect(fs.removeFile(path).isOk()).toBeTrue(); } function testItCanGetMetadata() external { string memory dirPath = "./test/fixtures/fs/read"; string memory filePath = HELLO_WORLD; - FsMetadata memory dirMetadata = fs.metadata(dirPath); - expect(dirMetadata.isDir).toBeTrue(); + FsMetadataResult dirMetadataResult = fs.metadata(dirPath); + + expect(dirMetadataResult.isOk()).toBeTrue(); + expect(dirMetadataResult.toValue().isDir).toBeTrue(); + + FsMetadataResult fileMetadataResult = fs.metadata(filePath); - FsMetadata memory fileMetadata = fs.metadata(filePath); - expect(fileMetadata.isDir).toBeFalse(); + expect(fileMetadataResult.isOk()).toBeTrue(); + expect(fileMetadataResult.toValue().isDir).toBeFalse(); } }