diff --git a/l1-contracts/test/foundry/unit/concrete/PriorityQueue/OnEmptyQueue.sol b/l1-contracts/test/foundry/unit/concrete/PriorityQueue/OnEmptyQueue.sol new file mode 100644 index 000000000..98a0e3e22 --- /dev/null +++ b/l1-contracts/test/foundry/unit/concrete/PriorityQueue/OnEmptyQueue.sol @@ -0,0 +1,22 @@ +pragma solidity 0.8.20; + +import {PriorityQueueSharedTest} from "./_PriorityQueue_Shared.t.sol"; + +contract OnEmptyQueueTest is PriorityQueueSharedTest { + function test_gets() public { + assertEq(0, priorityQueue.getSize()); + assertEq(0, priorityQueue.getFirstUnprocessedPriorityTx()); + assertEq(0, priorityQueue.getTotalPriorityTxs()); + assertTrue(priorityQueue.isEmpty()); + } + + function test_failGetFront() public { + vm.expectRevert(bytes("D")); + priorityQueue.front(); + } + + function test_failPopFront() public { + vm.expectRevert(bytes("s")); + priorityQueue.popFront(); + } +} diff --git a/l1-contracts/test/foundry/unit/concrete/PriorityQueue/PopOperations.sol b/l1-contracts/test/foundry/unit/concrete/PriorityQueue/PopOperations.sol new file mode 100644 index 000000000..c5146838e --- /dev/null +++ b/l1-contracts/test/foundry/unit/concrete/PriorityQueue/PopOperations.sol @@ -0,0 +1,71 @@ +pragma solidity 0.8.20; + +import {PriorityQueueSharedTest} from "./_PriorityQueue_Shared.t.sol"; +import {PriorityOperation} from "../../../../../cache/solpp-generated-contracts/dev-contracts/test/PriorityQueueTest.sol"; + +contract PopOperationsTest is PriorityQueueSharedTest { + uint public constant NUMBER_OPERATIONS = 10; + + function setUp() public { + push_mock_entries(NUMBER_OPERATIONS); + } + + function test_after_pop() public { + assertEq(NUMBER_OPERATIONS, priorityQueue.getSize()); + + PriorityOperation memory front = priorityQueue.popFront(); + assertEq(keccak256(abi.encode(0)), front.canonicalTxHash); + assertEq(uint64(0), front.expirationTimestamp); + assertEq(uint192(0), front.layer2Tip); + + assertEq(NUMBER_OPERATIONS - 1, priorityQueue.getSize()); + assertEq(1, priorityQueue.getFirstUnprocessedPriorityTx()); + assertEq(NUMBER_OPERATIONS, priorityQueue.getTotalPriorityTxs()); + assertFalse(priorityQueue.isEmpty()); + + // Ok - one more pop + PriorityOperation memory front2 = priorityQueue.popFront(); + assertEq(keccak256(abi.encode(1)), front2.canonicalTxHash); + assertEq(uint64(1), front2.expirationTimestamp); + assertEq(uint192(1), front2.layer2Tip); + + assertEq(NUMBER_OPERATIONS - 2, priorityQueue.getSize()); + assertEq(2, priorityQueue.getFirstUnprocessedPriorityTx()); + assertEq(NUMBER_OPERATIONS, priorityQueue.getTotalPriorityTxs()); + assertFalse(priorityQueue.isEmpty()); + } + + function test_pop_until_limit() public { + for (uint i = 0; i < NUMBER_OPERATIONS; ++i) { + PriorityOperation memory front = priorityQueue.popFront(); + assertEq(keccak256(abi.encode(i)), front.canonicalTxHash); + } + + assertEq(0, priorityQueue.getSize()); + assertEq(NUMBER_OPERATIONS, priorityQueue.getFirstUnprocessedPriorityTx()); + assertEq(NUMBER_OPERATIONS, priorityQueue.getTotalPriorityTxs()); + assertTrue(priorityQueue.isEmpty()); + + // And now let's push something. + + PriorityOperation memory dummyOp = PriorityOperation({ + canonicalTxHash: keccak256(abi.encode(300)), + expirationTimestamp: uint64(300), + layer2Tip: uint192(300) + }); + priorityQueue.pushBack(dummyOp); + + assertEq(1, priorityQueue.getSize()); + assertEq(NUMBER_OPERATIONS, priorityQueue.getFirstUnprocessedPriorityTx()); + assertEq(NUMBER_OPERATIONS + 1, priorityQueue.getTotalPriorityTxs()); + assertFalse(priorityQueue.isEmpty()); + + PriorityOperation memory front_end = priorityQueue.popFront(); + assertEq(keccak256(abi.encode(300)), front_end.canonicalTxHash); + assertTrue(priorityQueue.isEmpty()); + + // And now let's go over the limit and fail. + vm.expectRevert(bytes.concat("s")); + priorityQueue.popFront(); + } +} diff --git a/l1-contracts/test/foundry/unit/concrete/PriorityQueue/PushOperations.sol b/l1-contracts/test/foundry/unit/concrete/PriorityQueue/PushOperations.sol new file mode 100644 index 000000000..0fb540d00 --- /dev/null +++ b/l1-contracts/test/foundry/unit/concrete/PriorityQueue/PushOperations.sol @@ -0,0 +1,25 @@ +pragma solidity 0.8.20; + +import {PriorityQueueSharedTest} from "./_PriorityQueue_Shared.t.sol"; +import {PriorityOperation} from "../../../../../cache/solpp-generated-contracts/dev-contracts/test/PriorityQueueTest.sol"; + +contract PushOperationsTest is PriorityQueueSharedTest { + uint public constant NUMBER_OPERATIONS = 10; + + function setUp() public { + push_mock_entries(NUMBER_OPERATIONS); + } + + function test_front() public { + assertEq(NUMBER_OPERATIONS, priorityQueue.getSize()); + PriorityOperation memory front = priorityQueue.front(); + assertEq(keccak256(abi.encode(0)), front.canonicalTxHash); + assertEq(uint64(0), front.expirationTimestamp); + assertEq(uint192(0), front.layer2Tip); + // This is 'front' and not popFront, so the amount should not change. + assertEq(NUMBER_OPERATIONS, priorityQueue.getSize()); + assertEq(0, priorityQueue.getFirstUnprocessedPriorityTx()); + assertEq(NUMBER_OPERATIONS, priorityQueue.getTotalPriorityTxs()); + assertFalse(priorityQueue.isEmpty()); + } +} diff --git a/l1-contracts/test/foundry/unit/concrete/PriorityQueue/_PriorityQueue_Shared.t.sol b/l1-contracts/test/foundry/unit/concrete/PriorityQueue/_PriorityQueue_Shared.t.sol new file mode 100644 index 000000000..a219aea17 --- /dev/null +++ b/l1-contracts/test/foundry/unit/concrete/PriorityQueue/_PriorityQueue_Shared.t.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.20; + +import {Test} from "forge-std/Test.sol"; +import {PriorityQueueTest, PriorityOperation} from "solpp/dev-contracts/test/PriorityQueueTest.sol"; + +contract PriorityQueueSharedTest is Test { + PriorityQueueTest internal priorityQueue; + + constructor() { + priorityQueue = new PriorityQueueTest(); + } + + // Pushes 'count' entries into the priority queue. + function push_mock_entries(uint count) public { + for (uint i = 0; i < count; ++i) { + PriorityOperation memory dummyOp = PriorityOperation({ + canonicalTxHash: keccak256(abi.encode(i)), + expirationTimestamp: uint64(i), + layer2Tip: uint192(i) + }); + priorityQueue.pushBack(dummyOp); + } + } +} diff --git a/l1-contracts/test/unit_tests/priority_queue_test.spec.ts b/l1-contracts/test/unit_tests/priority_queue_test.spec.ts deleted file mode 100644 index a59c1e2f1..000000000 --- a/l1-contracts/test/unit_tests/priority_queue_test.spec.ts +++ /dev/null @@ -1,133 +0,0 @@ -import { expect } from "chai"; -import * as hardhat from "hardhat"; -import { ethers } from "hardhat"; -import type { PriorityQueueTest } from "../../typechain"; -import { PriorityQueueTestFactory } from "../../typechain"; -import { getCallRevertReason } from "./utils"; - -describe("Priority queue tests", function () { - let priorityQueueTest: PriorityQueueTest; - const queue = []; - - before(async () => { - const contractFactory = await hardhat.ethers.getContractFactory("PriorityQueueTest"); - const contract = await contractFactory.deploy(); - priorityQueueTest = PriorityQueueTestFactory.connect(contract.address, contract.signer); - }); - - describe("on empty queue", function () { - it("getSize", async () => { - const size = await priorityQueueTest.getSize(); - expect(size).equal(0); - }); - - it("getFirstUnprocessedPriorityTx", async () => { - const firstUnprocessedTx = await priorityQueueTest.getFirstUnprocessedPriorityTx(); - expect(firstUnprocessedTx).equal(0); - }); - - it("getTotalPriorityTxs", async () => { - const totalPriorityTxs = await priorityQueueTest.getTotalPriorityTxs(); - expect(totalPriorityTxs).equal(0); - }); - - it("isEmpty", async () => { - const isEmpty = await priorityQueueTest.isEmpty(); - expect(isEmpty).equal(true); - }); - - it("failed to get front", async () => { - const revertReason = await getCallRevertReason(priorityQueueTest.front()); - expect(revertReason).equal("D"); - }); - - it("failed to pop", async () => { - const revertReason = await getCallRevertReason(priorityQueueTest.popFront()); - expect(revertReason).equal("s"); - }); - }); - - describe("push operations", function () { - const NUMBER_OPERATIONS = 10; - - before(async () => { - for (let i = 0; i < NUMBER_OPERATIONS; ++i) { - const dummyOp = { canonicalTxHash: ethers.constants.HashZero, expirationTimestamp: i, layer2Tip: i }; - queue.push(dummyOp); - await priorityQueueTest.pushBack(dummyOp); - } - }); - - it("front", async () => { - const frontElement = await priorityQueueTest.front(); - - expect(frontElement.canonicalTxHash).equal(queue[0].canonicalTxHash); - expect(frontElement.expirationTimestamp).equal(queue[0].expirationTimestamp); - expect(frontElement.layer2Tip).equal(queue[0].layer2Tip); - }); - - it("getSize", async () => { - const size = await priorityQueueTest.getSize(); - expect(size).equal(queue.length); - }); - - it("getFirstUnprocessedPriorityTx", async () => { - const firstUnprocessedTx = await priorityQueueTest.getFirstUnprocessedPriorityTx(); - expect(firstUnprocessedTx).equal(0); - }); - - it("getTotalPriorityTxs", async () => { - const totalPriorityTxs = await priorityQueueTest.getTotalPriorityTxs(); - expect(totalPriorityTxs).equal(queue.length); - }); - - it("isEmpty", async () => { - const isEmpty = await priorityQueueTest.isEmpty(); - expect(isEmpty).equal(false); - }); - }); - - describe("pop operations", function () { - const NUMBER_OPERATIONS = 4; - - before(async () => { - for (let i = 0; i < NUMBER_OPERATIONS; ++i) { - const frontElement = await priorityQueueTest.front(); - expect(frontElement.canonicalTxHash).equal(queue[0].canonicalTxHash); - expect(frontElement.expirationTimestamp).equal(queue[0].expirationTimestamp); - expect(frontElement.layer2Tip).equal(queue[0].layer2Tip); - - await priorityQueueTest.popFront(); - queue.shift(); - } - }); - - it("front", async () => { - const frontElement = await priorityQueueTest.front(); - - expect(frontElement.canonicalTxHash).equal(queue[0].canonicalTxHash); - expect(frontElement.expirationTimestamp).equal(queue[0].expirationTimestamp); - expect(frontElement.layer2Tip).equal(queue[0].layer2Tip); - }); - - it("getSize", async () => { - const size = await priorityQueueTest.getSize(); - expect(size).equal(queue.length); - }); - - it("getFirstUnprocessedPriorityTx", async () => { - const firstUnprocessedTx = await priorityQueueTest.getFirstUnprocessedPriorityTx(); - expect(firstUnprocessedTx).equal(NUMBER_OPERATIONS); - }); - - it("getTotalPriorityTxs", async () => { - const totalPriorityTxs = await priorityQueueTest.getTotalPriorityTxs(); - expect(totalPriorityTxs).equal(queue.length + NUMBER_OPERATIONS); - }); - - it("isEmpty", async () => { - const isEmpty = await priorityQueueTest.isEmpty(); - expect(isEmpty).equal(false); - }); - }); -});