/[drupal]/contributions/modules/project_issue_file_test/pift.cron.inc
ViewVC logotype

Contents of /contributions/modules/project_issue_file_test/pift.cron.inc

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.37 - (show annotations) (download) (as text)
Tue Nov 3 23:42:28 2009 UTC (3 weeks, 3 days ago) by boombatower
Branch: MAIN
Branch point for: DRUPAL-7--1
Changes since 1.36: +9 -2 lines
File MIME type: text/x-php
Make exception for Drupal core in relation to repository information.
1 <?php
2 // $Id: pift.cron.inc,v 1.36 2009/11/03 23:29:20 boombatower Exp $
3
4 /**
5 * @file
6 * Provide cron functions.
7 *
8 * Copyright 2008-2009 by Jimmy Berry ("boombatower", http://drupal.org/user/214218)
9 */
10
11 /**
12 * Mark files for re-testing if they still meet all the criteria and the
13 * re-test interval has passed.
14 */
15 function pift_cron_retest() {
16 if (PIFT_RETEST == -1) {
17 return;
18 }
19
20 $api_versions = array_filter(variable_get('pift_core', array()));
21 $sids = variable_get('pift_status', array());
22
23 // Only attempt query if both values are not empty.
24 if ($api_versions && $sids) {
25 $retest_time = time() - PIFT_RETEST;
26 db_query("UPDATE {pift_test}
27 SET status = %d
28 WHERE type = %d
29 AND id IN (
30 SELECT f.fid
31 FROM {files} f
32 LEFT JOIN {upload} u
33 ON f.fid = u.fid
34 LEFT JOIN {comment_upload} cu
35 ON f.fid = cu.fid
36
37 JOIN {project_issues} pi
38 ON (u.nid = pi.nid OR cu.nid = pi.nid)
39 JOIN {pift_project} p
40 ON pi.pid = p.pid
41 JOIN {project_release_nodes} r
42 ON pi.rid = r.nid
43
44 JOIN {node} n
45 ON r.nid = n.nid
46 JOIN {term_node} t
47 ON (n.vid = t.vid AND t.tid IN(" . db_placeholders($api_versions, 'int') . "))
48
49 WHERE pi.sid IN (" . db_placeholders($sids, 'int') . ")
50 )
51 AND status > %d
52 AND last_tested < %d",
53 array_merge(array(PIFT_STATUS_QUEUE, PIFT_TYPE_FILE), $api_versions, $sids,
54 array(PIFT_STATUS_SENT, $retest_time)));
55 }
56 }
57
58 /**
59 * Queue testing of releases/branches whenever a commit is made involving them.
60 *
61 * @param integer $min Min CID.
62 * @param integer $max Max CID.
63 */
64 function pift_cron_test_release($min, $max) {
65 $result = db_query('SELECT nid AS pid, branch
66 FROM {cvs_files}
67 WHERE cid >= %d
68 AND cid <= %d
69 GROUP BY nid', $min, $max);
70 $commits = array();
71 while ($branch = db_fetch_array($result)) {
72 if (!pift_project_enabled($branch['pid'])) {
73 // Ignore commits for disabled projects.
74 continue;
75 }
76
77 if (!isset($commits[$branch['pid']])) {
78 $commits[$branch['pid']] = array();
79 }
80
81 // Use associative array to ensure that a branch is only added once.
82 $identifier = $branch['branch'] ? $branch['branch'] : 'HEAD';
83 $commits[$branch['pid']][$identifier] = $identifier;
84 }
85
86 foreach ($commits as $pid => $branches) {
87 $rids = pift_cron_get_release($pid, $branches);
88 foreach ($rids as $rid) {
89 $test_id = db_result(db_query('SELECT test_id
90 FROM {pift_test}
91 WHERE type = %d
92 AND id = %d', PIFT_TYPE_RELEASE, $rid));
93 if ($test_id) {
94 pift_test_requeue($test_id);
95 }
96 else if ($test_id !== 0) {
97 pift_test_add(PIFT_TYPE_RELEASE, $rid);
98 }
99 }
100 }
101 }
102
103 /**
104 * Send the all test batches to testing server.
105 */
106 function pift_cron_queue_batch() {
107 $i = 1;
108 while (($batch = pift_cron_queue_batch_build()) && $batch['projects']) {
109 $response = xmlrpc(PIFT_SERVER . 'xmlrpc.php', 'pifr.queue', PIFT_KEY, $batch);
110 if ($response === FALSE) {
111 watchdog('pift', 'Failed to send test queue requests to server.', array(), WATCHDOG_ERROR);
112 }
113 elseif (isset($response['response'])) {
114 watchdog('pift', 'Test queue request did not succeed: @code.', array('@code' => $response['response']), WATCHDOG_ERROR);
115 }
116 elseif (isset($response['branches']) && isset($response['files'])) {
117 // Store branch test IDs using the client_identifier (branch NID).
118 foreach ($batch['branches'] as $branch) {
119 if (isset($response['branches'][$branch['client_identifier']])) {
120 pift_test_sent($response['branches'][$branch['client_identifier']], PIFT_TYPE_RELEASE, $branch['client_identifier']);
121 }
122 }
123
124 // Store file test IDs using the client_identifier (file ID).
125 foreach ($batch['files'] as $file) {
126 if (isset($response['files'][$file['client_identifier']])) {
127 pift_test_sent($response['files'][$file['client_identifier']], PIFT_TYPE_FILE, $file['client_identifier']);
128 }
129 }
130 }
131 else {
132 watchdog('pift', 'Invalid response to test queue request.', array(), WATCHDOG_ERROR);
133 }
134
135 // Once the maximum number of batches has been sent break out of the loop
136 // and save the rest for the next cron run.
137 if ($i++ == PIFT_XMLRPC_MAX_BATCHES) {
138 break;
139 }
140 }
141 }
142
143 /**
144 * Build a batch of tests to queue.
145 *
146 * @return array Complete batch array as defined by pifr.queue().
147 */
148 function pift_cron_queue_batch_build() {
149 // Initialize basic batch structure.
150 $batch = array(
151 'branches' => array(),
152 'files' => array(),
153 'projects' => array(),
154 );
155
156 // Provide arrays to keep track of the required branches and projects.
157 $branches = array();
158 $projects = array();
159
160 // Load files that need to be reviewed.
161 pift_cron_queue_batch_build_files($batch, $branches);
162
163 // Load branches that need to be reviewed.
164 pift_cron_queue_batch_build_branches($branches);
165
166 // Process the required branches and add related projects.
167 pift_cron_queue_batch_build_branches_process($batch, $branches, $projects);
168
169 // Generate project information for each required project.
170 pift_cron_queue_batch_build_projects($batch, $projects);
171
172 return $batch;
173 }
174
175 /**
176 * Load files that need to be reviewed and add them to the batch.
177 *
178 * @param array $batch Batch information.
179 * @param array $branches Branches that must be loaded.
180 */
181 function pift_cron_queue_batch_build_files(array &$batch, array &$branches) {
182 // Load all files that are marked as needs testing or have never been tested.
183 $result = db_query('SELECT f.fid, f.filepath, u.nid AS u_nid, cu.nid AS c_nid, cu.cid AS c_cid
184 FROM {pift_test} t
185 JOIN {files} f
186 ON (t.type = %d AND t.id = f.fid)
187 LEFT JOIN {upload} u
188 ON f.fid = u.fid
189 LEFT JOIN {comment_upload} cu
190 ON f.fid = cu.fid
191 WHERE t.status = %d
192 LIMIT %d', PIFT_TYPE_FILE, PIFT_STATUS_QUEUE, PIFT_XMLRPC_MAX);
193 while ($file = db_fetch_array($result)) {
194 // Load the issue related to the file, either from the comment or node.
195 $issue = node_load($file['u_nid'] ? $file['u_nid'] : $file['c_nid']);
196
197 // Generate item information.
198 $item = array(
199 'branch_identifier' => $issue->project_issue['rid'],
200 'client_identifier' => $file['fid'],
201 'file_url' => file_create_url($file['filepath']),
202 );
203
204 // Generate link to file issue and comment if relevant.
205 if ($file['c_cid']) {
206 $item['link'] = url('node/' . $issue->nid, array('absolute' => TRUE, 'fragment' => 'comment-' . $file['c_cid']));
207 }
208 else {
209 $item['link'] = url('node/' . $issue->nid, array('absolute' => TRUE));
210 }
211
212 // Add file to batch.
213 $batch['files'][] = $item;
214
215 // Store branch as needed to be included with data.
216 $branches[$issue->project_issue['rid']] = FALSE; // Do not test unless commit found.
217 }
218 }
219
220 /**
221 * Load branches that need to be reviewed.
222 *
223 * @param array $branches Branches that must be loaded.
224 */
225 function pift_cron_queue_batch_build_branches(array &$branches) {
226 // Load the branches that are marked as needs testing or have never been tested.
227 $result = db_query('SELECT t.id AS rid
228 FROM {pift_test} t
229 WHERE t.type = %d
230 AND t.status = %d
231 LIMIT %d', PIFT_TYPE_RELEASE, PIFT_STATUS_QUEUE, PIFT_XMLRPC_MAX);
232 while ($branch = db_fetch_array($result)) {
233 $branches[$branch['rid']] = TRUE;
234 }
235 }
236
237 /**
238 * Process the required branches and add related projects.
239 *
240 * @param array $batch Batch information.
241 * @param array $branches Branches that must be loaded.
242 * @param array $projects List of required projects (pids).
243 */
244 function pift_cron_queue_batch_build_branches_process(array &$batch, array &$branches, array &$projects) {
245 // Relase API version vocabulary.
246 $api_vid = _project_release_get_api_vid();
247
248 // Include branch data for all dependency branches and those that require review.
249 foreach ($branches as $rid => $test) {
250 // Load branch release node.
251 $branch = node_load($rid);
252
253 // Generate branch information.
254 $item = array(
255 'project_identifier' => $branch->project_release['pid'],
256 'client_identifier' => $branch->nid,
257 'vcs_identifier' => $branch->project_release['tag'],
258 'dependency' => '',
259 'plugin_argument' => array(),
260 'test' => $test,
261 'link' => url('node/' . $branch->nid, array('absolute' => TRUE)),
262 );
263
264 // Attempt to determine the Drupal core API version.
265 list($api_version, $api_tid) = pift_core_api($branch);
266
267 // If the API version not found then ignore this branch.
268 if (empty($api_version)) {
269 watchdog('pift', 'Project release node [@nid] must have a Drupal core API taxonomy term.', array('@nid' => $branch->nid));
270 continue;
271 }
272
273 // If the project is Drupal core then add the plugin argument, otherwise
274 // determine the compatible core branch and add it as a dependency.
275 if (PIFT_PID == $item['project_nid']) {
276 $item['plugin_argument']['core'] = $api_version;
277 }
278 else {
279 // Load the Drupal core API release (branch) compatible with this branch.
280 $api_release = node_load(pift_core_api_release($api_tid));
281 $item['dependency'] = $api_release->nid;
282 $item['plugin_argument']['modules'] = array(); // TODO Include the list of modules.
283
284 // If the core branch has not already been added then add it.
285 if (!isset($branches[$api_release])) {
286 $branches[$api_release->nid] = FALSE;
287 $projects[PIFT_PID] = PIFT_PID;
288 }
289 }
290
291 // Add item information to the batch.
292 $batch['branches'][] = $item;
293
294 // Add branch's project to list to be laoded.
295 $projects[$branch->project_release['pid']] = $branch->project_release['pid'];
296 }
297 }
298
299 /**
300 * Generate project information for each required project.
301 *
302 * @param array $batch Batch information.
303 * @param array $projects List of required projects (pids).
304 */
305 function pift_cron_queue_batch_build_projects(array &$batch, array $projects) {
306 // Load CVS repositories.
307 $repositories = cvs_get_repository_info();
308
309 // Cycle through each required project.
310 foreach ($projects as $pid) {
311 // Load project node.
312 $project = node_load($pid);
313
314 // Generate project information.
315 $item = array(
316 'client_identifier' => $project->nid,
317 'name' => $project->title,
318 'repository_type' => 'cvs',
319 'repository_url' => $repositories[$project->cvs['repository']]->root . $project->cvs['directory'],
320 'link' => url('node/' . $project->nid, array('absolute' => TRUE)),
321 );
322
323 if ($project->nid == PIFT_PID) {
324 // Drupal core directory is set to '/' and needs to be '/drupal'.
325 $item['repository_url'] .= 'drupal';
326 }
327
328 $batch['projects'][] = $item;
329 }
330 }
331
332 /**
333 * Get the release NIDs that correspond to the specified project and tags.
334 *
335 * @param integer $pid Project NID.
336 * @param array $tags List of tags.
337 * @return array Release NIDs that match.
338 */
339 function pift_cron_get_release($pid, $tags) {
340 $result = db_query('SELECT nid
341 FROM {project_release_nodes}
342 WHERE pid = %d
343 AND tag IN (' . db_placeholders($tags, 'varchar') . ')',
344 array_merge(array($pid), $tags));
345 $rids = array();
346 while ($rid = db_result($result)) {
347 $rids[] = $rid;
348 }
349 return $rids;
350 }
351
352 /**
353 * Retrieve test results since the last cron run.
354 */
355 function pift_cron_retreive_results() {
356 $last = db_result(db_query('SELECT MAX(last_tested) FROM {pift_test}'));
357 $next = pifr_server_xmlrpc_time_gmt($last ? $last : 0);
358 while ($next) {
359 $response = xmlrpc(PIFT_SERVER . 'xmlrpc.php', 'pifr.retrieve', PIFT_KEY, $next);
360 $next = FALSE;
361
362 if ($response === FALSE) {
363 watchdog('pift', 'Failed to retrieve test results from to server: @message',
364 array('@message' => xmlrpc_error_msg()), WATCHDOG_ERROR);
365 }
366 elseif (isset($response['response'])) {
367 if ($response['response'] != PIFR_RESPONSE_ACCEPTED) {
368 watchdog('pift', 'Retrieval of test results did not succeed: @code.',
369 array('@code' => $response['response']), WATCHDOG_ERROR);
370 }
371 else {
372 watchdog('pift', 'No test results to retrieve.');
373 }
374 }
375 elseif (isset($response['results'])) {
376 foreach ($response['results'] as $result) {
377 db_query("UPDATE {pift_test}
378 SET status = %d,
379 message = '%s',
380 last_tested = %d
381 WHERE test_id = %d",
382 $result['pass'] ? PIFT_STATUS_PASS : PIFT_STATUS_FAIL, $result['message'], time(), $result['test_id']);
383
384 pift_cron_check_auto_followup($result);
385 }
386
387 $next = isset($response['next']) ? $response['next'] : FALSE;
388 }
389 else {
390 watchdog('pift', 'Invalid response to retrieval of test resutls.', array(), WATCHDOG_ERROR);
391 }
392 }
393 }
394
395 if (!function_exists('pifr_server_xmlrpc_time_local')) {
396 /**
397 * Convert a GMT Unix timestamp to a local Unix timestamp.
398 *
399 * @param integer $gmt_timestamp GMT Unix timestamp.
400 * @return integer Local timestamp.
401 */
402 function pifr_server_xmlrpc_time_local($gmt_timestamp) {
403 $gmt_now = gmmktime();
404 $offset = $gmt_now - $gmt_timestamp;
405 return time() - $offset;
406 }
407
408 /**
409 * Convert a local Unix timestamp to a GMT Unix timestamp.
410 *
411 * @param integer $timestamp Local Unix timestamp.
412 * @return integer GMT Unix timestamp
413 */
414 function pifr_server_xmlrpc_time_gmt($timestamp) {
415 $now = time();
416 $offset = $now - $timestamp;
417 return gmmktime() - $offset;
418 }
419 }
420
421 /**
422 * Check if an auto followup comment is applicable and if so make one.
423 *
424 * @param array $result Test result from XML-RPC retrieval.
425 */
426 function pift_cron_check_auto_followup(array $result) {
427 if (!$result['pass']) {
428 // Test did not pass, make sure that test is for a file.
429 $test = pift_test_get($result['test_id']);
430 if ($test['fid']) {
431 // Get the current issue state and ensure that the test is the last one for
432 // the particular issue.
433 $test_id = db_result(db_query('SELECT t.test_id
434 FROM {pift_test} t
435 LEFT JOIN {upload} u
436 ON (t.type = %d AND t.id = u.fid)
437 LEFT JOIN {comment_upload} cu
438 ON (t.type = %d AND t.id = cu.fid)
439 JOIN {project_issues} i
440 ON (u.nid = i.nid OR cu.nid = i.nid)
441 WHERE u.nid = %d OR cu.nid = %d
442 ORDER BY t.id DESC
443 LIMIT 1', PIFT_TYPE_FILE, PIFT_TYPE_FILE, $test['nid'], $test['nid']));
444
445 if ($test_id == $test['test_id'] && pift_test_check_criteria_issue(node_load($test['nid']))) {
446 // Test is last one for the particular issue and still fits the criteria.
447 pift_cron_auto_followup($test['nid'], $test['cid']);
448 }
449 }
450 }
451 }
452
453 /**
454 * Create an auto followup comment on the specified node.
455 *
456 * @param integer $nid Node ID to post the followup on.
457 * @param integer $cid Comment ID, if applicable, containing the failed test.
458 */
459 function pift_cron_auto_followup($nid, $cid) {
460 project_issue_add_auto_followup(array(
461 'nid' => $nid,
462 'sid' => PIFT_FOLLOWUP_FAIL,
463 'comment' => theme('pift_auto_followup', 'fail', $nid, $cid),
464 ));
465 }

  ViewVC Help
Powered by ViewVC 1.1.2