jenkins-bot has submitted this change and it was merged.

Change subject: Flag changed diffs in rt-server results views
......................................................................


Flag changed diffs in rt-server results views

* This patch adds new views; previous result views are still available.

* The new commit link in the regressions/fixes view now takes you to
  a results view that flags skips/fails occuring in the new commit
  but not in the old commit. And the old commit link takes you to a
  results view that flags skips/fails no longer in the new commit.

* Overflagging (eg flagging too many diffs as new) is possible.
  Say a page edit changes lines in a way that we would think of the
  "same diff" occurring in "different lines." Then it's technically a
  new line-based diff, and will be flagged as such. This does not seem
  to occur too much in practice (and won't happen at all if, in the
  future, results are pinned to a specific page edit across test runs).

Change-Id: I95dafb77e83ad63b7f46de25d48f78ba19fd0704
---
M lib/mediawiki.Util.js
A tests/server/diff.js
M tests/server/package.json
M tests/server/server.js
M tests/server/static/result.css
5 files changed, 241 insertions(+), 17 deletions(-)

Approvals:
  Marcoil: Looks good to me, approved
  jenkins-bot: Verified



diff --git a/lib/mediawiki.Util.js b/lib/mediawiki.Util.js
index f04ed86..8066754 100644
--- a/lib/mediawiki.Util.js
+++ b/lib/mediawiki.Util.js
@@ -1293,6 +1293,14 @@
        return pairs;
 };
 
+var diffTokens = function(oldString, newString, tokenize) {
+       if (oldString === newString) {
+               return [['=', [newString]]];
+       } else {
+               return simpleDiff.diff(tokenize(oldString), 
tokenize(newString));
+       }
+};
+
 var diffLines = function(oldString, newString) {
        var lineTokenize = function(value) {
                var retLines = [],
@@ -1306,17 +1314,11 @@
                        } else if (line) {
                                retLines.push(line);
                        }
-                       }
+               }
                return retLines;
        };
-
-       if (oldString === newString) {
-               return [['=', [newString]]];
-       } else {
-               return simpleDiff.diff(lineTokenize(oldString), 
lineTokenize(newString));
-       }
+       return diffTokens(oldString, newString, lineTokenize);
 };
-
 
 Util.convertDiffToOffsetPairs = convertDiffToOffsetPairs;
 Util.diffLines = diffLines;
