foundryでテストする際に、テストデータのimport及びoutputについてまとめていきたいと思います。
https://github.com/eggdragons/how-to-use-foundry
FFIテスト
foundryには、ffiテストというものが搭載されています。
このffiテストは、nodejsやpythonなどをfoundryのテストコードから実行できるものになります。もちろん、テストデータのimport及びoutputも容易にできます。
ただし、注意する点があって、例えばfileのread及びwriteはデフォルトで制限されていますが、ffiテストは制限されていません。
また、foundryは特性上、同じフォルダ内にあるものを何でもかんでも実行しますので、意図しないコードが実行される可能性があります。
もちろんリポジトリ内部に悪いものが入ってなければ問題ないです。私はガンガン使っています。
ただ、外部データの取得や出力をするためだけであれば、foudryの別の機能があるのでそれを紹介したいと思います。
https://github.com/eggdragons/how-to-use-foundry/blob/main/test/utils/WriteReadFile.t.sol
Jsonデータのやり取り
Jsonデータの読み書きについて、ここでは解説していきます。
単純なdictionary構造
// attenstion name sort ASC
struct Json {
address addr;
uint256 id;
}
// dict {...}
function testWriteReadJson(uint256 number, address addr) public {
string memory path = "./data/example.json";
// create writeJson
// {"addr": addrs[0],"id": ids[0]}
string memory obj1 = "key";
string memory writeJson = vm.serializeUint(obj1, "id", number);
writeJson = vm.serializeAddress(obj1, "addr", addr);
// write to file
vm.writeJson(writeJson, path);
// read to file
string memory readJson = vm.readFile(path);
// decode
bytes memory abiEncodedData = vm.parseJson(readJson);
Json memory result = abi.decode(abiEncodedData, (Json));
// check write data === read data
assertEq(result.id, number);
assertEq(result.addr, addr);
}
まず、dictionary構造を作ります(create writeJson)
// create writeJson
// {"addr": addrs[0],"id": ids[0]}
string memory obj1 = "key";
string memory writeJson = vm.serializeUint(obj1, "id", number);
writeJson = vm.serializeAddress(obj1, "addr", addr);
書き込みは、
vm.writeJson(writeJson, path);
読み込みは、
string memory readJson = vm.readFile(path);
あとは、読み込んだデータをdecodeするだけになります。
bytes memory abiEncodedData = vm.parseJson(readJson);
Json memory result = abi.decode(abiEncodedData, (Json));
arrayで囲まれたdictionary構造
function testWriteReadArrayJson(address[100] memory addrs, uint16[100] memory ids) public {
string memory path = "./data/example.json";
// create writeJson
// [{"addr": addrs[0],"id": ids[0]},{"addr": addrs[1],"id": ids[1]}...]
string memory obj1 = "key";
string memory writeJson = "[";
string memory last;
uint256 len = addrs.length;
for (uint256 i; i < len;) {
unchecked {
if (i != len - 1) {
last = ",";
} else {
last = "]";
}
string memory writeJson1 = vm.serializeUint(obj1, "id", ids[i]);
writeJson1 = vm.serializeAddress(obj1, "addr", addrs[i]);
writeJson = string(abi.encodePacked(writeJson, writeJson1, last));
i++;
}
}
// write to file
vm.writeJson(writeJson, path);
// read to file
string memory readJson = vm.readFile(path);
// check write data === read data
for (uint256 i; i < len;) {
unchecked {
// decode
bytes memory abiEncodedData = readJson.parseRaw(string(abi.encodePacked("[", i.toString(), "]")));
Json memory result = abi.decode(abiEncodedData, (Json));
// check
assertEq(result.id, ids[i]);
assertEq(result.addr, addrs[i]);
i++;
}
}
}
長々書いていますが、先ほどと大きく違うのデコード部分のみです。
デコードが、
bytes memory abiEncodedData = readJson.parseRaw(string(abi.encodePacked("[", i.toString(), "]")));
このような形になります。
あと、dict array dict {…:[{…},{…},…,{…}]}のような形についてもコードの中に記載していますので、必要であればご確認ください
テキストデータのやり取り
今度はテキストデータの読み込みです。
テキストデータ
function testWriteReadTxtDatas() public {
string memory path = "./data/datas.txt";
string memory data = "hello world";
// write to file
vm.writeFile(path, data);
// read to file
assertEq(vm.readFile(path), data);
}
説明は不要ですね。
テキストデータを1行づつ読み込む
function testWriteReadTxtLineData() public {
// reset line.txt data
string memory path = "./data/line.txt";
vm.writeFile(path, "");
// write to line1
string memory line1 = "first line";
vm.writeLine(path, line1);
// write to line2
string memory line2 = "second line";
vm.writeLine(path, line2);
// write to line3
string memory line3 = "third line";
vm.writeLine(path, line3);
// read to line1
assertEq(vm.readLine(path), line1);
// read to line2
assertEq(vm.readLine(path), line2);
// if you need reset offset(read line) --> closeFile
// offset 2 --> 0
vm.closeFile(path);
// read to line1
assertEq(vm.readLine(path), line1);
}
自動的に1行づつ処理が進みます。
offsetをリセットする関するがcloseになります。
複雑なことはありませんが、使う機会が少ないのでやり方をすぐ忘れちゃいます