From 2e22e03fb5dadc84669af8d895f9eb449b2e6d71 Mon Sep 17 00:00:00 2001
From: lucashedding <lucashedding@1463982.no-reply.drupal.org>
Date: Fri, 14 Oct 2016 14:11:37 -0600
Subject: [PATCH] Issue #2781573 by sgurlt, heddn: Method for CSV File Object
 creation

---
 composer.json                                 |  8 ++
 .../migrate_source_csv.source.schema.yml      |  3 +
 phpunit.xml.dist                              | 21 -----
 src/Plugin/migrate/source/CSV.php             | 33 +++++---
 tests/bootstrap.php                           | 27 -------
 tests/src/Unit/CSVFileObjectTest.php          | 20 ++---
 tests/src/Unit/CSVUnitTestCase.php            |  2 +
 .../Unit/Plugin/migrate/source/CSVTest.php    | 78 +++++++++++--------
 8 files changed, 87 insertions(+), 105 deletions(-)
 delete mode 100644 phpunit.xml.dist
 delete mode 100644 tests/bootstrap.php

diff --git a/composer.json b/composer.json
index 9cc6d6e..e0412a6 100644
--- a/composer.json
+++ b/composer.json
@@ -17,7 +17,15 @@
     "source": "https://cgit.drupalcode.org/migrate_source_csv"
   },
   "minimum-stability": "dev",
+  "prefer-stable": true,
   "require": {
     "drupal/core": "~8.1"
+  },
+  "require-dev": {
+    "mikey179/vfsStream": "~1",
+    "phpunit/phpunit": "~4"
+  },
+  "config": {
+    "preferred-install": "dist"
   }
 }
diff --git a/config/schema/migrate_source_csv.source.schema.yml b/config/schema/migrate_source_csv.source.schema.yml
index 24440a0..1aa2064 100644
--- a/config/schema/migrate_source_csv.source.schema.yml
+++ b/config/schema/migrate_source_csv.source.schema.yml
@@ -17,3 +17,6 @@ migrate.source.csv:
     column_names:
       type: sequence
       label: 'Numeric key 0-based index of the columns in source CSV file'
