1<?php 2 3/* 4 Phoronix Test Suite 5 URLs: http://www.phoronix.com, http://www.phoronix-test-suite.com/ 6 Copyright (C) 2008 - 2021, Phoronix Media 7 Copyright (C) 2008 - 2021, Michael Larabel 8 9 This program is free software; you can redistribute it and/or modify 10 it under the terms of the GNU General Public License as published by 11 the Free Software Foundation; either version 3 of the License, or 12 (at your option) any later version. 13 14 This program is distributed in the hope that it will be useful, 15 but WITHOUT ANY WARRANTY; without even the implied warranty of 16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 GNU General Public License for more details. 18 19 You should have received a copy of the GNU General Public License 20 along with this program. If not, see <http://www.gnu.org/licenses/>. 21*/ 22 23class pts_result_file 24{ 25 protected $save_identifier = null; 26 protected $result_objects = null; 27 protected $extra_attributes = null; 28 protected $is_multi_way_inverted = false; 29 protected $file_location = false; 30 31 private $title = null; 32 private $description = null; 33 private $notes = null; 34 private $internal_tags = null; 35 private $reference_id = null; 36 private $preset_environment_variables = null; 37 public $systems = null; 38 private $is_tracker = -1; 39 private $last_modified = null; 40 private $ro_relation_map = null; 41 42 public function __construct($result_file = null, $read_only_result_objects = false, $parse_only_qualified_result_objects = false) 43 { 44 $this->save_identifier = $result_file; 45 $this->extra_attributes = array(); 46 $this->systems = array(); 47 $this->result_objects = array(); 48 $this->ro_relation_map = array(); 49 50 if($result_file == null) 51 { 52 return; 53 } 54 else if(is_file($result_file)) 55 { 56 $this->file_location = $result_file; 57 $result_file = file_get_contents($result_file); 58 } 59 else if(!isset($result_file[1024]) && defined('PTS_SAVE_RESULTS_PATH') && is_file(PTS_SAVE_RESULTS_PATH . $result_file . '/composite.xml')) 60 { 61 $this->file_location = PTS_SAVE_RESULTS_PATH . $result_file . '/composite.xml'; 62 $result_file = file_get_contents($this->file_location); 63 } 64 65 $xml = simplexml_load_string($result_file, 'SimpleXMLElement', LIBXML_COMPACT | LIBXML_PARSEHUGE); 66 if(isset($xml->Generated)) 67 { 68 $this->title = self::clean_input($xml->Generated->Title); 69 $this->description = self::clean_input($xml->Generated->Description); 70 $this->notes = self::clean_input($xml->Generated->Notes); 71 $this->internal_tags = self::clean_input($xml->Generated->InternalTags); 72 $this->reference_id = self::clean_input($xml->Generated->ReferenceID); 73 $this->preset_environment_variables = self::clean_input($xml->Generated->PreSetEnvironmentVariables); 74 $this->last_modified = $xml->Generated->LastModified; 75 } 76 77 if(isset($xml->System)) 78 { 79 foreach($xml->System as $s) 80 { 81 $this->systems[] = new pts_result_file_system(self::clean_input($s->Identifier->__toString()), self::clean_input($s->Hardware->__toString()), self::clean_input($s->Software->__toString()), json_decode(self::clean_input($s->JSON), true), self::clean_input($s->User->__toString()), self::clean_input($s->Notes->__toString()), self::clean_input($s->TimeStamp->__toString()), self::clean_input($s->ClientVersion->__toString()), $this); 82 } 83 } 84 85 if(isset($xml->Result)) 86 { 87 foreach($xml->Result as $result) 88 { 89 if($parse_only_qualified_result_objects && ($result->Identifier == null || $result->Identifier->__toString() == null)) 90 { 91 continue; 92 } 93 94 $test_profile = new pts_test_profile(($result->Identifier != null ? $result->Identifier->__toString() : null), null, !$read_only_result_objects); 95 $test_profile->set_test_title($result->Title->__toString()); 96 $test_profile->set_version($result->AppVersion->__toString()); 97 $test_profile->set_result_scale($result->Scale->__toString()); 98 $test_profile->set_result_proportion($result->Proportion->__toString()); 99 $test_profile->set_display_format($result->DisplayFormat->__toString()); 100 101 $test_result = new pts_test_result($test_profile); 102 $test_result->set_used_arguments_description($result->Description->__toString()); 103 $test_result->set_used_arguments($result->Arguments->__toString()); 104 $test_result->set_annotation((isset($result->Annotation) ? $result->Annotation->__toString() : null)); 105 $parent = (isset($result->Parent) ? $result->Parent->__toString() : null); 106 $test_result->set_parent_hash($parent); 107 108 $result_buffer = new pts_test_result_buffer(); 109 foreach($result->Data->Entry as $entry) 110 { 111 $result_buffer->add_test_result($entry->Identifier->__toString(), $entry->Value->__toString(), $entry->RawString->__toString(), (isset($entry->JSON) ? $entry->JSON->__toString() : null)); 112 } 113 $test_result->set_test_result_buffer($result_buffer); 114 $this_ch = $test_result->get_comparison_hash(true, false); 115 $this->result_objects[$this_ch] = $test_result; 116 117 if($parent) 118 { 119 if(!isset($this->ro_relation_map[$parent])) 120 { 121 $this->ro_relation_map[$parent] = array(); 122 } 123 $this->ro_relation_map[$parent][] = $this_ch; 124 } 125 } 126 } 127 128 unset($xml); 129 } 130 public function __clone() 131 { 132 foreach($this->result_objects as $i => $v) 133 { 134 $this->result_objects[$i] = clone $this->result_objects[$i]; 135 } 136 } 137 public function get_relation_map($parent = null) 138 { 139 if($parent) 140 { 141 return isset($this->ro_relation_map[$parent]) ? $this->ro_relation_map[$parent] : array(); 142 } 143 else 144 { 145 return $this->ro_relation_map; 146 } 147 } 148 public function get_file_location() 149 { 150 if($this->file_location) 151 { 152 return $this->file_location; 153 } 154 else if($this->save_identifier) 155 { 156 return PTS_SAVE_RESULTS_PATH . $this->save_identifier . '/composite.xml'; 157 } 158 } 159 public function get_result_dir() 160 { 161 $composite_xml_dir = dirname($this->get_file_location()); 162 return empty($composite_xml_dir) || !is_dir($composite_xml_dir) ? false : $composite_xml_dir . '/'; 163 } 164 public function get_system_log_dir($result_identifier = null, $dir_check = true) 165 { 166 $log_dir = dirname($this->get_file_location()); 167 if(empty($log_dir) || !is_dir($log_dir)) 168 { 169 return false; 170 } 171 172 $sdir = $log_dir . '/system-logs/'; 173 174 if($result_identifier == null) 175 { 176 return $sdir; 177 } 178 else 179 { 180 $sdir = $sdir . pts_strings::simplify_string_for_file_handling($result_identifier) . '/'; 181 return !$dir_check || is_dir($sdir) ? $sdir : false; 182 } 183 } 184 public function get_test_log_dir(&$result_object = null) 185 { 186 $log_dir = dirname($this->get_file_location()); 187 if(empty($log_dir) || !is_dir($log_dir)) 188 { 189 return false; 190 } 191 192 return $log_dir . '/test-logs/' . ($result_object != null ? $result_object->get_comparison_hash(true, false) . '/' : null); 193 } 194 public function get_test_installation_log_dir() 195 { 196 $log_dir = dirname($this->get_file_location()); 197 if(empty($log_dir) || !is_dir($log_dir)) 198 { 199 return false; 200 } 201 202 return $log_dir . '/installation-logs/'; 203 } 204 public function save() 205 { 206 if($this->get_file_location() && is_file($this->get_file_location())) 207 { 208 return file_put_contents($this->get_file_location(), $this->get_xml()); 209 } 210 } 211 public function get_last_modified() 212 { 213 return $this->last_modified; 214 } 215 public function validate() 216 { 217 $dom = new DOMDocument(); 218 $dom->loadXML($this->get_xml()); 219 return $dom->schemaValidate(pts_openbenchmarking::openbenchmarking_standards_path() . 'schemas/result-file.xsd'); 220 } 221 public function __toString() 222 { 223 return $this->get_identifier(); 224 } 225 protected static function clean_input($value) 226 { 227 return strip_tags($value); 228 /* 229 if(is_array($value)) 230 { 231 return array_map(array($this, 'clean_input'), $value); 232 } 233 else 234 { 235 return strip_tags($value); 236 } 237 */ 238 } 239 public function default_result_folder_path() 240 { 241 return PTS_SAVE_RESULTS_PATH . $this->save_identifier . '/'; 242 } 243 public function get_identifier() 244 { 245 return $this->save_identifier; 246 } 247 public function read_extra_attribute($key) 248 { 249 return isset($this->extra_attributes[$key]) ? $this->extra_attributes[$key] : false; 250 } 251 public function set_extra_attribute($key, $value) 252 { 253 $this->extra_attributes[$key] = $value; 254 } 255 public function add_system($system) 256 { 257 if(!in_array($system, $this->systems)) 258 { 259 $this->systems[] = $system; 260 } 261 } 262 public function add_system_direct($identifier, $hw = null, $sw = null, $json = null, $user = null, $notes = null, $timestamp = null, $version = null) 263 { 264 $this->systems[] = new pts_result_file_system($identifier, $hw, $sw, $json, $user, $notes, $timestamp, $version, $this); 265 } 266 public function get_systems() 267 { 268 return $this->systems; 269 } 270 public function get_system_hardware() 271 { 272 // XXX this is deprecated 273 $hw = array(); 274 foreach($this->systems as &$s) 275 { 276 $hw[] = $s->get_hardware(); 277 } 278 return $hw; 279 } 280 public function get_system_software() 281 { 282 // XXX this is deprecated 283 $sw = array(); 284 foreach($this->systems as &$s) 285 { 286 $sw[] = $s->get_software(); 287 } 288 return $sw; 289 } 290 public function get_system_identifiers() 291 { 292 // XXX this is deprecated 293 $ids = array(); 294 foreach($this->systems as &$s) 295 { 296 $ids[] = $s->get_identifier(); 297 } 298 return $ids; 299 } 300 public function is_system_identifier_in_result_file($identifier) 301 { 302 foreach($this->systems as &$s) 303 { 304 if($s->get_identifier() == $identifier) 305 { 306 return true; 307 } 308 } 309 310 return false; 311 } 312 public function system_logs_available() 313 { 314 foreach($this->systems as &$s) 315 { 316 if($s->has_log_files()) 317 { 318 return true; 319 } 320 } 321 322 return false; 323 } 324 public function get_system_count() 325 { 326 return count($this->systems); 327 } 328 public function set_title($new_title) 329 { 330 if($new_title != null) 331 { 332 $this->title = $new_title; 333 } 334 } 335 public function get_title() 336 { 337 return $this->title; 338 } 339 public function append_description($append_description) 340 { 341 if($append_description != null && strpos($this->description, $append_description) === false) 342 { 343 $this->description .= PHP_EOL . $append_description; 344 } 345 } 346 public function set_description($new_description) 347 { 348 if($new_description != null) 349 { 350 $this->description = $new_description; 351 } 352 } 353 public function get_description() 354 { 355 return $this->description; 356 } 357 public function set_notes($notes) 358 { 359 if($notes != null) 360 { 361 $this->notes = $notes; 362 } 363 } 364 public function get_notes() 365 { 366 return $this->notes; 367 } 368 public function set_internal_tags($tags) 369 { 370 if($tags != null) 371 { 372 $this->internal_tags = $tags; 373 } 374 } 375 public function get_internal_tags() 376 { 377 return $this->internal_tags; 378 } 379 public function set_reference_id($new_reference_id) 380 { 381 if($new_reference_id != null) 382 { 383 $this->reference_id = $new_reference_id; 384 } 385 } 386 public function get_reference_id() 387 { 388 return $this->reference_id; 389 } 390 public function set_preset_environment_variables($env) 391 { 392 if($env != null) 393 { 394 $this->preset_environment_variables = $env; 395 } 396 } 397 public function get_preset_environment_variables() 398 { 399 return $this->preset_environment_variables; 400 } 401 public function get_test_count() 402 { 403 return count($this->get_result_objects()); 404 } 405 public function get_qualified_test_count() 406 { 407 $q_count = 0; 408 foreach($this->get_result_objects() as $ro) 409 { 410 if($ro->test_profile->get_identifier() != null) 411 { 412 $q_count++; 413 } 414 } 415 return $q_count; 416 } 417 public function has_matching_test_and_run_identifier(&$test_result, $run_identifier_to_check) 418 { 419 $found_match = false; 420 $hash_to_check = $test_result->get_comparison_hash(); 421 422 foreach($this->get_result_objects() as $result_object) 423 { 424 if($hash_to_check == $result_object->get_comparison_hash()) 425 { 426 if(in_array($run_identifier_to_check, $result_object->test_result_buffer->get_identifiers()) && $result_object->test_result_buffer->get_result_from_identifier($run_identifier_to_check) != '') 427 { 428 $found_match = true; 429 } 430 break; 431 } 432 } 433 434 return $found_match; 435 } 436 public function get_contained_tests_hash($raw_output = true) 437 { 438 $result_object_hashes = $this->get_result_object_hashes(); 439 sort($result_object_hashes); 440 return sha1(implode(',', $result_object_hashes), $raw_output); 441 } 442 public function get_result_object_hashes() 443 { 444 $object_hashes = array(); 445 446 foreach($this->get_result_objects() as $result_object) 447 { 448 $object_hashes[] = $result_object->get_comparison_hash(); 449 } 450 451 return $object_hashes; 452 } 453 public function is_results_tracker() 454 { 455 // If there are more than five results and the only changes in the system identifier names are numeric changes, assume it's a tracker 456 // i.e. different dates or different versions of a package being tested 457 if($this->is_tracker === -1) 458 { 459 $identifiers = $this->get_system_identifiers(); 460 461 if(isset($identifiers[5])) 462 { 463 // dirty SHA1 hash check 464 $is_sha1_hash = strlen($identifiers[0]) == 40 && strpos($identifiers[0], ' ') === false; 465 $has_sha1_shorthash = false; 466 467 foreach($identifiers as $i => &$identifier) 468 { 469 $has_sha1_shorthash = ($i == 0 || $has_sha1_shorthash) && isset($identifier[7]) && pts_strings::string_only_contains(substr($identifier, -8), pts_strings::CHAR_NUMERIC | pts_strings::CHAR_LETTER) && strpos($identifier, ' ') === false; 470 $identifier = pts_strings::remove_from_string($identifier, pts_strings::CHAR_NUMERIC | pts_strings::CHAR_DASH | pts_strings::CHAR_DECIMAL); 471 } 472 473 $this->is_tracker = count(array_unique($identifiers)) <= 1 || $is_sha1_hash || $has_sha1_shorthash; 474 475 if($this->is_tracker) 476 { 477 $hw = $this->get_system_hardware(); 478 479 if(isset($hw[1]) && count($hw) == count(array_unique($hw))) 480 { 481 // it can't be a results tracker if the hardware is always different 482 $this->is_tracker = false; 483 } 484 } 485 486 if($this->is_tracker == false) 487 { 488 // See if only numbers are changing between runs 489 foreach($identifiers as $i => &$identifier) 490 { 491 if(($x = strpos($identifier, ': ')) !== false) 492 { 493 $identifier = substr($identifier, ($x + 2)); 494 } 495 if($i > 0 && pts_strings::remove_from_string($identifier, pts_strings::CHAR_NUMERIC | pts_strings::CHAR_DECIMAL) != pts_strings::remove_from_string($identifiers[($i - 1)], pts_strings::CHAR_NUMERIC | pts_strings::CHAR_DECIMAL)) 496 { 497 return false; 498 } 499 } 500 $this->is_tracker = true; 501 } 502 } 503 else 504 { 505 // Definitely not a tracker as not over 5 results 506 $this->is_tracker = false; 507 } 508 } 509 510 return $this->is_tracker; 511 } 512 public function is_multi_way_comparison($identifiers = false, $extra_attributes = null) 513 { 514 if(isset($extra_attributes['force_tracking_line_graph'])) 515 { 516 // Phoromatic result tracker 517 $is_multi_way = true; 518 $this->is_multi_way_inverted = true; 519 } 520 else 521 { 522 $hw = null; // XXX: this isn't used anymore at least for now on system hardware 523 if($identifiers == false) 524 { 525 $identifiers = $this->get_system_identifiers(); 526 } 527 $is_multi_way = count($identifiers) < 2 ? false : pts_render::multi_way_identifier_check($identifiers, $hw, $this); 528 $this->is_multi_way_inverted = $is_multi_way && $is_multi_way[1]; 529 } 530 531 return $is_multi_way; 532 } 533 public function invert_multi_way_invert() 534 { 535 $this->is_multi_way_inverted = !$this->is_multi_way_inverted; 536 } 537 public function is_multi_way_inverted() 538 { 539 return $this->is_multi_way_inverted; 540 } 541 public function get_contained_test_profiles($unique = false) 542 { 543 $test_profiles = array(); 544 545 foreach($this->get_result_objects() as $object) 546 { 547 $test_profiles[] = $object->test_profile; 548 } 549 if($unique) 550 { 551 $test_profiles = array_unique($test_profiles); 552 } 553 554 return $test_profiles; 555 } 556 public function override_result_objects($result_objects) 557 { 558 $this->result_objects = $result_objects; 559 } 560 public function get_result($ch) 561 { 562 return isset($this->result_objects[$ch]) ? $this->result_objects[$ch] : false; 563 } 564 public function remove_result_object_by_id($index_or_indexes, $delete_child_objects = true) 565 { 566 $did_remove = false; 567 foreach(pts_arrays::to_array($index_or_indexes) as $index) 568 { 569 if(isset($this->result_objects[$index])) 570 { 571 unset($this->result_objects[$index]); 572 $did_remove = true; 573 574 if($delete_child_objects) 575 { 576 foreach($this->get_relation_map($index) as $child_ro) 577 { 578 if(isset($this->result_objects[$child_ro])) 579 { 580 unset($this->result_objects[$child_ro]); 581 } 582 } 583 } 584 } 585 } 586 return $did_remove; 587 } 588 public function remove_noisy_results($noise_level_percent = 6) 589 { 590 foreach($this->result_objects as $i => &$ro) 591 { 592 if($ro->has_noisy_result($noise_level_percent)) 593 { 594 $this->remove_result_object_by_id($i); 595 } 596 } 597 } 598 public function reduce_precision() 599 { 600 foreach($this->result_objects as $i => &$ro) 601 { 602 $ro->test_result_buffer->reduce_precision(); 603 } 604 } 605 public function update_annotation_for_result_object_by_id($index, $annotation) 606 { 607 if(isset($this->result_objects[$index])) 608 { 609 $this->result_objects[$index]->set_annotation($annotation); 610 return true; 611 } 612 return false; 613 } 614 public function get_result_object_by_hash($h) 615 { 616 return isset($this->result_objects[$h]) ? $this->result_objects[$h] : false; 617 } 618 public function get_result_objects($select_indexes = -1) 619 { 620 if($select_indexes != -1 && $select_indexes !== null) 621 { 622 $objects = array(); 623 624 if($select_indexes == 'ONLY_CHANGED_RESULTS') 625 { 626 foreach($this->result_objects as &$result) 627 { 628 // Only show results where the variation was greater than or equal to 1% 629 if(abs($result->largest_result_variation(0.01)) >= 0.01) 630 { 631 $objects[] = $result; 632 } 633 } 634 } 635 else 636 { 637 foreach(pts_arrays::to_array($select_indexes) as $index) 638 { 639 if(isset($this->result_objects[$index])) 640 { 641 $objects[] = $this->result_objects[$index]; 642 } 643 } 644 } 645 646 return $objects; 647 } 648 649 $skip_objects = defined('SKIP_RESULT_OBJECTS') ? explode(',', SKIP_RESULT_OBJECTS) : false; 650 if($skip_objects) 651 { 652 $ros = $this->result_objects; 653 foreach($ros as $index => $ro) 654 { 655 foreach($skip_objects as $skip) 656 { 657 if(stripos($ro->test_profile->get_identifier(), $skip) !== false || stripos($ro->get_arguments_description(), $skip) !== false) 658 { 659 unset($ros[$index]); 660 break; 661 } 662 } 663 } 664 665 return $ros; 666 } 667 668 return $this->result_objects; 669 } 670 public function to_json() 671 { 672 $file = $this->get_xml(); 673 $file = str_replace(array("\n", "\r", "\t"), '', $file); 674 $file = trim(str_replace('"', "'", $file)); 675 $simple_xml = simplexml_load_string($file); 676 return json_encode($simple_xml); 677 } 678 public function avoid_duplicate_identifiers() 679 { 680 // avoid duplicate test identifiers 681 $identifiers = $this->get_system_identifiers(); 682 if(count($identifiers) < 2) 683 { 684 return; 685 } 686 foreach(pts_arrays::duplicates_in_array($identifiers) as $duplicate) 687 { 688 while($this->is_system_identifier_in_result_file($duplicate)) 689 { 690 $i = 0; 691 do 692 { 693 $i++; 694 $new_identifier = $duplicate . ' #' . $i; 695 } 696 while($this->is_system_identifier_in_result_file($new_identifier)); 697 $this->rename_run($duplicate, $new_identifier, false); 698 } 699 } 700 } 701 public function rename_run($from, $to, $rename_logs = true) 702 { 703 if($from == 'PREFIX') 704 { 705 foreach($this->systems as &$s) 706 { 707 $s->set_identifier($to . ': ' . $s->get_identifier()); 708 } 709 } 710 else if($from == null) 711 { 712 if(count($this->systems) == 1) 713 { 714 foreach($this->systems as &$s) 715 { 716 $s->set_identifier($to); 717 break; 718 } 719 } 720 } 721 else 722 { 723 $found = false; 724 foreach($this->systems as &$s) 725 { 726 if($s->get_identifier() == $from) 727 { 728 $found = true; 729 $s->set_identifier($to); 730 break; 731 } 732 } 733 if($found && $rename_logs && ($d = $this->get_system_log_dir($from, true))) 734 { 735 $d = dirname(dirname($d)) . '/'; 736 737 foreach(array('test-logs', 'system-logs', 'installation-logs') as $dir_name) 738 { 739 if(is_dir($d . $dir_name . '/' . $from)) 740 { 741 rename($d . $dir_name . '/' . $from, $d . $dir_name . '/' . $to); 742 } 743 } 744 } 745 } 746 747 foreach($this->result_objects as &$result) 748 { 749 $result->test_result_buffer->rename($from, $to); 750 } 751 } 752 public function reorder_runs($new_order) 753 { 754 foreach($new_order as $identifier) 755 { 756 foreach($this->systems as $i => $s) 757 { 758 if($s->get_identifier() == $identifier) 759 { 760 $c = $s; 761 unset($this->systems[$i]); 762 $this->systems[] = $c; 763 break; 764 } 765 } 766 } 767 768 foreach($this->result_objects as &$result) 769 { 770 $result->test_result_buffer->reorder($new_order); 771 } 772 } 773 public function remove_run($remove) 774 { 775 $remove = pts_arrays::to_array($remove); 776 foreach($this->systems as $i => &$s) 777 { 778 if(in_array($s->get_identifier(), $remove)) 779 { 780 unset($this->systems[$i]); 781 } 782 } 783 784 foreach($this->result_objects as &$result) 785 { 786 $result->test_result_buffer->remove($remove); 787 } 788 } 789 public function add_to_result_file(&$result_file, $only_merge_results_already_present = false) 790 { 791 foreach($result_file->get_systems() as $s) 792 { 793 if(!in_array($s, $this->systems)) 794 { 795 $this->systems[] = $s; 796 } 797 } 798 799 foreach($result_file->get_result_objects() as $result) 800 { 801 $this->add_result($result, $only_merge_results_already_present); 802 } 803 } 804 public function result_hash_exists(&$result_object) 805 { 806 $ch = $result_object->get_comparison_hash(true, false); 807 return isset($this->result_objects[$ch]) && isset($this->result_objects[$ch]->test_result_buffer); 808 } 809 public function add_result(&$result_object, $only_if_result_already_present = false) 810 { 811 if($result_object == null) 812 { 813 return false; 814 } 815 816 $ch = $result_object->get_comparison_hash(true, false); 817 if(isset($this->result_objects[$ch]) && isset($this->result_objects[$ch]->test_result_buffer)) 818 { 819 if($result_object->get_annotation() != null) 820 { 821 $this->result_objects[$ch]->append_annotation($result_object->get_annotation()); 822 } 823 foreach($result_object->test_result_buffer->get_buffer_items() as $bi) 824 { 825 if($bi->get_result_value() === null) 826 { 827 continue; 828 } 829 830 $this->result_objects[$ch]->test_result_buffer->add_buffer_item($bi); 831 } 832 } 833 else if($only_if_result_already_present == false) 834 { 835 $this->result_objects[$ch] = $result_object; 836 } 837 838 $parent = $result_object->get_parent_hash(); 839 if($parent) 840 { 841 if(!isset($this->ro_relation_map[$parent])) 842 { 843 $this->ro_relation_map[$parent] = array(); 844 } 845 $this->ro_relation_map[$parent][] = $ch; 846 } 847 848 return $ch; 849 } 850 public function add_result_return_object(&$result_object, $only_if_result_already_present = false) 851 { 852 $ch = $this->add_result($result_object, $only_if_result_already_present); 853 return isset($this->result_objects[$ch]) ? $this->result_objects[$ch] : false; 854 } 855 public function get_xml($to = null, $force_nice_formatting = false) 856 { 857 $xml_writer = new nye_XmlWriter(null, $force_nice_formatting); 858 $xml_writer->addXmlNode('PhoronixTestSuite/Generated/Title', $this->get_title()); 859 $xml_writer->addXmlNode('PhoronixTestSuite/Generated/LastModified', date('Y-m-d H:i:s', pts_client::current_time())); 860 $xml_writer->addXmlNode('PhoronixTestSuite/Generated/TestClient', pts_core::program_title(true)); 861 $xml_writer->addXmlNode('PhoronixTestSuite/Generated/Description', $this->get_description()); 862 $xml_writer->addXmlNodeWNE('PhoronixTestSuite/Generated/Notes', $this->get_notes()); 863 $xml_writer->addXmlNodeWNE('PhoronixTestSuite/Generated/InternalTags', $this->get_internal_tags()); 864 $xml_writer->addXmlNodeWNE('PhoronixTestSuite/Generated/ReferenceID', $this->get_reference_id()); 865 $xml_writer->addXmlNodeWNE('PhoronixTestSuite/Generated/PreSetEnvironmentVariables', $this->get_preset_environment_variables()); 866 867 // Write the system hardware/software information 868 foreach($this->get_systems() as $s) 869 { 870 $xml_writer->addXmlNode('PhoronixTestSuite/System/Identifier', $s->get_identifier()); 871 $xml_writer->addXmlNode('PhoronixTestSuite/System/Hardware', $s->get_hardware()); 872 $xml_writer->addXmlNode('PhoronixTestSuite/System/Software', $s->get_software()); 873 $xml_writer->addXmlNode('PhoronixTestSuite/System/User', $s->get_username()); 874 $xml_writer->addXmlNode('PhoronixTestSuite/System/TimeStamp', $s->get_timestamp()); 875 $xml_writer->addXmlNode('PhoronixTestSuite/System/TestClientVersion', $s->get_client_version()); 876 $xml_writer->addXmlNode('PhoronixTestSuite/System/Notes', $s->get_notes()); 877 878 if(!defined('USER_PTS_CORE_VERSION') || USER_PTS_CORE_VERSION > 3722) 879 { 880 // Ensure that a supported result file schema is being written... 881 // USER_PTS_CORE_VERSION is set by OpenBenchmarking.org so if the requested client is old, don't write this data to send back to their version 882 $xml_writer->addXmlNodeWNE('PhoronixTestSuite/System/JSON', ($s->get_json() ? json_encode($s->get_json()) : null)); 883 } 884 } 885 886 // Write the results 887 foreach($this->get_result_objects() as $result_object) 888 { 889 $buffer_items = $result_object->test_result_buffer->get_buffer_items(); 890 891 if(count($buffer_items) == 0) 892 { 893 continue; 894 } 895 896 $xml_writer->addXmlNode('PhoronixTestSuite/Result/Identifier', $result_object->test_profile->get_identifier()); 897 $xml_writer->addXmlNode('PhoronixTestSuite/Result/Title', $result_object->test_profile->get_title()); 898 $xml_writer->addXmlNode('PhoronixTestSuite/Result/AppVersion', $result_object->test_profile->get_app_version()); 899 $xml_writer->addXmlNode('PhoronixTestSuite/Result/Arguments', $result_object->get_arguments()); 900 $xml_writer->addXmlNode('PhoronixTestSuite/Result/Description', $result_object->get_arguments_description()); 901 $xml_writer->addXmlNode('PhoronixTestSuite/Result/Scale', $result_object->test_profile->get_result_scale()); 902 $xml_writer->addXmlNode('PhoronixTestSuite/Result/Proportion', $result_object->test_profile->get_result_proportion()); 903 $xml_writer->addXmlNode('PhoronixTestSuite/Result/DisplayFormat', $result_object->test_profile->get_display_format()); 904 $xml_writer->addXmlNodeWNE('PhoronixTestSuite/Result/Annotation', $result_object->get_annotation()); 905 $xml_writer->addXmlNodeWNE('PhoronixTestSuite/Result/Parent', $result_object->get_parent_hash()); 906 907 foreach($buffer_items as $i => &$buffer_item) 908 { 909 $xml_writer->addXmlNode('PhoronixTestSuite/Result/Data/Entry/Identifier', $buffer_item->get_result_identifier()); 910 $xml_writer->addXmlNode('PhoronixTestSuite/Result/Data/Entry/Value', $buffer_item->get_result_value()); 911 $xml_writer->addXmlNode('PhoronixTestSuite/Result/Data/Entry/RawString', $buffer_item->get_result_raw()); 912 913 if(!defined('USER_PTS_CORE_VERSION') || USER_PTS_CORE_VERSION > 3722) 914 { 915 // Ensure that a supported result file schema is being written... 916 // USER_PTS_CORE_VERSION is set by OpenBenchmarking.org so if the requested client is old, don't write this data to send back to their version 917 $xml_writer->addXmlNodeWNE('PhoronixTestSuite/Result/Data/Entry/JSON', ($buffer_item->get_result_json() ? json_encode($buffer_item->get_result_json()) : null)); 918 } 919 } 920 } 921 922 return $to == null ? $xml_writer->getXML() : $xml_writer->saveXMLFile($to); 923 } 924 public function merge($result_merges_to_combine, $pass_attributes = 0, $add_prefix = null, $merge_meta = false, $only_prefix_on_collision = false) 925 { 926 if(!is_array($result_merges_to_combine) || empty($result_merges_to_combine)) 927 { 928 return false; 929 } 930 931 foreach($result_merges_to_combine as $i => &$merge_select) 932 { 933 if(!($merge_select instanceof $merge_select)) 934 { 935 $merge_select = new pts_result_merge_select($merge_select); 936 } 937 938 if(!is_file($merge_select->get_result_file()) && !($merge_select->get_result_file() instanceof pts_result_file)) 939 { 940 if(defined('PTS_SAVE_RESULTS_PATH') && is_file(PTS_SAVE_RESULTS_PATH . $merge_select->get_result_file() . '/composite.xml')) 941 { 942 $merge_select->set_result_file(PTS_SAVE_RESULTS_PATH . $merge_select->get_result_file() . '/composite.xml'); 943 } 944 else 945 { 946 unset($result_merges_to_combine[$i]); 947 } 948 } 949 } 950 951 if(empty($result_merges_to_combine)) 952 { 953 return false; 954 } 955 956 foreach($result_merges_to_combine as &$merge_select) 957 { 958 if($merge_select->get_result_file() instanceof pts_result_file) 959 { 960 $result_file = $merge_select->get_result_file(); 961 } 962 else 963 { 964 $result_file = new pts_result_file($merge_select->get_result_file(), true); 965 } 966 967 if($add_prefix) 968 { 969 if($only_prefix_on_collision) 970 { 971 $this_identifiers = $this->get_system_identifiers(); 972 foreach($result_file->systems as &$s) 973 { 974 if(in_array($s->get_identifier(), $this_identifiers)) 975 { 976 $s->set_identifier($add_prefix . ': ' . $s->get_identifier()); 977 } 978 } 979 } 980 else 981 { 982 $result_file->rename_run('PREFIX', $add_prefix); 983 } 984 } 985 else if($merge_select->get_rename_identifier()) 986 { 987 $result_file->rename_run(null, $merge_select->get_rename_identifier()); 988 } 989 990 if($this->get_title() == null && $result_file->get_title() != null) 991 { 992 $this->set_title($result_file->get_title()); 993 } 994 995 if($this->get_description() == null && $result_file->get_description() != null) 996 { 997 $this->set_description($result_file->get_description()); 998 } 999 1000 $this->add_to_result_file($result_file); 1001 1002 if($merge_meta) 1003 { 1004 if($result_file->get_title() != null && stripos($this->get_title(), $result_file->get_title()) === false) 1005 { 1006 $this->set_title($this->get_title() . ', ' . $result_file->get_title()); 1007 } 1008 if($result_file->get_description() != null && stripos($this->get_description(), $result_file->get_description()) === false) 1009 { 1010 $this->set_description($this->get_description() . PHP_EOL . PHP_EOL . $result_file->get_title() . ': ' . $result_file->get_description()); 1011 } 1012 } 1013 unset($result_file); 1014 } 1015 } 1016 public function contains_system_hardware($search) 1017 { 1018 foreach($this->get_system_hardware() as $h) 1019 { 1020 if(stripos($h, $search) !== false) 1021 { 1022 return true; 1023 } 1024 } 1025 return false; 1026 } 1027 public function contains_system_software($search) 1028 { 1029 foreach($this->get_system_software() as $s) 1030 { 1031 if(stripos($s, $search) !== false) 1032 { 1033 return true; 1034 } 1035 } 1036 return false; 1037 } 1038 public function contains_test($search) 1039 { 1040 foreach($this->get_contained_test_profiles() as $test_profile) 1041 { 1042 if(stripos($test_profile->get_identifier(), $search) !== false || stripos($test_profile->get_title(), $search) !== false) 1043 { 1044 return true; 1045 } 1046 } 1047 return false; 1048 } 1049 public function sort_result_object_order_by_spread($asc = false) 1050 { 1051 uasort($this->result_objects, array('pts_result_file', 'result_spread_comparison')); 1052 1053 if($asc == false) 1054 { 1055 $this->result_objects = array_reverse($this->result_objects, true); 1056 } 1057 } 1058 public static function result_spread_comparison($a, $b) 1059 { 1060 return strcmp($a->get_spread(), $b->get_spread()); 1061 } 1062 public function sort_result_object_order_by_title($asc = true) 1063 { 1064 uasort($this->result_objects, array('pts_result_file', 'result_title_comparison')); 1065 1066 if($asc == false) 1067 { 1068 $this->result_objects = array_reverse($this->result_objects, true); 1069 } 1070 } 1071 public static function result_title_comparison($a, $b) 1072 { 1073 return strcmp(strtolower($a->test_profile->get_title()) . ' ' . $a->test_profile->get_app_version(), strtolower($b->test_profile->get_title()) . ' ' . $b->test_profile->get_app_version()); 1074 } 1075 public function sort_result_object_order_by_result_scale($asc = true) 1076 { 1077 uasort($this->result_objects, array('pts_result_file', 'result_scale_comparison')); 1078 1079 if($asc == false) 1080 { 1081 $this->result_objects = array_reverse($this->result_objects, true); 1082 } 1083 } 1084 public static function result_scale_comparison($a, $b) 1085 { 1086 return strcmp($a->test_profile->get_result_proportion() . ' ' . strtolower($a->test_profile->get_result_scale()) . ' ' . $a->test_profile->get_identifier(), $b->test_profile->get_result_proportion() . ' ' . strtolower($b->test_profile->get_result_scale()) . ' ' . $a->test_profile->get_identifier()); 1087 } 1088 public function get_test_run_times() 1089 { 1090 $run_times = array(); 1091 foreach($this->get_system_identifiers() as $si) 1092 { 1093 $run_times[$si] = 0; 1094 } 1095 foreach($this->result_objects as &$ro) 1096 { 1097 foreach($ro->get_run_times() as $si => $elapsed_time) 1098 { 1099 $run_times[$si] += $elapsed_time; 1100 } 1101 } 1102 1103 return $run_times; 1104 } 1105 public function sort_result_object_order_by_run_time($asc = false) 1106 { 1107 uasort($this->result_objects, array('pts_result_file', 'result_run_time_comparison')); 1108 1109 if($asc == false) 1110 { 1111 $this->result_objects = array_reverse($this->result_objects, true); 1112 } 1113 } 1114 public static function result_run_time_comparison($a, $b) 1115 { 1116 $a = $a->get_run_time_avg(); 1117 $b = $b->get_run_time_avg(); 1118 1119 if($a == $b) 1120 { 1121 return 0; 1122 } 1123 1124 return $a < $b ? -1 : 1; 1125 } 1126 public function get_install_log_for_test(&$test_profile, $read_file = false, $cleanse_file = true) 1127 { 1128 // if $read_file is false, index will be returned. if $read_file is -2, will return whether log files simply exist 1129 $files = array(); 1130 static $logs_exist_for_test; // caching helper 1131 $test_file_name_chunk = $test_profile->get_identifier_simplified() . '.log'; 1132 1133 if($read_file == -2 && isset($logs_exist_for_test[$test_file_name_chunk])) 1134 { 1135 return $logs_exist_for_test[$test_file_name_chunk]; 1136 } 1137 1138 if($this->get_test_installation_log_dir() && count($d = pts_file_io::glob($this->get_test_installation_log_dir() . '*/' . $test_file_name_chunk)) > 0) 1139 { 1140 $logs_exist_for_test[$test_file_name_chunk] = true; 1141 if($read_file == -2) 1142 { 1143 return true; 1144 } 1145 1146 foreach($d as $file) 1147 { 1148 $basename_file = basename(dirname($file)); 1149 if($read_file !== false && $basename_file == $read_file) 1150 { 1151 $file = file_get_contents($file); 1152 return $cleanse_file ? phodevi_vfs::cleanse_file($file, $basename_file) : $file; 1153 } 1154 $files[] = $basename_file; 1155 } 1156 } 1157 else if($this->get_result_dir() && is_file($this->get_result_dir() . 'installation-logs.zip') && extension_loaded('zip')) 1158 { 1159 $logs_exist_for_test[$test_file_name_chunk] = true; 1160 if($read_file == -2) 1161 { 1162 // TODO: could make this more accurate to ensure a precise match, but could become expensive 1163 return true; 1164 } 1165 1166 $zip = new ZipArchive(); 1167 $res = $zip->open($this->get_result_dir() . 'installation-logs.zip'); 1168 1169 if($res === true) 1170 { 1171 $search_for_file_name_length = strlen($test_file_name_chunk); 1172 for($i = 0; $i < $zip->numFiles; $i++) 1173 { 1174 $index = $zip->getNameIndex($i); 1175 if(isset($index[$search_for_file_name_length]) && substr($index, (0 - $search_for_file_name_length)) == $test_file_name_chunk) 1176 { 1177 $basename_file = basename(dirname($index)); 1178 1179 if($basename_file != null) 1180 { 1181 if($read_file !== false && $basename_file == $read_file) 1182 { 1183 $c = $zip->getFromName($index); 1184 $contents = $cleanse_file ? phodevi_vfs::cleanse_file($c, $basename_file) : $c; 1185 $zip->close(); 1186 return $contents; 1187 } 1188 $files[] = $basename_file; 1189 } 1190 } 1191 } 1192 $zip->close(); 1193 } 1194 } 1195 1196 $logs_exist_for_test[$test_file_name_chunk] = !empty($files); 1197 if($read_file == -2) 1198 { 1199 return false; 1200 } 1201 1202 return $read_file !== false ? false : $files; 1203 } 1204 public function get_test_run_log_for_result(&$result_object, $read_file = false, $cleanse_file = true) 1205 { 1206 // if $read_file is false, index will be returned. if $read_file is -2, will return whether log files simply exist 1207 $files = array(); 1208 static $logs_exist_for_test; // caching helper 1209 $ro_hash = $result_object->get_comparison_hash(true, false); 1210 1211 if($read_file == -2 && isset($logs_exist_for_test[$ro_hash])) 1212 { 1213 return $logs_exist_for_test[$ro_hash]; 1214 } 1215 1216 if(($test_log_dir = $this->get_test_log_dir($result_object)) && count($d = pts_file_io::glob($test_log_dir . '*.log')) > 0) 1217 { 1218 $logs_exist_for_test[$ro_hash] = true; 1219 if($read_file == -2) 1220 { 1221 return true; 1222 } 1223 1224 foreach($d as $file) 1225 { 1226 $basename_file = basename($file); 1227 if($read_file !== false && $basename_file == $read_file) 1228 { 1229 $file = file_get_contents($file); 1230 return $cleanse_file ? phodevi_vfs::cleanse_file($file, $basename_file) : $file; 1231 } 1232 $files[] = $basename_file; 1233 } 1234 } 1235 else if($this->get_result_dir() && is_file($this->get_result_dir() . 'test-logs.zip') && extension_loaded('zip')) 1236 { 1237 $logs_exist_for_test[$ro_hash] = true; 1238 if($read_file == -2) 1239 { 1240 // TODO: could make this more accurate to ensure a precise match, but could become expensive 1241 return true; 1242 } 1243 1244 $zip = new ZipArchive(); 1245 $res = $zip->open($this->get_result_dir() . 'test-logs.zip'); 1246 1247 if($res === true) 1248 { 1249 $log_path = 'test-logs/' . $ro_hash . '/'; 1250 $log_path_l = strlen($log_path); 1251 for($i = 0; $i < $zip->numFiles; $i++) 1252 { 1253 $index = $zip->getNameIndex($i); 1254 if(isset($index[$log_path_l]) && substr($index, 0, $log_path_l) == $log_path) 1255 { 1256 $basename_file = substr($index, $log_path_l); 1257 if($basename_file != null) 1258 { 1259 if($read_file !== false && $basename_file == $read_file) 1260 { 1261 $c = $zip->getFromName($index); 1262 $contents = $cleanse_file ? phodevi_vfs::cleanse_file($c, $basename_file) : $c; 1263 $zip->close(); 1264 return $contents; 1265 } 1266 $files[] = $basename_file; 1267 } 1268 } 1269 } 1270 $zip->close(); 1271 } 1272 } 1273 1274 $logs_exist_for_test[$ro_hash] = !empty($files); 1275 if($read_file == -2) 1276 { 1277 return false; 1278 } 1279 1280 return $read_file !== false ? false : $files; 1281 } 1282} 1283 1284?> 1285