diff options
Diffstat (limited to '')
-rw-r--r-- | tests/unit/highlighter.js | 106 | ||||
-rw-r--r-- | tests/unit/insertSorted.js | 76 | ||||
-rw-r--r-- | tests/unit/jsParse.js | 194 | ||||
-rw-r--r-- | tests/unit/markup.js | 143 | ||||
-rw-r--r-- | tests/unit/params.js | 32 | ||||
-rw-r--r-- | tests/unit/signalTracker.js | 115 | ||||
-rw-r--r-- | tests/unit/url.js | 77 | ||||
-rw-r--r-- | tests/unit/versionCompare.js | 52 |
8 files changed, 795 insertions, 0 deletions
diff --git a/tests/unit/highlighter.js b/tests/unit/highlighter.js new file mode 100644 index 0000000..d582d38 --- /dev/null +++ b/tests/unit/highlighter.js @@ -0,0 +1,106 @@ +// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- + +// Test cases for SearchResult description match highlighter + +const JsUnit = imports.jsUnit; +const Pango = imports.gi.Pango; + +const Environment = imports.ui.environment; +Environment.init(); + +const Util = imports.misc.util; + +const tests = [ + { input: 'abc cba', + terms: null, + output: 'abc cba' }, + { input: 'abc cba', + terms: [], + output: 'abc cba' }, + { input: 'abc cba', + terms: [''], + output: 'abc cba' }, + { input: 'abc cba', + terms: ['a'], + output: '<b>a</b>bc cb<b>a</b>' }, + { input: 'abc cba', + terms: ['a', 'a'], + output: '<b>a</b>bc cb<b>a</b>' }, + { input: 'CaSe InSenSiTiVe', + terms: ['cas', 'sens'], + output: '<b>CaS</b>e In<b>SenS</b>iTiVe' }, + { input: 'This contains the < character', + terms: null, + output: 'This contains the < character' }, + { input: 'Don\'t', + terms: ['t'], + output: 'Don'<b>t</b>' }, + { input: 'Don\'t', + terms: ['n\'t'], + output: 'Do<b>n't</b>' }, + { input: 'Don\'t', + terms: ['o', 't'], + output: 'D<b>o</b>n'<b>t</b>' }, + { input: 'salt&pepper', + terms: ['salt'], + output: '<b>salt</b>&pepper' }, + { input: 'salt&pepper', + terms: ['salt', 'alt'], + output: '<b>salt</b>&pepper' }, + { input: 'salt&pepper', + terms: ['pepper'], + output: 'salt&<b>pepper</b>' }, + { input: 'salt&pepper', + terms: ['salt', 'pepper'], + output: '<b>salt</b>&<b>pepper</b>' }, + { input: 'salt&pepper', + terms: ['t', 'p'], + output: 'sal<b>t</b>&<b>p</b>e<b>p</b><b>p</b>er' }, + { input: 'salt&pepper', + terms: ['t', '&', 'p'], + output: 'sal<b>t</b><b>&</b><b>p</b>e<b>p</b><b>p</b>er' }, + { input: 'salt&pepper', + terms: ['e'], + output: 'salt&p<b>e</b>pp<b>e</b>r' }, + { input: 'salt&pepper', + terms: ['&a', '&am', '&', '&'], + output: 'salt&pepper' }, + { input: '&&&&&', + terms: ['a'], + output: '&&&&&' }, + { input: '&;&;&;&;&;', + terms: ['a'], + output: '&;&;&;&;&;' }, + { input: '&;&;&;&;&;', + terms: [';'], + output: '&<b>;</b>&<b>;</b>&<b>;</b>&<b>;</b>&<b>;</b>' }, + { input: '&', + terms: ['a'], + output: '&<b>a</b>mp;' } +]; + +try { + for (let i = 0; i < tests.length; i++) { + let highlighter = new Util.Highlighter(tests[i].terms); + let output = highlighter.highlight(tests[i].input); + + JsUnit.assertEquals(`Test ${i + 1} highlight ` + + `"${tests[i].terms}" in "${tests[i].input}"`, + output, tests[i].output); + + let parsed = false; + try { + Pango.parse_markup(output, -1, ''); + parsed = true; + } catch (e) {} + JsUnit.assertEquals(`Test ${i + 1} is valid markup`, true, parsed); + } +} catch (e) { + if (typeof(e.isJsUnitException) != 'undefined' + && e.isJsUnitException) + { + if (e.comment) + log(`Error in: ${e.comment}`); + } + throw e; +} diff --git a/tests/unit/insertSorted.js b/tests/unit/insertSorted.js new file mode 100644 index 0000000..610aeed --- /dev/null +++ b/tests/unit/insertSorted.js @@ -0,0 +1,76 @@ +/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */ + +// Test cases for Util.insertSorted + +const JsUnit = imports.jsUnit; + +// Needed so that Util can bring some UI stuff +// we don't actually use +const Environment = imports.ui.environment; +Environment.init(); +const Util = imports.misc.util; + +function assertArrayEquals(errorMessage, array1, array2) { + JsUnit.assertEquals(errorMessage + ' length', + array1.length, array2.length); + for (let j = 0; j < array1.length; j++) { + JsUnit.assertEquals(errorMessage + ' item ' + j, + array1[j], array2[j]); + } +} + +function cmp(one, two) { + return one-two; +} + +let arrayInt = [1, 2, 3, 5, 6]; +Util.insertSorted(arrayInt, 4, cmp); + +assertArrayEquals('first test', [1,2,3,4,5,6], arrayInt); + +// no comparator, integer sorting is implied +Util.insertSorted(arrayInt, 3); + +assertArrayEquals('second test', [1,2,3,3,4,5,6], arrayInt); + +let obj1 = { a: 1 }; +let obj2 = { a: 2, b: 0 }; +let obj3 = { a: 2, b: 1 }; +let obj4 = { a: 3 }; + +function objCmp(one, two) { + return one.a - two.a; +} + +let arrayObj = [obj1, obj3, obj4]; + +// obj2 compares equivalent to obj3, should be +// inserted before +Util.insertSorted(arrayObj, obj2, objCmp); + +assertArrayEquals('object test', [obj1, obj2, obj3, obj4], arrayObj); + +function checkedCmp(one, two) { + if (typeof one != 'number' || + typeof two != 'number') + throw new TypeError('Invalid type passed to checkedCmp'); + + return one-two; +} + +let arrayEmpty = []; + +// check that no comparisons are made when +// inserting in a empty array +Util.insertSorted(arrayEmpty, 3, checkedCmp); + +// Insert at the end and check that we don't +// access past it +Util.insertSorted(arrayEmpty, 4, checkedCmp); +Util.insertSorted(arrayEmpty, 5, checkedCmp); + +// Some more insertions +Util.insertSorted(arrayEmpty, 2, checkedCmp); +Util.insertSorted(arrayEmpty, 1, checkedCmp); + +assertArrayEquals('checkedCmp test', [1, 2, 3, 4, 5], arrayEmpty); diff --git a/tests/unit/jsParse.js b/tests/unit/jsParse.js new file mode 100644 index 0000000..468138b --- /dev/null +++ b/tests/unit/jsParse.js @@ -0,0 +1,194 @@ +/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */ + +// Test cases for MessageTray URLification + +const JsUnit = imports.jsUnit; + +const Environment = imports.ui.environment; +Environment.init(); + +const JsParse = imports.misc.jsParse; + +const HARNESS_COMMAND_HEADER = "let imports = obj;" + + "let global = obj;" + + "let Main = obj;" + + "let foo = obj;" + + "let r = obj;"; + +const testsFindMatchingQuote = [ + { input: '"double quotes"', + output: 0 }, + { input: '\'single quotes\'', + output: 0 }, + { input: 'some unquoted "some quoted"', + output: 14 }, + { input: '"mixed \' quotes\'"', + output: 0 }, + { input: '"escaped \\" quote"', + output: 0 } +]; +const testsFindMatchingSlash = [ + { input: '/slash/', + output: 0 }, + { input: '/slash " with $ funny ^\' stuff/', + output: 0 }, + { input: 'some unslashed /some slashed/', + output: 15 }, + { input: '/escaped \\/ slash/', + output: 0 } +]; +const testsFindMatchingBrace = [ + { input: '[square brace]', + output: 0 }, + { input: '(round brace)', + output: 0 }, + { input: '([()][nesting!])', + output: 0 }, + { input: '[we have "quoted [" braces]', + output: 0 }, + { input: '[we have /regex [/ braces]', + output: 0 }, + { input: '([[])[] mismatched braces ]', + output: 1 } +]; +const testsGetExpressionOffset = [ + { input: 'abc.123', + output: 0 }, + { input: 'foo().bar', + output: 0 }, + { input: 'foo(bar', + output: 4 }, + { input: 'foo[abc.match(/"/)]', + output: 0 } +]; +const testsGetDeclaredConstants = [ + { input: 'const foo = X; const bar = Y;', + output: ['foo', 'bar'] }, + { input: 'const foo=X; const bar=Y', + output: ['foo', 'bar'] } +]; +const testsIsUnsafeExpression = [ + { input: 'foo.bar', + output: false }, + { input: 'foo[\'bar\']', + output: false }, + { input: 'foo["a=b=c".match(/=/)', + output: false }, + { input: 'foo[1==2]', + output: false }, + { input: '(x=4)', + output: true }, + { input: '(x = 4)', + output: true }, + { input: '(x;y)', + output: true } +]; +const testsModifyScope = [ + "foo['a", + "foo()['b'", + "obj.foo()('a', 1, 2, 'b')().", + "foo.[.", + "foo]]]()))].", + "123'ab\"", + "Main.foo.bar = 3; bar.", + "(Main.foo = 3).", + "Main[Main.foo+=-1]." +]; + + + +// Utility function for comparing arrays +function assertArrayEquals(errorMessage, array1, array2) { + JsUnit.assertEquals(errorMessage + ' length', + array1.length, array2.length); + for (let j = 0; j < array1.length; j++) { + JsUnit.assertEquals(errorMessage + ' item ' + j, + array1[j], array2[j]); + } +} + +// +// Test javascript parsing +// + +for (let i = 0; i < testsFindMatchingQuote.length; i++) { + let text = testsFindMatchingQuote[i].input; + let match = JsParse.findMatchingQuote(text, text.length - 1); + + JsUnit.assertEquals('Test testsFindMatchingQuote ' + i, + match, testsFindMatchingQuote[i].output); +} + +for (let i = 0; i < testsFindMatchingSlash.length; i++) { + let text = testsFindMatchingSlash[i].input; + let match = JsParse.findMatchingSlash(text, text.length - 1); + + JsUnit.assertEquals('Test testsFindMatchingSlash ' + i, + match, testsFindMatchingSlash[i].output); +} + +for (let i = 0; i < testsFindMatchingBrace.length; i++) { + let text = testsFindMatchingBrace[i].input; + let match = JsParse.findMatchingBrace(text, text.length - 1); + + JsUnit.assertEquals('Test testsFindMatchingBrace ' + i, + match, testsFindMatchingBrace[i].output); +} + +for (let i = 0; i < testsGetExpressionOffset.length; i++) { + let text = testsGetExpressionOffset[i].input; + let match = JsParse.getExpressionOffset(text, text.length - 1); + + JsUnit.assertEquals('Test testsGetExpressionOffset ' + i, + match, testsGetExpressionOffset[i].output); +} + +for (let i = 0; i < testsGetDeclaredConstants.length; i++) { + let text = testsGetDeclaredConstants[i].input; + let match = JsParse.getDeclaredConstants(text); + + assertArrayEquals('Test testsGetDeclaredConstants ' + i, + match, testsGetDeclaredConstants[i].output); +} + +for (let i = 0; i < testsIsUnsafeExpression.length; i++) { + let text = testsIsUnsafeExpression[i].input; + let unsafe = JsParse.isUnsafeExpression(text); + + JsUnit.assertEquals('Test testsIsUnsafeExpression ' + i, + unsafe, testsIsUnsafeExpression[i].output); +} + +// +// Test safety of eval to get completions +// + +for (let i = 0; i < testsModifyScope.length; i++) { + let text = testsModifyScope[i]; + // We need to use var here for the with statement + var obj = {}; + + // Just as in JsParse.getCompletions, we will find the offset + // of the expression, test whether it is unsafe, and then eval it. + let offset = JsParse.getExpressionOffset(text, text.length - 1); + if (offset >= 0) { + text = text.slice(offset); + + let matches = text.match(/(.*)\.(.*)/); + if (matches) { + let [expr, base, attrHead] = matches; + + if (!JsParse.isUnsafeExpression(base)) { + with (obj) { + try { + eval(HARNESS_COMMAND_HEADER + base); + } catch (e) { + JsUnit.assertNotEquals("Code '" + base + "' is valid code", e.constructor, SyntaxError); + } + } + } + } + } + let propertyNames = Object.getOwnPropertyNames(obj); + JsUnit.assertEquals("The context '" + JSON.stringify(obj) + "' was not modified", propertyNames.length, 0); +} diff --git a/tests/unit/markup.js b/tests/unit/markup.js new file mode 100644 index 0000000..603ca81 --- /dev/null +++ b/tests/unit/markup.js @@ -0,0 +1,143 @@ +// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- + +// Test cases for MessageList markup parsing + +const JsUnit = imports.jsUnit; +const Pango = imports.gi.Pango; + +const Environment = imports.ui.environment; +Environment.init(); + +const Main = imports.ui.main; // unused, but needed to break dependency loop +const MessageList = imports.ui.messageList; + +// Assert that @input, assumed to be markup, gets "fixed" to @output, +// which is valid markup. If @output is null, @input is expected to +// convert to itself +function assertConverts(input, output) { + if (!output) + output = input; + let fixed = MessageList._fixMarkup(input, true); + JsUnit.assertEquals(output, fixed); + + let parsed = false; + try { + Pango.parse_markup(fixed, -1, ''); + parsed = true; + } catch (e) {} + JsUnit.assertEquals(true, parsed); +} + +// Assert that @input, assumed to be plain text, gets escaped to @output, +// which is valid markup. +function assertEscapes(input, output) { + let fixed = MessageList._fixMarkup(input, false); + JsUnit.assertEquals(output, fixed); + + let parsed = false; + try { + Pango.parse_markup(fixed, -1, ''); + parsed = true; + } catch (e) {} + JsUnit.assertEquals(true, parsed); +} + + + +// CORRECT MARKUP + +assertConverts('foo'); +assertEscapes('foo', 'foo'); + +assertConverts('<b>foo</b>'); +assertEscapes('<b>foo</b>', '<b>foo</b>'); + +assertConverts('something <i>foo</i>'); +assertEscapes('something <i>foo</i>', 'something <i>foo</i>'); + +assertConverts('<u>foo</u> something'); +assertEscapes('<u>foo</u> something', '<u>foo</u> something'); + +assertConverts('<b>bold</b> <i>italic <u>and underlined</u></i>'); +assertEscapes('<b>bold</b> <i>italic <u>and underlined</u></i>', '<b>bold</b> <i>italic <u>and underlined</u></i>'); + +assertConverts('this & that'); +assertEscapes('this & that', 'this &amp; that'); + +assertConverts('this < that'); +assertEscapes('this < that', 'this &lt; that'); + +assertConverts('this < that > the other'); +assertEscapes('this < that > the other', 'this &lt; that &gt; the other'); + +assertConverts('this <<i>that</i>>'); +assertEscapes('this <<i>that</i>>', 'this &lt;<i>that</i>&gt;'); + +assertConverts('<b>this</b> > <i>that</i>'); +assertEscapes('<b>this</b> > <i>that</i>', '<b>this</b> > <i>that</i>'); + + + +// PARTIALLY CORRECT MARKUP +// correct bits are kept, incorrect bits are escaped + +// unrecognized entity +assertConverts('<b>smile</b> ☺!', '<b>smile</b> &#9786;!'); +assertEscapes('<b>smile</b> ☺!', '<b>smile</b> &#9786;!'); + +// stray '&'; this is really a bug, but it's easier to do it this way +assertConverts('<b>this</b> & <i>that</i>', '<b>this</b> & <i>that</i>'); +assertEscapes('<b>this</b> & <i>that</i>', '<b>this</b> & <i>that</i>'); + +// likewise with stray '<' +assertConverts('this < that', 'this < that'); +assertEscapes('this < that', 'this < that'); + +assertConverts('<b>this</b> < <i>that</i>', '<b>this</b> < <i>that</i>'); +assertEscapes('<b>this</b> < <i>that</i>', '<b>this</b> < <i>that</i>'); + +assertConverts('this < that > the other', 'this < that > the other'); +assertEscapes('this < that > the other', 'this < that > the other'); + +assertConverts('this <<i>that</i>>', 'this <<i>that</i>>'); +assertEscapes('this <<i>that</i>>', 'this <<i>that</i>>'); + +// unknown tags +assertConverts('<unknown>tag</unknown>', '<unknown>tag</unknown>'); +assertEscapes('<unknown>tag</unknown>', '<unknown>tag</unknown>'); + +// make sure we check beyond the first letter +assertConverts('<bunknown>tag</bunknown>', '<bunknown>tag</bunknown>'); +assertEscapes('<bunknown>tag</bunknown>', '<bunknown>tag</bunknown>'); + +// with mix of good and bad, we keep the good and escape the bad +assertConverts('<i>known</i> and <unknown>tag</unknown>', '<i>known</i> and <unknown>tag</unknown>'); +assertEscapes('<i>known</i> and <unknown>tag</unknown>', '<i>known</i> and <unknown>tag</unknown>'); + + + +// FULLY INCORRECT MARKUP +// (fall back to escaping the whole thing) + +// tags not matched up +assertConverts('<b>in<i>com</i>plete', '<b>in<i>com</i>plete'); +assertEscapes('<b>in<i>com</i>plete', '<b>in<i>com</i>plete'); + +assertConverts('in<i>com</i>plete</b>', 'in<i>com</i>plete</b>'); +assertEscapes('in<i>com</i>plete</b>', 'in<i>com</i>plete</b>'); + +// we don't support attributes, and it's too complicated to try +// to escape both start and end tags, so we just treat it as bad +assertConverts('<b>good</b> and <b style=\'bad\'>bad</b>', '<b>good</b> and <b style='bad'>bad</b>'); +assertEscapes('<b>good</b> and <b style=\'bad\'>bad</b>', '<b>good</b> and <b style='bad'>bad</b>'); + +// this is just syntactically invalid +assertConverts('<b>unrecognized</b stuff>', '<b>unrecognized</b stuff>'); +assertEscapes('<b>unrecognized</b stuff>', '<b>unrecognized</b stuff>'); + +// mismatched tags +assertConverts('<b>mismatched</i>', '<b>mismatched</i>'); +assertEscapes('<b>mismatched</i>', '<b>mismatched</i>'); + +assertConverts('<b>mismatched/unknown</bunknown>', '<b>mismatched/unknown</bunknown>'); +assertEscapes('<b>mismatched/unknown</bunknown>', '<b>mismatched/unknown</bunknown>'); diff --git a/tests/unit/params.js b/tests/unit/params.js new file mode 100644 index 0000000..6ac4cc1 --- /dev/null +++ b/tests/unit/params.js @@ -0,0 +1,32 @@ +const JsUnit = imports.jsUnit; +const Params = imports.misc.params; + +function assertParamsEqual(params, expected) { + for (let p in params) { + JsUnit.assertTrue(p in expected); + JsUnit.assertEquals(params[p], expected[p]); + } +} + +let defaults = { + foo: 'This is a test', + bar: null, + baz: 42 +}; + +assertParamsEqual( + Params.parse(null, defaults), + defaults); + +assertParamsEqual( + Params.parse({ bar: 23 }, defaults), + { foo: 'This is a test', bar: 23, baz: 42 }); + +JsUnit.assertRaises( + () => { + Params.parse({ extraArg: 'quz' }, defaults); + }); + +assertParamsEqual( + Params.parse({ extraArg: 'quz' }, defaults, true), + { foo: 'This is a test', bar: null, baz: 42, extraArg: 'quz' }); diff --git a/tests/unit/signalTracker.js b/tests/unit/signalTracker.js new file mode 100644 index 0000000..7943d0a --- /dev/null +++ b/tests/unit/signalTracker.js @@ -0,0 +1,115 @@ +// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- + +// Test cases for version comparison + +const { GObject } = imports.gi; + +const JsUnit = imports.jsUnit; +const Signals = imports.misc.signals; + +const Environment = imports.ui.environment; +const { TransientSignalHolder, registerDestroyableType } = imports.misc.signalTracker; + +Environment.init(); + +const Destroyable = GObject.registerClass({ + Signals: { 'destroy': {} }, +}, class Destroyable extends GObject.Object {}); +registerDestroyableType(Destroyable); + +const GObjectEmitter = GObject.registerClass({ + Signals: { 'signal': {} }, +}, class GObjectEmitter extends Destroyable {}); + +const emitter1 = new Signals.EventEmitter(); +const emitter2 = new GObjectEmitter(); + +const tracked1 = new Destroyable(); +const tracked2 = {}; + +let count = 0; +const handler = () => count++; + +emitter1.connectObject('signal', handler, tracked1); +emitter2.connectObject('signal', handler, tracked1); + +emitter1.connectObject('signal', handler, tracked2); +emitter2.connectObject('signal', handler, tracked2); + +JsUnit.assertEquals(count, 0); + +emitter1.emit('signal'); +emitter2.emit('signal'); + +JsUnit.assertEquals(count, 4); + +tracked1.emit('destroy'); + +emitter1.emit('signal'); +emitter2.emit('signal'); + +JsUnit.assertEquals(count, 6); + +emitter1.disconnectObject(tracked2); +emitter2.emit('destroy'); + +emitter1.emit('signal'); +emitter2.emit('signal'); + +JsUnit.assertEquals(count, 6); + +emitter1.connectObject( + 'signal', handler, + 'signal', handler, GObject.ConnectFlags.AFTER, + tracked1); +emitter2.connectObject( + 'signal', handler, + 'signal', handler, GObject.ConnectFlags.AFTER, + tracked1); + +emitter1.emit('signal'); +emitter2.emit('signal'); + +JsUnit.assertEquals(count, 10); + +tracked1.emit('destroy'); +emitter1.emit('signal'); +emitter2.emit('signal'); + +JsUnit.assertEquals(count, 10); + +emitter1.connectObject('signal', handler, tracked1); +emitter2.connectObject('signal', handler, tracked1); + +transientHolder = new TransientSignalHolder(tracked1); + +emitter1.connectObject('signal', handler, transientHolder); +emitter2.connectObject('signal', handler, transientHolder); + +emitter1.emit('signal'); +emitter2.emit('signal'); + +JsUnit.assertEquals(count, 14); + +transientHolder.destroy(); + +emitter1.emit('signal'); +emitter2.emit('signal'); + +JsUnit.assertEquals(count, 16); + +transientHolder = new TransientSignalHolder(tracked1); + +emitter1.connectObject('signal', handler, transientHolder); +emitter2.connectObject('signal', handler, transientHolder); + +emitter1.emit('signal'); +emitter2.emit('signal'); + +JsUnit.assertEquals(count, 20); + +tracked1.emit('destroy'); +emitter1.emit('signal'); +emitter2.emit('signal'); + +JsUnit.assertEquals(count, 20); diff --git a/tests/unit/url.js b/tests/unit/url.js new file mode 100644 index 0000000..84aecc9 --- /dev/null +++ b/tests/unit/url.js @@ -0,0 +1,77 @@ +// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- + +// Test cases for MessageTray URLification + +const JsUnit = imports.jsUnit; + +const Environment = imports.ui.environment; +Environment.init(); + +const Util = imports.misc.util; + +const tests = [ + { input: 'This is a test', + output: [] }, + { input: 'This is http://www.gnome.org a test', + output: [ { url: 'http://www.gnome.org', pos: 8 } ] }, + { input: 'This is http://www.gnome.org', + output: [ { url: 'http://www.gnome.org', pos: 8 } ] }, + { input: 'http://www.gnome.org a test', + output: [ { url: 'http://www.gnome.org', pos: 0 } ] }, + { input: 'http://www.gnome.org', + output: [ { url: 'http://www.gnome.org', pos: 0 } ] }, + { input: 'Go to http://www.gnome.org.', + output: [ { url: 'http://www.gnome.org', pos: 6 } ] }, + { input: 'Go to http://www.gnome.org/.', + output: [ { url: 'http://www.gnome.org/', pos: 6 } ] }, + { input: '(Go to http://www.gnome.org!)', + output: [ { url: 'http://www.gnome.org', pos: 7 } ] }, + { input: 'Use GNOME (http://www.gnome.org).', + output: [ { url: 'http://www.gnome.org', pos: 11 } ] }, + { input: 'This is a http://www.gnome.org/path test.', + output: [ { url: 'http://www.gnome.org/path', pos: 10 } ] }, + { input: 'This is a www.gnome.org scheme-less test.', + output: [ { url: 'www.gnome.org', pos: 10 } ] }, + { input: 'This is a www.gnome.org/scheme-less test.', + output: [ { url: 'www.gnome.org/scheme-less', pos: 10 } ] }, + { input: 'This is a http://www.gnome.org:99/port test.', + output: [ { url: 'http://www.gnome.org:99/port', pos: 10 } ] }, + { input: 'This is an ftp://www.gnome.org/ test.', + output: [ { url: 'ftp://www.gnome.org/', pos: 11 } ] }, + { input: 'https://www.gnome.org/(some_url,_with_very_unusual_characters)', + output: [ { url: 'https://www.gnome.org/(some_url,_with_very_unusual_characters)', pos: 0 } ] }, + { input: 'https://www.gnome.org/(some_url_with_unbalanced_parenthesis', + output: [ { url: 'https://www.gnome.org/', pos: 0 } ] }, + { input: 'https://www.gnome.org/ plus trailing junk', + output: [ { url: 'https://www.gnome.org/', pos: 0 } ] }, + + { input: 'Visit http://www.gnome.org/ and http://developer.gnome.org', + output: [ { url: 'http://www.gnome.org/', pos: 6 }, + { url: 'http://developer.gnome.org', pos: 32 } ] }, + + { input: 'This is not.a.domain test.', + output: [ ] }, + { input: 'This is not:a.url test.', + output: [ ] }, + { input: 'This is not:/a.url/ test.', + output: [ ] }, + { input: 'This is not:/a.url/ test.', + output: [ ] }, + { input: 'This is not@a.url/ test.', + output: [ ] }, + { input: 'This is surely@not.a/url test.', + output: [ ] } +]; + +for (let i = 0; i < tests.length; i++) { + let match = Util.findUrls(tests[i].input); + + JsUnit.assertEquals('Test ' + i + ' match length', + match.length, tests[i].output.length); + for (let j = 0; j < match.length; j++) { + JsUnit.assertEquals('Test ' + i + ', match ' + j + ' url', + match[j].url, tests[i].output[j].url); + JsUnit.assertEquals('Test ' + i + ', match ' + j + ' position', + match[j].pos, tests[i].output[j].pos); + } +} diff --git a/tests/unit/versionCompare.js b/tests/unit/versionCompare.js new file mode 100644 index 0000000..1997a6c --- /dev/null +++ b/tests/unit/versionCompare.js @@ -0,0 +1,52 @@ +// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- + +// Test cases for version comparison + +const JsUnit = imports.jsUnit; + +const Environment = imports.ui.environment; +Environment.init(); + +const Util = imports.misc.util; + +const tests = [ + { v1: '40', + v2: '40', + res: 0 }, + { v1: '40', + v2: '42', + res: -1 }, + { v1: '42', + v2: '40', + res: 1 }, + { v1: '3.38.0', + v2: '40', + res: -1 }, + { v1: '40', + v2: '3.38.0', + res: 1 }, + { v1: '40', + v2: '3.38.0', + res: 1 }, + { v1: '40.alpha.1.1', + v2: '40', + res: -1 }, + { v1: '40', + v2: '40.alpha.1.1', + res: 1 }, + { v1: '40.beta', + v2: '40', + res: -1 }, + { v1: '40.1', + v2: '40', + res: 1 }, + { v1: '', + v2: '40.alpha', + res: -1 }, +]; + +for (let i = 0; i < tests.length; i++) { + name = 'Test #' + i + ' v1: ' + tests[i].v1 + ', v2: ' + tests[i].v2; + print(name); + JsUnit.assertEquals(name, Util.GNOMEversionCompare (tests[i].v1, tests[i].v2), tests[i].res); +} |