Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions test/lib/LibCodeGen.commentPrefix.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// SPDX-License-Identifier: LicenseRef-DCL-1.0
// SPDX-FileCopyrightText: Copyright (c) 2020 Rain Open Source Software Ltd
pragma solidity =0.8.25;

import {Test} from "forge-std-1.16.1/src/Test.sol";
import {LibCodeGen} from "src/lib/LibCodeGen.sol";
import {LibCodeGenSlow} from "./LibCodeGenSlow.sol";

/// @title LibCodeGenCommentPrefixTest
/// @notice `commentPrefix` is the one rule every constant declaration shares:
/// a blank line always, and a comment line only when there is a comment. The
/// library states it as a choice between two whole prefix literals, so these
/// assert it against a reference that assembles the prefix a line at a time,
/// and pin that reference's own two cases to literals so agreement between the
/// two is not the only thing holding either.
contract LibCodeGenCommentPrefixTest is Test {
/// No comment is one blank line and nothing else. Two consecutive newlines
/// here would be a blank line `forge fmt` collapses, which makes a
/// regenerated file fail the consumer's `forge fmt --check`.
function testCommentPrefixSlowEmptyComment() external pure {
assertEq(LibCodeGenSlow.commentPrefixSlow(""), "\n");
}

/// A comment is a blank line, then the comment on a line of its own. The
/// comment is emitted as given, so the caller owns whether it carries
/// `///` and where it breaks.
function testCommentPrefixSlowWithComment() external pure {
assertEq(LibCodeGenSlow.commentPrefixSlow("/// @dev Some comment."), "\n/// @dev Some comment.\n");
}

/// Whatever the comment, the library emits the lines the reference
/// assembles. Fuzzed over the comment because emptiness is the only thing
/// the rule branches on and every other comment has to come back untouched.
function testCommentPrefixMatchesSlow(string memory comment) external pure {
assertEq(LibCodeGen.commentPrefix(comment), LibCodeGenSlow.commentPrefixSlow(comment));
}
}
35 changes: 26 additions & 9 deletions test/lib/LibCodeGenSlow.sol
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,10 @@ string constant SLOW_TAIL_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnop
/// than imported, so a change to a literal in `LibCodeGen` shows up as a
/// disagreement instead of moving both sides at once.
///
/// The empty comment case is the one place the two sides state the same rule
/// rather than deriving it independently: a declaration is preceded by one
/// blank line, and by a comment line only when there is a comment. The exact
/// text of both cases is pinned separately by literal assertions, so this
/// reference is not the only thing holding it.
/// The lines that precede a declaration are assembled here from the rule they
/// follow: one blank line always, and a comment line only when there is a
/// comment. The exact text of both cases is pinned separately by literal
/// assertions, so this reference is not the only thing holding it.
library LibCodeGenSlow {
/// `vm.toString` on a `bytes` always prefixes `0x`, which a `hex"..."`
/// literal must not carry. Dropped by copying the tail one byte at a time so
Expand All @@ -63,13 +62,31 @@ library LibCodeGenSlow {
return oneLine;
}

/// The lines that precede a declaration, listed out and then joined one
/// newline terminated line at a time: a blank line always, and a comment
/// line only when there is a comment. `LibCodeGen.commentPrefix` instead
/// chooses between two whole prefix literals, so the two agree only when
/// both the number of lines and the text of each one is right.
function commentPrefixSlow(string memory comment) internal pure returns (string memory) {
string[] memory lines = new string[](bytes(comment).length == 0 ? 1 : 2);
lines[0] = "";
if (lines.length == 2) {
lines[1] = comment;
}
string memory prefix = "";
for (uint256 i = 0; i < lines.length; i++) {
prefix = string.concat(prefix, lines[i], "\n");
}
return prefix;
}

function bytesConstantStringSlow(Vm vm, string memory comment, string memory name, bytes memory data)
internal
pure
returns (string memory)
{
return string.concat(
bytes(comment).length == 0 ? "\n" : string.concat("\n", comment, "\n"),
commentPrefixSlow(comment),
joinSlow(string.concat("bytes constant ", name, " ="), string.concat("hex\"", hexOfSlow(vm, data), "\";")),
"\n"
);
Expand All @@ -81,7 +98,7 @@ library LibCodeGenSlow {
returns (string memory)
{
return string.concat(
bytes(comment).length == 0 ? "\n" : string.concat("\n", comment, "\n"),
commentPrefixSlow(comment),
joinSlow(string.concat("uint8 constant ", name, " ="), string.concat(vm.toString(uint256(data)), ";")),
"\n"
);
Expand All @@ -93,7 +110,7 @@ library LibCodeGenSlow {
returns (string memory)
{
return string.concat(
bytes(comment).length == 0 ? "\n" : string.concat("\n", comment, "\n"),
commentPrefixSlow(comment),
joinSlow(
string.concat("bytes32 constant ", name, " ="), string.concat("bytes32(", vm.toString(data), ");")
),
Expand All @@ -107,7 +124,7 @@ library LibCodeGenSlow {
returns (string memory)
{
return string.concat(
bytes(comment).length == 0 ? "\n" : string.concat("\n", comment, "\n"),
commentPrefixSlow(comment),
joinSlow(
string.concat("address constant ", name, " ="), string.concat("address(", vm.toString(data), ");")
),
Expand Down
Loading