RangeTest.php 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. <?php
  2. namespace PhpOffice\PhpSpreadsheetTests\Calculation\Engine;
  3. use PhpOffice\PhpSpreadsheet\Calculation\Functions;
  4. use PhpOffice\PhpSpreadsheet\NamedRange;
  5. use PhpOffice\PhpSpreadsheet\Spreadsheet;
  6. use PHPUnit\Framework\TestCase;
  7. class RangeTest extends TestCase
  8. {
  9. /** @var string */
  10. private $incompleteMessage = 'Must be revisited';
  11. /**
  12. * @var Spreadsheet
  13. */
  14. private $spreadSheet;
  15. protected function setUp(): void
  16. {
  17. $this->spreadSheet = new Spreadsheet();
  18. $this->spreadSheet->getActiveSheet()
  19. ->fromArray(array_chunk(range(1, 240), 6), null, 'A1', true);
  20. }
  21. /**
  22. * @dataProvider providerRangeEvaluation
  23. *
  24. * @param mixed $expectedResult
  25. */
  26. public function testRangeEvaluation(string $formula, $expectedResult): void
  27. {
  28. $workSheet = $this->spreadSheet->getActiveSheet();
  29. $workSheet->setCellValue('H1', $formula);
  30. $actualRresult = $workSheet->getCell('H1')->getCalculatedValue();
  31. self::assertSame($expectedResult, $actualRresult);
  32. }
  33. public function providerRangeEvaluation(): array
  34. {
  35. return[
  36. 'Sum with Simple Range' => ['=SUM(A1:C3)', 72],
  37. 'Count with Simple Range' => ['=COUNT(A1:C3)', 9],
  38. 'Sum with UNION #1' => ['=SUM(A1:B3,A1:C2)', 75],
  39. 'Count with UNION #1' => ['=COUNT(A1:B3,A1:C2)', 12],
  40. 'Sum with INTERSECTION #1' => ['=SUM(A1:B3 A1:C2)', 18],
  41. 'Count with INTERSECTION #1' => ['=COUNT(A1:B3 A1:C2)', 4],
  42. 'Sum with UNION #2' => ['=SUM(A1:A3,C1:C3)', 48],
  43. 'Count with UNION #2' => ['=COUNT(A1:A3,C1:C3)', 6],
  44. 'Sum with INTERSECTION #2 - No Intersect' => ['=SUM(A1:A3 C1:C3)', Functions::null()],
  45. 'Count with INTERSECTION #2 - No Intersect' => ['=COUNT(A1:A3 C1:C3)', 0],
  46. 'Sum with UNION #3' => ['=SUM(A1:B2,B2:C3)', 64],
  47. 'Count with UNION #3' => ['=COUNT(A1:B2,B2:C3)', 8],
  48. 'Sum with INTERSECTION #3 - Single Cell' => ['=SUM(A1:B2 B2:C3)', 8],
  49. 'Count with INTERSECTION #3 - Single Cell' => ['=COUNT(A1:B2 B2:C3)', 1],
  50. 'Sum with Triple UNION' => ['=SUM(A1:C1,A3:C3,B1:C3)', 99],
  51. 'Count with Triple UNION' => ['=COUNT(A1:C1,A3:C3,B1:C3)', 12],
  52. 'Sum with UNION and INTERSECTION' => ['=SUM(A1:C1,A3:C3 B1:C3)', 35],
  53. 'Count with UNION and INTERSECTION' => ['=COUNT(A1:C1,A3:C3 B1:C3)', 5],
  54. 'Sum with UNION with Worksheet Reference' => ['=SUM(Worksheet!A1:B3,Worksheet!A1:C2)', 75],
  55. 'Sum with UNION with full Worksheet Reference' => ['=SUM(Worksheet!A1:Worksheet!B3,Worksheet!A1:Worksheet!C2)', 75],
  56. 'Sum with Chained UNION #1' => ['=SUM(A3:B1:C2)', 72],
  57. 'Count with Chained UNION #1' => ['=COUNT(A3:B1:C2)', 9],
  58. 'Sum with Chained UNION #2' => ['=SUM(A5:C10:C20:F1)', 7260],
  59. 'Count with Chained UNION#2' => ['=COUNT(A5:C10:C20:F1)', 120],
  60. ];
  61. }
  62. public function test3dRangeEvaluation(): void
  63. {
  64. $workSheet = $this->spreadSheet->getActiveSheet();
  65. $workSheet->setCellValue('E1', '=SUM(Worksheet!A1:Worksheet2!B3)');
  66. $this->expectExceptionMessage('3D Range references are not yet supported');
  67. $workSheet->getCell('E1')->getCalculatedValue();
  68. }
  69. /**
  70. * @dataProvider providerNamedRangeEvaluation
  71. */
  72. public function testNamedRangeEvaluation(array $ranges, string $formula, int $expectedResult): void
  73. {
  74. $workSheet = $this->spreadSheet->getActiveSheet();
  75. foreach ($ranges as $id => $range) {
  76. $this->spreadSheet->addNamedRange(new NamedRange('GROUP' . ++$id, $workSheet, $range));
  77. }
  78. $workSheet->setCellValue('H1', $formula);
  79. $sumRresult = $workSheet->getCell('H1')->getCalculatedValue();
  80. self::assertSame($expectedResult, $sumRresult);
  81. }
  82. public function providerNamedRangeEvaluation(): array
  83. {
  84. return[
  85. [['$A$1:$B$3', '$A$1:$C$2'], '=SUM(GROUP1,GROUP2)', 75],
  86. [['$A$1:$B$3', '$A$1:$C$2'], '=COUNT(GROUP1,GROUP2)', 12],
  87. [['$A$1:$B$3', '$A$1:$C$2'], '=SUM(GROUP1 GROUP2)', 18],
  88. [['$A$1:$B$3', '$A$1:$C$2'], '=COUNT(GROUP1 GROUP2)', 4],
  89. [['$A$1:$B$2', '$B$2:$C$3'], '=SUM(GROUP1,GROUP2)', 64],
  90. [['$A$1:$B$2', '$B$2:$C$3'], '=COUNT(GROUP1,GROUP2)', 8],
  91. [['$A$1:$B$2', '$B$2:$C$3'], '=SUM(GROUP1 GROUP2)', 8],
  92. [['$A$1:$B$2', '$B$2:$C$3'], '=COUNT(GROUP1 GROUP2)', 1],
  93. [['$A$5', '$C$10:$C$20', '$F$1'], '=SUM(GROUP1:GROUP2:GROUP3)', 7260],
  94. [['$A$5:$A$7', '$C$20', '$F$1'], '=SUM(GROUP1:GROUP2:GROUP3)', 7260],
  95. [['$A$5:$A$7', '$C$10:$C$20', '$F$1'], '=SUM(GROUP1:GROUP2:GROUP3)', 7260],
  96. [['Worksheet!$A$1:$B$2', 'Worksheet!$B$2:$C$3'], '=SUM(GROUP1,GROUP2)', 64],
  97. [['Worksheet!$A$1:Worksheet!$B$2', 'Worksheet!$B$2:Worksheet!$C$3'], '=SUM(GROUP1,GROUP2)', 64],
  98. ];
  99. }
  100. /**
  101. * @dataProvider providerUTF8NamedRangeEvaluation
  102. *
  103. * @param string[] $names
  104. * @param string[] $ranges
  105. */
  106. public function testUTF8NamedRangeEvaluation(array $names, array $ranges, string $formula, int $expectedResult): void
  107. {
  108. $workSheet = $this->spreadSheet->getActiveSheet();
  109. foreach ($names as $index => $name) {
  110. $range = $ranges[$index];
  111. $this->spreadSheet->addNamedRange(new NamedRange($name, $workSheet, $range));
  112. }
  113. $workSheet->setCellValue('E1', $formula);
  114. $sumRresult = $workSheet->getCell('E1')->getCalculatedValue();
  115. self::assertSame($expectedResult, $sumRresult);
  116. }
  117. public function providerUTF8NamedRangeEvaluation(): array
  118. {
  119. return[
  120. [['Γειά', 'σου', 'Κόσμε'], ['$A$1', '$B$1:$B$2', '$C$1:$C$3'], '=SUM(Γειά,σου,Κόσμε)', 38],
  121. [['Γειά', 'σου', 'Κόσμε'], ['$A$1', '$B$1:$B$2', '$C$1:$C$3'], '=COUNT(Γειά,σου,Κόσμε)', 6],
  122. [['Здравствуй', 'мир'], ['$A$1:$A$3', '$C$1:$C$3'], '=SUM(Здравствуй,мир)', 48],
  123. ];
  124. }
  125. /**
  126. * @dataProvider providerCompositeNamedRangeEvaluation
  127. */
  128. public function testCompositeNamedRangeEvaluation(string $composite, int $expectedSum, int $expectedCount): void
  129. {
  130. if ($this->incompleteMessage !== '') {
  131. self::markTestIncomplete($this->incompleteMessage);
  132. }
  133. $workSheet = $this->spreadSheet->getActiveSheet();
  134. $this->spreadSheet->addNamedRange(new NamedRange('COMPOSITE', $workSheet, $composite));
  135. $workSheet->setCellValue('E1', '=SUM(COMPOSITE)');
  136. $workSheet->setCellValue('E2', '=COUNT(COMPOSITE)');
  137. $actualSum = $workSheet->getCell('E1')->getCalculatedValue();
  138. self::assertSame($expectedSum, $actualSum);
  139. $actualCount = $workSheet->getCell('E2')->getCalculatedValue();
  140. self::assertSame($expectedCount, $actualCount);
  141. }
  142. public function providerCompositeNamedRangeEvaluation(): array
  143. {
  144. return[
  145. // Calculation engine doesn't yet handle union ranges with overlap
  146. 'Union with overlap' => [
  147. 'A1:C1,A3:C3,B1:C3',
  148. 63,
  149. 12,
  150. ],
  151. 'Union and Intersection' => [
  152. 'A1:C1,A3:C3 B1:C3',
  153. 23,
  154. 5,
  155. ],
  156. ];
  157. }
  158. }