Completed
Push — master ( 44f7bc...3e4910 )
by
unknown
12s
created

BatchedProcessorTest::setUpBeforeClass()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 8
rs 9.4285
cc 3
eloc 4
nc 2
nop 0
1
<?php
2
3
namespace SilverStripe\FullTextSearch\Tests;
4
5
use SilverStripe\CMS\Model\SiteTree;
6
use SilverStripe\Core\Config\Config;
7
use SilverStripe\Core\Injector\Injector;
8
use SilverStripe\Dev\SapphireTest;
9
use SilverStripe\FullTextSearch\Search\FullTextSearch;
10
use SilverStripe\FullTextSearch\Tests\BatchedProcessorTest\BatchedProcessor_QueuedJobService;
11
use SilverStripe\FullTextSearch\Tests\BatchedProcessorTest\BatchedProcessorTest_Index;
12
use SilverStripe\FullTextSearch\Tests\BatchedProcessorTest\BatchedProcessorTest_Object;
13
use SilverStripe\FullTextSearch\Search\Processors\SearchUpdateCommitJobProcessor;
14
use SilverStripe\FullTextSearch\Search\Processors\SearchUpdateQueuedJobProcessor;
15
use SilverStripe\FullTextSearch\Search\Processors\SearchUpdateBatchedProcessor;
16
use SilverStripe\FullTextSearch\Search\Updaters\SearchUpdater;
17
use SilverStripe\FullTextSearch\Search\Variants\SearchVariantVersioned;
18
use SilverStripe\ORM\FieldType\DBDatetime;
19
use SilverStripe\Versioned\Versioned;
20
use Symbiote\QueuedJobs\Services\QueuedJob;
21
use Symbiote\QueuedJobs\Services\QueuedJobService;
22
23
/**
24
 * Tests {@see SearchUpdateQueuedJobProcessor}
25
 */
