~canonical-isd-hackers/drupal-launchpad/6.x-code_coverage

« back to all changes in this revision

Viewing changes to code_coverage.admin.inc

  • Committer: Szilveszter Farkas
  • Date: 2010-04-30 12:31:05 UTC
  • Revision ID: szilveszter.farkas@canonical.com-20100430123105-ikpvjo1zf5rn5ti6
Initial commit of a code_coverage snapshot that works with D6.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
<?php
 
2
// $Id: code_coverage.admin.inc,v 1.11 2008/06/29 23:46:23 cwgordon7 Exp $
 
3
 
 
4
/**
 
5
 * Returns all the currently supported reporters.
 
6
 *
 
7
 * @return
 
8
 *   All the currently supported reports, in the format 'code' => 'readable'.
 
9
 */
 
10
function code_coverage_available_reporters() {
 
11
  return array(
 
12
    'csv' => t('CSV'),
 
13
    'db' => t('Database dump'),
 
14
    'html' => t('HTML'),
 
15
    'xml' => t('XML'),
 
16
  );
 
17
}
 
18
 
 
19
/**
 
20
 * FAPI callback for the code coverage settings form.
 
21
 */
 
22
function code_coverage_settings_form() {
 
23
  drupal_add_js(drupal_get_path('module', 'code_coverage') .'/code_coverage.js');
 
24
 
 
25
  $form = array();
 
26
  $form['#prefix'] = t('Changing these settings does not require regeneration of coverage statistics.');
 
27
 
 
28
  // Generate a list of all files we /could/ generate reports for.
 
29
  $result = db_query('SELECT DISTINCT(filename) FROM {code_coverage}');
 
30
  $files = array();
 
31
  while ($filename = db_fetch_array($result)) {
 
32
    $files[$filename['filename']] = str_replace('\\', '/', substr(str_replace(getcwd() , '', $filename['filename']), 1));
 
33
  }
 
34
 
 
35
  $form['code_coverage_all'] = array(
 
36
    '#type' => 'checkbox',
 
37
    '#title' => t('Include all files'),
 
38
    '#description' => t('If checked, all files will be included in coverage reports. Otherwise, you may chose from a list of files to include.'),
 
39
    '#default_value' => variable_get('code_coverage_all', TRUE),
 
40
  );
 
41
 
 
42
  $form['code_coverage_files'] = array(
 
43
    '#type' => 'select',
 
44
    '#title' => t('Code coverage files'),
 
45
    '#description' => t('Select the files which should be included in all code coverage reports.'),
 
46
    '#multiple' => TRUE,
 
47
    '#options' => $files,
 
48
    '#default_value' => variable_get('code_coverage_files', array()),
 
49
  );
 
50
 
 
51
  $form['code_coverage_format'] = array(
 
52
    '#type' => 'select',
 
53
    '#title' => t('Available code coverage report formats.'),
 
54
    '#options' => code_coverage_available_reporters(),
 
55
    '#description' => t('Select the format in which code coverage reports should be allowed to be generated.'),
 
56
    '#default_value' => variable_get('code_coverage_format', array('html')),
 
57
    '#multiple' => TRUE,
 
58
    '#required' => TRUE,
 
59
  );
 
60
 
 
61
  $form['code_coverage_covered'] = array(
 
62
    '#type' => 'select',
 
63
    '#title' => t('Definition of coverage'),
 
64
    '#description' => t('Select the number of calls at or beyond which a line of code is considered "covered".'),
 
65
    '#options' => drupal_map_assoc(array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 15, 20, 25, 35, 50, 75, 100)),
 
66
    '#default_value' => variable_get('code_coverage_covered', 1),
 
67
    '#required' => TRUE,
 
68
  );
 
69
 
 
70
  $form['code_coverage_file_good'] = array(
 
71
    '#type' => 'select',
 
72
    '#title' => t('Point of "good" coverage'),
 
73
    '#description' => t('Select the percentage of called lines a file needs to have to be considered to have "good" coverage.'),
 
74
    '#options' => drupal_map_assoc(array(10, 25, 40, 50, 60, 65, 70, 75, 80, 85, 90, 93, 95, 96, 97, 98, 99, 100)),
 
75
    '#default_value' => variable_get('code_coverage_file_good', 80),
 
76
    '#required' => TRUE,
 
77
  );
 
