public function testDotFileName() { $fn = new TestPaths("test/", 2, true, 3); $this->assertEquals("test/llab_2.1.3.visual.dat", $fn->visualDataFile()); $fn = new TestPaths("test", 1, false, 3); $this->assertEquals("test/llab_1.0.3.visual.dat", $fn->visualDataFile()); $fn = new TestPaths("test", 4, true, 1); $this->assertEquals("test/llab_4.1.visual.dat", $fn->visualDataFile()); $fn = new TestPaths("test", 5, false, 1); $this->assertEquals("test/llab_5.0.visual.dat", $fn->visualDataFile()); }
/** * @param $testRoot string Root directory for the test * @param $fileName string Name of the uploaded file * @return string Destination path for the uploaded file */ function getFileDestination($testRoot, $fileName) { if (!isVideoFile($fileName)) { // non-video files are simply copied to the test root return $testRoot . "/" . $fileName; } // put each run of video data in it's own directory $testPaths = TestPaths::fromUnderscoreFileName($testRoot, $fileName); // make sure video dir exists $videoDir = $testPaths->videoDir(); if (!is_dir($videoDir)) { mkdir($videoDir, 0777, true); } return getVideoFilePath($testPaths); }
public function testPostProcessRun() { $firstStepPaths = new TestPaths($this->resultDir, 1, true, 1); $secondStepPaths = new TestPaths($this->resultDir, 1, true, 2); $csiCacheFile = $firstStepPaths->csiCacheFile(CSI_CACHE_VERSION) . ".gz"; $breakdownCacheFile = $firstStepPaths->breakdownCacheFile(BREAKDOWN_CACHE_VERSION) . ".gz"; $this->assertEquals($csiCacheFile, $secondStepPaths->csiCacheFile(CSI_CACHE_VERSION) . ".gz"); $this->assertEquals($breakdownCacheFile, $secondStepPaths->breakdownCacheFile(BREAKDOWN_CACHE_VERSION) . ".gz"); $this->assertNotEquals($firstStepPaths->cacheKey(), $secondStepPaths->cacheKey()); $this->assertFileNotExists($csiCacheFile); $this->assertFileNotExists($breakdownCacheFile); $resultProcessing = new ResultProcessing($this->resultDir, $this->testId, 1, true); $error = $resultProcessing->postProcessRun(); $this->assertNull($error); $this->assertFileExists($csiCacheFile); $cache = json_decode(gz_file_get_contents($csiCacheFile), true); $this->assertNotEmpty(@$cache[$firstStepPaths->cacheKey()], "CSI Cache for step 1 is empty"); // actually, the second step doesn't contain CSI info. But it should be in the cache anyway $this->assertArrayHasKey($secondStepPaths->cacheKey(), $cache, "CSI Cache for step 2 is empty"); $this->assertFileExists($breakdownCacheFile); $cache = json_decode(gz_file_get_contents($breakdownCacheFile), true); $this->assertNotEmpty(@$cache[$firstStepPaths->cacheKey()], "Breakdown Cache for step 1 is empty"); $this->assertNotEmpty(@$cache[$secondStepPaths->cacheKey()], "Breakdown Cache for step 2 is empty"); }
/** * @param TestPaths $localPaths Paths for this run/step to get the CPU time for * @param int $endTime End time to consider (optional, will be retrieved from requests otherwise) * @return array */ function GetDevToolsCPUTimeForStep($localPaths, $endTime = 0) { if (!$endTime) { require_once __DIR__ . '/page_data.inc'; $runCompleted = IsTestRunComplete($localPaths->getRunNumber(), $testInfo); $pageData = loadPageStepData($localPaths, $runCompleted); if (isset($pageData) && is_array($pageData) && isset($pageData['fullyLoaded'])) { $endTime = $pageData['fullyLoaded']; } } $times = null; $ver = 3; $cacheFile = $localPaths->devtoolsCPUTimeCacheFile($ver); if (gz_is_file($cacheFile)) { $cache = json_decode(gz_file_get_contents($cacheFile), true); } if (isset($cache) && is_array($cache) && isset($cache[$endTime])) { $times = $cache[$endTime]; } else { $cpu = DevToolsGetCPUSlicesForStep($localPaths); if (isset($cpu) && is_array($cpu) && isset($cpu['main_thread']) && isset($cpu['slices'][$cpu['main_thread']]) && isset($cpu['slice_usecs'])) { $busy = 0; $times = array(); if (!$endTime && isset($cpu['total_usecs'])) { $endTime = $cpu['total_usecs'] / 1000; } foreach ($cpu['slices'][$cpu['main_thread']] as $name => $slices) { $last_slice = min(intval(ceil($endTime * 1000 / $cpu['slice_usecs'])), count($slices)); $times[$name] = 0; for ($i = 0; $i < $last_slice; $i++) { $times[$name] += $slices[$i] / 1000.0; } $busy += $times[$name]; $times[$name] = intval(round($times[$name])); } $times['Idle'] = max($endTime - intval(round($busy)), 0); } // Cache the result if (!isset($cache) || !is_array($cache)) { $cache = array(); } $cache[$endTime] = $times; gz_file_put_contents($cacheFile, json_encode($cache)); } return $times; }
/** * Load information about each of the tests (particularly about the video frames) * */ function LoadTestData() { global $tests; global $admin; global $supportsAuth; global $user; global $supports60fps; $count = 0; foreach ($tests as &$test) { $count++; $testInfo = null; $testPath =& $test['path']; if (!empty($test['stepResult'])) { $url = trim($test['stepResult']->getUrl()); if (strlen($url)) { $test['url'] = $url; } } if (array_key_exists('label', $test) && strlen($test['label'])) { $test['name'] = $test['label']; } else { $testInfo = GetTestInfo($test['id']); if ($testInfo && array_key_exists('label', $testInfo)) { $test['name'] = trim($testInfo['label']); } } // See if we have an overridden test label in the sqlite DB $new_label = getLabel($test['id'], $user); if (!empty($new_label)) { $test['name'] = $new_label; } if (!strlen($test['name'])) { $test['name'] = $test['url']; $test['name'] = str_replace('http://', '', $test['name']); $test['name'] = str_replace('https://', '', $test['name']); } $test['index'] = $count; $localPaths = new TestPaths("./{$testPath}", $test["run"], $test["cached"], $test["step"]); $videoPath = $localPaths->videoDir(); $test['video'] = array(); if (is_dir($videoPath)) { $test['video']['start'] = 20000; $test['video']['end'] = 0; $test['video']['frames'] = array(); $test['video']['frame_progress'] = array(); $end = null; if (is_numeric($test['end']) && $test['end'] > 0) { $end = $test['end'] / 1000.0; } if (!empty($test["stepResult"])) { $test['video']['progress'] = $test["stepResult"]->getVisualProgress($end); } if (!empty($test['video']['progress']['frames'])) { foreach ($test['video']['progress']['frames'] as $ms => $frame) { if (!$supports60fps && is_array($frame) && array_key_exists('file', $frame) && substr($frame['file'], 0, 3) == 'ms_') { $supports60fps = true; } if ((!$test['end'] || $test['end'] == -1 || $ms <= $test['end']) && (!isset($test['initial']) || !count($test['video']['frames']) || $ms >= $test['initial'])) { $path = "{$videoPath}/{$frame['file']}"; if ($ms < $test['video']['start']) { $test['video']['start'] = $ms; } if ($ms > $test['video']['end']) { $test['video']['end'] = $ms; } // figure out the dimensions of the source image if (!array_key_exists('width', $test['video']) || !$test['video']['width'] || !array_key_exists('height', $test['video']) || !$test['video']['height']) { $size = getimagesize($path); $test['video']['width'] = $size[0]; $test['video']['height'] = $size[1]; } $test['video']['frames'][$ms] = $frame['file']; $test['video']['frame_progress'][$ms] = $frame['progress']; } } if ($test['end'] == -1) { $test['end'] = $test['video']['end']; } elseif ($test['end']) { $test['video']['end'] = $test['end']; } } if (!isset($test['video']['frames'][0])) { $test['video']['frames'][0] = $test['video']['frames'][$test['video']['start']]['file']; $test['video']['frame_progress'][0] = $test['video']['frames'][$test['video']['start']]['progress']; } } } }
/** * @param TestStepResult $testStepResult The test results of this step * @param UrlGenerator $urlGenerator For video frame URL generation for this tep * @param TestPaths $nameOnlyPaths To get the name of the video dir for this step * @return array Array with the additional information about this step */ private function getAdditionalInfoArray($testStepResult, $urlGenerator, $nameOnlyPaths) { $ret = array(); $progress = $testStepResult->getVisualProgress(); if (array_key_exists('frames', $progress) && is_array($progress['frames']) && count($progress['frames'])) { $ret['videoFrames'] = array(); foreach ($progress['frames'] as $ms => $frame) { $videoFrame = array('time' => $ms); $videoFrame['image'] = $urlGenerator->getFile($frame['file'], $nameOnlyPaths->videoDir()); $videoFrame['VisuallyComplete'] = $frame['progress']; $ret['videoFrames'][] = $videoFrame; } } $requests = $testStepResult->getRequests(); $ret['domains'] = $testStepResult->getDomainBreakdown(); $ret['breakdown'] = $testStepResult->getMimeTypeBreakdown(); // add requests if (!$this->hasInfoFlag(self::WITHOUT_REQUESTS)) { $ret['requests'] = $requests; } // Check to see if we're adding the console log if (!$this->hasInfoFlag(self::WITHOUT_CONSOLE)) { $console_log = $testStepResult->getConsoleLog(); if (isset($console_log)) { $ret['consoleLog'] = $console_log; } } $statusMessages = $testStepResult->getStatusMessages(); if ($statusMessages) { $ret['status'] = $statusMessages; return $ret; } return $ret; }
/** * Build a side-by-side table with the captured frames from each test * */ function ScreenShotTable() { global $tests; global $thumbSize; global $interval; global $maxCompare; global $color; global $bgcolor; global $supports60fps; $endTime = 'visual'; if (array_key_exists('end', $_REQUEST) && strlen($_REQUEST['end'])) { $endTime = htmlspecialchars(trim($_REQUEST['end'])); } $filmstrip_end_time = 0; if (count($tests)) { // figure out how many columns there are $end = 0; foreach ($tests as &$test) { if ($test['video']['end'] > $end) { $end = $test['video']['end']; } } if (!defined('EMBED')) { echo '<br>'; } echo '<form id="createForm" name="create" method="get" action="/video/create.php">'; echo "<input type=\"hidden\" name=\"end\" value=\"{$endTime}\">"; echo '<input type="hidden" name="tests" value="' . htmlspecialchars($_REQUEST['tests']) . '">'; echo "<input type=\"hidden\" name=\"bg\" value=\"{$bgcolor}\">"; echo "<input type=\"hidden\" name=\"text\" value=\"{$color}\">"; if (isset($_REQUEST['labelHeight']) && is_numeric($_REQUEST['labelHeight'])) { echo '<input type="hidden" name="labelHeight" value="' . htmlspecialchars($_REQUEST['labelHeight']) . '">"'; } if (isset($_REQUEST['timeHeight']) && is_numeric($_REQUEST['timeHeight'])) { echo '<input type="hidden" name="timeHeight" value="' . htmlspecialchars($_REQUEST['timeHeight']) . '">"'; } echo '<table id="videoContainer"><tr>'; // build a table with the labels echo '<td id="labelContainer"><table id="videoLabels"><tr><th> </th></tr>'; foreach ($tests as &$test) { // figure out the height of this video $height = 100; if ($test['video']['width'] && $test['video']['height']) { if ($test['video']['width'] > $test['video']['height']) { $height = 22 + (int) ((double) $thumbSize / (double) $test['video']['width'] * (double) $test['video']['height']); } else { $height = 22 + $thumbSize; } } $break = ''; if (!strpos($test['name'], ' ')) { $break = ' style="word-break: break-all;"'; } echo "<tr width=10% height={$height}px ><td{$break} class=\"pagelinks\">"; // Print the index outside of the link tag echo $test['index'] . ': '; if (!defined('EMBED')) { $urlGenerator = UrlGenerator::create(FRIENDLY_URLS, "", $test['id'], $test['run'], $test['cached'], $test['step']); $href = $urlGenerator->resultPage("details"); echo "<a class=\"pagelink\" id=\"label_{$test['id']}\" href=\"{$href}\">" . WrapableString(htmlspecialchars($test['name'])) . '</a>'; } else { echo WrapableString(htmlspecialchars($test['name'])); } // Print out a link to edit the test echo '<br/>'; echo '<a href="#" class="editLabel" data-test-guid="' . $test['id'] . '" data-current-label="' . htmlentities($test['name']) . '">'; if (class_exists("SQLite3")) { echo '(Edit)'; } echo '</a>'; echo "</td></tr>\n"; } echo '</table></td>'; // the actual video frames echo '<td><div id="videoDiv"><table id="video"><thead><tr>'; $filmstrip_end_time = ceil($end / $interval) * $interval; $decimals = $interval >= 100 ? 1 : 3; $frameCount = 0; $ms = 0; while ($ms < $filmstrip_end_time) { $ms = $frameCount * $interval; echo '<th>' . number_format((double) $ms / 1000.0, $decimals) . 's</th>'; $frameCount++; } echo "</tr></thead><tbody>\n"; $firstFrame = 0; $maxThumbWidth = 0; foreach ($tests as &$test) { $aft = (int) $test['aft'] / 100; // figure out the height of the image $height = 0; $width = $thumbSize; if ($test['video']['width'] && $test['video']['height']) { if ($test['video']['width'] > $test['video']['height']) { $width = $thumbSize; $height = (int) ((double) $thumbSize / (double) $test['video']['width'] * (double) $test['video']['height']); } else { $height = $thumbSize; $width = (int) ((double) $thumbSize / (double) $test['video']['height'] * (double) $test['video']['width']); } } $maxThumbWidth = max($maxThumbWidth, $width); echo "<tr>"; $testEnd = ceil($test['video']['end'] / $interval) * $interval; $lastThumb = null; $frameCount = 0; $progress = null; $ms = 0; $localPaths = new TestPaths(GetTestPath($test['id']), $test['run'], $test['cached'], $test['step']); $urlGenerator = UrlGenerator::create(false, "", $test['id'], $test['run'], $test['cached'], $test['step']); while ($ms < $filmstrip_end_time) { $ms = $frameCount * $interval; // find the closest video frame <= the target time $frame_ms = null; foreach ($test['video']['frames'] as $frameTime => $file) { if ($frameTime <= $ms && (!isset($frame_ms) || $frameTime > $frame_ms)) { $frame_ms = $frameTime; } } $path = null; if (isset($frame_ms)) { $path = $test['video']['frames'][$frame_ms]; } if (array_key_exists('frame_progress', $test['video']) && array_key_exists($frame_ms, $test['video']['frame_progress'])) { $progress = $test['video']['frame_progress'][$frame_ms]; } if (!isset($lastThumb)) { $lastThumb = $path; } echo '<td>'; if ($ms <= $testEnd) { $imgPath = $localPaths->videoDir() . "/" . $path; echo "<a href=\"/{$imgPath}\">"; echo "<img title=\"" . htmlspecialchars($test['name']) . "\""; $class = 'thumb'; if ($lastThumb != $path) { if (!$firstFrame || $frameCount < $firstFrame) { $firstFrame = $frameCount; } $class = 'thumbChanged'; } echo " class=\"{$class}\""; echo " width=\"{$width}\""; if ($height) { echo " height=\"{$height}\""; } $imgUrl = $urlGenerator->videoFrameThumbnail($path, $thumbSize); echo " src=\"{$imgUrl}\"></a>"; if (isset($progress)) { echo "<br>{$progress}%"; } $lastThumb = $path; } $frameCount++; echo '</td>'; } echo "</tr>\n"; } echo "</tr>\n"; // end of the table echo "</tbody></table></div>\n"; // end of the container table echo "</td></tr></table>\n"; echo "<div id=\"image\">"; echo "<a id=\"export\" class=\"pagelink\" href=\"filmstrip.php?tests=" . htmlspecialchars($_REQUEST['tests']) . "&thumbSize={$thumbSize}&ival={$interval}&end={$endTime}&text={$color}&bg={$bgcolor}\">Export filmstrip as an image...</a>"; echo "</div>"; echo '<div id="bottom"><input type="checkbox" name="slow" value="1"> Slow Motion<br><br>'; echo "<input id=\"SubmitBtn\" type=\"submit\" value=\"Create Video\">"; echo '<br><br><a class="pagelink" href="javascript:ShowAdvanced()">Advanced customization options...</a>'; echo "</div></form>"; if (!defined('EMBED')) { ?> <div id="layout"> <form id="layoutForm" name="layout" method="get" action="/video/compare.php"> <?php echo "<input type=\"hidden\" name=\"tests\" value=\"" . htmlspecialchars($_REQUEST['tests']) . "\">\n"; ?> <table id="layoutTable"> <tr><th>Thumbnail Size</th><th>Thumbnail Interval</th><th>Comparison End Point</th></th></tr> <?php // fill in the thumbnail size selection echo "<tr><td>"; $checked = ''; if ($thumbSize <= 100) { $checked = ' checked=checked'; } echo "<input type=\"radio\" name=\"thumbSize\" value=\"100\"{$checked} onclick=\"this.form.submit();\"> Small<br>"; $checked = ''; if ($thumbSize <= 150 && $thumbSize > 100) { $checked = ' checked=checked'; } echo "<input type=\"radio\" name=\"thumbSize\" value=\"150\"{$checked} onclick=\"this.form.submit();\"> Medium<br>"; $checked = ''; if ($thumbSize > 150) { $checked = ' checked=checked'; } echo "<input type=\"radio\" name=\"thumbSize\" value=\"200\"{$checked} onclick=\"this.form.submit();\"> Large"; echo "</td>"; // fill in the interval selection echo "<td>"; if ($supports60fps) { $checked = ''; if ($interval < 100) { $checked = ' checked=checked'; } echo "<input type=\"radio\" name=\"ival\" value=\"16.67\"{$checked} onclick=\"this.form.submit();\"> 60 FPS<br>"; } $checked = ''; if ($supports60fps && $interval == 100 || !$supports60fps && $interval < 500) { $checked = ' checked=checked'; } echo "<input type=\"radio\" name=\"ival\" value=\"100\"{$checked} onclick=\"this.form.submit();\"> 0.1 sec<br>"; $checked = ''; if ($interval == 500) { $checked = ' checked=checked'; } echo "<input type=\"radio\" name=\"ival\" value=\"500\"{$checked} onclick=\"this.form.submit();\"> 0.5 sec<br>"; $checked = ''; if ($interval == 1000) { $checked = ' checked=checked'; } echo "<input type=\"radio\" name=\"ival\" value=\"1000\"{$checked} onclick=\"this.form.submit();\"> 1 sec<br>"; $checked = ''; if ($interval > 1000) { $checked = ' checked=checked'; } echo "<input type=\"radio\" name=\"ival\" value=\"5000\"{$checked} onclick=\"this.form.submit();\"> 5 sec<br>"; echo "</td>"; // fill in the end-point selection echo "<td>"; if (!strcasecmp($endTime, 'aft')) { $endTime = 'visual'; } $checked = ''; if (!strcasecmp($endTime, 'visual')) { $checked = ' checked=checked'; } echo "<input type=\"radio\" name=\"end\" value=\"visual\"{$checked} onclick=\"this.form.submit();\"> Visually Complete<br>"; $checked = ''; if (!strcasecmp($endTime, 'all')) { $checked = ' checked=checked'; } echo "<input type=\"radio\" name=\"end\" value=\"all\"{$checked} onclick=\"this.form.submit();\"> Last Change<br>"; $checked = ''; if (!strcasecmp($endTime, 'doc')) { $checked = ' checked=checked'; } echo "<input type=\"radio\" name=\"end\" value=\"doc\"{$checked} onclick=\"this.form.submit();\"> Document Complete<br>"; $checked = ''; if (!strcasecmp($endTime, 'full')) { $checked = ' checked=checked'; } echo "<input type=\"radio\" name=\"end\" value=\"full\"{$checked} onclick=\"this.form.submit();\"> Fully Loaded<br>"; echo "</td></tr>"; ?> </table> </form> </div> <?php // display the waterfall if there is only one test $end_seconds = $filmstrip_end_time / 1000; if (count($tests) == 1) { /* @var TestStepResult $stepResult */ $stepResult = $tests[0]["stepResult"]; $requests = $stepResult->getRequestsWithInfo(true, true)->getRequests(); echo CreateWaterfallHtml('', $requests, $tests[0]['id'], $tests[0]['run'], $tests[0]['cached'], $stepResult->getRawResults(), "&max={$end_seconds}&mime=1&state=1&cpu=1&bw=1", $tests[0]['step']); echo '<br><br>'; } else { $waterfalls = array(); foreach ($tests as &$test) { $waterfalls[] = array('id' => $test['id'], 'label' => $test['name'], 'run' => $test['run'], 'step' => $test['step'], 'cached' => $test['cached']); } $labels = ''; if (array_key_exists('hideurls', $_REQUEST) && $_REQUEST['hideurls']) { $labels = '&labels=0'; } InsertMultiWaterfall($waterfalls, "&max={$end_seconds}&mime=1&state=1&cpu=1&bw=1{$labels}"); } ?> <div id="advanced" style="display:none;"> <h3>Advanced Visual Comparison Configuration</h3> <p>There are additional customizations that can be done by modifying the <b>tests</b> parameter in the comparison URL directly.</p> <p>URL structure: ...compare.php?tests=<Test 1 ID>,<Test 2 ID>...</p> <p>The tests are displayed in the order listed and can be customized with options:</p> <table> <tr><td>Custom label</td><td>-l:<label></td><td>110606_MJ_RZEY-l:Original</td></tr> <tr><td>Specific run</td><td>-r:<run></td><td>110606_MJ_RZEY-r:3</td></tr> <tr><td>Repeat view</td><td>-c:1</td><td>110606_MJ_RZEY-c:1</td></tr> <tr><td>Specific step</td><td>-s:3</td><td>110606_MJ_RZEY-s:3</td></tr> <tr><td>Specific End Time</td><td>-e:<seconds></td><td>110606_MJ_RZEY-e:1.1</td></tr> </table> <br> <p>You can also customize the background and text color by passing HTML color values to <b>bg</b> and <b>text</b> query parameters.</p> <p>Examples:</p> <ul> <li><b>Customizing labels:</b> http://www.webpagetest.org/video/compare.php?tests=110606_MJ_RZEY-l:Original,110606_AE_RZN5-l:No+JS</li> <li><b>Compare First vs. Repeat view:</b> http://www.webpagetest.org/video/compare.php?tests=110606_MJ_RZEY, 110606_MJ_RZEY-c:1</li> <li><b>Second step of first run vs. Second step of second run:</b> http://www.webpagetest.org/video/compare.php?tests=110606_MJ_RZEY-r:1-s:2,110606_MJ_RZEY-r:2-s:2</li> <li><b>White background with black text:</b> http://www.webpagetest.org/video/compare.php?tests=110606_MJ_RZEY, 110606_MJ_RZEY-c:1&bg=ffffff&text=000000</li> </ul> <input id="advanced-ok" type=button class="simplemodal-close" value="OK"> </div> <?php } // EMBED // scroll the table to show the first thumbnail change $scrollPos = $firstFrame * ($maxThumbWidth + 6); ?> <script language="javascript"> var thumbWidth = <?php echo "{$maxThumbWidth};"; ?> var scrollPos = <?php echo "{$scrollPos};"; ?> document.getElementById("videoDiv").scrollLeft = scrollPos; </script> <?php } }
/** * Retrieves the visual progress for a run/step, based on the files passed through $localPaths * * @param TestPaths $localPaths TestPaths object for this step/run * @param bool $runCompleted If the run was completed * @param array $options Options for image histogram * @param float|int $end Optional end value * @param float|int $startOffset Optional start offset * @return array|null The visual progress as an array or null */ function GetVisualProgressForStep($localPaths, $runCompleted, $options = null, $end = null, $startOffset = null) { $frames = null; $video_directory = $localPaths->videoDir(); $cache_file = $localPaths->visualDataCacheFile(); if (!isset($startOffset)) { $startOffset = 0; } $visual_data_file = $localPaths->visualDataFile(); $histograms_file = $localPaths->histogramsFile(); if (gz_is_file($visual_data_file)) { $visual_data = json_decode(gz_file_get_contents($visual_data_file), true); // see if we are processing an externally-uploaded visual data file if (isset($visual_data['timespans']['page_load']['startOffset'])) { $startOffset += $visual_data['timespans']['page_load']['startOffset']; } } $dirty = false; $current_version = VIDEO_CODE_VERSION; if (isset($end)) { if (is_numeric($end)) { $end = (int) ($end * 1000); } else { unset($end); } } if (!isset($end) && !isset($options) && gz_is_file($cache_file)) { $frames = json_decode(gz_file_get_contents($cache_file), true); if (!array_key_exists('frames', $frames) || !array_key_exists('version', $frames)) { unset($frames); } elseif (array_key_exists('version', $frames) && $frames['version'] !== $current_version) { unset($frames); } } $base_path = substr($video_directory, 1); if ((!isset($frames) || !count($frames)) && (is_dir($video_directory) || gz_is_file($histograms_file))) { $frames = array('version' => $current_version); $frames['frames'] = array(); $dirty = true; if (is_dir($video_directory)) { $files = scandir($video_directory); $last_file = null; $first_file = null; $previous_file = null; foreach ($files as $file) { if (strpos($file, 'frame_') !== false && strpos($file, '.hist') === false) { $parts = explode('_', $file); if (count($parts) >= 2) { $time = (int) $parts[1] * 100 - $startOffset; if ($time >= 0 && (!isset($end) || $time <= $end)) { if (isset($previous_file) && !array_key_exists(0, $frames['frames']) && $time > 0) { $frames['frames'][0] = array('path' => "{$base_path}/{$previous_file}", 'file' => $previous_file); $first_file = $previous_file; } elseif (!isset($first_file)) { $first_file = $file; } $last_file = $file; $frames['frames'][$time] = array('path' => "{$base_path}/{$file}", 'file' => $file); } $previous_file = $file; } } elseif (strpos($file, 'ms_') !== false && strpos($file, '.hist') === false) { $parts = explode('_', $file); if (count($parts) >= 2) { $time = intval($parts[1]) - $startOffset; if ($time >= 0 && (!isset($end) || $time <= $end)) { if (isset($previous_file) && !array_key_exists(0, $frames['frames']) && $time > 0) { $frames['frames'][0] = array('path' => "{$base_path}/{$previous_file}", 'file' => $previous_file); $first_file = $previous_file; } elseif (!isset($first_file)) { $first_file = $file; } $last_file = $file; $frames['frames'][$time] = array('path' => "{$base_path}/{$file}", 'file' => $file); } $previous_file = $file; } } } if (count($frames['frames']) == 1) { foreach ($frames['frames'] as $time => &$frame) { $frame['progress'] = 100; $frames['complete'] = $time; } } elseif (isset($first_file) && strlen($first_file) && isset($last_file) && strlen($last_file) && count($frames['frames'])) { $histograms = null; if (gz_is_file($histograms_file)) { $histograms = json_decode(gz_file_get_contents($histograms_file), true); } $start_histogram = GetImageHistogram("{$video_directory}/{$first_file}", $options, $histograms); $final_histogram = GetImageHistogram("{$video_directory}/{$last_file}", $options, $histograms); foreach ($frames['frames'] as $time => &$frame) { $histogram = GetImageHistogram("{$video_directory}/{$frame['file']}", $options, $histograms); $frame['progress'] = CalculateFrameProgress($histogram, $start_histogram, $final_histogram, 5); if ($frame['progress'] == 100 && !array_key_exists('complete', $frames)) { $frames['complete'] = $time; } } } } elseif (gz_is_file($histograms_file)) { $raw = json_decode(gz_file_get_contents($histograms_file), true); $histograms = array(); foreach ($raw as $h) { if (isset($h['time']) && isset($h['histogram'])) { $histograms[$h['time']] = $h['histogram']; } } ksort($histograms, SORT_NUMERIC); $final_histogram = end($histograms); $start_histogram = reset($histograms); foreach ($histograms as $time => $histogram) { $frames['frames'][$time] = array(); $progress = CalculateFrameProgress($histogram, $start_histogram, $final_histogram, 5); $frames['frames'][$time]['progress'] = $progress; if ($progress == 100 && !isset($frames['complete'])) { $frames['complete'] = $time; } } } } if (isset($frames) && !array_key_exists('SpeedIndex', $frames)) { $dirty = true; $frames['SpeedIndex'] = CalculateSpeedIndex($frames); } if (isset($frames)) { $frames['visualComplete'] = 0; foreach ($frames['frames'] as $time => &$frame) { if ($frame['progress'] > 0 && !array_key_exists('startRender', $frames)) { $frames['startRender'] = $time; } if (!$frames['visualComplete'] && $frame['progress'] == 100) { $frames['visualComplete'] = $time; } // fix up the frame paths in case we have a cached version referencing a relay path if (isset($frame['path'])) { $frame['path'] = $base_path . '/' . basename($frame['path']); } } } if ($runCompleted && !isset($end) && !isset($options) && $dirty && isset($frames) && count($frames)) { gz_file_put_contents($cache_file, json_encode($frames)); } return $frames; }
} // fill in the actual thumbnails $top = $thumbTop; $thumb = null; foreach ($tests as &$test) { $left = $thumbLeft; $top += $rowMargin; $testEnd = ceil($test['video']['end'] / $interval) * $interval; $lastThumb = null; if ($thumb) { imagedestroy($thumb); unset($thumb); } $frameCount = 0; $ms = 0; $localPaths = new TestPaths(GetTestPath($test['id']), $test['run'], $test['cached'], $test['step']); $videoDir = $localPaths->videoDir(); while ($ms < $filmstrip_end_time) { $ms = $frameCount * $interval; $frameCount++; // find the closest video frame <= the target time $frame_ms = null; foreach ($test['video']['frames'] as $frameTime => $file) { if ($frameTime <= $ms && (!isset($frame_ms) || $frameTime > $frame_ms)) { $frame_ms = $frameTime; } } $path = null; if (isset($frame_ms)) { $path = $test['video']['frames'][$frame_ms]; }
require_once __DIR__ . '/include/TestInfo.php'; require_once __DIR__ . '/include/TestStepResult.php'; // not functional, but to declare what to expect from common.inc global $testPath, $run, $cached, $step, $id, $url, $test; $testInfo = TestInfo::fromFiles($testPath); $testStepResult = TestStepResult::fromFiles($testInfo, $run, $cached, $step); $is_mime = isset($_REQUEST['mime']) ? (bool) @$_REQUEST['mime'] : (bool) GetSetting('mime_waterfalls'); $is_state = (bool) @$_REQUEST['state']; $include_js = isset($_REQUEST['js']) ? (bool) @$_REQUEST['js'] : true; $use_dots = !isset($_REQUEST['dots']) || $_REQUEST['dots'] != 0; $show_labels = !isset($_REQUEST['labels']) || $_REQUEST['labels'] != 0; $rowcount = array_key_exists('rowcount', $_REQUEST) ? $_REQUEST['rowcount'] : 0; // Get all of the requests; $requests = $testStepResult->getRequests(); if ($include_js) { $localPaths = new TestPaths($testInfo->getRootDirectory(), $run, $cached, $step); AddRequestScriptTimings($requests, $localPaths->devtoolsScriptTimingFile()); } if (@$_REQUEST['type'] == 'connection') { $is_state = true; $include_js = false; $rows = GetConnectionRows($requests, $show_labels); } else { $rows = GetRequestRows($requests, $use_dots, $show_labels); } $page_events = GetPageEvents($testStepResult->getRawResults()); $bwIn = 0; if (isset($test) && array_key_exists('testinfo', $test) && array_key_exists('bwIn', $test['testinfo'])) { $bwIn = $test['testinfo']['bwIn']; } else { if (isset($test) && array_key_exists('test', $test) && array_key_exists('bwIn', $test['test'])) {
/** * @param TestStepResult $testResult Result Data */ private function printPageSpeedData($testResult) { $testRoot = $this->testInfo->getRootDirectory(); $localPaths = new TestPaths($testRoot, $testResult->getRunNumber(), $testResult->isCachedRun()); $urlPaths = new TestPaths($this->baseUrl . substr($testRoot, 1), $testResult->isCachedRun()); if ($this->fileHandler->gzFileExists($localPaths->pageSpeedFile())) { echo "<PageSpeedData>" . $urlPaths->pageSpeedFile() . "</PageSpeedData>\n"; } }