diff --git a/tests/server/diff.js b/tests/server/diff.js
new file mode 100644
index 0000000..b532dcc
--- /dev/null
+++ b/tests/server/diff.js
@@ -0,0 +1,98 @@
+/*
+* This file contains diff related functions for use in tests/server,
+* to compare test results for a given page from different revisions.
+*/
+
+"use strict";
+var simpleDiff = require('simplediff');
+
+var Diff = {};
+( function (exports) {
+
+       var diffTokens = function(oldString, newString, tokenize) {
+               if (oldString === newString) {
+                       return [['=', [newString]]];
+               } else {
+                       return simpleDiff.diff(tokenize(oldString), 
tokenize(newString));
+               }
+       };
+
+       var diffResults = function(oldString, newString) {
+               var testcaseTokenize = function(resultString) {
+                       var testcases = 
resultString.split(/<\/skipped>|<\/failure>/);
+                       // Omit everything that's not part of a <skipped> or 
<failure> element,
+                       // as this can contain info we're not interested in 
diffing
+                       // (eg character number within original text, 
perfstats).
+                       testcases = testcases.slice(0, -1);
+                       testcases = testcases.map(function(testcase) {
+                               var skipTagIndex = testcase.indexOf('<skipped');
+                               if (skipTagIndex !== -1) {
+                                       return testcase.slice(skipTagIndex);
+                               } else {
+                                       return 
testcase.slice(testcase.indexOf('<failure'));
+                               }
+                       });
+                       return testcases;
+               };
+
+               return diffTokens(oldString, newString, testcaseTokenize);
+       };
+
+       var testcaseStatus = function(diff, flag) {
+               // Returns an array of 0's and 1's, where, supposing flag is 
'+', 1 in the nth position
+               // means that the n'th token of the newer diffed item isn't a 
token of the older item.
+               // (And symmetrically for '-', interchanging roles of 'newer' 
and 'older'.)
+               var array = [];
+               for (var i = 0, l = diff.length; i < l; i++) {
+                       var change = diff[i];
+                       if (change[0] === flag) {
+                               for (var j = 0; j < change[1].length; j++) {
+                                       array.push(1);
+                               }
+                       } else if (change[0] === '=') {
+                               for (var k = 0; k < change[1].length; k++) {
+                                       array.push(0);
+                               }
+                       }
+               }
+               return array;
+       };
+
+       // If flag is '+', adds status="new" attribute to <testcase> tags for 
testcases ocurring in
+       // newString but not in oldString. Otherwise (flag is '-') adds 
status="old" to <testcase>'s occuring in
+       // oldString but not in newString.
+       exports.resultFlagged = function(oldString, newString, oldCommit, 
newCommit, flag) {
+               // If one of the two results is an error, don't flag 
differences.
+               if (oldString.slice(0,6) === '<error' || newString.slice(0,6) 
=== '<error') {
+                       var output = flag === '+' ? newString : oldString;
+                       return output;
+               }
+
+               var status = flag === '+' ? 'new' : 'old';
+               var xmlWrapper = flag === '+' ? 'FlagNewTestcases' : 
'FlagOldTestcases';
+               var testcases = flag === '+' ? 
newString.split(/(<\/testcase>)/) : oldString.split(/(<\/testcase>)/);
+
+               var diff = diffResults(oldString, newString);
+               var statusArray = testcaseStatus(diff, flag);
+               var startTestcases = testcases[0].indexOf('<testcase');
+               var pre = testcases[0].slice(0, startTestcases);
+               var post = testcases[testcases.length - 1];
+               testcases[0] = testcases[0].slice(startTestcases);
+
+               var results = [];
+               for (var i = 0, l = testcases.length - 1; i < l; i++) {
+                       if (i%2 === 0 && statusArray[i/2]) {
+                               testcases[i] = 
testcases[i].replace('<testcase', '<testcase status="' + status + '"');
+                       }
+                       results.push(testcases[i]);
+               }
+               var result = results.join('');
+               return '<' + xmlWrapper  + ' oldCommit ="' + oldCommit + '" 
newCommit="' + newCommit + '" >' +
+                       pre + result + post + '</' + xmlWrapper + '>';
+       };
+
+}(Diff));
+
+if (typeof module === "object") {
+       module.exports.Diff = Diff;
+}
\ No newline at end of file
diff --git a/tests/server/package.json b/tests/server/package.json
index 79560a1..02eac44 100644
--- a/tests/server/package.json
+++ b/tests/server/package.json
@@ -10,7 +10,8 @@
                "mysql": "2.x.x",
                "mysql-queues": "1.x.x",
                "express": "2.5.x",
-               "handlebars": "~1.3.0"
+               "handlebars": "~1.3.0",
+               "simplediff": "0.1.1"
        },
        "main": "server.js"
 }
diff --git a/tests/server/server.js b/tests/server/server.js
index 8b5ee33..3b053a5 100755
--- a/tests/server/server.js
+++ b/tests/server/server.js
@@ -4,7 +4,8 @@
 
 var express = require( 'express' ),
        optimist = require( 'optimist' ),
-       hbs = require( 'handlebars' );
+       hbs = require( 'handlebars' ),
+       Diff = require('./diff.js').Diff;
 
 // Default options
 var defaults = {
@@ -328,6 +329,14 @@
     'SELECT result FROM results ' +
     'JOIN pages ON pages.id = results.page_id ' +
     'WHERE results.commit_hash = ? AND pages.title = ? AND pages.prefix = ?';
+
+var dbGetTwoResults =
+       'SELECT result FROM results ' +
+       'JOIN commits ON results.commit_hash = commits.hash ' +
+       'JOIN pages ON pages.id = results.page_id ' +
+       'WHERE pages.title = ? AND pages.prefix = ? ' +
+       'AND (commits.hash = ? OR commits.hash = ?) ' +
+       'ORDER BY commits.timestamp';
 
 var dbFailedFetches =
        'SELECT title, prefix FROM pages WHERE num_fetch_errors >= ?';
@@ -941,7 +950,6 @@
                                return Math.round( val * 100 ) / 100;
                        }
                });
