Heap.test.js 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. const { ethers } = require('hardhat');
  2. const { expect } = require('chai');
  3. const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
  4. const { PANIC_CODES } = require('@nomicfoundation/hardhat-chai-matchers/panic');
  5. const { TYPES } = require('../../../scripts/generate/templates/Heap.opts');
  6. async function fixture() {
  7. const mock = await ethers.deployContract('$Heap');
  8. return { mock };
  9. }
  10. describe('Heap', function () {
  11. beforeEach(async function () {
  12. Object.assign(this, await loadFixture(fixture));
  13. });
  14. for (const { struct, valueType } of TYPES) {
  15. describe(struct, function () {
  16. const popEvent = `return$pop_Heap_${struct}`;
  17. const replaceEvent = `return$replace_Heap_${struct}_${valueType}`;
  18. beforeEach(async function () {
  19. this.helper = {
  20. clear: (...args) => this.mock[`$clear_Heap_${struct}`](0, ...args),
  21. insert: (...args) => this.mock[`$insert(uint256,${valueType})`](0, ...args),
  22. replace: (...args) => this.mock[`$replace(uint256,${valueType})`](0, ...args),
  23. length: (...args) => this.mock[`$length_Heap_${struct}`](0, ...args),
  24. pop: (...args) => this.mock[`$pop_Heap_${struct}`](0, ...args),
  25. peek: (...args) => this.mock[`$peek_Heap_${struct}`](0, ...args),
  26. };
  27. });
  28. it('starts empty', async function () {
  29. expect(await this.helper.length()).to.equal(0n);
  30. });
  31. it('peek, pop and replace from empty', async function () {
  32. await expect(this.helper.peek()).to.be.revertedWithPanic(PANIC_CODES.ARRAY_ACCESS_OUT_OF_BOUNDS);
  33. await expect(this.helper.pop()).to.be.revertedWithPanic(PANIC_CODES.POP_ON_EMPTY_ARRAY);
  34. await expect(this.helper.replace(0n)).to.be.revertedWithPanic(PANIC_CODES.POP_ON_EMPTY_ARRAY);
  35. });
  36. it('clear', async function () {
  37. await this.helper.insert(42n);
  38. expect(await this.helper.length()).to.equal(1n);
  39. expect(await this.helper.peek()).to.equal(42n);
  40. await this.helper.clear();
  41. expect(await this.helper.length()).to.equal(0n);
  42. await expect(this.helper.peek()).to.be.revertedWithPanic(PANIC_CODES.ARRAY_ACCESS_OUT_OF_BOUNDS);
  43. });
  44. it('support duplicated items', async function () {
  45. expect(await this.helper.length()).to.equal(0n);
  46. // insert 5 times
  47. await this.helper.insert(42n);
  48. await this.helper.insert(42n);
  49. await this.helper.insert(42n);
  50. await this.helper.insert(42n);
  51. await this.helper.insert(42n);
  52. // pop 5 times
  53. await expect(this.helper.pop()).to.emit(this.mock, popEvent).withArgs(42n);
  54. await expect(this.helper.pop()).to.emit(this.mock, popEvent).withArgs(42n);
  55. await expect(this.helper.pop()).to.emit(this.mock, popEvent).withArgs(42n);
  56. await expect(this.helper.pop()).to.emit(this.mock, popEvent).withArgs(42n);
  57. await expect(this.helper.pop()).to.emit(this.mock, popEvent).withArgs(42n);
  58. // popping a 6th time panics
  59. await expect(this.helper.pop()).to.be.revertedWithPanic(PANIC_CODES.POP_ON_EMPTY_ARRAY);
  60. });
  61. it('insert, pop and replace', async function () {
  62. const heap = [];
  63. for (const { op, value } of [
  64. { op: 'insert', value: 712 }, // [712]
  65. { op: 'insert', value: 20 }, // [20, 712]
  66. { op: 'insert', value: 4337 }, // [20, 712, 4437]
  67. { op: 'pop' }, // 20, [712, 4437]
  68. { op: 'insert', value: 1559 }, // [712, 1559, 4437]
  69. { op: 'insert', value: 165 }, // [165, 712, 1559, 4437]
  70. { op: 'insert', value: 155 }, // [155, 165, 712, 1559, 4437]
  71. { op: 'insert', value: 7702 }, // [155, 165, 712, 1559, 4437, 7702]
  72. { op: 'pop' }, // 155, [165, 712, 1559, 4437, 7702]
  73. { op: 'replace', value: 721 }, // 165, [712, 721, 1559, 4437, 7702]
  74. { op: 'pop' }, // 712, [721, 1559, 4437, 7702]
  75. { op: 'pop' }, // 721, [1559, 4437, 7702]
  76. { op: 'pop' }, // 1559, [4437, 7702]
  77. { op: 'pop' }, // 4437, [7702]
  78. { op: 'pop' }, // 7702, []
  79. { op: 'pop' }, // panic
  80. { op: 'replace', value: '1363' }, // panic
  81. ]) {
  82. switch (op) {
  83. case 'insert':
  84. await this.helper.insert(value);
  85. heap.push(value);
  86. heap.sort((a, b) => a - b);
  87. break;
  88. case 'pop':
  89. if (heap.length == 0) {
  90. await expect(this.helper.pop()).to.be.revertedWithPanic(PANIC_CODES.POP_ON_EMPTY_ARRAY);
  91. } else {
  92. await expect(this.helper.pop()).to.emit(this.mock, popEvent).withArgs(heap.shift());
  93. }
  94. break;
  95. case 'replace':
  96. if (heap.length == 0) {
  97. await expect(this.helper.replace(value)).to.be.revertedWithPanic(PANIC_CODES.POP_ON_EMPTY_ARRAY);
  98. } else {
  99. await expect(this.helper.replace(value)).to.emit(this.mock, replaceEvent).withArgs(heap.shift());
  100. heap.push(value);
  101. heap.sort((a, b) => a - b);
  102. }
  103. break;
  104. }
  105. expect(await this.helper.length()).to.equal(heap.length);
  106. if (heap.length == 0) {
  107. await expect(this.helper.peek()).to.be.revertedWithPanic(PANIC_CODES.ARRAY_ACCESS_OUT_OF_BOUNDS);
  108. } else {
  109. expect(await this.helper.peek()).to.equal(heap[0]);
  110. }
  111. }
  112. });
  113. });
  114. }
  115. });