diff options
Diffstat (limited to 'devtools/shared/tests/unit/test_csslexer.js')
-rw-r--r-- | devtools/shared/tests/unit/test_csslexer.js | 242 |
1 files changed, 242 insertions, 0 deletions
diff --git a/devtools/shared/tests/unit/test_csslexer.js b/devtools/shared/tests/unit/test_csslexer.js new file mode 100644 index 000000000..35855640b --- /dev/null +++ b/devtools/shared/tests/unit/test_csslexer.js @@ -0,0 +1,242 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +// This file is a copy of layout/style/test/test_csslexer.js, modified +// to use both our pure-JS lexer and the DOMUtils lexer for +// cross-checking. + +"use strict"; + +const jsLexer = require("devtools/shared/css/lexer"); +const domutils = Components.classes["@mozilla.org/inspector/dom-utils;1"] + .getService(Components.interfaces.inIDOMUtils); + +// An object that acts like a CSSLexer but verifies that the DOM lexer +// and the JS lexer do the same thing. +function DoubleLexer(input) { + do_print("DoubleLexer input: " + input); + this.domLexer = domutils.getCSSLexer(input); + this.jsLexer = jsLexer.getCSSLexer(input); +} + +DoubleLexer.prototype = { + checkState: function () { + equal(this.domLexer.lineNumber, this.jsLexer.lineNumber, + "check line number"); + equal(this.domLexer.columnNumber, this.jsLexer.columnNumber, + "check column number"); + }, + + get lineNumber() { + return this.domLexer.lineNumber; + }, + + get columnNumber() { + return this.domLexer.columnNumber; + }, + + performEOFFixup: function (inputString, preserveBackslash) { + let d = this.domLexer.performEOFFixup(inputString, preserveBackslash); + let j = this.jsLexer.performEOFFixup(inputString, preserveBackslash); + + equal(d, j); + return d; + }, + + mungeNumber: function (token) { + if (token && (token.tokenType === "number" || + token.tokenType === "percentage") && + !token.isInteger) { + // The JS lexer does its computations in double, but the + // platform lexer does its computations in float. Account for + // this discrepancy in a way that's sufficient for this test. + // See https://bugzilla.mozilla.org/show_bug.cgi?id=1163047 + token.number = parseFloat(token.number.toPrecision(8)); + } + }, + + nextToken: function () { + // Check state both before and after. + this.checkState(); + + let d = this.domLexer.nextToken(); + let j = this.jsLexer.nextToken(); + + this.mungeNumber(d); + this.mungeNumber(j); + + deepEqual(d, j); + + this.checkState(); + + return d; + } +}; + +function test_lexer(cssText, tokenTypes) { + let lexer = new DoubleLexer(cssText); + let reconstructed = ""; + let lastTokenEnd = 0; + let i = 0; + while (true) { + let token = lexer.nextToken(); + if (!token) { + break; + } + let combined = token.tokenType; + if (token.text) { + combined += ":" + token.text; + } + equal(combined, tokenTypes[i]); + ok(token.endOffset > token.startOffset); + equal(token.startOffset, lastTokenEnd); + lastTokenEnd = token.endOffset; + reconstructed += cssText.substring(token.startOffset, token.endOffset); + ++i; + } + // Ensure that we saw the correct number of tokens. + equal(i, tokenTypes.length); + // Ensure that the reported offsets cover all the text. + equal(reconstructed, cssText); +} + +var LEX_TESTS = [ + ["simple", ["ident:simple"]], + ["simple: { hi; }", + ["ident:simple", "symbol::", + "whitespace", "symbol:{", + "whitespace", "ident:hi", + "symbol:;", "whitespace", + "symbol:}"]], + ["/* whatever */", ["comment"]], + ["'string'", ["string:string"]], + ['"string"', ["string:string"]], + ["rgb(1,2,3)", ["function:rgb", "number", + "symbol:,", "number", + "symbol:,", "number", + "symbol:)"]], + ["@media", ["at:media"]], + ["#hibob", ["id:hibob"]], + ["#123", ["hash:123"]], + ["23px", ["dimension:px"]], + ["23%", ["percentage"]], + ["url(http://example.com)", ["url:http://example.com"]], + ["url('http://example.com')", ["url:http://example.com"]], + ["url( 'http://example.com' )", + ["url:http://example.com"]], + // In CSS Level 3, this is an ordinary URL, not a BAD_URL. + ["url(http://example.com", ["url:http://example.com"]], + // See bug 1153981 to understand why this gets a SYMBOL token. + ["url(http://example.com @", ["bad_url:http://example.com", "symbol:@"]], + ["quo\\ting", ["ident:quoting"]], + ["'bad string\n", ["bad_string:bad string", "whitespace"]], + ["~=", ["includes"]], + ["|=", ["dashmatch"]], + ["^=", ["beginsmatch"]], + ["$=", ["endsmatch"]], + ["*=", ["containsmatch"]], + + // URANGE may be on the way out, and it isn't used by devutils, so + // let's skip it. + + ["<!-- html comment -->", ["htmlcomment", "whitespace", "ident:html", + "whitespace", "ident:comment", "whitespace", + "htmlcomment"]], + + // earlier versions of CSS had "bad comment" tokens, but in level 3, + // unterminated comments are just comments. + ["/* bad comment", ["comment"]] +]; + +function test_lexer_linecol(cssText, locations) { + let lexer = new DoubleLexer(cssText); + let i = 0; + while (true) { + let token = lexer.nextToken(); + let startLine = lexer.lineNumber; + let startColumn = lexer.columnNumber; + + // We do this in a bit of a funny way so that we can also test the + // location of the EOF. + let combined = ":" + startLine + ":" + startColumn; + if (token) { + combined = token.tokenType + combined; + } + + equal(combined, locations[i]); + ++i; + + if (!token) { + break; + } + } + // Ensure that we saw the correct number of tokens. + equal(i, locations.length); +} + +function test_lexer_eofchar(cssText, argText, expectedAppend, + expectedNoAppend) { + let lexer = new DoubleLexer(cssText); + while (lexer.nextToken()) { + // Nothing. + } + + do_print("EOF char test, input = " + cssText); + + let result = lexer.performEOFFixup(argText, true); + equal(result, expectedAppend); + + result = lexer.performEOFFixup(argText, false); + equal(result, expectedNoAppend); +} + +var LINECOL_TESTS = [ + ["simple", ["ident:0:0", ":0:6"]], + ["\n stuff", ["whitespace:0:0", "ident:1:4", ":1:9"]], + ['"string with \\\nnewline" \r\n', ["string:0:0", "whitespace:1:8", + ":2:0"]] +]; + +var EOFCHAR_TESTS = [ + ["hello", "hello"], + ["hello \\", "hello \\\\", "hello \\\uFFFD"], + ["'hello", "'hello'"], + ["\"hello", "\"hello\""], + ["'hello\\", "'hello\\\\'", "'hello'"], + ["\"hello\\", "\"hello\\\\\"", "\"hello\""], + ["/*hello", "/*hello*/"], + ["/*hello*", "/*hello*/"], + ["/*hello\\", "/*hello\\*/"], + ["url(hello", "url(hello)"], + ["url('hello", "url('hello')"], + ["url(\"hello", "url(\"hello\")"], + ["url(hello\\", "url(hello\\\\)", "url(hello\\\uFFFD)"], + ["url('hello\\", "url('hello\\\\')", "url('hello')"], + ["url(\"hello\\", "url(\"hello\\\\\")", "url(\"hello\")"], +]; + +function run_test() { + let text, result; + for ([text, result] of LEX_TESTS) { + test_lexer(text, result); + } + + for ([text, result] of LINECOL_TESTS) { + test_lexer_linecol(text, result); + } + + let expectedAppend, expectedNoAppend; + for ([text, expectedAppend, expectedNoAppend] of EOFCHAR_TESTS) { + if (!expectedNoAppend) { + expectedNoAppend = expectedAppend; + } + test_lexer_eofchar(text, text, expectedAppend, expectedNoAppend); + } + + // Ensure that passing a different inputString to performEOFFixup + // doesn't cause an assertion trying to strip a backslash from the + // end of an empty string. + test_lexer_eofchar("'\\", "", "\\'", "'"); +} |