foundry

【foundry】テストデータのimport及びoutputについて

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になります。

複雑なことはありませんが、使う機会が少ないのでやり方をすぐ忘れちゃいます

-foundry