-
                res.render('index.html', data);
                }
        });
@@ -1026,6 +1034,46 @@
        } else {
                db.query( dbGetOneResult, [ title, prefix ], 
resultWebCallback.bind( null, req, res ) );
        }
+};
+
+var diffResultWebCallback = function(req, res, flag, err, row) {
+       if ( err ) {
+               console.error( err );
+               res.send( err.toString(), 500 );
+       } else if (row.length === 2) {
+               var oldCommit = req.params[0].slice(0,10);
+               var newCommit = req.params[1].slice(0,10);
+               var oldResult = row[0].result;
+               var newResult = row[1].result;
+               var flagResult = Diff.resultFlagged(oldResult, newResult, 
oldCommit, newCommit, flag);
+               res.setHeader( 'Content-Type', 'text/xml; charset=UTF-8' );
+               res.status(200);
+               res.write( '<?xml-stylesheet href="/static/result.css"?>\n' );
+               res.end(flagResult);
+       } else {
+               var commit = flag === '+' ? req.params[1] : req.params[0];
+               res.redirect('/result/' + commit + '/' + req.params[2] + '/' + 
req.params[3]);
+       }
+};
+
+var resultFlagNewWebInterface = function(req, res) {
+       var oldCommit = req.params[0];
+       var newCommit = req.params[1];
+       var prefix = req.params[2];
+       var title = req.params[3];
+
+       db.query(dbGetTwoResults, [ title, prefix, oldCommit, newCommit ],
+               diffResultWebCallback.bind(null, req, res, '+'));
+};
+
+var resultFlagOldWebInterface = function(req, res) {
+       var oldCommit = req.params[0];
+       var newCommit = req.params[1];
+       var prefix = req.params[2];
+       var title = req.params[3];
+
+       db.query(dbGetTwoResults, [ title, prefix, oldCommit, newCommit ],
+               diffResultWebCallback.bind(null, req, res, '-'));
 };
 
 var GET_failedFetches = function( req, res ) {
@@ -1146,12 +1194,26 @@
        };
 };
 
+var newCommitLinkData = function(oldCommit, newCommit, title, prefix) {
+       return {
+               url: '/resultFlagNew/' + oldCommit + '/' + newCommit + '/' + 
prefix + '/' + title,
+               name: newCommit.substr(0,7)
+       };
+};
+
+var oldCommitLinkData = function(oldCommit, newCommit, title, prefix) {
+       return {
+               url: '/resultFlagOld/' + oldCommit + '/' + newCommit + '/' + 
prefix + '/' + title,
+               name: oldCommit.substr(0,7)
+       };
+};
+
 var makeRegressionRow = function(row) {
        return [
                pageTitleData(row),
-               commitLinkData(row.old_commit, row.title, row.prefix),
+               oldCommitLinkData(row.old_commit, row.new_commit, row.title, 
row.prefix),
                row.old_errors + "|" + row.old_fails + "|" + row.old_skips,
-               commitLinkData(row.new_commit, row.title, row.prefix),
+               newCommitLinkData(row.old_commit, row.new_commit, row.title, 
row.prefix),
                row.errors + "|" + row.fails + "|" + row.skips
        ];
 };
@@ -1159,8 +1221,8 @@
 var makeOneDiffRegressionRow = function(row) {
        return [
                pageTitleData(row),
-               commitLinkData(row.old_commit, row.title, row.prefix),
-               commitLinkData(row.new_commit, row.title, row.prefix)
+               oldCommitLinkData(row.old_commit, row.new_commit, row.title, 
row.prefix),
+               newCommitLinkData(row.old_commit, row.new_commit, row.title, 
row.prefix)
        ];
 };
 