+    file_class:
+      type: string
+      label: 'Full class name that includes namespace for an alternative CSV reader'
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
deleted file mode 100644
index ac02787..0000000
--- a/phpunit.xml.dist
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<phpunit bootstrap="tests/bootstrap.php" colors="true">
-  <php>
-    <!-- Set error reporting to E_ALL. -->
-    <ini name="error_reporting" value="32767"/>
-    <!-- Do not limit the amount of memory tests take to run. -->
-    <ini name="memory_limit" value="-1"/>
-  </php>
-  <testsuites>
-    <testsuite name="Migrate CSV Test Suite">
-      <directory>./tests/src/Unit</directory>
-    </testsuite>
-  </testsuites>
-  <!-- Filter for coverage reports. -->
-  <filter>
-    <whitelist processUncoveredFilesFromWhitelist="true">
-        <directory>./src</directory>
-    </whitelist>
-  </filter>
-</phpunit>
diff --git a/src/Plugin/migrate/source/CSV.php b/src/Plugin/migrate/source/CSV.php
index 94f48a0..cdba000 100644
--- a/src/Plugin/migrate/source/CSV.php
+++ b/src/Plugin/migrate/source/CSV.php
@@ -40,6 +40,20 @@ class CSV extends SourcePluginBase {
    */
   protected $keys = [];
 
+  /**
+   * The file class to read the file.
+   *
+   * @var string
+   */
+  protected $fileClass = '';
+
+  /**
+   * The file object that reads the CSV file.
+   *
+   * @var \SplFileObject
+   */
+  protected $file = NULL;
+
   /**
    * {@inheritdoc}
    */
@@ -56,6 +70,7 @@ class CSV extends SourcePluginBase {
       throw new MigrateException('You must declare "keys" as a unique array of fields in your source settings.');
     }
 
+    $this->fileClass = empty($configuration['file_class']) ? CSVFileObject::class : $configuration['file_class'];
   }
 
   /**
@@ -73,36 +88,36 @@ class CSV extends SourcePluginBase {
    */
   public function initializeIterator() {
     // File handler using header-rows-respecting extension of SPLFileObject.
-    $file = new CSVFileObject($this->configuration['path']);
+    $this->file = new $this->fileClass($this->configuration['path']);
 
     // Set basics of CSV behavior based on configuration.
     $delimiter = !empty($this->configuration['delimiter']) ? $this->configuration['delimiter'] : ',';
     $enclosure = !empty($this->configuration['enclosure']) ? $this->configuration['enclosure'] : '"';
     $escape = !empty($this->configuration['escape']) ? $this->configuration['escape'] : '\\';
-    $file->setCsvControl($delimiter, $enclosure, $escape);
+    $this->file->setCsvControl($delimiter, $enclosure, $escape);
 
     // Figure out what CSV column(s) to use. Use either the header row(s) or
     // explicitly provided column name(s).
     if (!empty($this->configuration['header_row_count'])) {
-      $file->setHeaderRowCount($this->configuration['header_row_count']);
+      $this->file->setHeaderRowCount($this->configuration['header_row_count']);
 
       // Find the last header line.
-      $file->rewind();
-      $file->seek($file->getHeaderRowCount() - 1);
+      $this->file->rewind();
+      $this->file->seek($this->file->getHeaderRowCount() - 1);
 
-      $row = $file->current();
+      $row = $this->file->current();
       foreach ($row as $header) {
         $header = trim($header);
         $column_names[] = [$header => $header];
       }
-      $file->setColumnNames($column_names);
+      $this->file->setColumnNames($column_names);
     }
     // An explicit list of column name(s) will override any header row(s).
     if (!empty($this->configuration['column_names'])) {
-      $file->setColumnNames($this->configuration['column_names']);
+      $this->file->setColumnNames($this->configuration['column_names']);
     }
 
-    return $file;
+    return $this->file;
   }
 
   /**
diff --git a/tests/bootstrap.php b/tests/bootstrap.php
deleted file mode 100644
index 079563d..0000000
--- a/tests/bootstrap.php
+++ /dev/null
@@ -1,27 +0,0 @@
-<?php
-
-/**
- * @file
- * Searches for the core bootstrap file.
- */
-
-$dir = __DIR__;
-
-// Match against previous dir for Windows.
-$previous_dir = '';
-
-while ($dir = dirname($dir)) {
-  // We've reached the root.
-  if ($dir === $previous_dir) {
-    break;
-  }
-
-  $previous_dir = $dir;
-
-  if (is_file($dir . '/core/tests/bootstrap.php')) {
-    require_once $dir . '/core/tests/bootstrap.php';
-    return;
-  }
-}
-
-throw new RuntimeException('Unable to load core bootstrap.php.');
diff --git a/tests/src/Unit/CSVFileObjectTest.php b/tests/src/Unit/CSVFileObjectTest.php
index 9cc4754..2cf926d 100644
--- a/tests/src/Unit/CSVFileObjectTest.php
+++ b/tests/src/Unit/CSVFileObjectTest.php
@@ -33,11 +33,9 @@ class CSVFileObjectTest extends CSVUnitTestCase {
   /**
    * Tests that the construction appropriately creates a CSVFileObject.
    *
-   * @test
-   *
    * @covers ::__construct
    */
-  public function create() {
+  public function testCreate() {
     $this->assertInstanceOf(CSVFileObject::class, $this->csvFileObject);
     $flags = CSVFileObject::READ_CSV | CSVFileObject::READ_AHEAD | CSVFileObject::DROP_NEW_LINE | CSVFileObject::SKIP_EMPTY;
     $this->assertSame($flags, $this->csvFileObject->getFlags());
@@ -46,11 +44,9 @@ class CSVFileObjectTest extends CSVUnitTestCase {
   /**
    * Tests that the header row count is correctly set.
    *
-   * @test
-   *
    * @covers ::setHeaderRowCount
    */
-  public function setHeaderRowCount() {
+  public function testSetHeaderRowCount() {
     $expected = 2;
     $this->csvFileObject->setHeaderRowCount($expected);
 
@@ -60,13 +56,11 @@ class CSVFileObjectTest extends CSVUnitTestCase {
   /**
    * Tests that the header row count is correctly returned.
    *
-   * @test
-   *
    * @depends setHeaderRowCount
    *
    * @covers ::getHeaderRowCount
    */
-  public function getHeaderRowCount($actual) {
+  public function testGetHeaderRowCount($actual) {
     $expected = 2;
     $this->assertEquals($expected, $actual);
   }
@@ -74,11 +68,9 @@ class CSVFileObjectTest extends CSVUnitTestCase {
   /**
    * Tests that line count is correct.
    *
-   * @test
-   *
    * @covers ::count
    */
-  public function countLines() {
+  public function testCountLines() {
     $expected = 15;
     $this->csvFileObject->setHeaderRowCount(1);
     $actual = $this->csvFileObject->count();
@@ -89,14 +81,12 @@ class CSVFileObjectTest extends CSVUnitTestCase {
   /**
    * Tests that the current row is correctly returned.
    *
-   * @test
-   *
    * @covers ::current
    * @covers ::rewind
    * @covers ::getColumnNames
    * @covers ::setColumnNames
    */
-  public function current() {
+  public function testCurrent() {
     $column_names = [
       ['id' => 'Identifier'],
       ['first_name' => 'First Name'],
diff --git a/tests/src/Unit/CSVUnitTestCase.php b/tests/src/Unit/CSVUnitTestCase.php
index 69da1d5..270de60 100644
--- a/tests/src/Unit/CSVUnitTestCase.php
+++ b/tests/src/Unit/CSVUnitTestCase.php
@@ -34,6 +34,8 @@ abstract class CSVUnitTestCase extends UnitTestCase {
    * {@inheritdoc}
    */
   protected function setUp() {
+    parent::setUp();
+
     $root_dir = vfsStream::setup('root');
     $happy = <<<'EOD'
 id,first_name,last_name,email,country,ip_address
diff --git a/tests/src/Unit/Plugin/migrate/source/CSVTest.php b/tests/src/Unit/Plugin/migrate/source/CSVTest.php
index 0e5e4fc..c9aa05f 100644
--- a/tests/src/Unit/Plugin/migrate/source/CSVTest.php
+++ b/tests/src/Unit/Plugin/migrate/source/CSVTest.php
@@ -7,9 +7,9 @@
 namespace Drupal\Tests\migrate_source_csv\Unit\Plugin\migrate\source;
 
 use Drupal\migrate\Plugin\MigrationInterface;
+use Drupal\migrate_source_csv\CSVFileObject;
 use Drupal\migrate_source_csv\Plugin\migrate\source\CSV;
 use Drupal\Tests\migrate_source_csv\Unit\CSVUnitTestCase;
-use Prophecy\Argument;
 
 /**
  * @coversDefaultClass \Drupal\migrate_source_csv\Plugin\migrate\source\CSV
@@ -52,9 +52,6 @@ class CSVTest extends CSVUnitTestCase {
     $plugin = $this->prophesize(MigrationInterface::class);
     $plugin->getIdMap()
       ->willReturn(NULL);
-    // @topo Swap it out for getHighWaterProperty after https://www.drupal.org/node/2694009
-    $plugin->getHighWaterProperty()
-      ->willReturn(NULL);
 
     $this->plugin = $plugin->reveal();
   }
@@ -62,11 +59,9 @@ class CSVTest extends CSVUnitTestCase {
   /**
    * Tests the construction of CSV.
    *
-   * @test
-   *
    * @covers ::__construct
    */
-  public function create() {
+  public function testCreate() {
     $configuration = [
       'path' => $this->happyPath,
       'keys' => ['id'],
@@ -81,26 +76,22 @@ class CSVTest extends CSVUnitTestCase {
   /**
    * Tests that a missing path will throw an exception.
    *
-   * @test
-   *
    * @expectedException \Drupal\migrate\MigrateException
    *
    * @expectedExceptionMessage You must declare the "path" to the source CSV file in your source settings.
    */
-  public function migrateExceptionPathMissing() {
+  public function testMigrateExceptionPathMissing() {
     new CSV([], $this->pluginId, $this->pluginDefinition, $this->plugin);
   }
 
   /**
    * Tests that missing keys will throw an exception.
    *
-   * @test
-   *
    * @expectedException \Drupal\migrate\MigrateException
    *
    * @expectedExceptionMessage You must declare "keys" as a unique array of fields in your source settings.
    */
-  public function migrateExceptionKeysMissing() {
+  public function testMigrateExceptionKeysMissing() {
     $configuration = [
       'path' => $this->happyPath,
     ];
@@ -111,11 +102,9 @@ class CSVTest extends CSVUnitTestCase {
   /**
    * Tests that toString functions as expected.
    *
-   * @test
-   *
    * @covers ::__toString
    */
-  public function toString() {
+  public function testToString() {
     $configuration = [
       'path' => $this->happyPath,
       'keys' => ['id'],
@@ -130,11 +119,9 @@ class CSVTest extends CSVUnitTestCase {
   /**
    * Tests initialization of the iterator.
    *
-   * @test
-   *
    * @covers ::initializeIterator
    */
-  public function initializeIterator() {
+  public function testInitializeIterator() {
     $configuration = [
       'path' => $this->happyPath,
       'keys' => ['id'],
@@ -227,11 +214,9 @@ class CSVTest extends CSVUnitTestCase {
   /**
    * Tests that the key is properly identified.
    *
-   * @test
-   *
    * @covers ::getIds
    */
-  public function getIds() {
+  public function testGetIds() {
     $configuration = [
       'path' => $this->happyPath,
       'keys' => ['id'],
@@ -247,11 +232,9 @@ class CSVTest extends CSVUnitTestCase {
   /**
    * Tests that fields have a machine name and description.
    *
-   * @test
-   *
    * @covers ::fields
    */
-  public function fields() {
+  public function testFields() {
     $configuration = [
       'path' => $this->happyPath,
       'keys' => ['id'],
@@ -263,11 +246,11 @@ class CSVTest extends CSVUnitTestCase {
     ];
 
     $expected = $fields + [
-      'last_name' => 'last_name',
-      'email' => 'email',
-      'country' => 'country',
-      'ip_address' => 'ip_address',
-    ];
+        'last_name' => 'last_name',
+        'email' => 'email',
+        'country' => 'country',
+        'ip_address' => 'ip_address',
+      ];
 
     $csv = new CSV($configuration, $this->pluginId, $this->pluginDefinition, $this->plugin);
     $csv = new CSV($configuration + ['fields' => $fields], $this->pluginId, $this->pluginDefinition, $this->plugin);
@@ -278,10 +261,39 @@ class CSVTest extends CSVUnitTestCase {
       2 => ['first_name' => 'User first name'],
     ];
     $csv = new CSV($configuration + [
-      'fields' => $fields,
-      'column_names' => $column_names,
-    ], $this->pluginId, $this->pluginDefinition, $this->plugin);
+        'fields' => $fields,
+        'column_names' => $column_names,
+      ], $this->pluginId, $this->pluginDefinition, $this->plugin);
     $this->assertArrayEquals($fields, $csv->fields());
   }
 
+  /**
+   * Tests configurable CSV file object.
+   *
+   * @covers ::__construct
+   */
+  public function testConfigurableCSVFileObject() {
+    $configuration = [
+      'path' => $this->happyPath,
+      'keys' => ['id'],
+      'header_row_count' => 1,
+      'file_class' => FooCSVFileObject::class ,
+    ];
+
+    $csv = new CSV($configuration, $this->pluginId, $this->pluginDefinition, $this->plugin);
+    $csv->initializeIterator();
+    $fileObject = $this->readAttribute($csv, 'file');
+
+    $this->assertInstanceOf(FooCSVFileObject::class, $fileObject);
+  }
+
 }
+
+/**
+ * Class FooCSVFileObject
+ *
+ * Test file object class.
+ *
+ * @package Drupal\Tests\migrate_source_csv\Unit\Plugin\migrate\source
+ */
+class FooCSVFileObject extends CSVFileObject { }
-- 
GitLab