78
 
 
79
  $form['code_coverage_file_ok'] = array(
 
80
    '#type' => 'select',
 
81
    '#title' => t('Point of "ok" coverage'),
 
82
    '#description' => t('Select the percentage of called lines a file needs to have to be considered to have "ok" coverage.'),
 
83
    '#options' => drupal_map_assoc(array(10, 25, 40, 50, 60, 65, 70, 75, 80, 85, 90, 93, 95, 96, 97, 98, 99, 100)),
 
84
    '#default_value' => variable_get('code_coverage_file_ok', 60),
 
85
    '#required' => TRUE,
 
86
  );
 
87
 
 
88
  $form['code_coverage_tmp_storage'] = array(
 
89
    '#type' => 'textfield',
 
90
    '#title' => t('Temporary storage'),
 
91
    '#description' => t('A place to temporarily store data, as an absolute path. You should include a trailing slash.'),
 
92
    '#default_value' => variable_get('code_coverage_tmp_storage', '/tmp/'),
 
93
    '#required' => TRUE,
 
94
  );
 
95
 
 
96
  return system_settings_form($form);
 
97
}
 
98
 
 
99
/**
 
100
 * Menu callback for the generation of the actual coverage report.
 
101
 */
 
102
function code_coverage_generate_report($reporter = NULL, $cid = NULL) {
 
103
  $reporters = variable_get('code_coverage_format', array('html'));
 
104
  if (is_null($cid) && count($reporters) == 1) {
 
105
    $cid = $reporter;
 
106
    $copy = $reporters;
 
107
    $reporter = array_shift($copy);
 
108
  }
 
109
  if (is_null($reporter)) {
 
110
    $items = array();
 
111
    $reporter_names = code_coverage_available_reporters();
 
112
    foreach ($reporters as $reporter) {
 
113
      if (isset($reporter_names[$reporter])) {
 
114
        $items[] = l($reporter_names[$reporter], 'coverage/' . $reporter);
 
115
      }
 
116
    }
 
117
    return theme('item_list', $items, t('Choose from any of the following formats.'));
 
118
  }
 
119
  $callback = 'code_coverage_generate_' . $reporter . '_report';
 
120
  if (!function_exists($callback) || !in_array($reporter, $reporters)) {
 
121
    return t('The %format format is not available.', array('%format' => $reporter));
 
122
  }
 
123
 
 
124
  if (is_null($cid)) {
 
125
    $result = db_query('SELECT DISTINCT(cid) FROM {code_coverage}');
 
126
    $items = array();
 
127
    while ($cid = db_fetch_array($result)) {
 
128
      $items[] = l(t('Code coverage report #@num', array('@num' => $cid['cid'])), 'coverage/'. $reporter . '/' . $cid['cid']);
 
129
    }
 
130
    return theme('item_list', $items, t('Choose from any of the following reports.'), 'ol');
 
131
  }
 
132
  $covered = variable_get('code_coverage_covered', 1);
 
133
 
 
134
  if (!isset($_GET['file'])) {
 
135
    $files = variable_get('code_coverage_all', TRUE) ? NULL : variable_get('code_coverage_files', array());
 
136
    if (is_array($files) && empty($files)) {
 
137
      return t('No code coverage data found.');
 
138
    }
 
139
  }
 
140
  else {
 
141
    $files = $_GET['file'];
 
142
    if (file_exists($files)) {
 
143
      $file = $files;
 
144
    }
 
145
  }
 
146
  $lines = code_coverage_load_lines($files, $cid);
 
147
  if (isset($file) && !isset($lines[$file]) || empty($lines)) {
 
148
    return drupal_not_found();
 
149
  }
 
150
  return $callback($lines, $covered, isset($file) ? $file : NULL, $cid);
 
151
}
 
152
 
 
153
/**
 
154
 * Helper function for code_coverage_generate_report() - loads all of the lines
 
155
 * from the database.
 
156
 *
 
157
 * @param $files
 
158
 *   The files to restrict the load to.
 
159
 * @return
 
160
 *   An array of lines loaded from the database.
 
161
 */
 