@@ -1514,6 +1576,12 @@
 // Results for a title on any commit
 app.get( /^\/result\/([a-f0-9]*)\/([^\/]+)\/(.*)$/, resultWebInterface );
 
+// Results for a title on a commit, flag skips/fails new since older commit
+app.get( /^\/resultFlagNew\/([a-f0-9]*)\/([a-f0-9]*)\/([^\/]+)\/(.*)$/, 
resultFlagNewWebInterface );
+
+// Results for a title on a commit, flag skips/fails no longer in newer commit
+app.get( /^\/resultFlagOld\/([a-f0-9]*)\/([a-f0-9]*)\/([^\/]+)\/(.*)$/, 
resultFlagOldWebInterface );
+
 // List of failures sorted by severity
 app.get( /^\/topfails\/(\d+)$/, failsWebInterface );
 // 0th page
diff --git a/tests/server/static/result.css b/tests/server/static/result.css
index 8661c53..a914d45 100644
--- a/tests/server/static/result.css
+++ b/tests/server/static/result.css
@@ -4,11 +4,11 @@
     display: block;
     border-left: 7px solid red;
 }
+
 skipped {
     font-family: monospace;
     white-space: pre;
     display: block;
-    /* orange border */
     border-left: 7px solid yellow;
 }
 
@@ -20,8 +20,63 @@
 testcase:before {
     font-size: 70%;
     font-weight: bold;
-    color: grey;
+    color: gray;
     margin-left: 8px;
     content: attr(name) ":";
 }
 
+/* For result comparison views (flag changed skips/fails) */
+
+FlagNewTestcases:before {
+    padding-top: 8px;
+    padding-left: 8px;
+    margin-bottom: 8px;
+    display: block;
+    font-size: 1.2em;
+    border-bottom: 1px solid #aaa;
+    content: "Diffs in commit " attr(newCommit) " (flagged if new since older 
" attr(oldCommit) ")";
+    font-family: sans-serif;
+}
+
+FlagOldTestcases:before {
+    padding-top: 8px;
+    padding-left: 8px;
+    margin-bottom: 8px;
+    display: block;
+    font-size: 1.2em;
+    border-bottom: 1px solid #aaa;
+    content: "Diffs in commit " attr(oldCommit) " (flagged if no longer in 
newer " attr(newCommit) ")";
+    font-family: sans-serif;
+}
+
+testcase[status='new'] skipped:before {
+    background: yellow;
+    font-size: 70%;
+    content: 'New Syntactic Diff ';
+    font-family: sans-serif;
+    padding: 2px;
+}
+
+testcase[status='new'] failure:before {
+    background: red;
+    font-size: 70%;
+    content: 'New Semantic Diff ';
+    font-family: sans-serif;
+    padding: 2px;
+}
+
+testcase[status='old'] skipped:before {
+    background-color: rgba(255,255,0,0.2);
+    font-size: 70%;
+    content: 'Not in newer revision ';
+    font-family: sans-serif;
+    padding: 2px;
+}
+
+testcase[status='old'] failure:before {
+    background-color: rgba(255,0,0,0.2);
+    font-size: 70%;
+    content: 'Not in newer revision ';
+    font-family: sans-serif;
+    padding: 2px;
+}
\ No newline at end of file

-- 
To view, visit https://gerrit.wikimedia.org/r/117116
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: I95dafb77e83ad63b7f46de25d48f78ba19fd0704
Gerrit-PatchSet: 5
Gerrit-Project: mediawiki/services/parsoid
Gerrit-Branch: master
Gerrit-Owner: Bebirchall <bebirch...@gmail.com>
Gerrit-Reviewer: Bebirchall <bebirch...@gmail.com>
Gerrit-Reviewer: Marcoil <marc...@wikimedia.org>
Gerrit-Reviewer: Subramanya Sastry <ssas...@wikimedia.org>
Gerrit-Reviewer: jenkins-bot <>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to