1d3569bbb98b01024c18eb71b445c95579126979
[project/feeds.git] / tests / feeds_scheduler.test
1 <?php
2 // $Id$
3
4 /**
5 * @file
6 * Feeds tests.
7 */
8
9 // Require FeedsWebTestCase class definition.
10 require_once(dirname(__FILE__) .'/feeds.test.inc');
11
12 /**
13 * Test cron scheduling.
14 */
15 class FeedsSchedulerTestCase extends FeedsWebTestCase {
16
17 /**
18 * Describe this test.
19 */
20 public function getInfo() {
21 return array(
22 'name' => t('Scheduler'),
23 'description' => t('Tests for feeds scheduler.'),
24 'group' => t('Feeds'),
25 );
26 }
27
28 /**
29 * Set up test.
30 */
31 public function setUp() {
32 parent::setUp('feeds', 'feeds_ui', 'ctools', 'job_scheduler');
33 $this->loginAdmin();
34 $this->createImporterConfiguration();
35 $this->addMappings('syndication',
36 array(
37 array(
38 'source' => 'title',
39 'target' => 'title',
40 'unique' => FALSE,
41 ),
42 array(
43 'source' => 'description',
44 'target' => 'body',
45 'unique' => FALSE,
46 ),
47 array(
48 'source' => 'timestamp',
49 'target' => 'created',
50 'unique' => FALSE,
51 ),
52 array(
53 'source' => 'url',
54 'target' => 'url',
55 'unique' => TRUE,
56 ),
57 array(
58 'source' => 'guid',
59 'target' => 'guid',
60 'unique' => TRUE,
61 ),
62 )
63 );
64 }
65
66 /**
67 * Test scheduling on cron.
68 */
69 public function testScheduling() {
70 // Create 10 feed nodes. Turn off import on create before doing that.
71 $edit = array(
72 'import_on_create' => FALSE,
73 );
74 $this->drupalPost('admin/structure/feeds/edit/syndication/settings', $edit, 'Save');
75 $this->assertText('Do not import on create');
76
77 $nids = $this->createFeedNodes();
78 // This implicitly tests the import_on_create node setting being 0.
79 $this->assertTrue($nids[0] == 1 && $nids[1] == 2, 'Node ids sequential.');
80
81 // Check whether feed got properly added to scheduler.
82 foreach ($nids as $nid) {
83 $this->assertEqual(1, db_query("SELECT COUNT(*) FROM {job_schedule} WHERE type = 'syndication' AND id = :nid AND callback = 'feeds_source_import' AND last <> 0 AND scheduled = 0 AND period = 1800 AND periodic = 1", array(':nid' => $nid))->fetchField());
84 }
85
86 // Take time for comparisons.
87 $time = time();
88 sleep(1);
89
90 // Log out and run cron, no changes.
91 $this->drupalLogout();
92 $this->runCron();
93 $count = db_query("SELECT COUNT(*) FROM {job_schedule} WHERE last > :time", array(':time' => $time))->fetchField();
94 $this->assertEqual($count, 0, '0 feeds refreshed on cron.');
95
96 // Set next time to 0 to simulate updates.
97 // There should be 2 x job_schedule_num (= 10) feeds updated now.
98 db_query("UPDATE {job_schedule} SET next = 0");
99 $this->runCron();
100 $this->runCron();
101
102 $schedule = array();
103 $count = db_query("SELECT COUNT(*) FROM {job_schedule} WHERE last > :time", array(':time' => $time))->fetchField();
104 $this->assertEqual($count, 10, '10 feeds refreshed on cron.');
105 $result = db_query("SELECT * FROM {job_schedule}", $time);
106
107 // There should be 100 story nodes in the database.
108 $count = db_query("SELECT COUNT(*) FROM {node} WHERE type = 'story'")->fetchField();
109 $this->assertEqual($count, 100, 'There are 100 story nodes aggregated.');
110
111 // Hit twice cron again.
112 $this->runCron();
113 $this->runCron();
114
115 // There should be feeds_schedule_num X 2 (= 20) feeds updated now.
116 $schedule = array();
117 $rows = db_query("SELECT id, last, scheduled FROM {job_schedule} WHERE last > :time", array(':time' => $time));
118 foreach ($rows as $row) {
119 $schedule[$row->id] = $row;
120 }
121 $this->assertEqual(count($schedule), 20, '20 feeds refreshed on cron.');
122
123 // There should be 200 story nodes in the database.
124 $count = db_query("SELECT COUNT(*) FROM {node} WHERE type = 'story' AND status = 1")->fetchField();
125 $this->assertEqual($count, 200, 'There are 200 story nodes aggregated.');
126
127 // There shouldn't be any items with scheduled = 1 now, if so, this would
128 // mean they are stuck.
129 $count = db_query("SELECT COUNT(*) FROM {job_schedule} WHERE scheduled = 1")->fetchField();
130 $this->assertEqual($count, 0, 'All items are unscheduled (schedule flag = 0).');
131
132 // Hit cron again twice.
133 $this->runCron();
134 $this->runCron();
135
136 // The import_period setting of the feed configuration is 1800, there
137 // shouldn't be any change to the database now.
138 $equal = TRUE;
139 $rows = db_query("SELECT id, last, scheduled FROM {job_schedule} WHERE last > :time", array(':time' => $time));
140 foreach ($rows as $row) {
141 $equal = $equal && ($row->last == $schedule[$row->id]->last);
142 }
143 $this->assertTrue($equal, 'Schedule did not change.');
144
145 // Log back in and set refreshing to as often as possible.
146 $this->loginAdmin();
147 $edit = array(
148 'import_period' => 0,
149 );
150 $this->drupalPost('admin/structure/feeds/edit/syndication/settings', $edit, 'Save');
151 $this->assertText('Refresh: as often as possible');
152 $this->drupalLogout();
153
154 // Hit cron once, this should cause Feeds to reschedule all entries.
155 $this->runCron();
156 $equal = FALSE;
157 $rows = db_query("SELECT id, last, scheduled FROM {job_schedule} WHERE last > :time", array(':time' => $time));
158 foreach ($rows as $row) {
159 $equal = $equal && ($row->last == $schedule[$row->id]->last);
160 $schedule[$row->id] = $row;
161 }
162 $this->assertFalse($equal, 'Every feed schedule time changed.');
163
164 // Hit cron again, 4 times now, every item should change again.
165 for ($i = 0; $i < 4; $i++) {
166 $this->runCron();
167 }
168 $equal = FALSE;
169 $rows = db_query("SELECT id, last, scheduled FROM {job_schedule} WHERE last > :time", array(':time' => $time));
170 foreach ($rows as $row) {
171 $equal = $equal && ($row->last == $schedule[$row->id]->last);
172 }
173 $this->assertFalse($equal, 'Every feed schedule time changed.');
174
175 // There should be 200 story nodes in the database.
176 $count = db_query("SELECT COUNT(*) FROM {node} WHERE type = 'story' AND status = 1")->fetchField();
177 $this->assertEqual($count, 200, 'The total of 200 story nodes has not changed.');
178
179 // Set expire settings, check rescheduling.
180 $max_last = db_query("SELECT MAX(last) FROM {job_schedule} WHERE type = 'syndication' AND callback = 'feeds_source_import' AND period = 0")->fetchField();
181 $min_last = db_query("SELECT MIN(last) FROM {job_schedule} WHERE type = 'syndication' AND callback = 'feeds_source_import' AND period = 0")->fetchField();
182 $this->assertEqual(0, db_query("SELECT COUNT(*) FROM {job_schedule} WHERE type = 'syndication' AND callback = 'feeds_importer_expire' AND last <> 0 AND scheduled = 0")->fetchField());
183 $this->loginAdmin();
184 $this->setSettings('syndication', 'FeedsNodeProcessor', array('expire' => 86400));
185 $this->drupalLogout();
186 sleep(1);
187 $this->runCron();
188 // There should be a feeds_importer_expire callback now, and all last fields should be reset.
189 $this->assertEqual(1, db_query("SELECT COUNT(*) FROM {job_schedule} WHERE type = 'syndication' AND callback = 'feeds_importer_expire' AND last <> 0 AND scheduled = 0 AND period = 3600")->fetchField());
190 $new_max_last = db_query("SELECT MAX(last) FROM {job_schedule} WHERE type = 'syndication' AND callback = 'feeds_source_import' AND period = 0")->fetchField();
191 $new_min_last = db_query("SELECT MIN(last) FROM {job_schedule} WHERE type = 'syndication' AND callback = 'feeds_source_import' AND period = 0")->fetchField();
192 $this->assertNotEqual($new_max_last, $max_last);
193 $this->assertNotEqual($new_min_last, $min_last);
194 $this->assertEqual($new_max_last, $new_min_last);
195 $max_last = $new_max_last;
196 $min_last = $new_min_last;
197
198 // Set import settings, check rescheduling.
199 $this->loginAdmin();
200 $this->setSettings('syndication', '', array('import_period' => 3600));
201 $this->drupalLogout();
202 sleep(1);
203 $this->runCron();
204 $new_max_last = db_query("SELECT MAX(last) FROM {job_schedule} WHERE type = 'syndication' AND callback = 'feeds_source_import' AND period = 3600")->fetchField();
205 $new_min_last = db_query("SELECT MIN(last) FROM {job_schedule} WHERE type = 'syndication' AND callback = 'feeds_source_import' AND period = 3600")->fetchField();
206 $this->assertNotEqual($new_max_last, $max_last);
207 $this->assertNotEqual($new_min_last, $min_last);
208 $this->assertEqual($new_max_last, $new_min_last);
209 $this->assertEqual(0, db_query("SELECT COUNT(*) FROM {job_schedule} WHERE type = 'syndication' AND callback = 'feeds_source_import' AND period <> 3600")->fetchField());
210 $this->assertEqual(1, db_query("SELECT COUNT(*) FROM {job_schedule} WHERE type = 'syndication' AND callback = 'feeds_importer_expire' AND period = 3600 AND last = :last", array(':last' => $new_min_last))->fetchField());
211
212 // Delete source, delete importer, check schedule.
213 $this->loginAdmin();
214 $nid = array_shift($nids);
215 $this->drupalPost("node/$nid/delete", array(), t('Delete'));
216 $this->assertEqual(0, db_query("SELECT COUNT(*) FROM {job_schedule} WHERE type = 'syndication' AND callback = 'feeds_source_import' AND id = :nid", array(':nid' => $nid))->fetchField());
217 $this->assertEqual(count($nids), db_query("SELECT COUNT(*) FROM {job_schedule} WHERE type = 'syndication' AND callback = 'feeds_source_import'")->fetchField());
218 $this->assertEqual(1, db_query("SELECT COUNT(*) FROM {job_schedule} WHERE type = 'syndication' AND callback = 'feeds_importer_expire' AND id = 0")->fetchField());
219
220 $this->drupalPost('admin/structure/feeds/delete/syndication', array(), t('Delete'));
221 $this->assertEqual(0, db_query("SELECT COUNT(*) FROM {job_schedule} WHERE type = 'syndication' AND callback = 'feeds_importer_expire' AND id = 0")->fetchField());
222 $this->assertEqual(count($nids), db_query("SELECT COUNT(*) FROM {job_schedule} WHERE type = 'syndication' AND callback = 'feeds_source_import'")->fetchField());
223 }
224
225 /**
226 * Test batching on cron.
227 */
228 function testBatching() {
229 // Verify that there are 150 nodes total.
230 $nid = $this->createFeedNode('syndication', $GLOBALS['base_url'] .'/'. drupal_get_path('module', 'feeds') .'/tests/feeds/many_items.rss2');
231 $this->assertText('Created 150 Story nodes.');
232 $this->drupalPost('node/'. $nid .'/delete-items', array(), 'Delete');
233 $this->assertText('Deleted 150 nodes.');
234
235 // Set next time to 0 to simulate updates.
236 db_query("UPDATE {job_schedule} SET next = 0");
237 // Hit cron 3 times, assert correct number of story nodes.
238 for ($i = 0; $i < 3; $i++) {
239 $this->runCron(1);
240 // 50 == FEEDS_NODE_BATCH_SIZE
241 $this->assertEqual(50 * ($i + 1), db_query("SELECT COUNT(*) FROM {node} WHERE type = 'story'")->fetchField());
242 $this->assertEqual(0, db_query("SELECT period FROM {job_schedule} WHERE type = 'syndication' AND id = :nid", array(':nid' => $nid))->fetchField());
243 }
244 // Run one more time to cause the batch to reset, check period back to 1800.
245 $this->runCron();
246 $this->assertEqual(1800, db_query("SELECT period FROM {job_schedule} WHERE type = 'syndication' AND id = :nid", array(':nid' => $nid))->fetchField());
247
248 // Delete a couple of nodes, then hit cron again. They should not be replaced
249 // as the minimum update time is 30 minutes.
250 $nodes = db_query_range("SELECT nid FROM {node} WHERE type = 'story'", 0, 2);
251 foreach ($nodes as $node) {
252 $this->drupalPost("node/{$node->nid}/delete", array(), 'Delete');
253 }
254 $this->assertEqual(148, db_query("SELECT COUNT(*) FROM {node} WHERE type = 'story'")->fetchField());
255 $this->runCron();
256 $this->assertEqual(148, db_query("SELECT COUNT(*) FROM {node} WHERE type = 'story'")->fetchField());
257 }
258
259 /**
260 * Helper, log in as an admin user.
261 */
262 protected function loginAdmin() {
263 $this->drupalLogin(
264 $this->drupalCreateUser(
265 array(
266 'administer feeds', 'administer nodes',
267 )
268 )
269 );
270 }
271
272 /**
273 * Helper, run cron.
274 */
275 protected function runCron($sleep = 0) {
276 $this->drupalGet($GLOBALS['base_url'] .'/cron.php');
277 sleep($sleep);
278 }
279 }