162
function code_coverage_load_lines($files, $cid) {
 
163
  if (is_array($files)) {
 
164
    $placeholders = db_placeholders($files, 'varchar');
 
165
    $result = db_query('SELECT * FROM {code_coverage} WHERE filename IN (' . $placeholders . ') AND cid = %d', array_merge($files, array($cid)));
 
166
  }
 
167
  elseif (!is_null($files)) {
 
168
    $result = db_query("SELECT * FROM {code_coverage} WHERE filename = '%s' AND cid = %d", $files, $cid);
 
169
  }
 
170
  else {
 
171
    $result = db_query('SELECT * FROM {code_coverage} WHERE cid = %d', $cid);
 
172
  }
 
173
  $lines = array();
 
174
  while ($line = db_fetch_array($result)) {
 
175
    $lines[$line['filename']][$line['line']] = $line['times'];
 
176
  }
 
177
  ksort($lines);
 
178
  return $lines;
 
179
}
 
180
 
 
181
/**
 
182
 * Callback for CSV reporter.
 
183
 */
 
184
function code_coverage_generate_csv_report($lines, $covered, $file, $cid) {
 
185
  $csv = '';
 
186
  foreach ($lines as $file => $info) {
 
187
    foreach ($info as $line => $count) {
 
188
      $csv .= '"' . str_replace('"', '""', $file) . '",' . $line . ',' . $count . "\n";
 
189
    }
 
190
  }
 
191
  header('Content-Type: text/plain');
 
192
  print $csv;
 
193
  drupal_page_footer();
 
194
  exit;
 
195
}
 
196
 
 
197
/**
 
198
 * Callback for database reporter.
 
199
 */
 
200
function code_coverage_generate_db_report($lines, $covered, $file, $cid) {
 
201
  $queries = array();
 
202
  $queries[] = '<?php';
 
203
  foreach ($lines as $file => $info) {
 
204
    foreach ($info as $line => $count) {
 
205
      $queries[] = 'db_query(\'INSERT INTO {code_coverage} (cid, filename, line, times) VALUES (%d, \\\'%s\\\' %d, %d)\', ' . $cid . ', ' . var_export($file, TRUE) . ', ' . $line . ', ' . $count . ');';
 
206
    }
 
207
  }
 
208
  header('Content-Type: text/plain');
 
209
  print implode("\n", $queries);
 
210
  drupal_page_footer();
 
211
  exit;
 
212
}
 
213
 
 
214
function code_coverage_render_filter_form() {
 
215
    $form = array();
 
216
    $form['sample_textfield'] = array(
 
217
      '#description' => 'Filter list of files by entering regular expression.',
 
218
      '#size' => '50',
 
219
      '#type' => 'textfield',
 
220
      '#title' => 'Filter',
 
221
      '#id' => 'file-filter',
 
222
    );
 
223
    return drupal_render_form('file-filter-form', $form);
 
224
}
 
225
 
 
226
/**
 
227
 * Callback for HTML reporter.
 
228
 */
 
