PageRenderTime 50ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 1ms

/phoronix-test-suite/pts-core/objects/pts_render.php

#
PHP | 1049 lines | 820 code | 117 blank | 112 comment | 182 complexity | b21779215ea64bb4b106f2cf32ddcf30 MD5 | raw file
Possible License(s): GPL-3.0
  1. <?php
  2. /*
  3. Phoronix Test Suite
  4. URLs: http://www.phoronix.com, http://www.phoronix-test-suite.com/
  5. Copyright (C) 2008 - 2012, Phoronix Media
  6. Copyright (C) 2008 - 2012, Michael Larabel
  7. This program is free software; you can redistribute it and/or modify
  8. it under the terms of the GNU General Public License as published by
  9. the Free Software Foundation; either version 3 of the License, or
  10. (at your option) any later version.
  11. This program is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. GNU General Public License for more details.
  15. You should have received a copy of the GNU General Public License
  16. along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. class pts_render
  19. {
  20. public static function render_graph(&$result_object, &$result_file = null, $save_as = false, $extra_attributes = null)
  21. {
  22. $graph = self::render_graph_process($result_object, $result_file, $save_as, $extra_attributes);
  23. $graph->renderGraph();
  24. return $graph->svg_dom->output($save_as);
  25. }
  26. public static function render_graph_inline_embed(&$object, &$result_file = null, $extra_attributes = null, $nested = true)
  27. {
  28. if($object instanceof pts_test_result)
  29. {
  30. $graph = self::render_graph_process($object, $result_file, false, $extra_attributes);
  31. }
  32. else if($object instanceof pts_Graph)
  33. {
  34. $graph = $object;
  35. }
  36. else
  37. {
  38. return false;
  39. }
  40. $graph->renderGraph();
  41. $output_format = 'SVG';
  42. $graph = $graph->svg_dom->output(null, $output_format);
  43. switch($output_format)
  44. {
  45. case 'PNG':
  46. case 'JPG':
  47. if($nested)
  48. {
  49. $graph = '<img src="data:image/png;base64,' . base64_encode($graph) . '" />';
  50. }
  51. else
  52. {
  53. header('Content-Type: image/' . strtolower($output_format));
  54. }
  55. break;
  56. default:
  57. case 'SVG':
  58. if($nested)
  59. {
  60. // strip out any DOCTYPE and other crud that would be redundant, so start at SVG tag
  61. $graph = substr($graph, strpos($graph, '<svg'));
  62. }
  63. else
  64. {
  65. header('Content-type: image/svg+xml');
  66. }
  67. break;
  68. }
  69. return $graph;
  70. }
  71. public static function multi_way_compact(&$result_file, &$result_object, $extra_attributes = null)
  72. {
  73. if($result_file == null)
  74. {
  75. return;
  76. }
  77. if(!isset($extra_attributes['compact_to_scalar']) && $result_object->test_profile->get_display_format() == 'LINE_GRAPH' && $result_file->get_system_count() > 8)
  78. {
  79. // If there's too many lines being plotted on line graph, likely to look messy, so convert to scalar automatically
  80. $extra_attributes['compact_to_scalar'] = true;
  81. }
  82. if($result_file->is_multi_way_comparison() || isset($extra_attributes['compact_to_scalar']) || $result_file->is_results_tracker())
  83. {
  84. if((isset($extra_attributes['compact_to_scalar']) || (false && $result_file->is_multi_way_comparison())) && in_array($result_object->test_profile->get_display_format(), array('LINE_GRAPH', 'FILLED_LINE_GRAPH')))
  85. {
  86. // Convert multi-way line graph into horizontal box plot
  87. if(false)
  88. {
  89. $result_object->test_profile->set_display_format('HORIZONTAL_BOX_PLOT');
  90. }
  91. else
  92. {
  93. // Turn a multi-way line graph into an averaged bar graph
  94. $buffer_items = $result_object->test_result_buffer->get_buffer_items();
  95. $result_object->test_result_buffer = new pts_test_result_buffer();
  96. foreach($buffer_items as $buffer_item)
  97. {
  98. $values = pts_strings::comma_explode($buffer_item->get_result_value());
  99. $avg_value = pts_math::set_precision(array_sum($values) / count($values), 2);
  100. $result_object->test_result_buffer->add_test_result($buffer_item->get_result_identifier(), $avg_value);
  101. }
  102. $result_object->test_profile->set_display_format('BAR_GRAPH');
  103. }
  104. }
  105. if($result_object->test_profile->get_display_format() != 'PIE_CHART')
  106. {
  107. $result_table = false;
  108. pts_render::compact_result_file_test_object($result_object, $result_table, $result_file, $extra_attributes);
  109. }
  110. }
  111. else if(in_array($result_object->test_profile->get_display_format(), array('LINE_GRAPH', 'FILLED_LINE_GRAPH')))
  112. {
  113. // Check to see for line graphs if every result is an array of the same result (i.e. a flat line for every result).
  114. // If all the results are just flat lines, you might as well convert it to a bar graph
  115. $buffer_items = $result_object->test_result_buffer->get_buffer_items();
  116. $all_values_are_flat = false;
  117. $flat_values = array();
  118. foreach($buffer_items as $i => $buffer_item)
  119. {
  120. $unique_in_buffer = array_unique(explode(',', $buffer_item->get_result_value()));
  121. $all_values_are_flat = count($unique_in_buffer) == 1;
  122. if($all_values_are_flat == false)
  123. {
  124. break;
  125. }
  126. $flat_values[$i] = array_pop($unique_in_buffer);
  127. }
  128. if($all_values_are_flat)
  129. {
  130. $result_object->test_result_buffer = new pts_test_result_buffer();
  131. foreach($buffer_items as $i => $buffer_item)
  132. {
  133. $result_object->test_result_buffer->add_test_result($buffer_item->get_result_identifier(), $flat_values[$i]);
  134. }
  135. $result_object->test_profile->set_display_format('BAR_GRAPH');
  136. }
  137. }
  138. }
  139. public static function render_graph_process(&$result_object, &$result_file = null, $save_as = false, $extra_attributes = null)
  140. {
  141. if(isset($extra_attributes['sort_result_buffer']))
  142. {
  143. $result_object->test_result_buffer->sort_buffer_items();
  144. }
  145. if(isset($extra_attributes['reverse_result_buffer']))
  146. {
  147. $result_object->test_result_buffer->buffer_values_reverse();
  148. }
  149. if(isset($extra_attributes['normalize_result_buffer']))
  150. {
  151. if(isset($extra_attributes['highlight_graph_values']) && is_array($extra_attributes['highlight_graph_values']) && count($extra_attributes['highlight_graph_values']) == 1)
  152. {
  153. $normalize_against = $extra_attributes['highlight_graph_values'][0];
  154. }
  155. else
  156. {
  157. $normalize_against = false;
  158. }
  159. $result_object->normalize_buffer_values($normalize_against);
  160. }
  161. if($result_file != null)
  162. {
  163. // Cache the redundant words on identifiers so it's not re-computed on every graph
  164. static $redundant_word_cache;
  165. if(!isset($redundant_word_cache[$result_file->get_title()]))
  166. {
  167. $redundant_word_cache[$result_file->get_title()] = pts_render::evaluate_redundant_identifier_words($result_file->get_system_identifiers());
  168. }
  169. if($redundant_word_cache[$result_file->get_title()])
  170. {
  171. $result_object->test_result_buffer->auto_shorten_buffer_identifiers($redundant_word_cache[$result_file->get_title()]);
  172. }
  173. }
  174. self::multi_way_compact($result_file, $result_object, $extra_attributes);
  175. $display_format = $result_object->test_profile->get_display_format();
  176. $bar_orientation = 'HORIZONTAL'; // default to horizontal bar graph
  177. switch($display_format)
  178. {
  179. case 'LINE_GRAPH':
  180. if(false && $result_object->test_result_buffer->get_count() > 5)
  181. {
  182. // If there's too many lines close to each other, it's likely to look cluttered so turn it into horizontal range bar / box chart graph
  183. $display_format = 'HORIZONTAL_BOX_PLOT';
  184. $graph = new pts_HorizontalBoxPlotGraph($result_object, $result_file);
  185. }
  186. else
  187. {
  188. $graph = new pts_LineGraph($result_object, $result_file);
  189. }
  190. break;
  191. case 'HORIZONTAL_BOX_PLOT':
  192. $graph = new pts_HorizontalBoxPlotGraph($result_object, $result_file);
  193. break;
  194. case 'BAR_ANALYZE_GRAPH':
  195. case 'BAR_GRAPH':
  196. if($bar_orientation == 'VERTICAL')
  197. {
  198. $graph = new pts_VerticalBarGraph($result_object, $result_file);
  199. }
  200. else
  201. {
  202. $graph = new pts_HorizontalBarGraph($result_object, $result_file);
  203. }
  204. break;
  205. case 'PASS_FAIL':
  206. $graph = new pts_PassFailGraph($result_object, $result_file);
  207. break;
  208. case 'MULTI_PASS_FAIL':
  209. $graph = new pts_MultiPassFailGraph($result_object, $result_file);
  210. break;
  211. case 'TEST_COUNT_PASS':
  212. $graph = new pts_TestCountPassGraph($result_object, $result_file);
  213. break;
  214. case 'PIE_CHART':
  215. $graph = new pts_PieChart($result_object, $result_file);
  216. break;
  217. case 'IMAGE_COMPARISON':
  218. $graph = new pts_ImageComparisonGraph($result_object, $result_file);
  219. break;
  220. case 'FILLED_LINE_GRAPH':
  221. $graph = new pts_FilledLineGraph($result_object, $result_file);
  222. break;
  223. case 'SCATTER_PLOT':
  224. $graph = new pts_ScatterPlot($result_object, $result_file);
  225. break;
  226. default:
  227. if(isset($extra_attributes['graph_render_type']))
  228. {
  229. $requested_graph_type = $extra_attributes['graph_render_type'];
  230. }
  231. else if(defined('GRAPH_RENDER_TYPE'))
  232. {
  233. $requested_graph_type = GRAPH_RENDER_TYPE;
  234. }
  235. else
  236. {
  237. $requested_graph_type = null;
  238. }
  239. switch($requested_graph_type)
  240. {
  241. case 'CANDLESTICK':
  242. $graph = new pts_CandleStickGraph($result_object, $result_file);
  243. break;
  244. case 'LINE_GRAPH':
  245. $graph = new pts_LineGraph($result_object, $result_file);
  246. break;
  247. case 'FILLED_LINE_GRAPH':
  248. $graph = new pts_FilledLineGraph($result_object, $result_file);
  249. break;
  250. default:
  251. if($bar_orientation == 'VERTICAL')
  252. {
  253. $graph = new pts_VerticalBarGraph($result_object, $result_file);
  254. }
  255. else
  256. {
  257. $graph = new pts_HorizontalBarGraph($result_object, $result_file);
  258. }
  259. break;
  260. }
  261. break;
  262. }
  263. if(isset($extra_attributes['regression_marker_threshold']))
  264. {
  265. $graph->markResultRegressions($extra_attributes['regression_marker_threshold']);
  266. }
  267. if(isset($extra_attributes['set_alternate_view']))
  268. {
  269. $graph->setAlternateView($extra_attributes['set_alternate_view']);
  270. }
  271. if(isset($extra_attributes['sort_result_buffer_values']))
  272. {
  273. $result_object->test_result_buffer->buffer_values_sort();
  274. if($result_object->test_profile->get_result_proportion() == 'HIB')
  275. {
  276. $result_object->test_result_buffer->buffer_values_reverse();
  277. }
  278. }
  279. if(isset($extra_attributes['highlight_graph_values']))
  280. {
  281. $graph->highlight_values($extra_attributes['highlight_graph_values']);
  282. }
  283. else if(PTS_IS_CLIENT && pts_client::read_env('GRAPH_HIGHLIGHT') != false)
  284. {
  285. $graph->highlight_values(pts_strings::comma_explode(pts_client::read_env('GRAPH_HIGHLIGHT')));
  286. }
  287. switch($display_format)
  288. {
  289. case 'LINE_GRAPH':
  290. if(isset($extra_attributes['no_overview_text']) && $graph instanceof pts_LineGraph)
  291. {
  292. $graph->plot_overview_text = false;
  293. }
  294. case 'FILLED_LINE_GRAPH':
  295. case 'BAR_ANALYZE_GRAPH':
  296. case 'SCATTER_PLOT':
  297. //$graph->hideGraphIdentifiers();
  298. foreach($result_object->test_result_buffer->get_buffer_items() as $buffer_item)
  299. {
  300. $graph->loadGraphValues(pts_strings::comma_explode($buffer_item->get_result_value()), $buffer_item->get_result_identifier());
  301. $graph->loadGraphRawValues(pts_strings::comma_explode($buffer_item->get_result_raw()));
  302. }
  303. $scale_special = $result_object->test_profile->get_result_scale_offset();
  304. if(!empty($scale_special) && count(($ss = pts_strings::comma_explode($scale_special))) > 0)
  305. {
  306. $graph->loadGraphIdentifiers($ss);
  307. }
  308. break;
  309. case 'HORIZONTAL_BOX_PLOT':
  310. // TODO: should be able to load pts_test_result_buffer_item objects more cleanly into pts_Graph
  311. $identifiers = array();
  312. $values = array();
  313. foreach($result_object->test_result_buffer->get_buffer_items() as $buffer_item)
  314. {
  315. array_push($identifiers, $buffer_item->get_result_identifier());
  316. array_push($values, pts_strings::comma_explode($buffer_item->get_result_value()));
  317. }
  318. $graph->loadGraphIdentifiers($identifiers);
  319. $graph->loadGraphValues($values);
  320. break;
  321. default:
  322. // TODO: should be able to load pts_test_result_buffer_item objects more cleanly into pts_Graph
  323. $identifiers = array();
  324. $values = array();
  325. $raw_values = array();
  326. foreach($result_object->test_result_buffer->get_buffer_items() as $buffer_item)
  327. {
  328. array_push($identifiers, $buffer_item->get_result_identifier());
  329. array_push($values, $buffer_item->get_result_value());
  330. array_push($raw_values, $buffer_item->get_result_raw());
  331. }
  332. $graph->loadGraphIdentifiers($identifiers);
  333. $graph->loadGraphValues($values);
  334. $graph->loadGraphRawValues($raw_values);
  335. break;
  336. }
  337. self::report_test_notes_to_graph($graph, $result_object);
  338. return $graph;
  339. }
  340. public static function report_system_notes_to_table(&$result_file, &$table)
  341. {
  342. $system_json = $result_file->get_system_json();
  343. $identifiers = $result_file->get_system_identifiers();
  344. $compiler_configuration = array();
  345. $disk_options = array();
  346. foreach($system_json as $i => $json)
  347. {
  348. if(isset($json['compiler-configuration']) && $json['compiler-configuration'] != null)
  349. {
  350. $compiler_configuration[$identifiers[$i]] = $json['compiler-configuration'];
  351. }
  352. if(isset($json['disk-scheduler']) && isset($json['disk-mount-options']))
  353. {
  354. $disk_options[$identifiers[$i]] = $json['disk-scheduler'] . ' / ' . $json['disk-mount-options'];
  355. }
  356. }
  357. switch(count(array_unique($compiler_configuration)))
  358. {
  359. case 0:
  360. break;
  361. case 1:
  362. $intent = -1;
  363. if($result_file->get_system_count() > 1 && ($intent = pts_result_file_analyzer::analyze_result_file_intent($result_file, $intent, true)) && isset($intent[0]) && is_array($intent[0]) && array_shift($intent[0]) == 'Compiler')
  364. {
  365. // Even if the compiler build configuration isn't changing but a compiler is being compared, might as well report its build configuration
  366. $table->addTestNote('CC: ' . array_pop($compiler_configuration));
  367. }
  368. break;
  369. default:
  370. foreach($compiler_configuration as $identifier => $configuration)
  371. {
  372. $table->addTestNote($identifier . ' CC: ' . $configuration);
  373. }
  374. break;
  375. }
  376. switch(count(array_unique($disk_options)))
  377. {
  378. case 0:
  379. break;
  380. case 1:
  381. $table->addTestNote(array_pop($disk_options));
  382. break;
  383. default:
  384. foreach($disk_options as $identifier => $configuration)
  385. {
  386. $table->addTestNote($identifier . ': ' . $configuration);
  387. }
  388. break;
  389. }
  390. }
  391. protected static function report_test_notes_to_graph(&$graph, &$result_object)
  392. {
  393. // do some magic here to report any test notes....
  394. $json = array();
  395. $unique_compiler_data = array();
  396. foreach($result_object->test_result_buffer->get_buffer_items() as $buffer_item)
  397. {
  398. $result_json = $buffer_item->get_result_json();
  399. if(!empty($result_json))
  400. {
  401. $json[$buffer_item->get_result_identifier()] = $result_json;
  402. if(isset($result_json['compiler-options']) && !empty($result_json['compiler-options']))
  403. {
  404. pts_arrays::unique_push($unique_compiler_data, $result_json['compiler-options']);
  405. }
  406. }
  407. // report against graph with $graph->addTestNote($note, $hover_title = null);
  408. }
  409. if(empty($json))
  410. {
  411. // no JSON data being reported to look at...
  412. return false;
  413. }
  414. $compiler_options_string = '(' . strtoupper($unique_compiler_data[0]['compiler-type']) . ') ' . $unique_compiler_data[0]['compiler'] . ' options: ';
  415. switch(count($unique_compiler_data))
  416. {
  417. case 0:
  418. break;
  419. case 1:
  420. if($unique_compiler_data[0]['compiler-options'] != null)
  421. {
  422. $graph->addTestNote($compiler_options_string . $unique_compiler_data[0]['compiler-options'], $unique_compiler_data[0]['compiler-options']);
  423. }
  424. break;
  425. default:
  426. $diff = call_user_func_array('array_diff_assoc', $unique_compiler_data);
  427. if(count($diff) == 1)
  428. {
  429. $key = array_keys($diff);
  430. $key = array_pop($key);
  431. }
  432. if(isset($diff[$key]))
  433. {
  434. $unique_compiler_data = array();
  435. foreach($json as $identifier => &$data)
  436. {
  437. if(isset($data['compiler-options'][$key]))
  438. {
  439. pts_arrays::unique_push($unique_compiler_data, explode(' ', $data['compiler-options'][$key]));
  440. }
  441. }
  442. if(($c0 = count($unique_compiler_data[0])) < ($c1 = count($unique_compiler_data[1])))
  443. {
  444. for($i = 0; $i < ($c1 - $c0); $i++)
  445. {
  446. // Make the length of the first array the same length
  447. array_push($unique_compiler_data[0], null);
  448. }
  449. }
  450. $diff = call_user_func_array('array_diff', $unique_compiler_data);
  451. if(count($diff) == 1)
  452. {
  453. $diff = array_search(array_pop($diff), $unique_compiler_data[0]);
  454. if($diff !== false)
  455. {
  456. if($key == 'compiler-options')
  457. {
  458. $intersect = call_user_func_array('array_intersect', $unique_compiler_data);
  459. $graph->addTestNote($compiler_options_string . implode(' ', $intersect));
  460. }
  461. foreach($json as $identifier => &$data)
  462. {
  463. if(isset($data['compiler-options'][$key]))
  464. {
  465. $options = explode(' ', $data['compiler-options'][$key]);
  466. if(isset($options[$diff]) && stripos($identifier, $options[$diff]) === false)
  467. {
  468. $graph->addGraphIdentifierNote($identifier, $options[$diff]);
  469. }
  470. }
  471. }
  472. }
  473. }
  474. }
  475. break;
  476. }
  477. }
  478. public static function evaluate_redundant_identifier_words($identifiers)
  479. {
  480. if(count($identifiers) < 6)
  481. {
  482. // Probably not worth shortening so few result identifiers
  483. return false;
  484. }
  485. // Breakup the an identifier into an array by spaces to be used for comparison
  486. $common_segments = explode(' ', pts_arrays::last_element($identifiers));
  487. if(!isset($common_segments[2]))
  488. {
  489. // If there aren't at least three words in identifier, probably can't be shortened well
  490. return false;
  491. }
  492. foreach($identifiers as &$identifier)
  493. {
  494. $this_identifier = explode(' ', $identifier);
  495. foreach($common_segments as $pos => $word)
  496. {
  497. if(!isset($this_identifier[$pos]) || $this_identifier[$pos] != $word || !isset($word[2]))
  498. {
  499. // The word isn't the same OR the string is less than three characters (e.g. i7 or HD don't chop off from product names
  500. unset($common_segments[$pos]);
  501. }
  502. }
  503. if(count($common_segments) == 0)
  504. {
  505. // There isn't any common words to each identifier in result set
  506. return false;
  507. }
  508. }
  509. return $common_segments;
  510. }
  511. public static function generate_overview_object(&$overview_table, $overview_type)
  512. {
  513. switch($overview_type)
  514. {
  515. case 'GEOMETRIC_MEAN':
  516. $title = 'Geometric Mean';
  517. $math_call = array('pts_math', 'geometric_mean');
  518. break;
  519. case 'HARMONIC_MEAN':
  520. $title = 'Harmonic Mean';
  521. $math_call = array('pts_math', 'harmonic_mean');
  522. break;
  523. case 'AGGREGATE_SUM':
  524. $title = 'Aggregate Sum';
  525. $math_call = 'array_sum';
  526. break;
  527. default:
  528. return false;
  529. }
  530. $result_buffer = new pts_test_result_buffer();
  531. if($overview_table instanceof pts_result_file)
  532. {
  533. list($days_keys1, $days_keys, $shred) = pts_ResultFileTable::result_file_to_result_table($overview_table);
  534. foreach($shred as $system_key => &$system)
  535. {
  536. $to_show = array();
  537. foreach($system as &$days)
  538. {
  539. $days = $days->get_value();
  540. }
  541. array_push($to_show, pts_math::set_precision(call_user_func($math_call, $system), 2));
  542. $result_buffer->add_test_result($system_key, implode(',', $to_show), null);
  543. }
  544. }
  545. else
  546. {
  547. $days_keys = null;
  548. foreach($overview_table as $system_key => &$system)
  549. {
  550. if($days_keys == null)
  551. {
  552. // TODO: Rather messy and inappropriate way of getting the days keys
  553. $days_keys = array_keys($system);
  554. break;
  555. }
  556. }
  557. foreach($overview_table as $system_key => &$system)
  558. {
  559. $to_show = array();
  560. foreach($system as &$days)
  561. {
  562. array_push($to_show, call_user_func($math_call, $days));
  563. }
  564. $result_buffer->add_test_result($system_key, implode(',', $to_show), null);
  565. }
  566. }
  567. $test_profile = new pts_test_profile(null);
  568. $test_profile->set_test_title($title);
  569. $test_profile->set_result_scale($title);
  570. $test_profile->set_display_format('BAR_GRAPH');
  571. $test_result = new pts_test_result($test_profile);
  572. $test_result->set_used_arguments_description('Analytical Overview');
  573. $test_result->set_test_result_buffer($result_buffer);
  574. return $test_result;
  575. }
  576. public static function compact_result_file_test_object(&$mto, &$result_table = false, &$result_file, $extra_attributes = null)
  577. {
  578. $identifiers_inverted = $result_file && $result_file->is_multi_way_inverted();
  579. // TODO: this may need to be cleaned up, its logic is rather messy
  580. $condense_multi_way = isset($extra_attributes['condense_multi_way']);
  581. if(count($mto->test_profile->get_result_scale_offset()) > 0)
  582. {
  583. // It's already doing something
  584. return;
  585. }
  586. $scale_special = array();
  587. $days = array();
  588. $systems = array();
  589. $prev_date = null;
  590. $is_tracking = true;
  591. $sha1_short_count = 0;
  592. $buffer_count = $mto->test_result_buffer->get_count();
  593. if($identifiers_inverted)
  594. {
  595. $system_index = 0;
  596. $date_index = 1;
  597. }
  598. else
  599. {
  600. $system_index = 1;
  601. $date_index = 0;
  602. }
  603. foreach($mto->test_result_buffer->get_buffer_items() as $buffer_item)
  604. {
  605. $identifier = array_map('trim', explode(':', $buffer_item->get_result_identifier()));
  606. switch(count($identifier))
  607. {
  608. case 2:
  609. $system = $identifier[$system_index];
  610. $date = $identifier[$date_index];
  611. break;
  612. case 1:
  613. $system = 0;
  614. $date = $identifier[0];
  615. break;
  616. default:
  617. return;
  618. break;
  619. }
  620. if(!isset($systems[$system]))
  621. {
  622. $systems[$system] = 0;
  623. }
  624. if(!isset($days[$date]))
  625. {
  626. $days[$date] = null;
  627. }
  628. if($is_tracking)
  629. {
  630. // First do a dirty SHA1 hash check
  631. if(strlen($date) != 40 || strpos($date, ' ') !== false)
  632. {
  633. if(($x = strpos($date, ' + ')) !== false)
  634. {
  635. $date = substr($date, 0, $x);
  636. }
  637. // Check to see if only numeric changes are being made
  638. $sha1_short_hash_ending = isset($date[7]) && ctype_alnum(substr($date, -8));
  639. $date = str_replace('s', null, pts_strings::remove_from_string($date, pts_strings::CHAR_NUMERIC | pts_strings::CHAR_DASH | pts_strings::CHAR_DECIMAL));
  640. if($sha1_short_hash_ending)
  641. {
  642. $sha1_short_count++;
  643. }
  644. if($prev_date != null && $date != $prev_date && $sha1_short_hash_ending == false && $sha1_short_count < 2)
  645. {
  646. $is_tracking = false;
  647. }
  648. $prev_date = $date;
  649. }
  650. }
  651. }
  652. if($is_tracking == false && $sha1_short_count > 5)
  653. {
  654. // It's probably actually tracking..... based upon Stefan's Wine 1.4 example on 15 March 2012
  655. $is_tracking = true;
  656. }
  657. foreach(array_keys($days) as $day_key)
  658. {
  659. $days[$day_key] = $systems;
  660. }
  661. $raw_days = $days;
  662. foreach($mto->test_result_buffer->get_buffer_items() as $buffer_item)
  663. {
  664. $identifier = array_map('trim', explode(':', $buffer_item->get_result_identifier()));
  665. switch(count($identifier))
  666. {
  667. case 2:
  668. $system = $identifier[$system_index];
  669. $date = $identifier[$date_index];
  670. break;
  671. case 1:
  672. $system = 0;
  673. $date = $identifier[0];
  674. break;
  675. default:
  676. return;
  677. break;
  678. }
  679. $days[$date][$system] = $buffer_item->get_result_value();
  680. $raw_days[$date][$system] = $buffer_item->get_result_raw();
  681. if(!is_numeric($days[$date][$system]))
  682. {
  683. return;
  684. }
  685. }
  686. $mto->test_result_buffer = new pts_test_result_buffer();
  687. $day_keys = array_keys($days);
  688. if($condense_multi_way)
  689. {
  690. $mto->set_used_arguments_description($mto->get_arguments_description() . ' | Composite Of: ' . implode(' - ', array_keys($days)));
  691. foreach(array_keys($systems) as $system_key)
  692. {
  693. $sum = 0;
  694. $count = 0;
  695. foreach($day_keys as $day_key)
  696. {
  697. $sum += $days[$day_key][$system_key];
  698. $count++;
  699. }
  700. $mto->test_result_buffer->add_test_result($system_key, ($sum / $count));
  701. }
  702. }
  703. else
  704. {
  705. $mto->test_profile->set_result_scale($mto->test_profile->get_result_scale() . ' | ' . implode(',', array_keys($days)));
  706. if($is_tracking && $buffer_count < 16 && $result_file && pts_result_file_analyzer::analyze_result_file_intent($result_file) != false)
  707. {
  708. // It can't be a tracker if the result file is comparing hardware/software, etc
  709. $is_tracking = false;
  710. }
  711. switch($mto->test_profile->get_display_format())
  712. {
  713. //case 'HORIZONTAL_BOX_PLOT':
  714. // $mto->test_profile->set_display_format('HORIZONTAL_BOX_PLOT_MULTI');
  715. // break;
  716. case 'SCATTER_PLOT';
  717. break;
  718. default:
  719. $line_graph_type = isset($extra_attributes['filled_line_graph']) ? 'FILLED_LINE_GRAPH' : 'LINE_GRAPH';
  720. $mto->test_profile->set_display_format((count($days) < 5 || ($is_tracking == false && !isset($extra_attributes['force_line_graph_compact'])) ? 'BAR_ANALYZE_GRAPH' : $line_graph_type));
  721. break;
  722. }
  723. foreach(array_keys($systems) as $system_key)
  724. {
  725. $results = array();
  726. $raw_results = array();
  727. foreach($day_keys as $day_key)
  728. {
  729. array_push($results, $days[$day_key][$system_key]);
  730. array_push($raw_results, $raw_days[$day_key][$system_key]);
  731. }
  732. $mto->test_result_buffer->add_test_result($system_key, implode(',', $results), implode(',', $raw_results));
  733. }
  734. }
  735. if($result_table !== false)
  736. {
  737. foreach(array_keys($systems) as $system_key)
  738. {
  739. foreach($day_keys as $day_key)
  740. {
  741. if(!isset($result_table[$system_key][$day_key]))
  742. {
  743. $result_table[$system_key][$day_key] = array();
  744. }
  745. array_push($result_table[$system_key][$day_key], $days[$day_key][$system_key], $raw_days[$day_key][$system_key]);
  746. }
  747. }
  748. }
  749. }
  750. public static function multi_way_identifier_check($identifiers, &$system_hardware = null, &$result_file = null)
  751. {
  752. /*
  753. Samples To Use For Testing:
  754. 1109026-LI-AMDRADEON57
  755. */
  756. $systems = array();
  757. $targets = array();
  758. $is_multi_way = true;
  759. $is_multi_way_inverted = false;
  760. $is_ordered = true;
  761. $prev_system = null;
  762. foreach($identifiers as $identifier)
  763. {
  764. $identifier_r = explode(':', $identifier);
  765. if(count($identifier_r) != 2 || (isset($identifier[14]) && $identifier[4] == '-' && $identifier[13] == ':'))
  766. {
  767. // the later check will fix 0000-00-00 00:00 as breaking into date
  768. return false;
  769. }
  770. if(false && $is_ordered && $prev_system != null && $prev_system != $identifier_r[0] && isset($systems[$identifier_r[0]]))
  771. {
  772. // The results aren't ordered
  773. $is_ordered = false;
  774. if($result_file == null)
  775. {
  776. return false;
  777. }
  778. }
  779. $prev_system = $identifier_r[0];
  780. $systems[$identifier_r[0]] = !isset($systems[$identifier_r[0]]) ? 1 : $systems[$identifier_r[0]] + 1;
  781. $targets[$identifier_r[1]] = !isset($targets[$identifier_r[1]]) ? 1 : $targets[$identifier_r[1]] + 1;
  782. }
  783. if(false && $is_ordered == false && $is_multi_way)
  784. {
  785. // TODO: get the reordering code to work
  786. if($result_file instanceof pts_result_file)
  787. {
  788. // Reorder the result file
  789. $to_order = array();
  790. sort($identifiers);
  791. foreach($identifiers as $identifier)
  792. {
  793. array_push($to_order, new pts_result_merge_select($result_file, $identifier));
  794. }
  795. $ordered_xml = pts_merge::merge_test_results_array($to_order);
  796. $result_file = new pts_result_file($ordered_xml);
  797. $is_multi_way = true;
  798. }
  799. else
  800. {
  801. $is_multi_way = false;
  802. }
  803. }
  804. $is_multi_way_inverted = $is_multi_way && count($targets) > count($systems);
  805. /*
  806. if($is_multi_way)
  807. {
  808. if(count($systems) < 3 && count($systems) != count($targets))
  809. {
  810. $is_multi_way = false;
  811. }
  812. }
  813. */
  814. // TODO XXX: for now temporarily disable inverted multi-way check to decide how to rework it appropriately
  815. /*
  816. if($is_multi_way)
  817. {
  818. $targets_count = count($targets);
  819. $systems_count = count($systems);
  820. if($targets_count > $systems_count)
  821. {
  822. $is_multi_way_inverted = true;
  823. }
  824. else if(is_array($system_hardware))
  825. {
  826. $hardware = array_unique($system_hardware);
  827. //$software = array_unique($system_software);
  828. if($targets_count != $systems_count && count($hardware) == $systems_count)
  829. {
  830. $is_multi_way_inverted = true;
  831. }
  832. else if(count($hardware) == ($targets_count * $systems_count))
  833. {
  834. $is_multi_way_inverted = true;
  835. }
  836. }
  837. }
  838. */
  839. // TODO: figure out what else is needed to reasonably determine if the result file is a multi-way comparison
  840. return $is_multi_way ? array($is_multi_way, $is_multi_way_inverted) : false;
  841. }
  842. public static function renderer_compatibility_check($user_agent)
  843. {
  844. $user_agent .= ' ';
  845. $selected_renderer = 'SVG';
  846. // Yahoo Slurp, msnbot, and googlebot should always be served SVG so no problems there
  847. if(($p = strpos($user_agent, 'Gecko/')) !== false)
  848. {
  849. // Mozilla Gecko-based browser (Firefox, etc)
  850. $gecko_date = substr($user_agent, ($p + 6));
  851. $gecko_date = substr($gecko_date, 0, 6);
  852. // Around Firefox 3.0 era is best
  853. // Firefox 2.0 mostly works except text might not show...
  854. if($gecko_date < 200702)
  855. {
  856. $selected_renderer = 'PNG';
  857. }
  858. }
  859. else if(($p = strpos($user_agent, 'AppleWebKit/')) !== false)
  860. {
  861. // Safari, Google Chrome, Google Chromium, etc
  862. $webkit_ver = substr($user_agent, ($p + 12));
  863. $webkit_ver = substr($webkit_ver, 0, strpos($webkit_ver, ' '));
  864. // Webkit 532.2 534.6 (WebOS 3.0.2) on WebOS is buggy for SVG
  865. // iPhone OS is using 533 right now
  866. if($webkit_ver < 533 || strpos($user_agent, 'hpwOS') !== false)
  867. {
  868. $selected_renderer = 'PNG';
  869. }
  870. if(($p = strpos($user_agent, 'Android ')) !== false)
  871. {
  872. $android_ver = substr($user_agent, ($p + 8), 3);
  873. // Android browser doesn't support SVG.
  874. // Google bug report 1376 for Android - http://code.google.com/p/android/issues/detail?id=1376
  875. // Looks like it might work though in 3.0 Honeycomb
  876. if($android_ver < 3.0)
  877. {
  878. $selected_renderer = 'PNG';
  879. }
  880. }
  881. }
  882. else if(($p = strpos($user_agent, 'Opera/')) !== false)
  883. {
  884. // Opera
  885. $ver = substr($user_agent, ($p + 6));
  886. $ver = substr($ver, 0, strpos($ver, ' '));
  887. // 9.27, 9.64 displays most everything okay
  888. if($ver < 9.27)
  889. {
  890. $selected_renderer = 'PNG';
  891. }
  892. // text-alignment is still fucked as of 11.50/12.0
  893. $selected_renderer = 'PNG';
  894. }
  895. else if(($p = strpos($user_agent, 'Epiphany/')) !== false)
  896. {
  897. // Older versions of Epiphany. Newer versions should report their Gecko or WebKit appropriately
  898. $ver = substr($user_agent, ($p + 9));
  899. $ver = substr($ver, 0, 4);
  900. if($ver < 2.22)
  901. {
  902. $selected_renderer = 'PNG';
  903. }
  904. }
  905. else if(($p = strpos($user_agent, 'KHTML/')) !== false)
  906. {
  907. // KDE Konqueror as of 4.7 is still broken for SVG
  908. $selected_renderer = 'PNG';
  909. }
  910. else if(($p = strpos($user_agent, 'MSIE ')) !== false)
  911. {
  912. $ver = substr($user_agent, ($p + 5), 1);
  913. // Microsoft Internet Explorer 9.0 finally seems to do SVG right
  914. if($ver < 10 && $ver != 1)
  915. {
  916. $selected_renderer = 'PNG';
  917. }
  918. }
  919. else if(strpos($user_agent, 'facebook') !== false)
  920. {
  921. // Facebook uses this string for its Like/Share crawler, so serve it a PNG so it can use it as an image
  922. $selected_renderer = 'PNG';
  923. }
  924. return $selected_renderer;
  925. }
  926. }
  927. ?>