Passed
Pull Request — master (#4396)
by Owen
14:27
created

AllSetupTeardown::runTestCaseReference()   B

Complexity

Conditions 8
Paths 10

Size

Total Lines 39
Code Lines 31

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 31
c 0
b 0
f 0
dl 0
loc 39
rs 8.1795
cc 8
nc 10
nop 3
1
<?php
2
3
declare(strict_types=1);
4
5
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\Statistical;
6
7
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
8
use PhpOffice\PhpSpreadsheet\Calculation\Exception as CalcException;
9
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
10
use PhpOffice\PhpSpreadsheet\Cell\DataType;
11
use PhpOffice\PhpSpreadsheet\Spreadsheet;
12
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
13
use PHPUnit\Framework\TestCase;
14
use Stringable;
15
16
class AllSetupTeardown extends TestCase
17
{
18
    private string $compatibilityMode;
19
20
    private ?Spreadsheet $spreadsheet = null;
21
22
    private ?Worksheet $sheet = null;
23
24
    protected string $returnArrayAs;
25
26
    protected function setUp(): void
27
    {
28
        $this->compatibilityMode = Functions::getCompatibilityMode();
29
        $this->returnArrayAs = '';
30
    }
31
32
    protected function tearDown(): void
33
    {
34
        Functions::setCompatibilityMode($this->compatibilityMode);
35
        $this->sheet = null;
36
        if ($this->spreadsheet !== null) {
37
            $this->spreadsheet->disconnectWorksheets();
38
            $this->spreadsheet = null;
39
        }
40
    }
41
42
    protected static function setOpenOffice(): void
43
    {
44
        Functions::setCompatibilityMode(Functions::COMPATIBILITY_OPENOFFICE);
45
    }
46
47
    protected static function setGnumeric(): void
48
    {
49
        Functions::setCompatibilityMode(Functions::COMPATIBILITY_GNUMERIC);
50
    }
51
52
    protected function mightHaveException(mixed $expectedResult): void
53
    {
54
        if ($expectedResult === 'exception') {
55
            $this->expectException(CalcException::class);
56
        }
57
    }
58
59
    protected function setCell(string $cell, mixed $value): void
60
    {
61
        if ($value !== null) {
62
            if (is_string($value) && is_numeric($value)) {
63
                $this->getSheet()->getCell($cell)->setValueExplicit($value, DataType::TYPE_STRING);
64
            } else {
65
                $this->getSheet()->getCell($cell)->setValue($value);
66
            }
67
        }
68
    }
69
70
    protected function getSpreadsheet(): Spreadsheet
71
    {
72
        if ($this->spreadsheet !== null) {
73
            return $this->spreadsheet;
74
        }
75
        $this->spreadsheet = new Spreadsheet();
76
77
        return $this->spreadsheet;
78
    }
79
80
    protected function getSheet(): Worksheet
81
    {
82
        if ($this->sheet !== null) {
83
            return $this->sheet;
84
        }
85
        $this->sheet = $this->getSpreadsheet()->getActiveSheet();
86
87
        return $this->sheet;
88
    }
89
90
    /**
91
     * Excel handles text/logical/empty cells differently when
92
     * passed directly as arguments as opposed to cell references or arrays.
93
     * This function will test both approaches.
94
     */
95
    protected function runTestCases(string $functionName, mixed $expectedResult, mixed ...$args): void
96
    {
97
        if (is_array($expectedResult)) {
98
            $this->runTestCaseReference($functionName, $expectedResult[0], ...$args);
99
            $this->runTestCaseDirect($functionName, $expectedResult[1], ...$args);
100
        } else {
101
            $this->runTestCaseReference($functionName, $expectedResult, ...$args);
102
            $this->runTestCaseDirect($functionName, $expectedResult, ...$args);
103
        }
104
    }
105
106
    /**
107
     * Excel handles text/logical/empty cells differently when
108
     * passed directly as arguments as opposed to cell references or arrays.
109
     * This functions tests passing as arrays.
110
     */
111
    protected function runTestCaseReference(string $functionName, mixed $expectedResult, mixed ...$args): void
112
    {
113
        $this->mightHaveException($expectedResult);
114
        $sheet = $this->getSheet();
115
        if ($this->returnArrayAs !== '') {
116
            $calculation = Calculation::getInstance($this->spreadsheet);
117
            $calculation->setInstanceArrayReturnType(
118
                $this->returnArrayAs
119
            );
120
        }
121
        $formula = "=$functionName(";
122
        $comma = '';
123
        $row = 0;
124
        foreach ($args as $arg) {
125
            ++$row;
126
            if (is_array($arg)) {
127
                $arrayArg = '{';
128
                $arrayComma = '';
129
                foreach ($arg as $arrayItem) {
130
                    $arrayArg .= $arrayComma;
131
                    if ($arrayItem !== null && !is_scalar($arrayItem) && !($arrayItem instanceof Stringable)) {
132
                        self::fail('non-stringable item');
133
                    }
134
                    $arrayArg .= $this->convertToString($arrayItem);
135
                    $arrayComma = ';';
136
                }
137
                $arrayArg .= '}';
138
                $formula .= "$comma$arrayArg";
139
                $comma = ',';
140
            } else {
141
                $cellId = "A$row";
142
                $formula .= "$comma$cellId";
143
                $comma = ',';
144
                $this->setCell($cellId, $arg);
145
            }
146
        }
147
        $formula .= ')';
148
        $this->setCell('B1', $formula);
149
        self::assertEqualsWithDelta($expectedResult, $sheet->getCell('B1')->getCalculatedValue(), 1.0e-8, 'arguments supplied as references');
150
    }
151
152
    /**
153
     * Excel handles text/logical/empty cells differently when
154
     * passed directly as arguments as opposed to cell references or arrays.
155
     * This functions tests passing as direct arguments.
156
     */
157
    protected function runTestCaseDirect(string $functionName, mixed $expectedResult, mixed ...$args): void
158
    {
159
        $this->mightHaveException($expectedResult);
160
        $sheet = $this->getSheet();
161
        $formula = "=$functionName(";
162
        $comma = '';
163
        foreach ($args as $arg) {
164
            if (is_array($arg)) {
165
                foreach ($arg as $arrayItem) {
166
                    $formula .= $comma;
167
                    $comma = ',';
168
                    if ($arrayItem !== null && !is_scalar($arrayItem) && !($arrayItem instanceof Stringable)) {
169
                        self::fail('non-stringable item');
170
                    }
171
                    $formula .= $this->convertToString($arrayItem);
172
                }
173
            } else {
174
                $formula .= $comma;
175
                $comma = ',';
176
                /** @var string */
177
                $argx = $arg;
178
                $formula .= $this->convertToString($argx);
179
            }
180
        }
181
        $formula .= ')';
182
        $this->setCell('B2', $formula);
183
        self::assertEqualsWithDelta($expectedResult, $sheet->getCell('B2')->getCalculatedValue(), 1.0e-8, 'arguments supplied directly');
184
    }
185
186
    /**
187
     * Excel seems to reject bracket notation for literal arrays
188
     * for some functions.
189
     */
190
    protected function runTestCaseNoBracket(string $functionName, mixed $expectedResult, mixed ...$args): void
191
    {
192
        $this->mightHaveException($expectedResult);
193
        $sheet = $this->getSheet();
194
        $formula = "=$functionName(";
195
        $comma = '';
196
        $row = 0;
197
        foreach ($args as $arg) {
198
            ++$row;
199
            if (is_array($arg)) {
200
                $col = 'A';
201
                $arrayRange = '';
202
                foreach ($arg as $arrayItem) {
203
                    $cellId = "$col$row";
204
                    $arrayRange = "A$row:$cellId";
205
                    $this->setCell($cellId, $arrayItem);
206
                    ++$col;
207
                }
208
                $formula .= "$comma$arrayRange";
209
                $comma = ',';
210
            } else {
211
                $cellId = "A$row";
212
                $formula .= "$comma$cellId";
213
                $comma = ',';
214
                if (is_string($arg) && str_starts_with($arg, '=')) {
215
                    $sheet->getCell($cellId)->setValueExplicit($arg, DataType::TYPE_STRING);
216
                } else {
217
                    $this->setCell($cellId, $arg);
218
                }
219
            }
220
        }
221
        $formula .= ')';
222
        $this->setCell('Z99', $formula);
223
        self::assertEqualsWithDelta($expectedResult, $sheet->getCell('Z99')->getCalculatedValue(), 1.0e-8, 'arguments supplied as ranges');
224
    }
225
226
    private function convertToString(null|bool|float|int|string|Stringable $arg): string
227
    {
228
        if (is_string($arg)) {
229
            return '"' . $arg . '"';
230
        }
231
        if (is_bool($arg)) {
232
            return $arg ? 'TRUE' : 'FALSE';
233
        }
234
235
        return (string) $arg;
236
    }
237
}
238