229
function code_coverage_generate_html_report($lines, $covered, $file, $cid) {
 
230
  drupal_add_css(drupal_get_path('module', 'code_coverage') .'/code_coverage.css');
 
231
  if (empty($file)) {
 
232
    $good = variable_get('code_coverage_file_good', 80);
 
233
    $ok = variable_get('code_coverage_file_ok', 60);
 
234
    drupal_add_js(drupal_get_path('module', 'code_coverage') .'/code_coverage.js');
 
235
    drupal_add_js(drupal_get_path('module', 'code_coverage') .'/file_list_filter.js');
 
236
    $rows = array();
 
237
    foreach ($lines as $file => $info) {
 
238
      $covered_lines[$file] = 0;
 
239
      $uncovered_lines[$file] = 0;
 
240
      foreach ($info as $line => $count) {
 
241
        if ($count >= $covered) {
 
242
          $covered_lines[$file]++;
 
243
        }
 
244
        else {
 
245
          $uncovered_lines[$file]++;
 
246
        }
 
247
      }
 
248
      $row = array();
 
249
      $row[] = l(str_replace('\\', '/', substr(str_replace(getcwd() , '', $file), 1)), 'coverage/html/' . $cid, array('query' => array('file' => $file)));
 
250
      $row[] = $covered_lines[$file];
 
251
      $row[] = $uncovered_lines[$file];
 
252
      $row[] = sprintf('%04.2f%%', ($covered_lines[$file] * 100) / ($covered_lines[$file] + $uncovered_lines[$file]));
 
253
      $rows[] = array('data' => $row, 'class' => ((substr(end($row), 0, -1) >= $good) ? 'code-coverage-covered' : ((substr(end($row), 0, -1) >= $ok) ? 'code-coverage-moderate' : 'code-coverage-uncovered')));
 
254
    }
 
255
    $total_covered = array_sum($covered_lines);
 
256
    $total_uncovered = array_sum($uncovered_lines);
 
257
    $row = array();
 
258
    $row[] = t('Overall');
 
259
    $row[] = $total_covered;
 
260
    $row[] = $total_uncovered;
 
261
    $row[] = sprintf('%04.2f%%', ($total_covered * 100) / ($total_covered + $total_uncovered));
 
262
    $row = array('data' => $row, 'class' => 'code-coverage-overview ' . ((substr(end($row), 0, -1) > $good) ? 'code-coverage-covered' : ((substr(end($row), 0, -1) > $ok) ? 'code-coverage-moderate' : 'code-coverage-uncovered')));
 
263
    array_unshift($rows, $row);
 
264
 
 
265
    return code_coverage_render_filter_form() .
 
266
      theme('table', array(t('File !arrow', array('!arrow' => theme('image', 'misc/arrow-asc.png'))), t('Covered lines'), t('Uncovered lines'), t('Code coverage %')), $rows, array('class' => 'code-coverage-overview-report'));
 
267
  }
 
268
  else {
 
269
    drupal_set_title(t('Code coverage for @file', array('@file' => str_replace('\\', '/', substr(str_replace(getcwd() , '', $file), 1)))));
 
270
    $contents = file_get_contents($file);
 
271
    $file_lines = explode("\n", $contents);
 
272
    $lines = $lines[$file];
 
273
    $rows = array();
 
274
    foreach ($file_lines as $key => $line) {
 
275
      $row = array();
 
276
      $row[] = $key + 1;
 
277
      $row[] = isset($lines[$key + 1]) ? $lines[$key + 1] : '';
 
278
      $row[] = '<pre>' . check_plain(wordwrap($line)) . '</pre>';
 
279
      $rows[] = array('data' => $row, 'id' => 'code-coverage-' . ($key + 1), 'class' => (isset($lines[$key + 1]) ? ($lines[$key + 1] >= $covered ? 'code-coverage-covered' : 'code-coverage-uncovered') : 'code-coverage-none'));
 
280
    }
 
281
    return theme('table', array(t('Line #'), t('Times called'), t('Code')), $rows, array('class' => 'code-coverage-file-report'));
 
282
  }
 
283
}
 
284
 
 
285
/**
 
286
 * Callback for XML reporter.
 
287
 */
 
288
function code_coverage_generate_xml_report($lines, $covered, $file, $cid) {
 
289
  $xml = '<?xml version="1.0" encoding="utf-8"?>' . "\n";
 
290
  $xml .= '<coverage_report>' . "\n";
 
291
  foreach ($lines as $file => $info) {
 
292
    $xml .= '  <file>' . "\n";
 
293
    $xml .= '    <filename>' . check_plain($file) . '</filename>' . "\n";
 
294
    $xml .= '    <coverage>' . "\n";
 
295
    foreach ($info as $line => $count) {
 
296
      $xml .= '      <line>' . "\n";
 
297
      $xml .= '        <number>' . $line . '</number>' . "\n";
 
298
      $xml .= '        <count>' . $count . '</count>' . "\n";
 
299
      $xml .= '      </line>' . "\n";
 
300
    }
 
301
    $xml .= '    </coverage>' . "\n";
 
302
    $xml .= '  </file>' . "\n";
 
303
  }
 
304
  $xml .= '</coverage_report>';
 
305
  header('Content-Type: text/xml');
 
306
  print $xml;
 
307
  drupal_page_footer();
 
308
  exit;
 
309
}
 
 
b'\\ No newline at end of file'