Skip to content

Commit 84f259b

Browse files
authored
Merge pull request #4624 from oleibman/calccov
Minor Improvements to Calculation Coverage
2 parents 66ea405 + da9a414 commit 84f259b

File tree

15 files changed

+371
-5
lines changed

15 files changed

+371
-5
lines changed

src/PhpSpreadsheet/Calculation/Calculation.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -888,7 +888,7 @@ private static function resizeMatricesExtend(array &$matrix1, array &$matrix2, i
888888
}
889889
}
890890
if ($matrix1Rows < $matrix2Rows) {
891-
$x = ($matrix1Rows === 1) ? $matrix1[0] : array_fill(0, $matrix1Columns, null);
891+
$x = ($matrix1Rows === 1) ? $matrix1[0] : array_fill(0, $matrix2Columns, null);
892892
for ($i = $matrix1Rows; $i < $matrix2Rows; ++$i) {
893893
$matrix1[$i] = $x;
894894
}

src/PhpSpreadsheet/Calculation/CalculationLocale.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,11 @@ protected static function loadLocales(): void
6363
$filename = substr($filename, strlen($localeFileDirectory));
6464
if ($filename != 'en') {
6565
self::$validLocaleLanguages[] = $filename;
66+
$subdirs = glob("$localeFileDirectory$filename/*", GLOB_ONLYDIR) ?: [];
67+
foreach ($subdirs as $subdir) {
68+
$subdirx = basename($subdir);
69+
self::$validLocaleLanguages[] = "{$filename}_{$subdirx}";
70+
}
6671
}
6772
}
6873
}

