From a2282ecb79ccb0feda8cbbd5a824b369c615d466 Mon Sep 17 00:00:00 2001 From: oleibman <10341515+oleibman@users.noreply.github.com> Date: Fri, 3 Feb 2023 07:42:43 -0800 Subject: [PATCH] Additional Coverage for Reader Xls (#3340) * Additional Coverage for Reader Xls More than 750 lines are newly covered, increasing overall coverage on my machine from 91.14% to 93.09%. * Update FormulasTest.php --- .../Xls/ConditionalFormattingBasicTest.php | 23 +++------- .../ConditionalFormattingExpressionTest.php | 23 +++------- .../Reader/Xls/DataValidationTest.php | 23 +++------- .../Reader/Xls/FormulasTest.php | 43 ++++++++++++++++++ .../Reader/Xls/HiddenWorksheetTest.php | 5 +- .../Reader/Xls/PageSetupTest.php | 33 +++++++------- tests/data/Reader/XLS/formulas.database.xls | Bin 0 -> 13824 bytes tests/data/Reader/XLS/formulas.xls | Bin 0 -> 40448 bytes 8 files changed, 85 insertions(+), 65 deletions(-) create mode 100644 tests/PhpSpreadsheetTests/Reader/Xls/FormulasTest.php create mode 100644 tests/data/Reader/XLS/formulas.database.xls create mode 100644 tests/data/Reader/XLS/formulas.xls diff --git a/tests/PhpSpreadsheetTests/Reader/Xls/ConditionalFormattingBasicTest.php b/tests/PhpSpreadsheetTests/Reader/Xls/ConditionalFormattingBasicTest.php index ce2248640..20f6ed8d5 100644 --- a/tests/PhpSpreadsheetTests/Reader/Xls/ConditionalFormattingBasicTest.php +++ b/tests/PhpSpreadsheetTests/Reader/Xls/ConditionalFormattingBasicTest.php @@ -4,39 +4,30 @@ namespace PhpOffice\PhpSpreadsheetTests\Reader\Xls; use PhpOffice\PhpSpreadsheet\Reader\Xls; use PhpOffice\PhpSpreadsheet\Style\Conditional; -use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; use PHPUnit\Framework\TestCase; class ConditionalFormattingBasicTest extends TestCase { - /** - * @var Worksheet - */ - protected $sheet; - - protected function setUp(): void - { - $filename = 'tests/data/Reader/XLS/CF_Basic_Comparisons.xls'; - $reader = new Xls(); - $spreadsheet = $reader->load($filename); - $this->sheet = $spreadsheet->getActiveSheet(); - } - /** * @dataProvider conditionalFormattingProvider */ public function testReadConditionalFormatting(string $expectedRange, array $expectedRules): void { - $hasConditionalStyles = $this->sheet->conditionalStylesExists($expectedRange); + $filename = 'tests/data/Reader/XLS/CF_Basic_Comparisons.xls'; + $reader = new Xls(); + $spreadsheet = $reader->load($filename); + $sheet = $spreadsheet->getActiveSheet(); + $hasConditionalStyles = $sheet->conditionalStylesExists($expectedRange); self::assertTrue($hasConditionalStyles); - $conditionalStyles = $this->sheet->getConditionalStyles($expectedRange); + $conditionalStyles = $sheet->getConditionalStyles($expectedRange); foreach ($conditionalStyles as $index => $conditionalStyle) { self::assertSame($expectedRules[$index]['type'], $conditionalStyle->getConditionType()); self::assertSame($expectedRules[$index]['operator'], $conditionalStyle->getOperatorType()); self::assertSame($expectedRules[$index]['conditions'], $conditionalStyle->getConditions()); } + $spreadsheet->disconnectWorksheets(); } public function conditionalFormattingProvider(): array diff --git a/tests/PhpSpreadsheetTests/Reader/Xls/ConditionalFormattingExpressionTest.php b/tests/PhpSpreadsheetTests/Reader/Xls/ConditionalFormattingExpressionTest.php index 6cc566c1f..a9cce8c32 100644 --- a/tests/PhpSpreadsheetTests/Reader/Xls/ConditionalFormattingExpressionTest.php +++ b/tests/PhpSpreadsheetTests/Reader/Xls/ConditionalFormattingExpressionTest.php @@ -4,39 +4,30 @@ namespace PhpOffice\PhpSpreadsheetTests\Reader\Xls; use PhpOffice\PhpSpreadsheet\Reader\Xls; use PhpOffice\PhpSpreadsheet\Style\Conditional; -use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; use PHPUnit\Framework\TestCase; class ConditionalFormattingExpressionTest extends TestCase { - /** - * @var Worksheet - */ - protected $sheet; - - protected function setUp(): void - { - $filename = 'tests/data/Reader/XLS/CF_Expression_Comparisons.xls'; - $reader = new Xls(); - $spreadsheet = $reader->load($filename); - $this->sheet = $spreadsheet->getActiveSheet(); - } - /** * @dataProvider conditionalFormattingProvider */ public function testReadConditionalFormatting(string $expectedRange, array $expectedRule): void { - $hasConditionalStyles = $this->sheet->conditionalStylesExists($expectedRange); + $filename = 'tests/data/Reader/XLS/CF_Expression_Comparisons.xls'; + $reader = new Xls(); + $spreadsheet = $reader->load($filename); + $sheet = $spreadsheet->getActiveSheet(); + $hasConditionalStyles = $sheet->conditionalStylesExists($expectedRange); self::assertTrue($hasConditionalStyles); - $conditionalStyles = $this->sheet->getConditionalStyles($expectedRange); + $conditionalStyles = $sheet->getConditionalStyles($expectedRange); foreach ($conditionalStyles as $index => $conditionalStyle) { self::assertSame($expectedRule[$index]['type'], $conditionalStyle->getConditionType()); self::assertSame($expectedRule[$index]['operator'], $conditionalStyle->getOperatorType()); self::assertSame($expectedRule[$index]['conditions'], $conditionalStyle->getConditions()); } + $spreadsheet->disconnectWorksheets(); } public function conditionalFormattingProvider(): array diff --git a/tests/PhpSpreadsheetTests/Reader/Xls/DataValidationTest.php b/tests/PhpSpreadsheetTests/Reader/Xls/DataValidationTest.php index bdefa17e2..129fb96f1 100644 --- a/tests/PhpSpreadsheetTests/Reader/Xls/DataValidationTest.php +++ b/tests/PhpSpreadsheetTests/Reader/Xls/DataValidationTest.php @@ -4,36 +4,27 @@ namespace PhpOffice\PhpSpreadsheetTests\Reader\Xls; use PhpOffice\PhpSpreadsheet\Cell\DataValidation; use PhpOffice\PhpSpreadsheet\Reader\Xls; -use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet; use PHPUnit\Framework\TestCase; class DataValidationTest extends TestCase { - /** - * @var Worksheet - */ - protected $sheet; - - protected function setUp(): void - { - $filename = 'tests/data/Reader/XLS/DataValidation.xls'; - $reader = new Xls(); - $spreadsheet = $reader->load($filename); - $this->sheet = $spreadsheet->getActiveSheet(); - } - /** * @dataProvider dataValidationProvider */ public function testDataValidation(string $expectedRange, array $expectedRule): void { - $hasDataValidation = $this->sheet->dataValidationExists($expectedRange); + $filename = 'tests/data/Reader/XLS/DataValidation.xls'; + $reader = new Xls(); + $spreadsheet = $reader->load($filename); + $sheet = $spreadsheet->getActiveSheet(); + $hasDataValidation = $sheet->dataValidationExists($expectedRange); self::assertTrue($hasDataValidation); - $dataValidation = $this->sheet->getDataValidation($expectedRange); + $dataValidation = $sheet->getDataValidation($expectedRange); self::assertSame($expectedRule['type'], $dataValidation->getType()); self::assertSame($expectedRule['operator'], $dataValidation->getOperator()); self::assertSame($expectedRule['formula'], $dataValidation->getFormula1()); + $spreadsheet->disconnectWorksheets(); } public function dataValidationProvider(): array diff --git a/tests/PhpSpreadsheetTests/Reader/Xls/FormulasTest.php b/tests/PhpSpreadsheetTests/Reader/Xls/FormulasTest.php new file mode 100644 index 000000000..c27972054 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Reader/Xls/FormulasTest.php @@ -0,0 +1,43 @@ +load($filename); + $sheet = $spreadsheet->getActiveSheet(); + $originalArray = $sheet->toArray(null, false, false, false); + + $newSpreadsheet = $this->writeAndReload($spreadsheet, 'Xls'); + $spreadsheet->disconnectWorksheets(); + $newWorksheet = $newSpreadsheet->getActiveSheet(); + $newArray = $newWorksheet->toArray(null, false, false, false); + self::assertSame($originalArray, $newArray); + $newSpreadsheet->disconnectWorksheets(); + } + + public function testDatabaseFormulas(): void + { + // This file was created with Excel 2003. + $filename = 'tests/data/Reader/XLS/formulas.database.xls'; + $reader = new Xls(); + $spreadsheet = $reader->load($filename); + $sheet = $spreadsheet->getActiveSheet(); + $originalArray = $sheet->toArray(null, false, false, false); + + $newSpreadsheet = $this->writeAndReload($spreadsheet, 'Xls'); + $spreadsheet->disconnectWorksheets(); + $newWorksheet = $newSpreadsheet->getActiveSheet(); + $newArray = $newWorksheet->toArray(null, false, false, false); + self::assertSame($originalArray, $newArray); + $newSpreadsheet->disconnectWorksheets(); + } +} diff --git a/tests/PhpSpreadsheetTests/Reader/Xls/HiddenWorksheetTest.php b/tests/PhpSpreadsheetTests/Reader/Xls/HiddenWorksheetTest.php index e31b38804..aa50599c3 100644 --- a/tests/PhpSpreadsheetTests/Reader/Xls/HiddenWorksheetTest.php +++ b/tests/PhpSpreadsheetTests/Reader/Xls/HiddenWorksheetTest.php @@ -15,9 +15,11 @@ class HiddenWorksheetTest extends TestCase $spreadsheet = $reader->load($filename); $assertions = $this->worksheetAssertions(); + $sheetCount = 0; foreach ($spreadsheet->getAllSheets() as $worksheet) { + ++$sheetCount; if (!array_key_exists($worksheet->getTitle(), $assertions)) { - continue; + self::fail('Unexpected worksheet' . $worksheet->getTitle()); } $sheetAssertions = $assertions[$worksheet->getTitle()]; @@ -30,6 +32,7 @@ class HiddenWorksheetTest extends TestCase ); } } + self::assertCount($sheetCount, $assertions); $spreadsheet->disconnectWorksheets(); } diff --git a/tests/PhpSpreadsheetTests/Reader/Xls/PageSetupTest.php b/tests/PhpSpreadsheetTests/Reader/Xls/PageSetupTest.php index 034ce6ee7..f90595465 100644 --- a/tests/PhpSpreadsheetTests/Reader/Xls/PageSetupTest.php +++ b/tests/PhpSpreadsheetTests/Reader/Xls/PageSetupTest.php @@ -3,7 +3,6 @@ namespace PhpOffice\PhpSpreadsheetTests\Reader\Xls; use PhpOffice\PhpSpreadsheet\Reader\Xls; -use PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Worksheet\PageSetup; use PHPUnit\Framework\TestCase; @@ -13,25 +12,18 @@ class PageSetupTest extends TestCase private const MARGIN_UNIT_CONVERSION = 2.54; // Inches to cm - /** - * @var Spreadsheet - */ - private $spreadsheet; - - protected function setup(): void + public function testPageSetup(): void { $filename = 'tests/data/Reader/XLS/PageSetup.xls'; $reader = new Xls(); - $this->spreadsheet = $reader->load($filename); - } - - public function testPageSetup(): void - { + $spreadsheet = $reader->load($filename); $assertions = $this->pageSetupAssertions(); - foreach ($this->spreadsheet->getAllSheets() as $worksheet) { + $sheetCount = 0; + foreach ($spreadsheet->getAllSheets() as $worksheet) { + ++$sheetCount; if (!array_key_exists($worksheet->getTitle(), $assertions)) { - continue; + self::fail('Unexpected worksheet ' . $worksheet->getTitle()); } $sheetAssertions = $assertions[$worksheet->getTitle()]; @@ -45,15 +37,22 @@ class PageSetupTest extends TestCase ); } } + self::assertCount($sheetCount, $assertions); + $spreadsheet->disconnectWorksheets(); } public function testPageMargins(): void { + $filename = 'tests/data/Reader/XLS/PageSetup.xls'; + $reader = new Xls(); + $spreadsheet = $reader->load($filename); $assertions = $this->pageMarginAssertions(); - foreach ($this->spreadsheet->getAllSheets() as $worksheet) { + $sheetCount = 0; + foreach ($spreadsheet->getAllSheets() as $worksheet) { + ++$sheetCount; if (!array_key_exists($worksheet->getTitle(), $assertions)) { - continue; + self::fail('Unexpected worksheet ' . $worksheet->getTitle()); } $sheetAssertions = $assertions[$worksheet->getTitle()]; @@ -68,6 +67,8 @@ class PageSetupTest extends TestCase ); } } + self::assertCount($sheetCount, $assertions); + $spreadsheet->disconnectWorksheets(); } private function pageSetupAssertions(): array diff --git a/tests/data/Reader/XLS/formulas.database.xls b/tests/data/Reader/XLS/formulas.database.xls new file mode 100644 index 0000000000000000000000000000000000000000..bd18d0eb56405fc07c29bc39ce352f5d5abd6616 GIT binary patch literal 13824 zcmeHOU2IfE6h3#m+is<>ZTTrwDC-ua-IlggA_zjaAPocvO_6|FQ?_NH4Q;pC*0ema zA{b*N!Ds>xB*Gg=q9}>J2q}?7V;~v~5qtvU6FwLXCXfK@_nmuh_orJHO-K;$Y|qS{ zbI*6?+%q$0c4lvX_`c-&@z2U{NJd&Id2&BdDETRT1J|?cda>YsBEhO%&croXPWlgd zpwNqs%=gHKxt9vgkK!^<4{@lA^m~kEiAj%yrB@#9-D7C(LG8NH1GEZ%X;xLZXNyP5L;~{y zm9>?X4IS0X+pFwKdv%5T8O-6?Y4=}ii|lwkT__+0GAAii*O0CUb*fq*UI}DMWoWZR zmq<_=MLK9o&~IC2RoknS(O$jWVgwcjV0TAVr~|FkNi{xzrs9JBR8Y{LhG>#nS(e_I z`2^|hsc9)qHZ>(1eO_wN!^uW%{8+m-Ses5Fugf%!6!|OR*V4>J{p52HDgTyz>%n}E z>TL28@u@YN{FBT##LvgGlI_nL(+{^Z`PCEfYbW3bv+}tH-r`C(t{)30ly5ft0N82N z|AEUb#qqoZ%aVA$mZfpGmQ&*eT2705wUoGMIX&)4Y}fd708wv?F2(t>xjPaWTJ)Z{ zF}@5rZ+JnNljjV%D{YJf@)XD|@^UN^0cLHaXIJ--N92{BNbe4W_vT$>x5lD7d&q7c z80d{ayfqSzfxo&t5{vDD*vdtX7%RNlm$^KQ09lipJ4{hA+V?1BW{X3#-n>3WlS$#M&1q181-S+rCTU`4zx zER=ek&{7Pa@;J#o(5lJeal&Tu;L&kB+K?&7wlaC}`8Zx-79M?{iDjo{^5FJyJU*R~ z@f;;tc%@l*Q?u~c1DX2RBbmGzS$G`8nR5JcnaSgq(M(=>79N(radH(Iyn8xr1Y?lN ztQn}Wf6bap$ERJ(LydiE-gBeI-eipkAN(tFRw_`V7Lmpf^TnFiG7r5Pa;rQ?i%-93 zQ=VT+C|RYFW5c~jCv%_J(`V##yl(gM?4g(`1*xe(9KC}7`9^>U%4 zdYOL4474OU%9A>a(sVMmBy|>{*OEO~l%|uh$S1TfUlt2zgsBs=KO{0z`rW+>na`Ut z>K;WT?AFjazmP)*oaQRUan@KkJhMa1N#LL-2kxxm7!7=~y6D2& z^=l2_peG0JYsE2Q_~g{bOr^j81}wU;`&plh`)sf=3`#vzKs6_S zV^3EsIvCwKv@Sy#_>&m4oN9&$0{@^h7|Fw5M;r{Lk#FMKdAf5*t1Mz&|3@G(HU?%D<;hva(-C#J}vnG}J=W_aX4^R`A~y9fCr+MsHsw38eUI3933 z;CR6Cfa3wj1C9qA4>%rhJm7f1@xYVq0q%aeIp*)>{4Jj^fAQ50?w|R89o*~l9*Cur9#0RnNfe(U@yiBA?i&%O>`*r_&=Glc*Bh=Mb5szUpmL!3Vk+dkiw(E~CR9(f5Zbg2yy?SX%0&(eSK K{QBtD^ZyN@1p6!i literal 0 HcmV?d00001 diff --git a/tests/data/Reader/XLS/formulas.xls b/tests/data/Reader/XLS/formulas.xls new file mode 100644 index 0000000000000000000000000000000000000000..e18c80e692c78822bbd466ae0922774f3d872060 GIT binary patch literal 40448 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 l<+mYWXhekFl)kp8Rkr_-m!f1&;