summaryrefslogtreecommitdiffstats
path: root/WebKitTools/TestResultServer
diff options
context:
space:
mode:
Diffstat (limited to 'WebKitTools/TestResultServer')
-rw-r--r--WebKitTools/TestResultServer/handlers/testfilehandler.py57
-rw-r--r--WebKitTools/TestResultServer/index.yaml15
-rwxr-xr-xWebKitTools/TestResultServer/model/jsonresults.py65
-rwxr-xr-xWebKitTools/TestResultServer/model/jsonresults_unittest.py11
-rw-r--r--WebKitTools/TestResultServer/model/testfile.py28
-rw-r--r--WebKitTools/TestResultServer/templates/showfilelist.html5
-rw-r--r--WebKitTools/TestResultServer/templates/uploadform.html8
7 files changed, 119 insertions, 70 deletions
diff --git a/WebKitTools/TestResultServer/handlers/testfilehandler.py b/WebKitTools/TestResultServer/handlers/testfilehandler.py
index 4d1320f..d817890 100644
--- a/WebKitTools/TestResultServer/handlers/testfilehandler.py
+++ b/WebKitTools/TestResultServer/handlers/testfilehandler.py
@@ -36,6 +36,7 @@ from google.appengine.ext.webapp import template
from model.jsonresults import JsonResults
from model.testfile import TestFile
+PARAM_MASTER = "master"
PARAM_BUILDER = "builder"
PARAM_DIR = "dir"
PARAM_FILE = "file"
@@ -51,25 +52,26 @@ class DeleteFile(webapp.RequestHandler):
def get(self):
key = self.request.get(PARAM_KEY)
+ master = self.request.get(PARAM_MASTER)
builder = self.request.get(PARAM_BUILDER)
test_type = self.request.get(PARAM_TEST_TYPE)
name = self.request.get(PARAM_NAME)
logging.debug(
- "Deleting File, builder: %s, test_type: %s, name: %s, key: %s.",
- builder, test_type, name, key)
+ "Deleting File, master: %s, builder: %s, test_type: %s, name: %s, key: %s.",
+ master, builder, test_type, name, key)
- TestFile.delete_file(key, builder, test_type, name, 100)
+ TestFile.delete_file(key, master, builder, test_type, name, 100)
# Display file list after deleting the file.
- self.redirect("/testfile?builder=%s&testtype=%s&name=%s"
- % (builder, test_type, name))
+ self.redirect("/testfile?master=%s&builder=%s&testtype=%s&name=%s"
+ % (master, builder, test_type, name))
class GetFile(webapp.RequestHandler):
"""Get file content or list of files for given builder and name."""
- def _get_file_list(self, builder, test_type, name):
+ def _get_file_list(self, master, builder, test_type, name):
"""Get and display a list of files that matches builder and file name.
Args:
@@ -79,15 +81,16 @@ class GetFile(webapp.RequestHandler):
"""
files = TestFile.get_files(
- builder, test_type, name, load_data=False, limit=100)
+ master, builder, test_type, name, load_data=False, limit=100)
if not files:
- logging.info("File not found, builder: %s, test_type: %s, name: %s.",
- builder, test_type, name)
+ logging.info("File not found, master: %s, builder: %s, test_type: %s, name: %s.",
+ master, builder, test_type, name)
self.response.out.write("File not found")
return
template_values = {
"admin": users.is_current_user_admin(),
+ "master": master,
"builder": builder,
"test_type": test_type,
"name": name,
@@ -96,7 +99,7 @@ class GetFile(webapp.RequestHandler):
self.response.out.write(template.render("templates/showfilelist.html",
template_values))
- def _get_file_content(self, builder, test_type, name):
+ def _get_file_content(self, master, builder, test_type, name):
"""Return content of the file that matches builder and file name.
Args:
@@ -106,15 +109,15 @@ class GetFile(webapp.RequestHandler):
"""
files = TestFile.get_files(
- builder, test_type, name, load_data=True, limit=1)
+ master, builder, test_type, name, load_data=True, limit=1)
if not files:
- logging.info("File not found, builder: %s, test_type: %s, name: %s.",
- builder, test_type, name)
+ logging.info("File not found, master %s, builder: %s, test_type: %s, name: %s.",
+ master, builder, test_type, name)
return None
return files[0].data
- def _get_test_list_json(self, builder, test_type):
+ def _get_test_list_json(self, master, builder, test_type):
"""Return json file with test name list only, do not include test
results and other non-test-data .
@@ -123,13 +126,14 @@ class GetFile(webapp.RequestHandler):
test_type: type of test results.
"""
- json = self._get_file_content(builder, test_type, "results.json")
+ json = self._get_file_content(master, builder, test_type, "results.json")
if not json:
return None
return JsonResults.get_test_list(builder, json)
def get(self):
+ master = self.request.get(PARAM_MASTER)
builder = self.request.get(PARAM_BUILDER)
test_type = self.request.get(PARAM_TEST_TYPE)
name = self.request.get(PARAM_NAME)
@@ -137,19 +141,19 @@ class GetFile(webapp.RequestHandler):
test_list_json = self.request.get(PARAM_TEST_LIST_JSON)
logging.debug(
- "Getting files, builder: %s, test_type: %s, name: %s.",
- builder, test_type, name)
+ "Getting files, master %s, builder: %s, test_type: %s, name: %s.",
+ master, builder, test_type, name)
# If parameter "dir" is specified or there is no builder or filename
# specified in the request, return list of files, otherwise, return
# file content.
if dir or not builder or not name:
- return self._get_file_list(builder, test_type, name)
+ return self._get_file_list(master, builder, test_type, name)
if name == "results.json" and test_list_json:
- json = self._get_test_list_json(builder, test_type)
+ json = self._get_test_list_json(master, builder, test_type)
else:
- json = self._get_file_content(builder, test_type, name)
+ json = self._get_file_content(master, builder, test_type, name)
if json:
self.response.headers["Content-Type"] = "text/plain; charset=utf-8"
@@ -170,12 +174,13 @@ class Upload(webapp.RequestHandler):
self.response.out.write("FAIL: missing builder parameter.")
return
+ master = self.request.get(PARAM_MASTER)
test_type = self.request.get(PARAM_TEST_TYPE)
incremental = self.request.get(PARAM_INCREMENTAL)
logging.debug(
- "Processing upload request, builder: %s, test_type: %s.",
- builder, test_type)
+ "Processing upload request, master: %s, builder: %s, test_type: %s.",
+ master, builder, test_type)
# There are two possible types of each file_params in the request:
# one file item or a list of file items.
@@ -193,15 +198,15 @@ class Upload(webapp.RequestHandler):
if ((incremental and filename == "results.json") or
(filename == "incremental_results.json")):
# Merge incremental json results.
- saved_file = JsonResults.update(builder, test_type, file.value)
+ saved_file = JsonResults.update(master, builder, test_type, file.value)
else:
saved_file = TestFile.update(
- builder, test_type, file.filename, file.value)
+ master, builder, test_type, file.filename, file.value)
if not saved_file:
errors.append(
- "Upload failed, builder: %s, test_type: %s, name: %s." %
- (builder, test_type, file.filename))
+ "Upload failed, master: %s, builder: %s, test_type: %s, name: %s." %
+ (master, builder, test_type, file.filename))
if errors:
messages = "FAIL: " + "; ".join(errors)
diff --git a/WebKitTools/TestResultServer/index.yaml b/WebKitTools/TestResultServer/index.yaml
index 50284dc..a7d3e48 100644
--- a/WebKitTools/TestResultServer/index.yaml
+++ b/WebKitTools/TestResultServer/index.yaml
@@ -25,6 +25,15 @@ indexes:
- kind: TestFile
properties:
- name: builder
+ - name: master
+ - name: name
+ - name: test_type
+ - name: date
+ direction: desc
+
+- kind: TestFile
+ properties:
+ - name: builder
- name: name
- name: date
direction: desc
@@ -39,6 +48,12 @@ indexes:
- kind: TestFile
properties:
+ - name: master
+ - name: date
+ direction: desc
+
+- kind: TestFile
+ properties:
- name: name
- name: date
direction: desc
diff --git a/WebKitTools/TestResultServer/model/jsonresults.py b/WebKitTools/TestResultServer/model/jsonresults.py
index 4520e96..97b277a 100755
--- a/WebKitTools/TestResultServer/model/jsonresults.py
+++ b/WebKitTools/TestResultServer/model/jsonresults.py
@@ -33,6 +33,7 @@ import logging
from model.testfile import TestFile
JSON_RESULTS_FILE = "results.json"
+JSON_RESULTS_FILE_SMALL = "results-small.json"
JSON_RESULTS_PREFIX = "ADD_RESULTS("
JSON_RESULTS_SUFFIX = ");"
JSON_RESULTS_VERSION_KEY = "version"
@@ -45,6 +46,7 @@ JSON_RESULTS_NO_DATA = "N"
JSON_RESULTS_MIN_TIME = 1
JSON_RESULTS_VERSION = 3
JSON_RESULTS_MAX_BUILDS = 1500
+JSON_RESULTS_MAX_BUILDS_SMALL = 200
class JsonResults(object):
@@ -106,7 +108,7 @@ class JsonResults(object):
return None
@classmethod
- def _merge_json(cls, aggregated_json, incremental_json):
+ def _merge_json(cls, aggregated_json, incremental_json, num_runs):
"""Merge incremental json into aggregated json results.
Args:
@@ -120,19 +122,19 @@ class JsonResults(object):
# Merge non tests property data.
# Tests properties are merged in _merge_tests.
- if not cls._merge_non_test_data(aggregated_json, incremental_json):
+ if not cls._merge_non_test_data(aggregated_json, incremental_json, num_runs):
return False
# Merge tests results and times
incremental_tests = incremental_json[JSON_RESULTS_TESTS]
if incremental_tests:
aggregated_tests = aggregated_json[JSON_RESULTS_TESTS]
- cls._merge_tests(aggregated_tests, incremental_tests)
+ cls._merge_tests(aggregated_tests, incremental_tests, num_runs)
return True
@classmethod
- def _merge_non_test_data(cls, aggregated_json, incremental_json):
+ def _merge_non_test_data(cls, aggregated_json, incremental_json, num_runs):
"""Merge incremental non tests property data into aggregated json results.
Args:
@@ -173,13 +175,13 @@ class JsonResults(object):
return False
# Merge this build into aggreagated results.
- cls._merge_one_build(aggregated_json, incremental_json, index)
+ cls._merge_one_build(aggregated_json, incremental_json, index, num_runs)
return True
@classmethod
def _merge_one_build(cls, aggregated_json, incremental_json,
- incremental_index):
+ incremental_index, num_runs):
"""Merge one build of incremental json into aggregated json results.
Args:
@@ -198,12 +200,12 @@ class JsonResults(object):
aggregated_json[key].insert(
0, incremental_json[key][incremental_index])
aggregated_json[key] = \
- aggregated_json[key][:JSON_RESULTS_MAX_BUILDS]
+ aggregated_json[key][:num_runs]
else:
aggregated_json[key] = incremental_json[key]
@classmethod
- def _merge_tests(cls, aggregated_json, incremental_json):
+ def _merge_tests(cls, aggregated_json, incremental_json, num_runs):
"""Merge "tests" properties:results, times.
Args:
@@ -225,15 +227,15 @@ class JsonResults(object):
times = [[1, 0]]
cls._insert_item_run_length_encoded(
- results, aggregated_test[JSON_RESULTS_RESULTS])
+ results, aggregated_test[JSON_RESULTS_RESULTS], num_runs)
cls._insert_item_run_length_encoded(
- times, aggregated_test[JSON_RESULTS_TIMES])
+ times, aggregated_test[JSON_RESULTS_TIMES], num_runs)
cls._normalize_results_json(test_name, aggregated_json)
else:
aggregated_json[test_name] = incremental_json[test_name]
@classmethod
- def _insert_item_run_length_encoded(cls, incremental_item, aggregated_item):
+ def _insert_item_run_length_encoded(cls, incremental_item, aggregated_item, num_runs):
"""Inserts the incremental run-length encoded results into the aggregated
run-length encoded results.
@@ -245,7 +247,7 @@ class JsonResults(object):
for item in incremental_item:
if len(aggregated_item) and item[1] == aggregated_item[0][1]:
aggregated_item[0][0] = min(
- aggregated_item[0][0] + item[0], JSON_RESULTS_MAX_BUILDS)
+ aggregated_item[0][0] + item[0], num_runs)
else:
aggregated_item.insert(0, item)
@@ -340,7 +342,7 @@ class JsonResults(object):
return True
@classmethod
- def merge(cls, builder, aggregated, incremental, sort_keys=False):
+ def merge(cls, builder, aggregated, incremental, num_runs, sort_keys=False):
"""Merge incremental json file data with aggregated json file data.
Args:
@@ -378,9 +380,7 @@ class JsonResults(object):
logging.info("Merging json results...")
try:
- if not cls._merge_json(
- aggregated_json[builder],
- incremental_json[builder]):
+ if not cls._merge_json(aggregated_json[builder], incremental_json[builder], num_runs):
return None
except Exception, err:
logging.error("Failed to merge json results: %s", str(err))
@@ -391,37 +391,48 @@ class JsonResults(object):
return cls._generate_file_data(aggregated_json, sort_keys)
@classmethod
- def update(cls, builder, test_type, incremental):
+ def update(cls, master, builder, test_type, incremental):
"""Update datastore json file data by merging it with incremental json
- file.
+ file. Writes the large file and a small file. The small file just stores
+ fewer runs.
Args:
+ master: master name.
builder: builder name.
test_type: type of test results.
incremental: incremental json file data to merge.
Returns:
- TestFile object if update succeeds or
+ Large TestFile object if update succeeds or
None on failure.
"""
+ small_file = cls.update_file(master, builder, test_type, incremental, JSON_RESULTS_FILE_SMALL, JSON_RESULTS_MAX_BUILDS_SMALL)
+ large_file = cls.update_file(master, builder, test_type, incremental, JSON_RESULTS_FILE, JSON_RESULTS_MAX_BUILDS)
- files = TestFile.get_files(builder, test_type, JSON_RESULTS_FILE)
+ if small_file and large_file:
+ return large_file
+ return None
+
+ @classmethod
+ def update_file(cls, master, builder, test_type, incremental, filename, num_runs):
+ files = TestFile.get_files(master, builder, test_type, filename)
if files:
file = files[0]
- new_results = cls.merge(builder, file.data, incremental)
+ new_results = cls.merge(builder, file.data, incremental, num_runs)
else:
# Use the incremental data if there is no aggregated file to merge.
- file = TestFile()
+ file = TestFile()
+ file.master = master
file.builder = builder
file.test_type = test_type
- file.name = JSON_RESULTS_FILE
+ file.name = filename
new_results = incremental
logging.info("No existing json results, incremental json is saved.")
- if not new_results:
- return None
-
- if not file.save(new_results):
+ if not new_results or not file.save(new_results):
+ logging.info(
+ "Update failed, master: %s, builder: %s, test_type: %s, name: %s." %
+ (master, builder, test_type, filename))
return None
return file
diff --git a/WebKitTools/TestResultServer/model/jsonresults_unittest.py b/WebKitTools/TestResultServer/model/jsonresults_unittest.py
index 15b659b..c70b90c 100755
--- a/WebKitTools/TestResultServer/model/jsonresults_unittest.py
+++ b/WebKitTools/TestResultServer/model/jsonresults_unittest.py
@@ -26,10 +26,14 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-import jsonresults
+try:
+ import jsonresults
+ from jsonresults import JsonResults
+except ImportError:
+ print "ERROR: Add the TestResultServer, google_appengine and yaml/lib directories to your PYTHONPATH"
+
import unittest
-from jsonresults import JsonResults
JSON_RESULTS_TEMPLATE = (
'{"Webkit":{'
@@ -118,7 +122,8 @@ class JsonResultsTest(unittest.TestCase):
aggregated_results = self._make_test_json(aggregated_data)
incremental_results = self._make_test_json(incremental_data)
merged_results = JsonResults.merge(self._builder,
- aggregated_results, incremental_results, sort_keys=True)
+ aggregated_results, incremental_results, jsonresults.JSON_RESULTS_MAX_BUILDS,
+ sort_keys=True)
if expected_data:
expected_results = self._make_test_json(expected_data)
diff --git a/WebKitTools/TestResultServer/model/testfile.py b/WebKitTools/TestResultServer/model/testfile.py
index ce92b65..e600c99 100644
--- a/WebKitTools/TestResultServer/model/testfile.py
+++ b/WebKitTools/TestResultServer/model/testfile.py
@@ -35,11 +35,12 @@ from model.datastorefile import DataStoreFile
class TestFile(DataStoreFile):
+ master = db.StringProperty()
builder = db.StringProperty()
test_type = db.StringProperty()
@classmethod
- def delete_file(cls, key, builder, test_type, name, limit):
+ def delete_file(cls, key, master, builder, test_type, name, limit):
if key:
file = db.get(key)
if not file:
@@ -48,10 +49,10 @@ class TestFile(DataStoreFile):
file._delete_all()
else:
- files = cls.get_files(builder, test_type, name, limit)
+ files = cls.get_files(master, builder, test_type, name, limit)
if not files:
logging.warning(
- "File not found, builder: %s, test_type:%s, name: %s.",
+ "File not found, master: %s, builder: %s, test_type:%s, name: %s.",
builder, test_type, name)
return False
@@ -61,8 +62,10 @@ class TestFile(DataStoreFile):
return True
@classmethod
- def get_files(cls, builder, test_type, name, load_data=True, limit=1):
+ def get_files(cls, master, builder, test_type, name, load_data=True, limit=1):
query = TestFile.all()
+ if master:
+ query = query.filter("master =", master)
if builder:
query = query.filter("builder =", builder)
if test_type:
@@ -78,8 +81,9 @@ class TestFile(DataStoreFile):
return files
@classmethod
- def add_file(cls, builder, test_type, name, data):
+ def add_file(cls, master, builder, test_type, name, data):
file = TestFile()
+ file.master = master
file.builder = builder
file.test_type = test_type
file.name = name
@@ -88,24 +92,24 @@ class TestFile(DataStoreFile):
return None
logging.info(
- "File saved, builder: %s, test_type: %s, name: %s, key: %s.",
- builder, test_type, file.name, str(file.data_keys))
+ "File saved, master: %s, builder: %s, test_type: %s, name: %s, key: %s.",
+ master, builder, test_type, file.name, str(file.data_keys))
return file
@classmethod
- def update(cls, builder, test_type, name, data):
- files = cls.get_files(builder, test_type, name)
+ def update(cls, master, builder, test_type, name, data):
+ files = cls.get_files(master, builder, test_type, name)
if not files:
- return cls.add_file(builder, test_type, name, data)
+ return cls.add_file(master, builder, test_type, name, data)
file = files[0]
if not file.save(data):
return None
logging.info(
- "File replaced, builder: %s, test_type: %s, name: %s, data key: %s.",
- builder, test_type, file.name, str(file.data_keys))
+ "File replaced, master: %s, builder: %s, test_type: %s, name: %s, data key: %s.",
+ master, builder, test_type, file.name, str(file.data_keys))
return file
diff --git a/WebKitTools/TestResultServer/templates/showfilelist.html b/WebKitTools/TestResultServer/templates/showfilelist.html
index fa72b7f..d292fe2 100644
--- a/WebKitTools/TestResultServer/templates/showfilelist.html
+++ b/WebKitTools/TestResultServer/templates/showfilelist.html
@@ -13,6 +13,7 @@
<div>
<table>
<tr>
+ <th>Master</th>
<th>Builder</th>
<th>Test Type</th>
<th>File</th>
@@ -22,6 +23,10 @@
{% endif %}
{% for file in files %}
<tr>{% if file.builder and file.name %}
+ <td><a href="/testfile?master={{ file.master }}" >
+ {{ file.master }}
+ </a>
+ </td>
<td><a href="/testfile?builder={{ file.builder }}" >
{{ file.builder }}
</a>
diff --git a/WebKitTools/TestResultServer/templates/uploadform.html b/WebKitTools/TestResultServer/templates/uploadform.html
index 3506c9c..9974a24 100644
--- a/WebKitTools/TestResultServer/templates/uploadform.html
+++ b/WebKitTools/TestResultServer/templates/uploadform.html
@@ -10,12 +10,16 @@
<br>
<table>
<tr>
+ <td class=label><label>Master:</label></td>
+ <td><input class=inputtext type="text" name="master" placeholder="Chromium"/></td>
+ </tr>
+ <tr>
<td class=label><label>Builder:</label></td>
- <td><input class=inputtext type="text" name="builder" value="Webkit"/></td>
+ <td><input class=inputtext type="text" name="builder" placeholder="Webkit"/></td>
</tr>
<tr>
<td class=label><label>Test Type:</label></td>
- <td><input class=inputtext type="text" name="testtype" value=""/></td>
+ <td><input class=inputtext type="text" name="testtype" placeholder="layout-tests"/></td>
</tr>
</table>
<br>