src/PhpSpreadsheet/Shared/StringHelper.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -420,7 +420,7 @@ public static function UTF8toBIFF8UnicodeLong(string $textValue): string
420420
*/
421421
public static function convertEncoding(string $textValue, string $to, string $from): string
422422
{
423-
if (self::getIsIconvEnabled()) {
423+
if (static::getIsIconvEnabled()) {
424424
$result = iconv($from, $to . self::$iconvOptions, $textValue);
425425
if (false !== $result) {
426426
return $result;
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpOffice\PhpSpreadsheetTests\Calculation;
6+
7+
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
8+
use PhpOffice\PhpSpreadsheet\Calculation\Exception as CalcException;
9+
use PhpOffice\PhpSpreadsheet\Calculation\ExceptionHandler;
10+
use PhpOffice\PhpSpreadsheet\NamedRange;
11+
use PhpOffice\PhpSpreadsheet\Spreadsheet;
12+
use PHPUnit\Framework\Attributes;
13+
use PHPUnit\Framework\TestCase;
14+
15+
class CalculationCoverageTest extends TestCase
16+
{
17+
public function testClone(): void
18+
{
19+
$this->expectException(CalcException::class);
20+
$this->expectExceptionMessage('Cloning the calculation engine is not allowed!');
21+
$calc = Calculation::getInstance();
22+
$clone = clone $calc;
23+
$clone->flushInstance();
24+
}
25+
26+
public function testBadInstanceArray(): void
27+
{
28+
$spreadsheet = new Spreadsheet();
29+
$calc = Calculation::getInstance($spreadsheet);
30+
$type = $calc->getInstanceArrayReturnType();
31+
self::assertFalse($calc->setInstanceArrayReturnType('bad'));
32+
self::assertSame($type, $calc->getInstanceArrayReturnType());
33+
$spreadsheet->disconnectWorksheets();
34+
}
35+
36+
public function testCalculate(): void
37+
{
38+
$spreadsheet = new Spreadsheet();
39+
$sheet = $spreadsheet->getActiveSheet();
40+
$calc = Calculation::getInstance($spreadsheet);
41+
$sheet->getCell('A1')->setValue('=2+3');
42+
$result = $calc->calculate($sheet->getCell('A1'));
43+
self::assertSame(5, $result);
44+
self::assertSame('', Calculation::boolToString(null));
45+
$spreadsheet->disconnectWorksheets();
46+
}
47+
48+
public function testCalculateBad(): void
49+
{
50+
$this->expectException(CalcException::class);
51+
$this->expectExceptionMessage('Formula Error');
52+
$spreadsheet = new Spreadsheet();
53+
$sheet = $spreadsheet->getActiveSheet();
54+
$calc = Calculation::getInstance($spreadsheet);
55+
$sheet->getCell('A1')->setValue('=SUM(');
56+
$result = $calc->calculate($sheet->getCell('A1'));
57+
self::assertSame(5, $result);
58+
$spreadsheet->disconnectWorksheets();
59+
}
60+
61+
public function testParse(): void
62+
{
63+
$calc = Calculation::getInstance();
64+
self::assertSame([], $calc->parseFormula('2+3'), 'no leading =');
65+
self::assertSame([], $calc->parseFormula('='), 'leading = but no other text');
66+
}
67+
68+
public function testExtractNamedRange(): void
69+
{
70+
$spreadsheet = new Spreadsheet();
71+
$calc = Calculation::getInstance($spreadsheet);
72+
$sheet = $spreadsheet->getActiveSheet();
73+
$sheet->setTitle('mysheet');
74+
$sheet->setCellValue('A1', 1);
75+
$sheet->setCellValue('B1', 2);
76+
$sheet->setCellValue('A2', 3);
77+
$sheet->setCellValue('B2', 4);
78+
$spreadsheet->addNamedRange(
79+
new NamedRange('Whatever', $sheet, '$A$1:$B$2')
80+
);
81+
$range = 'Whatever';
82+
$result = $calc->extractNamedRange($range, $sheet);
83+
self::assertSame('$A$1:$B$2', $range);
84+
self::assertSame([1 => ['A' => 1, 'B' => 2], 2 => ['A' => 3, 'B' => 4]], $result);
85+
$range = 'mysheet!Whatever';
86+
$result = $calc->extractNamedRange($range, $sheet);
87+
self::assertSame('$A$1:$B$2', $range);
88+
self::assertSame([1 => ['A' => 1, 'B' => 2], 2 => ['A' => 3, 'B' => 4]], $result);
89+
90+
$range = 'mysheet!Whateverx';
91+
$result = $calc->extractNamedRange($range, $sheet);
92+
self::assertSame('Whateverx', $range);
93+
self::assertSame('#REF!', $result);
94+
95+
$range = 'Why';
96+
$result = $calc->extractNamedRange($range, $sheet);
97+
self::assertSame('Why', $range);
98+
self::assertSame('#REF!', $result);
99+
100+
$spreadsheet->addNamedRange(
101+
new NamedRange('OneCell', $sheet, '$A$1')
102+
);
103+
$range = 'OneCell';
104+
$result = $calc->extractNamedRange($range, $sheet);
105+
self::assertSame('$A$1', $range);
106+
self::assertSame([1 => ['A' => 1]], $result);
107+
108+
$spreadsheet->addNamedRange(
109+
new NamedRange('NoSuchCell', $sheet, '$Z$1')
110+
);
111+
$range = 'NoSuchCell';
112+
$result = $calc->extractNamedRange($range, $sheet);
113+
self::assertSame('$Z$1', $range);
114+
self::assertSame([1 => ['Z' => null]], $result);
115+
116+
$spreadsheet->addNamedRange(
117+
new NamedRange('SomeCells', $sheet, '$B$1:$C$2')
118+
);
119+
$range = 'SomeCells';
120+
$result = $calc->extractNamedRange($range, $sheet);
121+
self::assertSame('$B$1:$C$2', $range);
122+
self::assertSame([1 => ['B' => 2, 'C' => null], 2 => ['B' => 4, 'C' => null]], $result);
123+
124+
$spreadsheet->disconnectWorksheets();
125+
}
126+
127+
protected static int $winMinPhpToSkip = 80300;
128+
129+
protected static int $winMaxPhpToSkip = 80499;
130+
131+
protected static string $winIndicator = 'WIN';
132+
133+
// separate process because it sets its own handler
134+
#[Attributes\RunInSeparateProcess]
135+
public function testExceptionHandler(): void
136+
{
137+
if (
138+
strtoupper(substr(PHP_OS, 0, 3)) === self::$winIndicator
139+
&& PHP_VERSION_ID >= self::$winMinPhpToSkip
140+
&& PHP_VERSION_ID <= self::$winMaxPhpToSkip
141+
) {
142+
self::markTestSkipped('Mysterious problem on Windows with Php8.3/4 only');
143+
}
144+
$this->expectException(CalcException::class);
145+
$this->expectExceptionMessage('hello');
146+
$handler = new ExceptionHandler();
147+
trigger_error('hello');
148+
self::assertNotNull($handler); // @phpstan-ignore-line
149+
}
150+
}

tests/PhpSpreadsheetTests/Calculation/CustomFunctionTest.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,22 @@ public static function testCustomFunction(): void
3030
self::assertTrue(Calculation::removeFunction($key));
3131
self::assertSame('#NAME?', $calculation->calculateFormula('=FOURTHPOWER(3)'));
3232
self::assertFalse(Calculation::removeFunction('WHATEVER'));
33+
$key = 'NATIVECOS';
34+
$value = [
35+
'category' => 'custom',
36+
'functionCall' => 'cos',
37+
'argumentCount' => '1',
38+
];
39+
self::assertTrue(Calculation::addFunction($key, $value));
40+
self::assertSame(1.0, $calculation->calculateFormula('=NATIVECOS(0)'));
41+
self::assertTrue(Calculation::removeFunction($key));
42+
$key = 'PI';
43+
$value = [
44+
'category' => 'custom',
45+
'functionCall' => 'pi',
46+
'argumentCount' => '0',
47+
];
48+
self::assertFalse(Calculation::addFunction($key, $value));
3349
}
3450

3551
public static function testReplaceDummyFunction(): void

tests/PhpSpreadsheetTests/Calculation/Issue4451Test.php

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
class Issue4451Test extends TestCase
1313
{
14-
public static function testReflect(): void
14+
public static function testReflectExtend1(): void
1515
{
1616
// Sample matrices to test with
1717
$matrix1 = [[1], [3]];
@@ -27,6 +27,56 @@ public static function testReflect(): void
2727
self::assertSame([[1], [3], [null]], $matrix1); //* @phpstan-ignore-line
2828
}
2929

30+
public static function testReflectExtend2(): void
31+
{
32+
// Sample matrices to test with
33+
$matrix1 = [[1], [3]];
34+
$matrix2 = [[5, 6], [8, 9], [11, 12]];
35+
36+
// Use reflection to make the protected method accessible
37+
$calculation = new Calculation();
38+
$reflectionMethod = new ReflectionMethod(Calculation::class, 'resizeMatricesExtend');
39+
40+
// Call the method using reflection
41+
$reflectionMethod->invokeArgs($calculation, [&$matrix1, &$matrix2, count($matrix1), 1, count($matrix2), 2]);
42+
43+
self::assertSame([[1, 1], [3, 3], [null, null]], $matrix1); //* @phpstan-ignore-line
44+
}
45+
46+
public static function testReflectShrink1(): void
47+
{
48+
// Sample matrices to test with
49+
$matrix1 = [[10, 20], [30, 40]];
50+
$matrix2 = [[50, 60, 70], [80, 90, 100], [110, 120, 130]];
51+
52+
// Use reflection to make the protected method accessible
53+
$calculation = new Calculation();
54+
$reflectionMethod = new ReflectionMethod(Calculation::class, 'resizeMatricesShrink');
55+
56+
// Call the method using reflection
57+
$reflectionMethod->invokeArgs($calculation, [&$matrix1, &$matrix2, count($matrix1), count($matrix1), count($matrix2), count($matrix2)]);
58+
59+
self::assertSame([[10, 20], [30, 40]], $matrix1); //* @phpstan-ignore-line
60+
self::assertSame([[50, 60], [80, 90]], $matrix2); //* @phpstan-ignore-line
61+
}
62+
63+
public static function testReflectShrink2(): void
64+
{
65+
// Sample matrices to test with
66+
$matrix2 = [[10, 20], [30, 40]];
67+
$matrix1 = [[50, 60, 70], [80, 90, 100], [110, 120, 130]];
68+
69+
// Use reflection to make the protected method accessible
70+
$calculation = new Calculation();
71+
$reflectionMethod = new ReflectionMethod(Calculation::class, 'resizeMatricesShrink');
72+
73+
// Call the method using reflection
74+
$reflectionMethod->invokeArgs($calculation, [&$matrix1, &$matrix2, count($matrix1), count($matrix1), count($matrix2), count($matrix2)]);
75+
76+
self::assertSame([[10, 20], [30, 40]], $matrix2); //* @phpstan-ignore-line
77+
self::assertSame([[50, 60], [80, 90]], $matrix1); //* @phpstan-ignore-line
78+
}
79+
3080
/**
3181
* These 2 tests are contrived. They prove that method
3282
* works as desired, but Excel will actually return
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpOffice\PhpSpreadsheetTests\Collection;
6+
7+
use PhpOffice\PhpSpreadsheet\Collection\Cells;
8+
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
9+
use PHPUnit\Framework\TestCase;
10+
11+
class Cells2Test extends TestCase
12+
{
13+
public function testThrowsWhenCellCannotBeStoredInClonedCache(): void
14+
{
15+
$this->expectException(\PhpOffice\PhpSpreadsheet\Exception::class);
16+
$this->expectExceptionMessage('Failed to copy cells in cache');
17+
18+
$cache = new SimpleCache3xxx();
19+
20+
$worksheet = new Worksheet();
21+
$collection = new Cells($worksheet, $cache);
22+
23+
$collection->add('A1', $worksheet->getCell('A1'));
24+
$collection->add('A2', $worksheet->getCell('A2'));
25+
$collection->cloneCellCollection($worksheet);
26+
}
27+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpOffice\PhpSpreadsheetTests\Collection;
6+
7+
use DateInterval;
8+
use PhpOffice\PhpSpreadsheet\Collection\Memory\SimpleCache3;
9+
10+
class SimpleCache3xxx extends SimpleCache3
11+
{
12+
public static int $useCount = 0;
13+
14+
public function set(string $key, mixed $value, null|int|DateInterval $ttl = null): bool
15+
{
16+
++self::$useCount;
17+
if (self::$useCount >= 4) {
18+
return false;
19+
}
20+
21+
return parent::set($key, $value, $ttl);
22+
}
23+
}

tests/PhpSpreadsheetTests/Helper/DimensionTest.php

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,40 @@
66

77
use PhpOffice\PhpSpreadsheet\Exception;
88
use PhpOffice\PhpSpreadsheet\Helper\Dimension;
9+
use PhpOffice\PhpSpreadsheet\Helper\Size;
10+
use PHPUnit\Framework\Attributes\DataProvider;
911
use PHPUnit\Framework\TestCase;
1012

1113
class DimensionTest extends TestCase
1214
{
13-
#[\PHPUnit\Framework\Attributes\DataProvider('providerCellWidth')]
15+
#[DataProvider('providerCellWidth')]
1416
public function testCreateDimension(float $expectedResult, string $dimension): void
1517
{
1618
$result = (new Dimension($dimension))->width();
1719
self::assertSame($expectedResult, $result);
1820
}
1921

20-
#[\PHPUnit\Framework\Attributes\DataProvider('providerConvertUoM')]
22+
#[DataProvider('providerConvertUoM')]
2123
public function testConvertDimension(float $expectedResult, string $dimension, string $unitOfMeasure): void
2224
{
2325
$result = (new Dimension($dimension))->toUnit($unitOfMeasure);
2426
self::assertEqualsWithDelta($expectedResult, $result, 1.0e-12);
2527
}
2628

29+
public function testSizeAndUnit(): void
30+
{
31+
$size = new Size('10px');
32+
self::assertTrue($size->valid());
33+
self::assertSame('10', $size->size());
34+
self::assertSame('px', $size->unit());
35+
$size = new Size('9.3');
36+
self::assertTrue($size->valid());
37+
self::assertSame('9.3', $size->size());
38+
self::assertSame('pt', $size->unit());
39+
$size = new Size('8whatever');
40+
self::assertFalse($size->valid());
41+
}
42+
2743
public function testConvertDimensionInvalidUoM(): void
2844
{
2945
$this->expectException(Exception::class);

tests/PhpSpreadsheetTests/Helper/SampleCoverageTest.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,19 @@ public function testDirectoryFail(): void
3535
->willReturn(false);
3636
$helper->getFilename('a.xlsx');
3737
}
38+
39+
public function testTitles(): void
40+
{
41+
$helper = new Sample();
42+
ob_start();
43+
$helper->titles('Category', 'FunctionName');
44+
$output = (string) ob_get_clean();
45+
$output = str_replace("\r", '', $output);
46+
self::assertStringContainsString("Function: FunctionName()\n", $output);
47+
ob_start();
48+
$helper->titles('Category', 'FunctionName', 'Description');
49+
$output = (string) ob_get_clean();
50+
$output = str_replace("\r", '', $output);
51+
self::assertStringContainsString("Function: FunctionName() - Description.\n", $output);
52+
}
3853
}

0 commit comments

Comments
 (0)