/** * Re-process all of the video for an existing test * * @param mixed $id */ function ReprocessVideo($id) { $testPath = './' . GetTestPath($id); if (is_dir($testPath)) { $lock = LockTest($id); if (isset($lock)) { $cacheFiles = glob("{$testPath}/*.dat.gz"); if ($cacheFiles && is_array($cacheFiles) && count($cacheFiles)) { foreach ($cacheFiles as $cacheFile) { unlink($cacheFile); } } $videoFiles = glob("{$testPath}/*.mp4"); if ($videoFiles && is_array($videoFiles) && count($videoFiles)) { foreach ($videoFiles as $video) { if (preg_match('/^.*\\/(?P<run>[0-9]+)(?P<cached>_Cached)?_video\\.mp4$/i', $video, $matches)) { $run = $matches['run']; $cached = array_key_exists('cached', $matches) ? 1 : 0; $videoDir = "{$testPath}/video_{$run}"; if ($cached) { $videoDir .= '_cached'; } delTree($videoDir, false); ProcessAVIVideo($id, $testPath, $run, $cached); } } } UnlockTest($lock); } } }
/** * Check the given log file for all tests that match * * @param mixed $logFile * @param mixed $match */ function CheckLog($logFile, $match) { global $count; echo "\r({$count}): Checking {$logFile}"; $file = file_get_contents($logFile); if (stripos($file, $match) !== false) { $lines = explode("\n", $file); $file = ''; foreach ($lines as $line) { if (stripos($line, $match) !== false) { $parseLine = str_replace("\t", "\t ", $line); $parts = explode("\t", $parseLine); $testId = trim($parts[4]); $testPath = './' . GetTestPath($testId); if (strlen($testPath)) { delTree($testPath); usleep(100000); // give the system a chance to breathe $count++; echo "\r({$count}): Checking {$logFile}"; } } else { $file .= $line . "\n"; } } // rewrite the trimmed file file_put_contents($logFile, $file); } else { unset($file); } }
function GetLastFrameHistogram($test, $run) { $histogram = null; $videoPath = './' . GetTestPath($test) . "/video_{$run}"; $files = glob("{$videoPath}/*.jpg"); if ($files) { rsort($files); $lastFrame = $files[0]; $histogram = GetImageHistogram($lastFrame); } return $histogram; }
function GetLastFrameHistogram($test, $run) { $histogram = null; $testPath = GetTestPath($test); $videoPath = "./{$testPath}/video_{$run}"; $files = glob("{$videoPath}/*.jpg"); if ($files) { rsort($files); $lastFrame = $files[0]; if (gz_is_file("{$testPath}/{$run}.0.histograms.json")) { $histograms = json_decode(gz_file_get_contents("{$testPath}/{$run}.0.histograms.json"), true); } $histogram = GetImageHistogram($lastFrame, null, $histograms); } return $histogram; }
function ResubmitTest($id) { echo "{$id} - Resubmitting..."; $testPath = './' . GetTestPath($id); if (gz_is_file("{$testPath}/testinfo.json")) { $test = json_decode(gz_file_get_contents("{$testPath}/testinfo.json"), true); if (array_key_exists('job_file', $test) && array_key_exists('location', $test) && is_file("{$testPath}/test.job")) { if ($lock = LockLocation($test['location'])) { if (copy("{$testPath}/test.job", $test['job_file'])) { $files = scandir($testPath); foreach ($files as $file) { if ($file != '.' && $file != '..' && strncasecmp($file, 'test', 4)) { if (is_file("{$testPath}/{$file}")) { unlink("{$testPath}/{$file}"); } elseif (is_dir("{$testPath}/{$file}")) { delTree("{$testPath}/{$file}"); } } } AddJobFile($test['workdir'], $test['job'], $test['priority'], false); $test['started'] = time(); unset($test['completeTime']); gz_file_put_contents("{$testPath}/testinfo.json", json_encode($test)); echo "OK"; } else { echo "Failed to copy job file"; } UnlockLocation($lock); } else { echo "Failed to lock location"; } } else { echo "Invalid test"; } } else { echo "Test not found"; } echo "\n"; }
<?php // Generate a PNG image that is the difference of two existing images include 'common.inc'; require_once 'draw.inc'; // figure out the paths to the actual images $parts = explode('/', $_REQUEST['ref']); if (count($parts) == 2) { $ref = './' . GetTestPath($parts[0]) . '/' . $parts[1]; } $parts = explode('/', $_REQUEST['cmp']); if (count($parts) == 2) { $cmp = './' . GetTestPath($parts[0]) . '/' . $parts[1]; } $ok = false; if (isset($ref) && isset($cmp) && is_file($ref) && is_file($cmp)) { $refImg = imagecreatefrompng($ref); $cmpImg = imagecreatefrompng($cmp); if ($refImg !== FALSE && $cmpImg !== FALSE) { $refWidth = imagesx($refImg); $refHeight = imagesy($refImg); $cmpWidth = imagesx($cmpImg); $cmpHeight = imagesy($cmpImg); if ($refWidth && $refHeight && $cmpWidth && $cmpHeight) { $width = max($refWidth, $cmpWidth); $height = max($refHeight, $cmpHeight); $im = imagecreate($width, $height); if ($im !== FALSE) { $black = GetColor($im, 0, 0, 0); $white = GetColor($im, 255, 255, 255); imagefilledrectangle($im, 0, 0, $width, $height, $black);
} $maxReqs = 0; if (isset($_REQUEST['maxReqs']) && strlen($_REQUEST['maxReqs'])) { $maxReqs = $_REQUEST['maxReqs']; } header("Content-disposition: attachment; filename={$id}_headersMatch.csv"); header("Content-type: text/csv"); // list of metrics that will be produced // for each of these, the median, average and std dev. will be calculated echo "\"Test ID\",\"Found\"\r\n"; // and now the actual data foreach ($testIds as &$testId) { $cached = 0; RestoreTest($testId); GetTestStatus($testId); $testPath = './' . GetTestPath($testId); $pageData = loadAllPageData($testPath); $medianRun = GetMedianRun($pageData, $cached); $secured = 0; $haveLocations = 1; $requests = getRequests($testId, $testPath, $medianRun, $cached, $secure, $haveLocations, false, true); // Flag indicating if we matched $matched = array(); $nSearches = count($searches); $nRecords = count($requests); if ($nRecords > $maxReqs && $maxReqs != 0) { $nRecords = $maxReqs; } for ($rec = 0; $rec < $nRecords; $rec++) { $r = $requests[$rec]; if (isset($r['headers']) && isset($r['headers']['response'])) {
if ($p[0] == 'r') { $test['run'] = (int) $p[1]; } if ($p[0] == 'l') { $test['label'] = $p[1]; } if ($p[0] == 'c') { $test['cached'] = (int) $p[1]; } if ($p[0] == 'e') { $test['end'] = trim($p[1]); } } } RestoreTest($test['id']); $test['path'] = GetTestPath($test['id']); $test['pageData'] = loadAllPageData($test['path']); $info = GetTestInfo($test['id']); if ($info) { if (array_key_exists('discard', $info) && $info['discard'] >= 1 && array_key_exists('priority', $info) && $info['priority'] >= 1) { $defaultInterval = 100; } $test['url'] = $info['url']; } $testInfo = parse_ini_file("./{$test['path']}/testinfo.ini", true); if ($testInfo !== FALSE) { if (array_key_exists('test', $testInfo) && array_key_exists('location', $testInfo['test'])) { $test['location'] = $testInfo['test']['location']; } if (isset($testInfo['test']) && isset($testInfo['test']['completeTime'])) { $test['done'] = true;
/** * Determine the target TTFB for the given test * * @param mixed $pageData * @param mixed $test * @param mixed $id * @param mixed $run */ function getTargetTTFB(&$pageData, &$test, $id, $run, $cached) { $target = NULL; $rtt = null; if (isset($test['testinfo']['latency'])) { $rtt = (int) $test['testinfo']['latency']; } // load the object data (unavoidable, we need the socket connect time to the first host) require_once 'object_detail.inc'; $testPath = './' . GetTestPath($id); $secure = false; $haveLocations; $requests = getRequests($id, $testPath, $run, $cached, $secure, $haveLocations, false); if (count($requests)) { // figure out what the RTT is to the server (take the connect time from the first request unless it is over 3 seconds) $connect_ms = $requests[0]['connect_ms']; if (isset($rtt) && (!isset($connect_ms) || $connect_ms > 3000)) { $rtt += 100; } else { $rtt = $connect_ms; } if (isset($rtt)) { $ssl_ms = 0; if ($requests[0]['is_secure'] && (int) $requests[0]['ssl_ms'] > 0) { $ssl_ms = $requests[0]['ssl_ms']; } // RTT's: DNS + Socket Connect + HTTP Request $target = $rtt * 3 + $ssl_ms; } } return $target; }
/** * Determine the target TTFB for the given test * * @param mixed $pageData * @param mixed $test * @param mixed $id * @param mixed $run */ function getTargetTTFB(&$pageData, &$test, $id, $run, $cached) { $target = NULL; $rtt = 0; if (isset($test['testinfo']['latency'])) { $rtt = (int) $test['testinfo']['latency']; } // load the object data (unavoidable, we need the socket connect time to the first host) require_once 'object_detail.inc'; $testPath = './' . GetTestPath($id); $secure = false; $haveLocations; $requests = getRequests($id, $testPath, $run, $cached, $secure, $haveLocations, false); if (count($requests)) { // figure out what the RTT is to the server (take the connect time from the first request unless it is over 3 seconds) if (isset($requests[0]['connect_start']) && $requests[0]['connect_start'] >= 0 && isset($requests[0]['connect_end']) && $requests[0]['connect_end'] > $requests[0]['connect_start']) { $rtt = $requests[0]['connect_end'] - $requests[0]['connect_start']; } else { $connect_ms = $requests[0]['connect_ms']; if ($rtt > 0 && (!isset($connect_ms) || $connect_ms > 3000 || $connect_ms < 0)) { $rtt += 100; } else { $rtt = $connect_ms; } } // allow for a minimum of 100ms for the RTT $rtt = max($rtt, 100); $ssl_ms = 0; $i = 0; while (isset($requests[$i])) { if (isset($requests[$i]['contentType']) && (stripos($requests[$i]['contentType'], 'ocsp') !== false || stripos($requests[$i]['contentType'], 'crl') !== false)) { $i++; } else { if ($requests[$i]['is_secure']) { $ssl_ms = $rtt; } break; } } // RTT's: DNS + Socket Connect + HTTP Request + 100ms allowance $target = $rtt * 3 + $ssl_ms + 100; } return $target; }
} // 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]; }
header('Content-type: text/plain'); header("Cache-Control: no-cache, must-revalidate"); header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); set_time_limit(300); include '../common.inc'; $location = $_REQUEST['location']; $key = $_REQUEST['key']; $id = $_REQUEST['id']; logMsg("\n\nImage received for test: {$id}, location: {$location}, key: {$key}\n"); // load all of the locations $locations = parse_ini_file('../settings/locations.ini', true); $locKey = $locations[$location]['key']; if (!strlen($locKey) || !strcmp($key, $locKey) || !strcmp($_SERVER['REMOTE_ADDR'], "127.0.0.1")) { if (isset($_FILES['file'])) { $fileName = $_FILES['file']['name']; $path = '../' . GetTestPath($id); // put each run of video data in it's own directory if (strpos($fileName, 'progress')) { $parts = explode('_', $fileName); if (count($parts)) { $runNum = $parts[0]; $fileBase = $parts[count($parts) - 1]; $cached = ''; if (strpos($fileName, '_Cached')) { $cached = '_cached'; } $path .= "/video_{$runNum}{$cached}"; if (!is_dir($path)) { mkdir($path); } $fileName = 'frame_' . $fileBase;
} } $path = null; if (isset($frame_ms)) { $path = $test['video']['frames'][$frame_ms]; } if (!$lastThumb) { $lastThumb = $path; } if ($ms <= $testEnd) { unset($border); $cached = ''; if ($test['cached']) { $cached = '_cached'; } $imgPath = GetTestPath($test['id']) . "/video_{$test['run']}{$cached}/{$path}"; if ($lastThumb != $path || !$thumb) { if ($lastThumb != $path) { $border = $colChanged; } // load the new thumbnail if ($thumb) { imagedestroy($thumb); unset($thuumb); } $tmp = imagecreatefromjpeg("./{$imgPath}"); if ($tmp) { $thumb = imagecreatetruecolor($test['video']['thumbWidth'], $test['video']['thumbHeight']); fastimagecopyresampled($thumb, $tmp, 0, 0, 0, 0, $test['video']['thumbWidth'], $test['video']['thumbHeight'], imagesx($tmp), imagesy($tmp), 4); imagedestroy($tmp); }
/** * Gather all of the data for a given test and return it as an array * * @param mixed $id */ function GetTestResult($id) { global $url; global $median_metric; $testPath = './' . GetTestPath($id); $protocol = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on' || isset($_SERVER['HTTP_SSL']) && $_SERVER['HTTP_SSL'] == 'On' ? 'https' : 'http'; $host = $_SERVER['HTTP_HOST']; $uri = rtrim(dirname($_SERVER['PHP_SELF']), '/\\'); $path = substr($testPath, 1); $pageData = loadAllPageData($testPath); $stats = array(0 => array(), 1 => array()); $pageStats = calculatePageStats($pageData, $stats[0], $stats[1]); if (!strlen($url)) { $url = $pageData[1][0]['URL']; } $testInfo = GetTestInfo($id); if (is_file("{$testPath}/testinfo.ini")) { $test = parse_ini_file("{$testPath}/testinfo.ini", true); } $fvOnly = false; if (!count($stats[1])) { $fvOnly = true; } $cacheLabels = array('firstView', 'repeatView'); // summary information $ret = array('id' => $id, 'url' => $url, 'summary' => "{$protocol}://{$host}{$uri}/results.php?test={$id}"); $runs = max(array_keys($pageData)); if (isset($testInfo)) { if (array_key_exists('url', $testInfo) && strlen($testInfo['url'])) { $ret['testUrl'] = $testInfo['url']; } if (array_key_exists('location', $testInfo) && strlen($testInfo['location'])) { $locstring = $testInfo['location']; if (array_key_exists('browser', $testInfo) && strlen($testInfo['browser'])) { $locstring .= ':' . $testInfo['browser']; } $ret['location'] = $locstring; } if (isset($test) && array_key_exists('test', $test) && is_array($test['test']) && array_key_exists('location', $test['test']) && strlen($test['test']['location'])) { $ret['from'] = $test['test']['location']; } if (array_key_exists('connectivity', $testInfo) && strlen($testInfo['connectivity'])) { $ret['connectivity'] = $testInfo['connectivity']; } if (array_key_exists('bwIn', $testInfo)) { $ret['bwDown'] = $testInfo['bwIn']; } if (array_key_exists('bwOut', $testInfo)) { $ret['bwUp'] = $testInfo['bwOut']; } if (array_key_exists('latency', $testInfo)) { $ret['latency'] = $testInfo['latency']; } if (array_key_exists('plr', $testInfo)) { $ret['plr'] = $testInfo['plr']; } if (array_key_exists('label', $testInfo) && strlen($testInfo['label'])) { $ret['label'] = $testInfo['label']; } if (array_key_exists('completed', $testInfo)) { $ret['completed'] = $testInfo['completed']; } if (array_key_exists('tester', $testInfo) && strlen($testInfo['tester'])) { $ret['tester'] = $testInfo['tester']; } if (array_key_exists('testerDNS', $testInfo) && strlen($testInfo['testerDNS'])) { $ret['testerDNS'] = $testInfo['testerDNS']; } if (array_key_exists('runs', $testInfo) && $testInfo['runs']) { $runs = $testInfo['runs']; } if (array_key_exists('fvonly', $testInfo)) { $fvOnly = $testInfo['fvonly'] ? true : false; } } $cachedMax = 0; if (!$fvOnly) { $cachedMax = 1; } $ret['runs'] = $runs; $ret['fvonly'] = $fvOnly; $ret['successfulFVRuns'] = CountSuccessfulTests($pageData, 0); if (!$fvOnly) { $ret['successfulRVRuns'] = CountSuccessfulTests($pageData, 1); } // average // check if removing average $addAverage = 1; if (isset($_GET['average'])) { if ($_GET['average'] == 0) { $addAverage = 0; } } // add average if ($addAverage == 1) { $ret['average'] = array(); for ($cached = 0; $cached <= $cachedMax; $cached++) { $label = $cacheLabels[$cached]; $ret['average'][$label] = $stats[$cached]; } } // standard deviation // check if removing standard deviation $addStandard = 1; if (isset($_GET['standard'])) { if ($_GET['standard'] == 0) { $addStandard = 0; } } // add standard deviation if ($addStandard == 1) { $ret['standardDeviation'] = array(); for ($cached = 0; $cached <= $cachedMax; $cached++) { $label = $cacheLabels[$cached]; $ret['standardDeviation'][$label] = array(); foreach ($stats[$cached] as $key => $val) { $ret['standardDeviation'][$label][$key] = PageDataStandardDeviation($pageData, $key, $cached); } } } // median // check if removing median $addMedian = 1; if (isset($_GET['median'])) { if ($_GET['median'] == 0) { $addMedian = 0; } } // add median if ($addMedian == 1) { $ret['median'] = array(); for ($cached = 0; $cached <= $cachedMax; $cached++) { $label = $cacheLabels[$cached]; $medianRun = GetMedianRun($pageData, $cached, $median_metric); if (array_key_exists($medianRun, $pageData)) { $ret['median'][$label] = GetSingleRunData($id, $testPath, $medianRun, $cached, $pageData, $testInfo); } } } // runs // check if removing runs $addRuns = 1; if (isset($_GET['runs'])) { if ($_GET['runs'] == 0) { $addRuns = 0; } } // add runs if ($addRuns == 1) { $ret['runs'] = array(); for ($run = 1; $run <= $runs; $run++) { $ret['runs'][$run] = array(); for ($cached = 0; $cached <= $cachedMax; $cached++) { $label = $cacheLabels[$cached]; $ret['runs'][$run][$label] = GetSingleRunData($id, $testPath, $run, $cached, $pageData, $testInfo); } } } ArchiveApi($id); return $ret; }
} echo "\r\n"; $sentHeader = true; } $testInfo = GetTestInfo($path); for ($i = 1; $i <= $test['test']['runs']; $i++) { $additional = array($i, 0, SpeedIndex($path, $i, 0, $testInfo)); csvFile("{$path}/{$i}_{$fileType}", $label, $column_count, $additional); $additional = array($i, 1, SpeedIndex($path, $i, 1, $testInfo)); csvFile("{$path}/{$i}_Cached_{$fileType}", $label, $column_count, $additional); } } else { csvPageData($testData['id'], $path, $test['test']['runs']); } foreach ($testData['v'] as $variationIndex => $variationId) { $path = './' . GetTestPath($variationId); $testInfo = GetTestInfo($path); if ($hasCSV) { for ($i = 1; $i <= $test['test']['runs']; $i++) { $additional = array($i, 0, SpeedIndex($path, $i, 0, $testInfo)); csvFile("{$path}/{$i}_{$fileType}", "{$label} - {$tests['variations'][$variationIndex]['l']}", $column_count, $additional); $additional = array($i, 1, SpeedIndex($path, $i, 1, $testInfo)); csvFile("{$path}/{$i}_Cached_{$fileType}", "{$label} - {$tests['variations'][$variationIndex]['l']}", $column_count, $additional); } } else { csvPageData($variationId, $path, $test['test']['runs']); } } } } } else {
/** * Prune the extra data we don't need for benchmarks from the test result * video, screen shots, headers, etc * * @param mixed $id */ function PruneTestData($id) { $testPath = './' . GetTestPath($id); $files = scandir($testPath); foreach ($files as $file) { // just do the videos for now if (strpos($file, 'video_') !== false && is_dir("{$testPath}/{$file}")) { delTree("{$testPath}/{$file}"); } elseif (strpos($file, 'bodies') !== false) { unlink("{$testPath}/{$file}"); } elseif (strpos($file, 'pagespeed') !== false) { unlink("{$testPath}/{$file}"); } elseif (strpos($file, '_doc.jpg') !== false) { unlink("{$testPath}/{$file}"); } elseif (strpos($file, '_render.jpg') !== false) { unlink("{$testPath}/{$file}"); } elseif (strpos($file, 'status.txt') !== false) { unlink("{$testPath}/{$file}"); } } }
function CollectTestResult($test, &$data) { global $benchmark; $id = $test['id']; $count = 0; echo "Reprocessing Test {$id}..."; logMsg("Reprocessing Test {$id}", "./log/reprocess-{$benchmark}.log", true); RestoreTest($id); ReprocessVideo($id); $testPath = './' . GetTestPath($id); $page_data = loadAllPageData($testPath); if (count($page_data)) { foreach ($page_data as $run => &$page_run) { foreach ($page_run as $cached => &$test_data) { $data_row = $test_data; unset($data_row['URL']); // figure out the per-type request info (todo: measure how expensive this is and see if we have a better way) $breakdown = getBreakdown($test['id'], $testPath, $run, $cached, $requests); foreach ($breakdown as $mime => &$values) { $data_row["{$mime}_requests"] = $values['requests']; $data_row["{$mime}_bytes"] = $values['bytes']; } // capture the page speed score if ($cached) { $data_row['page_speed'] = GetPageSpeedScore("{$testPath}/{$run}_Cached_pagespeed.txt"); } else { $data_row['page_speed'] = GetPageSpeedScore("{$testPath}/{$run}_pagespeed.txt"); } $data_row['url'] = $test['url']; $data_row['label'] = $test['label']; $data_row['location'] = $test['location']; $data_row['config'] = $test['config']; $data_row['cached'] = $cached; $data_row['run'] = $run; $data_row['id'] = $test['id']; $data[] = $data_row; $count++; } } } else { $data_row = array(); $data_row['url'] = $test['url']; $data_row['label'] = $test['label']; $data_row['location'] = $test['location']; $data_row['config'] = $test['config']; $data_row['id'] = $test['id']; $data[] = $data_row; } // If the test was already archived, re-archive it. $testInfo = GetTestInfo($id); if (array_key_exists('archived', $testInfo) && $testInfo['archived']) { $lock = LockTest($id); if (isset($lock)) { $testInfo = GetTestInfo($id); $testInfo['archived'] = false; SaveTestInfo($id, $testInfo); UnlockTest($lock); } ArchiveTest($id); } echo "{$count} results\n"; }
<?php include 'common.inc'; require_once 'page_data.inc'; $page_keywords = array('image', 'comparison', 'Webpagetest', 'Website Speed Test'); $page_description = "Visual comparison of multiple website screen shots."; $title = 'Web page screen shot diff'; $gaTemplate = 'Screen Shot Diff'; $refPath = GetTestPath($_REQUEST['ref']); $refData = loadAllPageData($refPath); $cmpPath = GetTestPath($_REQUEST['cmp']); $cmpData = loadAllPageData($cmpPath); $refRun = GetMedianRun($refData, 0, $median_metric); $cmpRun = GetMedianRun($cmpData, 0, $median_metric); if ($refRun && $cmpRun) { $refFile = "{$refPath}/{$refRun}_screen.png"; $cmpFile = "{$cmpPath}/{$cmpRun}_screen.png"; if (is_file($refFile) && is_file($cmpFile)) { $refImg = urlencode("{$_REQUEST['ref']}/{$refRun}_screen.png"); $cmpImg = urlencode("{$_REQUEST['cmp']}/{$cmpRun}_screen.png"); } } ?> <!DOCTYPE html> <html> <head> <title>WebPagetest - Screen Shot diff</title> <?php include 'head.inc'; ?> </head>
function CollectTraces($tests, $outDir) { $count = 0; $test_count = 0; $total = count($tests); $index = 0; foreach ($tests as $id) { $index++; logMessage("Collecting traces from test {$index} of {$total} ({$id})..."); $testPath = './' . GetTestPath($id); $files = scandir($testPath); $found = false; foreach ($files as $file) { $pos = strpos($file, '_trace.json.gz'); if ($pos > 0) { $count++; $found = true; $src = "{$testPath}/{$file}"; $dst = "{$outDir}/{$id}.json.gz"; copy($src, $dst); } } if ($found) { $test_count++; } } logMessage("Collected {$count} traces from {$test_count} of {$total} tests in {$outDir}"); return $count; }
/** * Build a side-by-side table with the captured frames from each test * */ function ScreenShotTable() { global $tests; global $thumbSize; global $interval; global $maxCompare; 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']; } } echo '<br/><form id="createForm" name="create" method="get" action="/video/create.php" onsubmit="return ValidateInput(this)"><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']) { $height = 10 + (int) ((double) $thumbSize / (double) $test['video']['width'] * (double) $test['video']['height']); } $break = ''; if (!strpos($test['name'], ' ')) { $break = ' style="word-break: break-all;"'; } echo "<tr width=10% height={$height}px ><td{$break}>"; $name = urlencode($test['name']); $cached = 0; if ($test['cached']) { $cached = 1; } echo "<input type=\"checkbox\" name=\"t[]\" value=\"{$test['id']},{$test['run']}," . $name . ",{$cached}\" checked=checked> "; $cached = ''; if ($test['cached']) { $cached = 'cached/'; } echo "<a class=\"pagelink\" href=\"/result/{$test['id']}/{$test['run']}/details/{$cached}\" target=\"_blank\">"; echo WrapableString($test['name']); echo "</a></td></tr>\n"; } echo '</table></td>'; // the actual video frames echo '<td><div id="videoDiv"><table id="video"><thead><tr>'; $skipped = $interval; $last = $end + $interval - 1; for ($frame = 0; $frame <= $last; $frame++) { $skipped++; if ($skipped >= $interval) { $skipped = 0; echo '<th>' . number_format((double) $frame / 10.0, 1) . 's</th>'; } } echo "</tr></thead><tbody>\n"; $firstFrame = 0; foreach ($tests as &$test) { // figure out the height of the image $height = 0; if ($test['video']['width'] && $test['video']['height']) { $height = (int) ((double) $thumbSize / (double) $test['video']['width'] * (double) $test['video']['height']); } echo "<tr>"; $lastThumb = null; $frameCount = 0; $skipped = $interval; $last = $end + $interval - 1; for ($frame = 0; $frame <= $last; $frame++) { $path = $test['video']['frames'][$frame]; if (isset($path)) { $test['currentframe'] = $frame; } else { if (isset($test['currentframe'])) { $path = $test['video']['frames'][$test['currentframe']]; } else { $path = $test['video']['frames'][0]; } } if (!$lastThumb) { $lastThumb = $path; } $skipped++; if ($skipped >= $interval) { $skipped = 0; echo '<td>'; if ($frame - $interval + 1 <= $test['video']['end']) { echo ''; $cached = ''; if ($test['cached']) { $cached = '_cached'; } $imgPath = GetTestPath($test['id']) . "/video_{$test['run']}{$cached}/{$path}"; echo "<a href=\"/{$imgPath}\">"; echo "<img title=\"{$test['name']}\""; $class = 'thumb'; if ($lastThumb != $path) { if (!$firstFrame || $frameCount < $firstFrame) { $firstFrame = $frameCount; } $class = 'thumbChanged'; } echo " class=\"{$class}\""; echo " width=\"{$thumbSize}\""; if ($height) { echo " height=\"{$height}\""; } echo " src=\"/thumbnail.php?test={$test['id']}&width={$thumbSize}&file=video_{$test['run']}{$cached}/{$path}\"></a>"; $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\">"; $ival = $interval * 100; echo "<a class=\"pagelink\" href=\"filmstrip.php?tests={$_REQUEST['tests']}&thumbSize={$thumbSize}&ival={$ival}\">Export filmstrip as an image...</a>"; echo "</div>"; echo '<div id="bottom"><input type="checkbox" name="slow" value="1"> Slow Motion<br/><br/>'; echo "Select up to {$maxCompare} tests and <input id=\"SubmitBtn\" type=\"submit\" value=\"Create Video\"></div>"; echo "</form>"; ?> <div id="layout"> <form id="layoutForm" name="layout" method="get" action="/video/compare.php"> <?php echo "<input type=\"hidden\" name=\"tests\" value=\"{$_REQUEST['tests']}\" />\n"; ?> <table id="layoutTable"> <tr><th>Thumbnail Size</th><th>Thumbnail Interval</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>"; $checked = ''; if ($interval <= 1) { $checked = ' checked=checked'; } echo "<input type=\"radio\" name=\"ival\" value=\"100\"{$checked} onclick=\"this.form.submit();\"> 0.1 sec<br>"; $checked = ''; if ($interval == 5) { $checked = ' checked=checked'; } echo "<input type=\"radio\" name=\"ival\" value=\"500\"{$checked} onclick=\"this.form.submit();\"> 0.5 sec<br>"; $checked = ''; if ($interval == 10) { $checked = ' checked=checked'; } echo "<input type=\"radio\" name=\"ival\" value=\"1000\"{$checked} onclick=\"this.form.submit();\"> 1 sec<br>"; $checked = ''; if ($interval == 50) { $checked = ' checked=checked'; } echo "<input type=\"radio\" name=\"ival\" value=\"5000\"{$checked} onclick=\"this.form.submit();\"> 5 sec<br>"; echo "</td></tr>"; ?> </table> </form> </div> <?php // scroll the table to show the first thumbnail change $scrollPos = $firstFrame * ($thumbSize + 8); ?> <script language="javascript"> var scrollPos = <?php echo "{$scrollPos};"; ?> document.getElementById("videoDiv").scrollLeft = scrollPos; </script> <?php // display the waterfall if there is only one test if (count($tests) == 1) { ?> <div id="waterfall"> <map name="waterfall_map"> <?php $data = loadPageRunData($tests[0]['path'], $tests[0]['run'], $tests[0]['cached']); $secure = false; $haveLocations = false; $requests = getRequests($tests[0]['id'], $tests[0]['path'], $tests[0]['run'], $tests[0]['cached'], $secure, $haveLocations, false); $options = array('id' => $tests[0]['id'], 'path' => $tests[0]['path'], 'run' => $tests[0]['run'], 'cached' => $tests[0]['cached'], 'cpu' => false); $map = drawWaterfall($tests[0]['url'], $requests, $data, true, $options); foreach ($map as $entry) { if ($entry['request'] !== NULL) { $index = $entry['request'] + 1; $title = $index . ': ' . $entry['url']; echo '<area alt="' . $title . '" title="' . $title . '" shape=RECT coords="' . $entry['left'] . ',' . $entry['top'] . ',' . $entry['right'] . ',' . $entry['bottom'] . '">' . "\n"; } else { echo '<area alt="' . $entry['url'] . '" title="' . $entry['url'] . '" shape=RECT coords="' . $entry['left'] . ',' . $entry['top'] . ',' . $entry['right'] . ',' . $entry['bottom'] . '">' . "\n"; } } ?> </map> <?php echo "<img id=\"waterfallImage\" usemap=\"#waterfall_map\" border=\"0\" alt=\"Waterfall\" src=\"/waterfall.php?test={$tests[0]['id']}&run={$tests[0]['run']}&cached={$tests[0]['cached']}&cpu=0&bw=0\">"; ?> </div> <?php } echo '<br/><br/>'; } }
function ProcessTest($id) { global $tempDir; global $name; global $count; global $total; $ok = false; $testPath = './' . GetTestPath($id); $restored = false; if (!is_dir($testPath)) { // try restoring the test several times in case there are network issues $attempt = 0; do { $attempt++; har_log("{$id} - restoring test ({$attempt})"); RestoreTest($id); if (is_dir($testPath)) { $restored = true; } else { sleep(1); } } while (!$restored && $attempt < 120); } if (is_dir($testPath)) { har_log("{$id} - generating HAR"); $har = GenerateHAR($id, $testPath, ['bodies' => 1, 'run' => 'median', 'cached' => 0]); if (isset($har) && strlen($har)) { gz_file_put_contents("{$tempDir}/{$id}.har", $har); unset($har); $file = "{$tempDir}/{$id}.har.gz"; if (is_file($file)) { $file = realpath($file); $remoteFile = "{$name}/{$id}.har.gz"; $bucket = 'httparchive'; har_log("{$id} - Uploading to {$remoteFile}"); if (gsUpload($file, $bucket, $remoteFile)) { $ok = true; } else { har_log("{$id} - error uploading HAR"); } unlink($file); } else { har_log("{$id} - error saving HAR"); } } else { har_log("{$id} - error generating HAR"); } // clean up the test if we restored it if ($restored) { delTree($testPath, true); } } else { har_log("{$id} - error restoring test"); } return $ok; }
/** * 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 = 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\">"; $name = urlencode($test['name']); $cached = 0; if ($test['cached']) { $cached = 1; } // Print the index outside of the link tag echo $test['index'] . ': '; if (!defined('EMBED')) { $cached = ''; if ($test['cached']) { $cached = 'cached/'; } if (FRIENDLY_URLS) { $href = "/result/{$test['id']}/{$test['run']}/details/{$cached}"; } else { $href = "/details.php?test={$test['id']}&run={$test['run']}&cached={$test['cached']}"; } 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; 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) { $cached = ''; if ($test['cached']) { $cached = '_cached'; } $imgPath = GetTestPath($test['id']) . "/video_{$test['run']}{$cached}/{$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}\""; } echo " src=\"/thumbnail.php?test={$test['id']}&fit={$thumbSize}&file=video_{$test['run']}{$cached}/{$path}\"></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={$_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=\"{$_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) { $data = loadPageRunData($tests[0]['path'], $tests[0]['run'], $tests[0]['cached']); $secure = false; $haveLocations = false; $requests = getRequests($tests[0]['id'], $tests[0]['path'], $tests[0]['run'], $tests[0]['cached'], $secure, $haveLocations, true, true); InsertWaterfall('', $requests, $tests[0]['id'], $tests[0]['run'], $tests[0]['cached'], $data, "&max={$end_seconds}&mime=1&state=1&cpu=1&bw=1"); echo '<br><br>'; } else { $waterfalls = array(); foreach ($tests as &$test) { $waterfalls[] = array('id' => $test['id'], 'label' => $test['name'], 'run' => $test['run'], '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 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>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 } }
/** * Get an actual task to complete * */ function GetJob() { $is_done = false; global $location; global $key; global $pc; global $ec2; global $tester; global $recover; global $is_json; global $dnsServers; global $screenwidth; global $screenheight; global $winver; global $isWinServer; global $isWin64; $workDir = "./work/jobs/{$location}"; $locInfo = GetLocationInfo($location); $locKey = ''; if (isset($locInfo) && is_array($locInfo) && isset($locInfo['key'])) { $locKey = $locInfo['key']; } $incremental = true; if (GetSetting('no_incremental') || isset($locInfo['incremental']) && !$locInfo['incremental']) { $incremental = false; } if (strpos($location, '..') == false && strpos($location, '\\') == false && strpos($location, '/') == false && (!strlen($locKey) || !strcmp($key, $locKey))) { $now = time(); // If it is an EC2 auto-scaling location, make sure the agent isn't marked as offline $offline = false; if (isset($locInfo['ami'])) { $testers = GetTesters($location, true); // make sure the tester isn't marked as offline (usually when shutting down EC2 instances) $testerCount = isset($testers['testers']) ? count($testers['testers']) : 0; $testerIndex = null; if ($testerCount) { if (strlen($ec2)) { foreach ($testers['testers'] as $index => $testerInfo) { if (isset($testerInfo['ec2']) && $testerInfo['ec2'] == $ec2 && isset($testerInfo['offline']) && $testerInfo['offline']) { $offline = true; } break; } } foreach ($testers['testers'] as $index => $testerInfo) { if ($testerInfo['id'] == $tester) { $testerIndex = $index; break; } } } } if (!$offline) { $testInfo = GetTestJob($location, $fileName, $workDir, $priority, $testInfo, $pc, $testerIndex, $testerCount); if (isset($testInfo)) { $original_test_info = $testInfo; $is_done = true; $delete = true; if ($is_json) { header("Content-type: application/json"); } else { header('Content-type: text/plain'); } header("Cache-Control: no-cache, must-revalidate"); header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // send the test info to the test agent $newline = strpos($testInfo, "\n", 2); if ($newline) { $newline++; $after = substr($testInfo, $newline); $testInfo = substr($testInfo, 0, $newline); $software = GetSetting('software'); if ($software) { $testInfo .= "software={$software}\r\n"; } if (GetSetting('enable_agent_processing')) { $testInfo .= "processResults=1\r\n"; } if (!$incremental) { $testInfo .= "incremental=0\r\n"; } $testInfo .= $after; } // extract the test ID from the job file if (preg_match('/Test ID=([^\\r\\n]+)\\r/i', $testInfo, $matches)) { $testId = trim($matches[1]); } if (isset($testId)) { // figure out the path to the results $testPath = './' . GetTestPath($testId); // flag the test with the start time $ini = file_get_contents("{$testPath}/testinfo.ini"); if (stripos($ini, 'startTime=') === false) { $time = time(); $start = "[test]\r\nstartTime=" . gmdate("m/d/y G:i:s", $time); $out = str_replace('[test]', $start, $ini); file_put_contents("{$testPath}/testinfo.ini", $out); } $lock = LockTest($testId); if ($lock) { $testInfoJson = GetTestInfo($testId); if ($testInfoJson) { if (!array_key_exists('tester', $testInfoJson) || !strlen($testInfoJson['tester'])) { $testInfoJson['tester'] = $tester; } if (isset($dnsServers) && strlen($dnsServers)) { $testInfoJson['testerDNS'] = $dnsServers; } if (!array_key_exists('started', $testInfoJson) || !$testInfoJson['started']) { $testInfoJson['started'] = $time; logTestMsg($testId, "Starting test (initiated by tester {$tester})"); } if (!array_key_exists('test_runs', $testInfoJson)) { $testInfoJson['test_runs'] = array(); } for ($run = 1; $run <= $testInfoJson['runs']; $run++) { if (!array_key_exists($run, $testInfoJson['test_runs'])) { $testInfoJson['test_runs'][$run] = array('done' => false); } } $dotPos = stripos($testId, "."); $testInfoJson['id'] = $dotPos === false ? $testId : substr($testId, $dotPos + 1); ProcessTestShard($testInfoJson, $testInfo, $delete, $priority); SaveTestInfo($testId, $testInfoJson); } UnlockTest($lock); } } if ($delete) { if (isset($fileName) && is_file("{$workDir}/{$fileName}")) { unlink("{$workDir}/{$fileName}"); } } else { AddTestJobHead($location, $original_test_info, $workDir, $fileName, $priority, true); } if ($is_json) { $testJson = array(); $script = ''; $isScript = false; $lines = explode("\r\n", $testInfo); foreach ($lines as $line) { if (strlen(trim($line))) { if ($isScript) { if (strlen($script)) { $script .= "\r\n"; } $script .= $line; } elseif (!strcasecmp($line, '[Script]')) { $isScript = true; } else { $pos = strpos($line, '='); if ($pos !== false) { $key = trim(substr($line, 0, $pos)); $value = trim(substr($line, $pos + 1)); if (strlen($key) && strlen($value)) { if ($key == 'customMetric') { $pos = strpos($value, ':'); if ($pos !== false) { $metric = trim(substr($value, 0, $pos)); $code = base64_decode(substr($value, $pos + 1)); if ($code !== false && strlen($metric) && strlen($code)) { if (!isset($testJson['customMetrics'])) { $testJson['customMetrics'] = array(); } $testJson['customMetrics'][$metric] = $code; } } } elseif (is_numeric($value)) { $testJson[$key] = (int) $value; } else { $testJson[$key] = $value; } } } } } } if (strlen($script)) { $testJson['script'] = $script; } // See if we need to include apk information if (isset($_REQUEST['apk']) && is_file(__DIR__ . '/update/apk.dat')) { $apk_info = json_decode(file_get_contents(__DIR__ . '/update/apk.dat'), true); if (isset($apk_info) && is_array($apk_info) && isset($apk_info['packages']) && is_array($apk_info['packages'])) { $protocol = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on' || isset($_SERVER['HTTP_SSL']) && $_SERVER['HTTP_SSL'] == 'On' ? 'https' : 'http'; $update_path = dirname($_SERVER['PHP_SELF']) . '/update/'; $base_uri = "{$protocol}://{$_SERVER['HTTP_HOST']}{$update_path}"; foreach ($apk_info['packages'] as $package => $info) { $apk_info['packages'][$package]['apk_url'] = "{$base_uri}{$apk_info['packages'][$package]['file_name']}?md5={$apk_info['packages'][$package]['md5']}"; } $testJson['apk_info'] = $apk_info; } } echo json_encode($testJson); } else { echo $testInfo; } $ok = true; } // keep track of the last time this location reported in $testerInfo = array(); $testerInfo['ip'] = $_SERVER['REMOTE_ADDR']; $testerInfo['pc'] = $pc; $testerInfo['ec2'] = $ec2; $testerInfo['ver'] = array_key_exists('version', $_GET) ? $_GET['version'] : $_GET['ver']; $testerInfo['freedisk'] = @$_GET['freedisk']; $testerInfo['ie'] = @$_GET['ie']; $testerInfo['dns'] = $dnsServers; $testerInfo['video'] = @$_GET['video']; $testerInfo['GPU'] = @$_GET['GPU']; $testerInfo['screenwidth'] = $screenwidth; $testerInfo['screenheight'] = $screenheight; $testerInfo['winver'] = $winver; $testerInfo['isWinServer'] = $isWinServer; $testerInfo['isWin64'] = $isWin64; $testerInfo['test'] = ''; if (isset($testId)) { $testerInfo['test'] = $testId; } UpdateTester($location, $tester, $testerInfo); } } return $is_done; }
/** * Get an actual task to complete * */ function GetJob() { $is_done = false; global $location; global $key; global $pc; global $ec2; global $tester; global $recover; global $is_json; global $dnsServers; $workDir = "./work/jobs/{$location}"; $locKey = GetLocationKey($location); if (strpos($location, '..') == false && strpos($location, '\\') == false && strpos($location, '/') == false && (!strlen($locKey) || !strcmp($key, $locKey))) { if ($lock = LockLocation($location)) { $now = time(); $testers = GetTesters($location, true); // make sure the tester isn't marked as offline (usually when shutting down EC2 instances) $testerCount = isset($testers['testers']) ? count($testers['testers']) : 0; $testerIndex = null; $offline = false; if ($testerCount) { if (strlen($ec2)) { foreach ($testers['testers'] as $index => $testerInfo) { if (isset($testerInfo['ec2']) && $testerInfo['ec2'] == $ec2 && isset($testerInfo['offline']) && $testerInfo['offline']) { $offline = true; } break; } } foreach ($testers['testers'] as $index => $testerInfo) { if ($testerInfo['id'] == $tester) { $testerIndex = $index; break; } } } if (!$offline) { $fileName = GetJobFile($workDir, $priority, $pc, $testerIndex, $testerCount); if (isset($fileName) && strlen($fileName)) { $is_done = true; $delete = true; if ($is_json) { header("Content-type: application/json"); } else { header('Content-type: text/plain'); } header("Cache-Control: no-cache, must-revalidate"); header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // send the test info to the test agent $testInfo = file_get_contents("{$workDir}/{$fileName}"); // extract the test ID from the job file if (preg_match('/Test ID=([^\\r\\n]+)\\r/i', $testInfo, $matches)) { $testId = trim($matches[1]); } if (isset($testId)) { // figure out the path to the results $testPath = './' . GetTestPath($testId); // flag the test with the start time $ini = file_get_contents("{$testPath}/testinfo.ini"); if (stripos($ini, 'startTime=') === false) { $time = time(); $start = "[test]\r\nstartTime=" . gmdate("m/d/y G:i:s", $time); $out = str_replace('[test]', $start, $ini); file_put_contents("{$testPath}/testinfo.ini", $out); } $lock = LockTest($testId); if ($lock) { $testInfoJson = GetTestInfo($testId); if ($testInfoJson) { if (!array_key_exists('tester', $testInfoJson) || !strlen($testInfoJson['tester'])) { $testInfoJson['tester'] = $tester; } if (isset($dnsServers) && strlen($dnsServers)) { $testInfoJson['testerDNS'] = $dnsServers; } if (!array_key_exists('started', $testInfoJson) || !$testInfoJson['started']) { $testInfoJson['started'] = $time; logTestMsg($testId, "Starting test (initiated by tester {$tester})"); } if (!array_key_exists('test_runs', $testInfoJson)) { $testInfoJson['test_runs'] = array(); } for ($run = 1; $run <= $testInfoJson['runs']; $run++) { if (!array_key_exists($run, $testInfoJson['test_runs'])) { $testInfoJson['test_runs'][$run] = array('done' => false); } } $testInfoJson['id'] = $testId; ProcessTestShard($testInfoJson, $testInfo, $delete); SaveTestInfo($testId, $testInfoJson); } UnlockTest($lock); } file_put_contents("./tmp/last-test-{$location}-{$tester}.test", $testId); } if ($delete) { unlink("{$workDir}/{$fileName}"); } else { AddJobFileHead($workDir, $fileName, $priority, true); } if ($is_json) { $testJson = array(); $script = ''; $isScript = false; $lines = explode("\r\n", $testInfo); foreach ($lines as $line) { if (strlen(trim($line))) { if ($isScript) { if (strlen($script)) { $script .= "\r\n"; } $script .= $line; } elseif (!strcasecmp($line, '[Script]')) { $isScript = true; } else { $pos = strpos($line, '='); if ($pos !== false) { $key = trim(substr($line, 0, $pos)); $value = trim(substr($line, $pos + 1)); if (strlen($key) && strlen($value)) { if ($key == 'customMetric') { $pos = strpos($value, ':'); if ($pos !== false) { $metric = trim(substr($value, 0, $pos)); $code = base64_decode(substr($value, $pos + 1)); if ($code !== false && strlen($metric) && strlen($code)) { if (!isset($testJson['customMetrics'])) { $testJson['customMetrics'] = array(); } $testJson['customMetrics'][$metric] = $code; } } } elseif (is_numeric($value)) { $testJson[$key] = (int) $value; } else { $testJson[$key] = $value; } } } } } } if (strlen($script)) { $testJson['script'] = $script; } echo json_encode($testJson); } else { echo $testInfo; } $ok = true; } // zero out the tracked page loads in case some got lost if (!$is_done && is_file("./tmp/{$location}.tests")) { $tests = json_decode(file_get_contents("./tmp/{$location}.tests"), true); if ($tests) { $tests['tests'] = 0; file_put_contents("./tmp/{$location}.tests", json_encode($tests)); } } } UnlockLocation($lock); // keep track of the last time this location reported in $testerInfo = array(); $testerInfo['ip'] = $_SERVER['REMOTE_ADDR']; $testerInfo['pc'] = $pc; $testerInfo['ec2'] = $ec2; $testerInfo['ver'] = array_key_exists('version', $_GET) ? $_GET['version'] : $_GET['ver']; $testerInfo['freedisk'] = @$_GET['freedisk']; $testerInfo['ie'] = @$_GET['ie']; $testerInfo['dns'] = $dnsServers; $testerInfo['video'] = @$_GET['video']; $testerInfo['GPU'] = @$_GET['GPU']; $testerInfo['test'] = ''; if (isset($testId)) { $testerInfo['test'] = $testId; } UpdateTester($location, $tester, $testerInfo); } } return $is_done; }
include_once 'unique.inc'; $id = null; if ($test['private']) { $id = md5(uniqid(rand(), true)); } else { $id = uniqueId(); } $today = new DateTime("now", new DateTimeZone('America/New_York')); $test['id'] = $today->format('ymd_') . $id; $test['path'] = './' . GetTestPath($test['id']); // make absolutely CERTAIN that this test ID doesn't already exist while (is_dir($test['path'])) { // fall back to random ID's $id = md5(uniqid(rand(), true)); $test['id'] = $today->format('ymd_') . $id; $test['path'] = './' . GetTestPath($test['id']); } // create the folder for the test results if (!is_dir($test['path'])) { mkdir($test['path'], 0777, true); } // write out the url, DOM element and login file_put_contents("{$test['path']}/url.txt", $test['url']); if (strlen($test['domElement'])) { file_put_contents("{$test['path']}/dom.txt", $test['domElement']); } if (strlen($test['login'])) { file_put_contents("{$test['path']}/login.txt", $test['login']); } if (strlen($test['label'])) { file_put_contents("{$test['path']}/label.txt", $test['label']);
// The following params are set by the android agents (blaze and WebpageTest). // TODO(skerner): POST params are not saved to disk directly, so it is hard to // see what the agent uploaded after the fact. Consider writing them to a // file that gets uploaded. $runNumber = arrayLookupWithDefault('_runNumber', $_REQUEST, null); $runNumber = arrayLookupWithDefault('run', $_REQUEST, $runNumber); $runIndex = arrayLookupWithDefault('index', $_REQUEST, null); $cacheWarmed = arrayLookupWithDefault('_cacheWarmed', $_REQUEST, null); $cacheWarmed = arrayLookupWithDefault('cached', $_REQUEST, $cacheWarmed); $docComplete = arrayLookupWithDefault('_docComplete', $_REQUEST, null); $onFullyLoaded = arrayLookupWithDefault('_onFullyLoaded', $_REQUEST, null); $onRender = arrayLookupWithDefault('_onRender', $_REQUEST, null); $urlUnderTest = arrayLookupWithDefault('_urlUnderTest', $_REQUEST, null); $testInfo_dirty = false; if (ValidateTestId($id)) { $testPath = './' . GetTestPath($id); if (array_key_exists('video', $_REQUEST) && $_REQUEST['video']) { logMsg("Video file {$id} received from {$_REQUEST['location']}"); $dir = './' . GetVideoPath($id); if (array_key_exists('file', $_FILES) && array_key_exists('tmp_name', $_FILES['file'])) { if (!is_dir($dir)) { mkdir($dir, 0777, true); } $dest = $dir . '/video.mp4'; move_uploaded_file($_FILES['file']['tmp_name'], $dest); @chmod($dest, 0666); $iniFile = $dir . '/video.ini'; if (is_file($iniFile)) { $ini = file_get_contents($iniFile); } else { $ini = '';
/** * Parse the list of tests and identify the screen shots to compare * */ function ParseTests() { $tests = array(); global $median_metric; if (isset($_REQUEST['tests'])) { $groups = explode(',', $_REQUEST['tests']); foreach ($groups as $group) { $parts = explode('-', $group); if (count($parts) >= 1 && ValidateTestId($parts[0])) { $test = array(); $test['id'] = $parts[0]; $test['cached'] = 0; for ($i = 1; $i < count($parts); $i++) { $p = explode(':', $parts[$i]); if (count($p) >= 2) { if ($p[0] == 'r') { $test['run'] = (int) $p[1]; } if ($p[0] == 'l') { $test['label'] = $p[1]; } if ($p[0] == 'c') { $test['cached'] = (int) $p[1]; } } } RestoreTest($test['id']); $test['path'] = GetTestPath($test['id']); if (!isset($test['run'])) { $pageData = loadAllPageData($test['path']); $test['run'] = GetMedianRun($pageData, $test['cached'], $median_metric); } if (!isset($test['label'])) { $label = getLabel($test['id'], $user); if (!empty($label)) { $test['label'] = $new_label; } else { $info = GetTestInfo($test['id']); if ($info && isset($info['label']) && strlen($info['label'])) { $test['label'] = trim($info['label']); } } } if (!isset($test['label'])) { $test['label'] = $test['id']; } $cachedText = ''; if ($test['cached']) { $cachedText = '_Cached'; } $fileBase = "{$test['path']}/{$test['run']}{$cachedText}_screen"; if (is_file("{$fileBase}.png")) { $test['image'] = "{$fileBase}.png"; } elseif (is_file("{$fileBase}.jpg")) { $test['image'] = "{$fileBase}.jpg"; } if (isset($test['image'])) { $size = getimagesize($test['image']); if ($size && count($size) >= 2 && $size[0] > 0 && $size[1] > 0) { $test['width'] = $size[0]; $test['height'] = $size[1]; $tests[] = $test; } } } } } if (!count($tests)) { unset($tests); } return $tests; }
/** * A relay test came in, should include a job file already * */ function RelayTest() { global $error; global $locations; $error = null; $ret = array(); $ret['statusCode'] = 200; $rkey = $_POST['rkey']; $test = json_decode($_POST['testinfo'], true); $job = trim($_POST['job']); $ini = trim($_POST['ini']); $location = trim($_POST['location']); $test['workdir'] = $locations[$location]['localDir']; ValidateKey($test, $error, $rkey); if (!isset($error)) { $id = $rkey . '.' . $test['id']; $ret['id'] = $id; $test['job'] = $rkey . '.' . $test['job']; $testPath = './' . GetTestPath($id); @mkdir($testPath, 0777, true); $job = str_replace($test['id'], $id, $job); file_put_contents("{$testPath}/testinfo.ini", $ini); WriteJob($location, $test, $job, $id); SaveTestInfo($id, $test); } if (isset($error)) { $ret['statusCode'] = 400; $ret['statusText'] = "Relay: {$error}"; } header("Content-type: application/json"); echo json_encode($ret); }
/** * Load information about each of the tests (particularly about the video frames) * */ function LoadTestData() { global $tests; foreach ($tests as &$test) { $testPath = GetTestPath($test['id']); $test['url'] = htmlspecialchars(file_get_contents("./{$testPath}/url.txt")); if (strlen($test['label'])) { $test['name'] = $test['label']; } else { $test['name'] = htmlspecialchars(file_get_contents("./{$testPath}/label.txt")); } if (!strlen($test['name'])) { $test['name'] = $test['url']; $test['name'] = str_replace('http://', '', $test['name']); $test['name'] = str_replace('https://', '', $test['name']); } $videoPath = "./{$testPath}/video_{$test['run']}"; if ($test['cached']) { $videoPath .= '_cached'; } if (is_dir($videoPath)) { $test['video'] = array(); $test['video']['start'] = 20000; $test['video']['end'] = 0; $test['video']['frames'] = array(); // get the path to each of the video files $dir = opendir($videoPath); if ($dir) { while ($file = readdir($dir)) { $path = $videoPath . "/{$file}"; if (is_file($path) && !strncmp($file, 'frame_', 6) && strpos($file, '.thm') === false) { $parts = explode('_', $file); if (count($parts) >= 2) { $index = (int) $parts[1]; if ($index < $test['video']['start']) { $test['video']['start'] = $index; } if ($index > $test['video']['end']) { $test['video']['end'] = $index; } // figure out the dimensions of the source image if (!$test['video']['width'] || !$test['video']['height']) { $size = getimagesize($path); $test['video']['width'] = $size[0]; $test['video']['height'] = $size[1]; } $test['video']['frames'][$index] = "{$file}"; } } } closedir($dir); } if (!isset($test['video']['frames'][0])) { $test['video']['frames'][0] = $test['video']['frames'][$test['video']['start']]; } } } }