26
class BatchedProcessorTest extends SapphireTest
27
{
28
    protected $oldProcessor;
29
30
    protected static $extra_dataobjects = array(
31
        BatchedProcessorTest_Object::class
32
    );
33
34
    protected static $illegal_extensions = [
35
        SiteTree::class => [
36
            SiteTreeSubsites::class,
37
        ],
38
    ];
39
40
    public static function setUpBeforeClass()
41
    {
42
        // Disable illegal extensions if skipping this test
43
        if (class_exists(Subsite::class) || !interface_exists(QueuedJob::class)) {
44
            static::$illegal_extensions = [];
45
        }
46
        parent::setUpBeforeClass();
47
    }
48
49
    protected function setUp()
50
    {
51
        parent::setUp();
52
53
        if (!interface_exists(QueuedJob::class)) {
54
            $this->markTestSkipped("These tests need the QueuedJobs module installed to run");
55
        }
56
57
        if (class_exists(Subsite::class)) {
58
            $this->markTestSkipped(get_class() . ' skipped when running with subsites');
59
        }
60
61
        DBDatetime::set_mock_now('2015-05-07 06:00:00');
62
63
        Config::modify()->set(SearchUpdateBatchedProcessor::class, 'batch_size', 5);
64
        Config::modify()->set(SearchUpdateBatchedProcessor::class, 'batch_soft_cap', 0);
65
        Config::modify()->set(SearchUpdateCommitJobProcessor::class, 'cooldown', 600);
66
67
        Versioned::set_stage(Versioned::DRAFT);
68
69
        Injector::inst()->registerService(new BatchedProcessor_QueuedJobService(), QueuedJobService::class);
70
71
        FullTextSearch::force_index_list(BatchedProcessorTest_Index::class);
72
73
        SearchUpdateCommitJobProcessor::$dirty_indexes = array();
74
        SearchUpdateCommitJobProcessor::$has_run = false;
75
76
        $this->oldProcessor = SearchUpdater::$processor;
77
        SearchUpdater::$processor = new SearchUpdateQueuedJobProcessor();
78
    }
79
80
    public function tearDown()
81
    {
82
        if ($this->oldProcessor) {
83
            SearchUpdater::$processor = $this->oldProcessor;
84
        }
85
        FullTextSearch::force_index_list();
86
        parent::tearDown();
87
    }
88
89
    /**
90
     * @return SearchUpdateQueuedJobProcessor
91
     */
92
    protected function generateDirtyIds()
93
    {
94
        $processor = SearchUpdater::$processor;
95
        for ($id = 1; $id <= 42; $id++) {
96
            // Save to db
97
            $object = new BatchedProcessorTest_Object();
98
            $object->TestText = 'Object ' . $id;
0 ignored issues
show
Documentation introduced by
The property TestText does not exist on object<SilverStripe\Full...edProcessorTest_Object>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
99
            $object->write();
100
            // Add to index manually
101
            $processor->addDirtyIDs(
102
                BatchedProcessorTest_Object::class,
103
                array(array(
104
                    'id' => $id,
105
                    'state' => array(SearchVariantVersioned::class => 'Stage')
106
                )),
107
                BatchedProcessorTest_Index::class
108
            );
109
        }
110
        $processor->batchData();
111
        return $processor;
112
    }
113
114
    /**
115
     * Tests that large jobs are broken up into a suitable number of batches
116
     */
117
    public function testBatching()
118
    {
119
        $index = singleton(BatchedProcessorTest_Index::class);
120
        $index->reset();
121
        $processor = $this->generateDirtyIds();
122
123
        // Check initial state
124
        $data = $processor->getJobData();
125
        $this->assertEquals(9, $data->totalSteps);
126
        $this->assertEquals(0, $data->currentStep);
127
        $this->assertEmpty($data->isComplete);
128
        $this->assertEquals(0, count($index->getAdded()));
129
130
        // Advance state
131
        for ($pass = 1; $pass <= 8; $pass++) {
132
            $processor->process();
133
            $data = $processor->getJobData();
134
            $this->assertEquals($pass, $data->currentStep);
135
            $this->assertEquals($pass * 5, count($index->getAdded()));
136
        }
137
138
        // Last run should have two hanging items
139
        $processor->process();
140
        $data = $processor->getJobData();
141
        $this->assertEquals(9, $data->currentStep);
142
        $this->assertEquals(42, count($index->getAdded()));
143
        $this->assertTrue($data->isComplete);
144
145
        // Check any additional queued jobs
146
        $processor->afterComplete();
147
        $service = singleton(QueuedJobService::class);
148
        $jobs = $service->getJobs();
149
        $this->assertEquals(1, count($jobs));
150
        $this->assertInstanceOf(SearchUpdateCommitJobProcessor::class, $jobs[0]['job']);
151
    }
152
153
    /**
154
     * Test creation of multiple commit jobs
155
     */
156
    public function testMultipleCommits()
157
    {
158
        $index = singleton(BatchedProcessorTest_Index::class);
159
        $index->reset();
160
161
        // Test that running a commit immediately after submitting to the indexes
162
        // correctly commits
163
        $first = SearchUpdateCommitJobProcessor::queue();
164
        $second = SearchUpdateCommitJobProcessor::queue();
165
166
        $this->assertFalse($index->getIsCommitted());
167
168
        // First process will cause the commit
169
        $this->assertFalse($first->jobFinished());
0 ignored issues
show
Bug introduced by
The method jobFinished cannot be called on $first (of type integer).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
170
        $first->process();
0 ignored issues
show
Bug introduced by
The method process cannot be called on $first (of type integer).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
171
        $allMessages = $first->getMessages();
0 ignored issues
show
Bug introduced by
The method getMessages cannot be called on $first (of type integer).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
172
        $this->assertTrue($index->getIsCommitted());
173
        $this->assertTrue($first->jobFinished());
0 ignored issues
show
Bug introduced by
The method jobFinished cannot be called on $first (of type integer).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
174
        $this->assertStringEndsWith('All indexes committed', $allMessages[2]);
175
176
        // Executing the subsequent processor should not re-trigger a commit
177
        $index->reset();
178
        $this->assertFalse($second->jobFinished());
0 ignored issues
show
Bug introduced by
The method jobFinished cannot be called on $second (of type integer).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
179
        $second->process();
0 ignored issues
show
Bug introduced by
The method process cannot be called on $second (of type integer).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
180
        $allMessages = $second->getMessages();
0 ignored issues
show
Bug introduced by
The method getMessages cannot be called on $second (of type integer).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
181
        $this->assertFalse($index->getIsCommitted());
182
        $this->assertTrue($second->jobFinished());
0 ignored issues
show
Bug introduced by
The method jobFinished cannot be called on $second (of type integer).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
183
        $this->assertStringEndsWith('Indexing already completed this request: Discarding this job', $allMessages[0]);
184
185
        // Given that a third job is created, and the indexes are dirtied, attempting to run this job
186
        // should result in a delay
187
        $index->reset();
188
        $third = SearchUpdateCommitJobProcessor::queue();
189
        $this->assertFalse($third->jobFinished());
0 ignored issues
show
Bug introduced by
The method jobFinished cannot be called on $third (of type integer).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
190
        $third->process();
0 ignored issues
show
Bug introduced by
The method process cannot be called on $third (of type integer).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
191
        $this->assertTrue($third->jobFinished());
0 ignored issues
show
Bug introduced by
The method jobFinished cannot be called on $third (of type integer).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
192
        $allMessages = $third->getMessages();
0 ignored issues
show
Bug introduced by
The method getMessages cannot be called on $third (of type integer).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
193
        $this->assertStringEndsWith(
194
            'Indexing already run this request, but incomplete. Re-scheduling for 2015-05-07 06:10:00',
195
            $allMessages[0]
196
        );
197
    }
198
199
200
    /**
201
     * Tests that the batch_soft_cap setting is properly respected
202
     */
203
    public function testSoftCap()
204
    {
205
        $index = singleton(BatchedProcessorTest_Index::class);
206
        $index->reset();
207
        $processor = $this->generateDirtyIds();
208
209
        // Test that increasing the soft cap to 2 will reduce the number of batches
210
        Config::modify()->set(SearchUpdateBatchedProcessor::class, 'batch_soft_cap', 2);
211
        $processor->batchData();
212
        $data = $processor->getJobData();
213
        //Debug::dump($data);die;
0 ignored issues
show
Unused Code Comprehensibility introduced by
78% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
214
        $this->assertEquals(8, $data->totalSteps);
215
216
        // A soft cap of 1 should not fit in the hanging two items
217
        Config::modify()->set(SearchUpdateBatchedProcessor::class, 'batch_soft_cap', 1);
218
        $processor->batchData();
219
        $data = $processor->getJobData();
220
        $this->assertEquals(9, $data->totalSteps);
221
222
        // Extra large soft cap should fit both items
223
        Config::modify()->set(SearchUpdateBatchedProcessor::class, 'batch_soft_cap', 4);
224
        $processor->batchData();
225
        $data = $processor->getJobData();
226
        $this->assertEquals(8, $data->totalSteps);
227
228
        // Process all data and ensure that all are processed adequately
229
        for ($pass = 1; $pass <= 8; $pass++) {
230
            $processor->process();
231
        }
232
        $data = $processor->getJobData();
233
        $this->assertEquals(8, $data->currentStep);
234
        $this->assertEquals(42, count($index->getAdded()));
235
        $this->assertTrue($data->isComplete);
236
    }
237
}
238