diff options
Diffstat (limited to 'WebKitTools/TestResultServer')
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> |