Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
| 1 | <?php |
||
| 6 | class ElasticsearchBaseTest extends SapphireTest { |
||
| 7 | |||
| 8 | public static $ignoreFixtureFileFor = array(); |
||
| 9 | |||
| 10 | protected $extraDataObjects = array( |
||
| 11 | 'SearchableTestPage','FlickrPhotoTO','FlickrAuthorTO','FlickrSetTO','FlickrTagTO', |
||
| 12 | 'SearchableTestFatherPage','SearchableTestGrandFatherPage','AutoCompleteOption' |
||
| 13 | ); |
||
| 14 | |||
| 15 | |||
| 16 | public function setUpOnce() { |
||
| 17 | ElasticaUtil::setPrinterOutput(false); |
||
| 18 | |||
| 19 | // add Searchable extension where appropriate |
||
| 20 | FlickrSetTO::add_extension('SilverStripe\Elastica\Searchable'); |
||
| 21 | FlickrPhotoTO::add_extension('SilverStripe\Elastica\Searchable'); |
||
| 22 | FlickrTagTO::add_extension('SilverStripe\Elastica\Searchable'); |
||
| 23 | FlickrAuthorTO::add_extension('SilverStripe\Elastica\Searchable'); |
||
| 24 | SearchableTestPage::add_extension('SilverStripe\Elastica\Searchable'); |
||
| 25 | |||
| 26 | |||
| 27 | $config = Config::inst(); |
||
| 28 | $config->remove('Injector', 'SilverStripe\Elastica\ElasticaService'); |
||
| 29 | $constructor = array('constructor' => array('%$Elastica\Client', 'elastica_ss_module_test')); |
||
| 30 | $config->update('Injector', 'SilverStripe\Elastica\ElasticaService', $constructor); |
||
| 31 | parent::setUpOnce(); |
||
| 32 | } |
||
| 33 | |||
| 34 | |||
| 35 | 1 | public function setUp() { |
|
| 36 | // no need to index here as it's done when fixtures are loaded during setup method |
||
| 37 | 1 | $cache = SS_Cache::factory('elasticsearch'); |
|
| 38 | 1 | $cache->clean(Zend_Cache::CLEANING_MODE_ALL); |
|
| 39 | 1 | SS_Cache::set_cache_lifetime('elasticsearch', 3600, 1000); |
|
| 40 | |||
| 41 | // this needs to be called in order to create the list of searchable |
||
| 42 | // classes and fields that are available. Simulates part of a build |
||
| 43 | 1 | $classes = array('SearchableTestPage','SiteTree','Page','FlickrPhotoTO','FlickrSetTO', |
|
| 44 | 1 | 'FlickrTagTO', 'FlickrAuthorTO'); |
|
| 45 | 1 | $this->requireDefaultRecordsFrom = $classes; |
|
| 46 | |||
| 47 | |||
| 48 | // clear the index |
||
| 49 | 1 | $this->service = Injector::inst()->create('SilverStripe\Elastica\ElasticaService'); |
|
| 50 | 1 | $this->service->setTestMode(true); |
|
| 51 | |||
| 52 | // A previous test may have deleted the index and then failed, so check for this |
||
| 53 | 1 | if (!$this->service->getIndex()->exists()) { |
|
| 54 | 1 | $this->service->getIndex()->create(); |
|
| 55 | 1 | } |
|
| 56 | 1 | $this->service->reset(); |
|
| 57 | |||
| 58 | // FIXME - use request getVar instead? |
||
| 59 | 1 | $_GET['progress'] = 20; |
|
| 60 | // load fixtures |
||
| 61 | |||
| 62 | 1 | $orig_fixture_file = static::$fixture_file; |
|
| 63 | |||
| 64 | 1 | foreach (static::$ignoreFixtureFileFor as $testPattern) { |
|
| 65 | $pattern = '/'.$testPattern.'/'; |
||
| 66 | if (preg_match($pattern, $this->getName())) { |
||
| 67 | static::$fixture_file = null; |
||
| 68 | } |
||
| 69 | 1 | } |
|
| 70 | |||
| 71 | 1 | parent::setUp(); |
|
| 72 | 1 | static::$fixture_file = $orig_fixture_file; |
|
| 73 | |||
| 74 | 1 | $this->publishSiteTree(); |
|
| 75 | |||
| 76 | 1 | $this->service->reset(); |
|
| 77 | |||
| 78 | // index loaded fixtures |
||
| 79 | 1 | $task = new ReindexTask($this->service); |
|
| 80 | // null request is fine as no parameters used |
||
| 81 | |||
| 82 | 1 | $task->run(null); |
|
| 83 | |||
| 84 | 1 | } |
|
| 85 | |||
| 86 | |||
| 87 | protected function devBuild() { |
||
| 88 | $task = new \BuildTask(); |
||
| 89 | // null request is fine as no parameters used |
||
| 90 | $task->run(null); |
||
| 91 | } |
||
| 92 | |||
| 93 | |||
| 94 | 1 | private function publishSiteTree() { |
|
| 95 | 1 | foreach (SiteTree::get()->getIterator() as $page) { |
|
| 96 | // temporarily disable Elasticsearch indexing, it will be done in a batch |
||
| 97 | 1 | $page->IndexingOff = true; |
|
| 98 | 1 | $page->publish('Stage','Live'); |
|
| 99 | 1 | } |
|
| 100 | 1 | } |
|
| 101 | |||
| 102 | |||
| 103 | public function generateAssertionsFromArray($toAssert) { |
||
| 104 | echo '$expected = array('."\n"; |
||
| 105 | foreach ($toAssert as $key => $value) { |
||
| 106 | $escValue = str_replace("'", '\\\'', $value); |
||
| 107 | echo "'$key' => '$escValue',\n"; |
||
| 108 | } |
||
| 109 | echo ");\n"; |
||
| 110 | echo '$this->assertEquals($expected, $somevar);'."\n"; |
||
| 111 | } |
||
| 112 | |||
| 113 | |||
| 114 | public function generateAssertionsFromArray1D($toAssert) { |
||
| 115 | echo '$expected = array('."\n"; |
||
| 116 | foreach ($toAssert as $key => $value) { |
||
| 117 | $escValue = str_replace("'", '\\\'', $value); |
||
| 118 | echo "'$escValue',"; |
||
| 119 | } |
||
| 120 | echo ");\n"; |
||
| 121 | echo '$this->assertEquals($expected, $somevar);'."\n"; |
||
| 122 | } |
||
| 123 | |||
| 124 | |||
| 125 | public function generateAssertionsFromArrayRecurse($toAssert) { |
||
| 126 | echo '$expected = '; |
||
| 127 | $this->recurseArrayAssertion($toAssert,1, 'FIXME'); |
||
| 128 | echo '$this->assertEquals($expected, $somevar);'."\n"; |
||
| 129 | } |
||
| 130 | |||
| 131 | |||
| 132 | private function recurseArrayAssertion($toAssert, $depth, $parentKey) { |
||
| 133 | $prefix = str_repeat("\t",$depth); |
||
| 134 | echo "\t{$prefix}'$parentKey' => array(\n"; |
||
| 135 | $ctr = 0; |
||
| 136 | $len = sizeof(array_keys($toAssert)); |
||
| 137 | foreach ($toAssert as $key => $value) { |
||
| 138 | if (is_array($value)) { |
||
| 139 | $this->recurseArrayAssertion($value, $depth+1, $key); |
||
| 140 | } else { |
||
| 141 | $escValue = str_replace("'", '\\\'', $value); |
||
| 142 | $comma = ','; |
||
| 143 | if ($ctr == $len-1) { |
||
| 144 | $comma = ''; |
||
| 145 | } |
||
| 146 | echo "\t\t$prefix'$key' => '$escValue'$comma\n"; |
||
| 147 | } |
||
| 148 | |||
| 149 | $ctr++; |
||
| 150 | |||
| 151 | } |
||
| 152 | echo "\t$prefix),\n"; |
||
| 153 | |||
| 154 | } |
||
| 155 | |||
| 156 | |||
| 157 | /* |
||
| 158 | Helper methods for testing CMS fields |
||
| 159 | */ |
||
| 160 | public function checkTabExists($fields, $tabName) { |
||
| 161 | $tab = $fields->findOrMakeTab("Root.{$tabName}"); |
||
| 162 | $actualTabName = $tab->getName(); |
||
| 163 | $splits = explode('.', $tabName); |
||
| 164 | $size = sizeof($splits); |
||
| 165 | $nameToCheck = end($splits); |
||
| 166 | $this->assertEquals($actualTabName, $nameToCheck); |
||
| 167 | if ($size == 1) { |
||
| 168 | $this->assertEquals("Root_${tabName}", $tab->id()); |
||
| 169 | } else { |
||
| 170 | $expected = "Root_{$splits[0]}_set_{$splits[1]}"; |
||
| 171 | $this->assertEquals($expected, $tab->id()); |
||
| 172 | } |
||
| 173 | |||
| 174 | return $tab; |
||
| 175 | } |
||
| 176 | |||
| 177 | |||
| 178 | public function checkFieldExists($tab,$fieldName) { |
||
| 179 | $fields = $tab->Fields(); |
||
| 180 | $field = $tab->fieldByName($fieldName); |
||
| 181 | $this->assertTrue($field != null); |
||
| 182 | return $field; |
||
| 183 | } |
||
| 184 | |||
| 185 | |||
| 186 | /** |
||
| 187 | * From https://jtreminio.com/2013/03/unit-testing-tutorial-part-3-testing-protected-private-methods-coverage-reports-and-crap/ |
||
| 188 | * Call protected/private method of a class. |
||
| 189 | * |
||
| 190 | * @param object &$object Instantiated object that we will run method on. |
||
| 191 | * @param string $methodName Method name to call |
||
| 192 | * @param array $parameters Array of parameters to pass into method. |
||
| 193 | * |
||
| 194 | * @return mixed Method return. |
||
| 195 | */ |
||
| 196 | public function invokeMethod(&$object, $methodName, array $parameters = array()) |
||
| 197 | { |
||
| 198 | $reflection = new \ReflectionClass(get_class($object)); |
||
| 199 | 1 | $method = $reflection->getMethod($methodName); |
|
| 200 | $method->setAccessible(true); |
||
| 201 | |||
| 202 | return $method->invokeArgs($object, $parameters); |
||
| 203 | } |
||
| 204 | |||
| 205 | |||
| 206 | public function checkNumberOfIndexedDocuments($expectedAmount) { |
||
| 207 | 1 | $index = $this->service->getIndex(); |
|
| 208 | 1 | $status = $index->getStatus()->getData(); |
|
| 209 | |||
| 210 | 1 | $numberDocsInIndex = -1; // flag value for not yet indexed |
|
| 211 | 1 | if (isset($status['indices']['elastica_ss_module_test_en_us']['docs'])) { |
|
| 212 | 1 | error_log('CHECKING NUMBER OF INDEXED DOCUMENTS, SHOULD BE ' . $expectedAmount); |
|
| 213 | 1 | error_log(print_r($status,1)); |
|
| 214 | $numberDocsInIndex = $status['indices']['elastica_ss_module_test_en_us']['docs']['num_docs']; |
||
| 215 | 1 | error_log('DOCUMENTS FOUND:'.$numberDocsInIndex) |
|
| 216 | 1 | } |
|
|
|
|||
| 217 | |||
| 218 | $this->assertEquals($expectedAmount,$numberDocsInIndex); |
||
| 219 | } |
||
| 220 | |||
| 221 | /* |
||
| 222 | 1 | Get the number of documents in an index. It is assumed the index exists, if not the test will fail |
|
| 223 | 1 | */ |
|
| 224 | public function getNumberOfIndexedDocuments() { |
||
| 225 | 1 | $index = $this->service->getIndex(); |
|
| 226 | 1 | $status = $index->getStatus()->getData(); |
|
| 227 | 1 | ||
| 228 | 1 | $numberDocsInIndex = -1; // flag value for not yet indexed |
|
| 229 | if (isset($status['indices']['elastica_ss_module_test_en_us']['docs'])) { |
||
| 230 | 1 | $numberDocsInIndex = $status['indices']['elastica_ss_module_test_en_us']['docs']['num_docs']; |
|
| 231 | 1 | } |
|
| 232 | |||
| 233 | $this->assertGreaterThan(-1, $numberDocsInIndex); |
||
| 234 | return $numberDocsInIndex; |
||
| 237 |