From 5a266e48dcd0202e453356300f452bf1ae1927c9 Mon Sep 17 00:00:00 2001 From: Zack Weinberg Date: Thu, 13 Aug 2015 09:47:14 -0400 Subject: [PATCH] Convert non-webpage tests to the new format. This covers fs-spec-*.js, require/require_spec.js, module_spec.js, webkit-spec.js, and webserver-spec.js. Also, incorporate set/690-ttf-crash/ as a regression test (it wasn't being run automatically). Part of issue #13478 (test suite overhaul). --- test/basics/module.js | 27 +++ test/basics/require.js | 10 + test/{ => basics}/require/a.js | 0 test/{ => basics}/require/b.js | 0 test/{ => basics}/require/dir/dummy.js | 0 test/{ => basics}/require/dir/subdir/dummy.js | 0 .../{ => basics}/require/dir/subdir/loader.js | 0 .../require/dir/subdir2/loader.js | 0 test/{ => basics}/require/dummy.js | 0 test/{ => basics}/require/empty.js | 0 test/{ => basics}/require/json_dummy.json | 0 .../require/node_modules/dummy_file.js | 0 .../dummy_module/libdir/dummy_module.js | 0 .../node_modules/dummy_module/package.json | 0 .../node_modules/dummy_module2/index.js | 0 test/{ => basics}/require/not_found.js | 0 test/basics/require/require_spec.js | 131 +++++++++++ test/{ => basics}/require/stubber.js | 0 test/{ => basics}/require/stubber_child.js | 0 test/basics/require/thrower.js | 3 + test/fs-spec-01.js | 219 ----------------- test/fs-spec-02.js | 46 ---- test/fs-spec-03.js | 147 ------------ test/fs-spec-04.js | 70 ------ test/module/fs/basics.js | 220 ++++++++++++++++++ test/module/fs/fileattrs.js | 91 ++++++++ test/module/fs/paths.js | 72 ++++++ test/module/webserver/basics.js | 25 ++ test/module/webserver/requests.js | 145 ++++++++++++ test/module_spec.js | 18 -- test/regression/pjs-10690.js | 14 ++ test/regression/webkit-60448.js | 13 ++ test/require/require_spec.js | 215 ----------------- test/require/thrower.js | 3 - test/run-tests.js | 9 - test/set/690-ttf-crash/run.js | 12 - test/set/690-ttf-crash/serve.rb | 25 -- test/webkit-spec.js | 7 - test/webserver-spec.js | 192 --------------- test/www/regression/pjs-10690/Windsong.ttf | Bin 0 -> 84852 bytes .../regression/pjs-10690}/font.css | 4 +- .../regression/pjs-10690}/index.html | 0 .../regression/pjs-10690}/jquery.js | 0 .../regression/webkit-60448.html} | 0 44 files changed, 752 insertions(+), 966 deletions(-) create mode 100644 test/basics/module.js create mode 100644 test/basics/require.js rename test/{ => basics}/require/a.js (100%) rename test/{ => basics}/require/b.js (100%) rename test/{ => basics}/require/dir/dummy.js (100%) rename test/{ => basics}/require/dir/subdir/dummy.js (100%) rename test/{ => basics}/require/dir/subdir/loader.js (100%) rename test/{ => basics}/require/dir/subdir2/loader.js (100%) rename test/{ => basics}/require/dummy.js (100%) rename test/{ => basics}/require/empty.js (100%) rename test/{ => basics}/require/json_dummy.json (100%) rename test/{ => basics}/require/node_modules/dummy_file.js (100%) rename test/{ => basics}/require/node_modules/dummy_module/libdir/dummy_module.js (100%) rename test/{ => basics}/require/node_modules/dummy_module/package.json (100%) rename test/{ => basics}/require/node_modules/dummy_module2/index.js (100%) rename test/{ => basics}/require/not_found.js (100%) create mode 100644 test/basics/require/require_spec.js rename test/{ => basics}/require/stubber.js (100%) rename test/{ => basics}/require/stubber_child.js (100%) create mode 100644 test/basics/require/thrower.js delete mode 100644 test/fs-spec-01.js delete mode 100644 test/fs-spec-02.js delete mode 100644 test/fs-spec-03.js delete mode 100644 test/fs-spec-04.js create mode 100644 test/module/fs/basics.js create mode 100644 test/module/fs/fileattrs.js create mode 100644 test/module/fs/paths.js create mode 100644 test/module/webserver/basics.js create mode 100644 test/module/webserver/requests.js delete mode 100644 test/module_spec.js create mode 100644 test/regression/pjs-10690.js create mode 100644 test/regression/webkit-60448.js delete mode 100644 test/require/require_spec.js delete mode 100644 test/require/thrower.js delete mode 100644 test/set/690-ttf-crash/run.js delete mode 100644 test/set/690-ttf-crash/serve.rb delete mode 100644 test/webkit-spec.js delete mode 100644 test/webserver-spec.js create mode 100644 test/www/regression/pjs-10690/Windsong.ttf rename test/{set/690-ttf-crash => www/regression/pjs-10690}/font.css (54%) rename test/{set/690-ttf-crash => www/regression/pjs-10690}/index.html (100%) rename test/{set/690-ttf-crash => www/regression/pjs-10690}/jquery.js (100%) rename test/{webkit-spec/inline-destroy-dirty-lines-crash.html => www/regression/webkit-60448.html} (100%) diff --git a/test/basics/module.js b/test/basics/module.js new file mode 100644 index 000000000..58e8b33cd --- /dev/null +++ b/test/basics/module.js @@ -0,0 +1,27 @@ +// Test the properties of the 'module' object. +// Assumes the 'dummy_exposed' module is to be found in a directory +// named 'node_modules'. + +// Module load might fail, so do it in a setup function. +var module; +setup(function () { + module = require("dummy_exposed"); +}); + +test(function() { + assert_regexp_match(module.filename, /\/node_modules\/dummy_exposed\.js$/); +}, "module.filename is the absolute pathname of the module .js file"); + +test(function() { + assert_regexp_match(module.dirname, /\/node_modules$/); +}, "module.dirname is the absolute pathname of the directory containing "+ + "the module"); + +test(function() { + assert_equals(module.id, module.filename); +}, "module.id equals module.filename"); + +test(function() { + var dummy_file = module.require('./dummy_file'); + assert_equals(dummy_file, 'spec/node_modules/dummy_file'); +}, "module.require is callable and resolves relative to the module"); diff --git a/test/basics/require.js b/test/basics/require.js new file mode 100644 index 000000000..02a2d6380 --- /dev/null +++ b/test/basics/require.js @@ -0,0 +1,10 @@ +/* The require tests need to run inside a module to work correctly; that + module is require/require_spec.js. (That directory also contains a + bunch of other files used by this test.) The module exports an array + of test functions in the form expected by generate_tests(). */ + +var rtests = require("require/require_spec.js").tests; + +for (var i = 0; i < rtests.length; i++) { + test.apply(null, rtests[i]); +} diff --git a/test/require/a.js b/test/basics/require/a.js similarity index 100% rename from test/require/a.js rename to test/basics/require/a.js diff --git a/test/require/b.js b/test/basics/require/b.js similarity index 100% rename from test/require/b.js rename to test/basics/require/b.js diff --git a/test/require/dir/dummy.js b/test/basics/require/dir/dummy.js similarity index 100% rename from test/require/dir/dummy.js rename to test/basics/require/dir/dummy.js diff --git a/test/require/dir/subdir/dummy.js b/test/basics/require/dir/subdir/dummy.js similarity index 100% rename from test/require/dir/subdir/dummy.js rename to test/basics/require/dir/subdir/dummy.js diff --git a/test/require/dir/subdir/loader.js b/test/basics/require/dir/subdir/loader.js similarity index 100% rename from test/require/dir/subdir/loader.js rename to test/basics/require/dir/subdir/loader.js diff --git a/test/require/dir/subdir2/loader.js b/test/basics/require/dir/subdir2/loader.js similarity index 100% rename from test/require/dir/subdir2/loader.js rename to test/basics/require/dir/subdir2/loader.js diff --git a/test/require/dummy.js b/test/basics/require/dummy.js similarity index 100% rename from test/require/dummy.js rename to test/basics/require/dummy.js diff --git a/test/require/empty.js b/test/basics/require/empty.js similarity index 100% rename from test/require/empty.js rename to test/basics/require/empty.js diff --git a/test/require/json_dummy.json b/test/basics/require/json_dummy.json similarity index 100% rename from test/require/json_dummy.json rename to test/basics/require/json_dummy.json diff --git a/test/require/node_modules/dummy_file.js b/test/basics/require/node_modules/dummy_file.js similarity index 100% rename from test/require/node_modules/dummy_file.js rename to test/basics/require/node_modules/dummy_file.js diff --git a/test/require/node_modules/dummy_module/libdir/dummy_module.js b/test/basics/require/node_modules/dummy_module/libdir/dummy_module.js similarity index 100% rename from test/require/node_modules/dummy_module/libdir/dummy_module.js rename to test/basics/require/node_modules/dummy_module/libdir/dummy_module.js diff --git a/test/require/node_modules/dummy_module/package.json b/test/basics/require/node_modules/dummy_module/package.json similarity index 100% rename from test/require/node_modules/dummy_module/package.json rename to test/basics/require/node_modules/dummy_module/package.json diff --git a/test/require/node_modules/dummy_module2/index.js b/test/basics/require/node_modules/dummy_module2/index.js similarity index 100% rename from test/require/node_modules/dummy_module2/index.js rename to test/basics/require/node_modules/dummy_module2/index.js diff --git a/test/require/not_found.js b/test/basics/require/not_found.js similarity index 100% rename from test/require/not_found.js rename to test/basics/require/not_found.js diff --git a/test/basics/require/require_spec.js b/test/basics/require/require_spec.js new file mode 100644 index 000000000..2a637e278 --- /dev/null +++ b/test/basics/require/require_spec.js @@ -0,0 +1,131 @@ +var fs = require('fs'); +var tests = []; +exports.tests = tests; + +tests.push([function () { + assert_no_property(window, 'CoffeeScript'); + assert_own_property(window, 'require'); + + assert_own_property(require('webpage'), 'create'); + assert_own_property(require('webserver'), 'create'); + assert_own_property(require('cookiejar'), 'create'); + + assert_own_property(require('fs'), 'separator'); + assert_equals(require('system').platform, 'phantomjs'); + +}, "native modules"]); + +tests.push([function () { + assert_equals(require('./json_dummy').message, 'hello'); + assert_equals(require('./dummy.js'), 'require/dummy'); +}, "JS and JSON modules"]); + +tests.push([function () { + require('./empty').hello = 'hola'; + assert_equals(require('./empty').hello, 'hola'); + + // assert_own_property rejects Functions + assert_equals(require.hasOwnProperty('cache'), true); + + var exposed = require('dummy_exposed'); + assert_equals(require.cache[exposed.filename], exposed); + +}, "module caching"]); + +tests.push([function () { + var a = require('./a'); + var b = require('./b'); + assert_equals(a.b, b); + assert_equals(b.a, a); +}, "circular dependencies"]); + +tests.push([function () { + assert_throws("Cannot find module 'dummy_missing'", + function () { require('dummy_missing'); }); + + try { + require('./not_found').requireNonExistent(); + } catch (e) { + assert_regexp_match(e.stack, /\n\s+at require/); + } +}, "error handling 1"]); + +tests.push([function error_handling_2 () { + try { + require('./thrower').fn(); + } catch (e) { + assert_regexp_match(e.toString() + "\n" + e.stack, + /^Error: fn\n\s+at thrower\n\s+at error_handling_2\n/); + } +}, "error handling 2", { expected_fail: true }]); + +tests.push([function () { + assert_equals(require('./stubber').stubbed, 'stubbed module'); + assert_equals(require('./stubber').child.stubbed, 'stubbed module'); + assert_throws("Cannot find module 'stubbed'", + function () { require('stubbed'); }); + + var count = 0; + require.stub('lazily_stubbed', function() { + ++count; + return 'lazily stubbed module'; + }); + + assert_equals(require('lazily_stubbed'), 'lazily stubbed module'); + require('lazily_stubbed'); + assert_equals(count, 1); + +}, "stub modules"]); + +tests.push([function () { + assert_equals(require('./dummy'), 'require/dummy'); + assert_equals(require('../dummy'), 'spec/dummy'); + assert_equals(require('./dir/dummy'), 'dir/dummy'); + assert_equals(require('./dir/subdir/dummy'), 'subdir/dummy'); + assert_equals(require('./dir/../dummy'), 'require/dummy'); + assert_equals(require('./dir/./dummy'), 'dir/dummy'); + assert_equals(require( + fs.absolute(module.dirname + '/dummy.js')), 'require/dummy'); + +}, "relative and absolute paths"]); + +tests.push([function () { + assert_equals(require('dummy_file'), 'require/node_modules/dummy_file'); + assert_equals(require('dummy_file2'), 'spec/node_modules/dummy_file2'); + assert_equals(require('./dir/subdir/loader').dummyFile2, + 'spec/node_modules/dummy_file2'); + assert_equals(require('dummy_module'), + 'require/node_modules/dummy_module'); + assert_equals(require('dummy_module2'), + 'require/node_modules/dummy_module2'); +}, "loading from node_modules"]); + +function require_paths_tests_1 () { + assert_equals(require('loader').dummyFile2, + 'spec/node_modules/dummy_file2'); + assert_equals(require('../subdir2/loader'), + 'require/subdir2/loader'); + assert_equals(require('../dummy'), 'spec/dummy'); +} +function require_paths_tests_2 () { + assert_throws("Cannot find module 'loader'", + function () { require('loader'); }); +} + +tests.push([function () { + require.paths.push('dir/subdir'); + this.add_cleanup(function () { require.paths.pop(); }); + require_paths_tests_1(); +}, "relative paths in require.paths"]); + +tests.push([ + require_paths_tests_2, "relative paths in require paths (after removal)"]); + +tests.push([function () { + require.paths.push(fs.absolute(module.dirname + '/dir/subdir')); + this.add_cleanup(function () { require.paths.pop(); }); + require_paths_tests_1(); +}, "absolute paths in require.paths"]); + +tests.push([ + require_paths_tests_2, "relative paths in require paths (after removal)"]); diff --git a/test/require/stubber.js b/test/basics/require/stubber.js similarity index 100% rename from test/require/stubber.js rename to test/basics/require/stubber.js diff --git a/test/require/stubber_child.js b/test/basics/require/stubber_child.js similarity index 100% rename from test/require/stubber_child.js rename to test/basics/require/stubber_child.js diff --git a/test/basics/require/thrower.js b/test/basics/require/thrower.js new file mode 100644 index 000000000..6a98c3fd0 --- /dev/null +++ b/test/basics/require/thrower.js @@ -0,0 +1,3 @@ +exports.fn = function thrower() { + throw new Error('fn'); +}; diff --git a/test/fs-spec-01.js b/test/fs-spec-01.js deleted file mode 100644 index 86d9b61ac..000000000 --- a/test/fs-spec-01.js +++ /dev/null @@ -1,219 +0,0 @@ -describe("Basic Files API (read, write, remove, ...)", function() { - var FILENAME = "temp-01.test", - FILENAME_COPY = FILENAME + ".copy", - FILENAME_MOVED = FILENAME + ".moved", - FILENAME_EMPTY = FILENAME + ".empty", - FILENAME_ENC = FILENAME + ".enc", - FILENAME_BIN = FILENAME + ".bin", - ABSENT = "absent-01.test"; - - it("should be able to create and write a file", function() { - try{ - var f = fs.open(FILENAME, "w"); - - f.write("hello"); - f.writeLine(""); - f.writeLine("world"); - f.close(); - } catch (e) { } - expect(fs.exists(FILENAME)).toBeTruthy(); - }); - - it("should be able to create (touch) an empty file", function() { - expect(fs.exists(FILENAME_EMPTY)).toBeFalsy(); - fs.touch(FILENAME_EMPTY); - expect(fs.exists(FILENAME_EMPTY)).toBeTruthy(); - expect(fs.size(FILENAME_EMPTY)).toEqual(0); - }); - - it("should be able to read content from a file", function() { - var content = ""; - try{ - var f = fs.open(FILENAME, "r"); - - content = f.read(); - f.close(); - } catch (e) { } - expect(content).toEqual("hello\nworld\n"); - }); - - it("should be able to read specific number of bytes from a specific position in a file", function() { - var content = ""; - try{ - var f = fs.open(FILENAME, "r"); - f.seek(3); - content = f.read(5); - f.close(); - } catch (e) { } - expect(content).toEqual("lo\nwo"); - }); - - it("should be able to read/write/append content from a file", function() { - var content = ""; - try{ - var f = fs.open(FILENAME, "rw+"); - f.writeLine("asdf"); - content = f.read(); - f.close(); - } catch (e) { } - expect(content).toEqual("hello\nworld\nasdf\n"); - }); - - it("should be able to get the encoding (default: UTF-8)", function() { - var encoding = ""; - try { - var f = fs.open(FILENAME, "r"); - encoding = f.getEncoding(); - f.close(); - } catch (e) { - console.log(e); - } - expect(encoding).toEqual("UTF-8"); - }); - - it("should be able to set the encoding via options", function() { - var encoding = ""; - try { - var f = fs.open(FILENAME, { - charset: "UTF-8" - , mode: "r" - }); - encoding = f.getEncoding(); - f.close(); - } catch (e) { - console.log(e); - } - expect(encoding).toEqual("UTF-8"); - - try { - var f = fs.open(FILENAME, { - charset: "SJIS" - , mode: "r" - }); - encoding = f.getEncoding(); - f.close(); - } catch (e) { - console.log(e); - } - expect(encoding).toEqual("Shift_JIS"); - }); - - it("should be able to change the encoding", function() { - var encoding = ""; - try { - var f = fs.open(FILENAME, { - charset: "UTF-8" - , mode: "r" - }); - f.setEncoding("utf8"); - encoding = f.getEncoding(); - f.close(); - } catch (e) { - console.log(e); - } - expect(encoding).toEqual("UTF-8"); - - try { - var f = fs.open(FILENAME, { - charset: "SJIS" - , mode: "r" - }); - f.setEncoding("eucjp"); - encoding = f.getEncoding(); - f.close(); - } catch (e) { - console.log(e); - } - expect(encoding).toEqual("EUC-JP"); - }); - - it("should be able to copy a file", function() { - expect(fs.exists(FILENAME_COPY)).toBeFalsy(); - fs.copy(FILENAME, FILENAME_COPY); - expect(fs.exists(FILENAME_COPY)).toBeTruthy(); - expect(fs.read(FILENAME)).toEqual(fs.read(FILENAME_COPY)); - }); - - it("should be able to move a file", function() { - expect(fs.exists(FILENAME)).toBeTruthy(); - var contentBeforeMove = fs.read(FILENAME); - fs.move(FILENAME, FILENAME_MOVED); - expect(fs.exists(FILENAME)).toBeFalsy(); - expect(fs.exists(FILENAME_MOVED)).toBeTruthy(); - expect(fs.read(FILENAME_MOVED)).toEqual(contentBeforeMove); - }); - - it("should be able to remove a (moved) file", function() { - expect(fs.exists(FILENAME_MOVED)).toBeTruthy(); - fs.remove(FILENAME_MOVED); - expect(fs.exists(FILENAME_MOVED)).toBeFalsy(); - }); - - it("should be able to remove a (copied) file", function() { - expect(fs.exists(FILENAME_COPY)).toBeTruthy(); - fs.remove(FILENAME_COPY); - expect(fs.exists(FILENAME_COPY)).toBeFalsy(); - }); - - it("should be able to remove an empty file", function() { - expect(fs.exists(FILENAME_EMPTY)).toBeTruthy(); - fs.remove(FILENAME_EMPTY); - expect(fs.exists(FILENAME_EMPTY)).toBeFalsy(); - }); - - it("should throw an exception when trying to open for read a non existing file", function(){ - expect(function(){ - fs.open(ABSENT, "r"); - }).toThrow("Unable to open file '"+ ABSENT +"'"); - }); - - it("should throw an exception when trying to copy a non existing file", function() { - expect(function(){ - fs.copy(ABSENT, FILENAME_COPY); - }).toThrow("Unable to copy file '" + ABSENT + "' at '" + FILENAME_COPY + "'"); - }); - - it("should be read/write utf8 text by default", function() { - var content, output = "ÄABCÖ"; - try { - var f = fs.open(FILENAME_ENC, "w"); - f.write(output); - f.close(); - - f = fs.open(FILENAME_ENC, "r"); - content = f.read(); - f.close(); - - fs.remove(FILENAME_ENC); - } catch (e) { } - expect(content).toEqual(output); - }); - - it("should be read/write binary data", function() { - var content, output = String.fromCharCode(0, 1, 2, 3, 4, 5); - try { - var f = fs.open(FILENAME_BIN, "wb"); - f.write(output); - f.close(); - - f = fs.open(FILENAME_BIN, "rb"); - content = f.read(); - f.close(); - - fs.remove(FILENAME_BIN); - } catch (e) { } - expect(content).toEqual(output); - }); - - it("should be read/write binary data (shortcuts)", function() { - var content, output = String.fromCharCode(0, 1, 2, 3, 4, 5); - try { - fs.write(FILENAME_BIN, output, "b"); - - content = fs.read(FILENAME_BIN, "b"); - - fs.remove(FILENAME_BIN); - } catch (e) { } - expect(content).toEqual(output); - }); -}); diff --git a/test/fs-spec-02.js b/test/fs-spec-02.js deleted file mode 100644 index 2407743b0..000000000 --- a/test/fs-spec-02.js +++ /dev/null @@ -1,46 +0,0 @@ -describe("Attributes Files API", function() { - var FILENAME = "temp-02.test", - CONTENT = "This is a test for PhantomJS, an awesome headless browser to do all sort of stuff :) ", - CONTENT_MULTIPLIER = 1024, - ABSENT = "absent-02.test"; - - it("should throw an exception when trying to read the size of a non existing file", function(){ - expect(function(){ - fs.size(ABSENT); - }).toThrow("Unable to read file '"+ ABSENT +"' size"); - }); - - it("should return a null Date object when trying to read the last modified date of a non existing file", function(){ - expect(fs.lastModified(ABSENT)).toBeNull(); - }); - - it("should create temporary file '"+ FILENAME +"' and writes some content in it", function(){ - try{ - var f = fs.open(FILENAME, "w"); - - expect(f).toBeDefined(); - for (var i = 1; i <= CONTENT_MULTIPLIER; ++i) { - f.write(CONTENT); - } - f.close(); - } catch (e) { } - }); - - it("should be able to read the size of a temporary file '"+ FILENAME +"'", function() { - expect(fs.size(FILENAME)).toEqual(CONTENT.length * CONTENT_MULTIPLIER); - }); - - it("should be able to read the Date on which a temporary file '"+ FILENAME +"' was last modified", function() { - var flm = fs.lastModified(FILENAME), - now = new Date(); - - expect(now.getDay()).toEqual(flm.getDay()); - expect(now.getMonth()).toEqual(flm.getMonth()); - expect(now.getFullYear()).toEqual(flm.getFullYear()); - expect(now.getMilliseconds()).toNotEqual(flm.getMilliseconds()); - }); - - it("should remove temporary file '"+ FILENAME +"'", function(){ - fs.remove(FILENAME); - }); -}); diff --git a/test/fs-spec-03.js b/test/fs-spec-03.js deleted file mode 100644 index 1f4f2b5c6..000000000 --- a/test/fs-spec-03.js +++ /dev/null @@ -1,147 +0,0 @@ -describe("Files and Directories API", function() { - var TEST_DIR = "testdir", - TEST_FILE = "testfile", - START_CWD = fs.workingDirectory, - system = require('system'); - - it("should create a new temporary directory and change the Current Working Directory to it", function() { - expect(fs.makeDirectory(TEST_DIR)).toBeTruthy(); - expect(fs.changeWorkingDirectory(TEST_DIR)).toBeTruthy(); - }); - - it("should create a file in the Current Working Directory and check it's absolute path", function() { - fs.write(TEST_FILE, TEST_FILE, "w"); - var suffix = fs.join("", TEST_DIR, TEST_FILE), - abs = fs.absolute(".." + suffix), - lastIndex = abs.lastIndexOf(suffix); - expect(lastIndex).toNotEqual(-1); - expect(lastIndex + suffix.length === abs.length); - }); - - it("should return to previous Current Working Directory and remove temporary directory", function() { - expect(fs.changeWorkingDirectory(START_CWD)).toBeTruthy(); - fs.removeTree(TEST_DIR); - }); - - it("should copy Content of the '/test/' Directory in a temporary directory, compare with the original and then remove", function() { - var phantomLibraryPathListingLength = fs.list(phantom.libraryPath).length; - var targetDirectory = '/tmp/'; - - if (system.os.name === 'windows') { - targetDirectory = system.env['TMP']; - if (targetDirectory.indexOf('\\', targetDirectory.length - '\\'.length) === -1) { - targetDirectory += '\\'; - } - } - - fs.copyTree(phantom.libraryPath, targetDirectory + TEST_DIR); - expect(phantomLibraryPathListingLength === fs.list(targetDirectory + TEST_DIR).length); - fs.removeTree(targetDirectory + TEST_DIR); - }); - - // TODO: test the actual functionality once we can create symlink. - it("should have readLink function", function() { - expect(typeof fs.readLink).toEqual('function'); - }); - - fs.removeTree(TEST_DIR); - - describe("fs.join(...)", function() { - var parts, expected, actual; - - it("empty parts", function() { - parts = []; - expected = "."; - actual = fs.join.apply(null, parts); - expect(actual).toEqual(expected); - }); - - it("one part (empty string)", function() { - parts = [""]; - expected = "."; - actual = fs.join.apply(null, parts); - expect(actual).toEqual(expected); - }); - - it("one part (array)", function() { - parts = [[], null]; - expected = "."; - actual = fs.join.apply(null, parts); - expect(actual).toEqual(expected); - }); - - it("empty string and one part", function() { - parts = ["", "a"]; - expected = "/a"; - actual = fs.join.apply(null, parts); - expect(actual).toEqual(expected); - }); - - it("empty string and multiple parts", function() { - parts = ["", "a", "b", "c"]; - expected = "/a/b/c"; - actual = fs.join.apply(null, parts); - expect(actual).toEqual(expected); - }); - - it("empty string and multiple parts with empty strings", function() { - parts = ["", "a", "", "b", "", "c"]; - expected = "/a/b/c"; - actual = fs.join.apply(null, parts); - expect(actual).toEqual(expected); - }); - - it("multiple parts", function() { - parts = ["a", "b", "c"]; - expected = "a/b/c"; - actual = fs.join.apply(null, parts); - expect(actual).toEqual(expected); - }); - - it("multiple parts with empty strings", function() { - parts = ["a", "", "b", "", "c"]; - expected = "a/b/c"; - actual = fs.join.apply(null, parts); - expect(actual).toEqual(expected); - }); - }); - - describe("fs.split(path)", function() { - var path, expected, actual; - - it("should split absolute path with trailing separator", function() { - path = fs.separator + "a" + fs.separator + "b" + fs.separator + "c" + fs.separator + "d" + fs.separator; - actual = fs.split(path); - expected = ["", "a", "b", "c", "d"]; - expect(actual).toEqual(expected); - }); - - it("should split absolute path without trailing separator", function() { - path = fs.separator + "a" + fs.separator + "b" + fs.separator + "c" + fs.separator + "d"; - actual = fs.split(path); - expected = ["", "a", "b", "c", "d"]; - expect(actual).toEqual(expected); - }); - - it("should split non-absolute path with trailing separator", function() { - path = "a" + fs.separator + "b" + fs.separator + "c" + fs.separator + "d" + fs.separator; - actual = fs.split(path); - expected = ["a", "b", "c", "d"]; - expect(actual).toEqual(expected); - }); - - it("should split non-absolute path without trailing separator", function() { - path = "a" + fs.separator + "b" + fs.separator + "c" + fs.separator + "d"; - actual = fs.split(path); - expected = ["a", "b", "c", "d"]; - expect(actual).toEqual(expected); - }); - - it("should split path with consecutive separators", function() { - path = "a" + fs.separator + fs.separator + fs.separator + "b" + fs.separator + "c" + fs.separator + fs.separator + "d" + fs.separator + fs.separator + fs.separator; - expected = ["a", "b", "c", "d"]; - actual = fs.split(path); - expect(actual).toEqual(expected); - }); - }); -}); diff --git a/test/fs-spec-04.js b/test/fs-spec-04.js deleted file mode 100644 index 2d75eeb4b..000000000 --- a/test/fs-spec-04.js +++ /dev/null @@ -1,70 +0,0 @@ -describe("Tests Files API", function() { - var ABSENT_DIR = "absentdir04", - ABSENT_FILE = "absentfile04", - TEST_DIR = "testdir04", - TEST_FILE = "testfile04", - TEST_FILE_PATH = fs.join(TEST_DIR, TEST_FILE), - TEST_CONTENT = "test content", - START_CWD = null; - - it("should create some temporary file and directory", function(){ - fs.makeDirectory(TEST_DIR); - fs.write(TEST_FILE_PATH, TEST_CONTENT, "w"); - }); - - it("should confirm that test file and test dir exist, while the absent ones don't", function(){ - expect(fs.exists(TEST_FILE_PATH)).toBeTruthy(); - expect(fs.exists(TEST_DIR)).toBeTruthy(); - expect(fs.exists(ABSENT_FILE)).toBeFalsy(); - expect(fs.exists(ABSENT_DIR)).toBeFalsy(); - }); - - it("should confirm that the temporary directory is infact a directory, while the absent one doesn't", function(){ - expect(fs.isDirectory(TEST_DIR)).toBeTruthy(); - expect(fs.isDirectory(ABSENT_DIR)).toBeFalsy(); - }); - - it("should confirm that the temporary file is infact a file, while the absent one doesn't", function(){ - expect(fs.isFile(TEST_FILE_PATH)).toBeTruthy(); - expect(fs.isFile(ABSENT_FILE)).toBeFalsy(); - }); - - it("should confirm that a relative path is not absolute, while an absolute one is", function(){ - var absPath = fs.absolute(TEST_FILE_PATH); - - expect(fs.isAbsolute(TEST_FILE_PATH)).toBeFalsy(); - expect(fs.isAbsolute(absPath)).toBeTruthy(); - }); - - it("should confirm that temporary file is readable, writable and non-executable, while absent file is none of those", function(){ - expect(fs.isReadable(TEST_FILE_PATH)).toBeTruthy(); - expect(fs.isWritable(TEST_FILE_PATH)).toBeTruthy(); - expect(fs.isExecutable(TEST_FILE_PATH)).toBeFalsy(); - - expect(fs.isReadable(ABSENT_FILE)).toBeFalsy(); - expect(fs.isWritable(ABSENT_FILE)).toBeFalsy(); - expect(fs.isExecutable(ABSENT_FILE)).toBeFalsy(); - }); - - it("should confirm that temporary directory is readable, writable and executable, while absent dir is none of those", function(){ - expect(fs.isReadable(TEST_DIR)).toBeTruthy(); - expect(fs.isWritable(TEST_DIR)).toBeTruthy(); - expect(fs.isExecutable(TEST_DIR)).toBeTruthy(); - - expect(fs.isReadable(ABSENT_DIR)).toBeFalsy(); - expect(fs.isWritable(ABSENT_DIR)).toBeFalsy(); - expect(fs.isExecutable(ABSENT_DIR)).toBeFalsy(); - }); - - it("should confirm that neither temporary file/dir or absent file/dir are links", function(){ - expect(fs.isLink(TEST_DIR)).toBeFalsy(); - expect(fs.isLink(TEST_FILE_PATH)).toBeFalsy(); - expect(fs.isLink(ABSENT_DIR)).toBeFalsy(); - expect(fs.isLink(ABSENT_FILE)).toBeFalsy(); - }); - - it("should delete the temporary directory and file", function(){ - fs.removeTree(TEST_DIR); - }); - -}); diff --git a/test/module/fs/basics.js b/test/module/fs/basics.js new file mode 100644 index 000000000..8a639a155 --- /dev/null +++ b/test/module/fs/basics.js @@ -0,0 +1,220 @@ +// Basic Files API (read, write, remove, ...) + +var FILENAME = "temp-01.test", + FILENAME_COPY = FILENAME + ".copy", + FILENAME_MOVED = FILENAME + ".moved", + FILENAME_EMPTY = FILENAME + ".empty", + FILENAME_ENC = FILENAME + ".enc", + FILENAME_BIN = FILENAME + ".bin", + ABSENT = "absent-01.test"; + +var fs; + +setup(function () { + fs = require('fs'); + var f = fs.open(FILENAME, "w"); + + f.write("hello"); + f.writeLine(""); + f.writeLine("world"); + f.close(); +}); + +test(function () { + assert_is_true(fs.exists(FILENAME)); + // we might've gotten DOS line endings + assert_greater_than_equal(fs.size(FILENAME), "hello\nworld\n".length); + +}, "create a file with contents"); + +test(function () { + assert_is_false(fs.exists(FILENAME_EMPTY)); + fs.touch(FILENAME_EMPTY); + assert_is_true(fs.exists(FILENAME_EMPTY)); + assert_equals(fs.size(FILENAME_EMPTY), 0); + +}, "create (touch) an empty file"); + +test(function () { + var content = ""; + var f = fs.open(FILENAME, "r"); + this.add_cleanup(function () { f.close(); }); + + content = f.read(); + assert_equals(content, "hello\nworld\n"); + +}, "read content from a file"); + +test(function () { + var content = ""; + var f = fs.open(FILENAME, "r"); + this.add_cleanup(function () { f.close(); }); + + f.seek(3); + content = f.read(5); + assert_equals(content, "lo\nwo"); + +}, "read specific number of bytes from a specific position in a file"); + +test(function () { + var content = ""; + var f = fs.open(FILENAME, "rw+"); + this.add_cleanup(function () { f.close(); }); + + f.writeLine("asdf"); + content = f.read(); + assert_equals(content, "hello\nworld\nasdf\n"); + +}, "append content to a file"); + +test(function () { + var f = fs.open(FILENAME, "r"); + this.add_cleanup(function () { f.close(); }); + assert_equals(f.getEncoding(), "UTF-8"); + +}, "get the file encoding (default: UTF-8)"); + +test(function () { + var f = fs.open(FILENAME, { charset: "UTF-8", mode: "r" }); + this.add_cleanup(function () { f.close(); }); + assert_equals(f.getEncoding(), "UTF-8"); + + var g = fs.open(FILENAME, { charset: "SJIS", mode: "r" }); + this.add_cleanup(function () { g.close(); }); + assert_equals(g.getEncoding(), "Shift_JIS"); + +}, "set the encoding on open"); + +test(function () { + var f = fs.open(FILENAME, { charset: "UTF-8", mode: "r" }); + this.add_cleanup(function () { f.close(); }); + assert_equals(f.getEncoding(), "UTF-8"); + f.setEncoding("utf8"); + assert_equals(f.getEncoding(), "UTF-8"); + + var g = fs.open(FILENAME, { charset: "SJIS", mode: "r" }); + this.add_cleanup(function () { g.close(); }); + assert_equals(g.getEncoding(), "Shift_JIS"); + g.setEncoding("eucjp"); + assert_equals(g.getEncoding(), "EUC-JP"); + +}, "change the encoding using setEncoding"); + +test(function () { + assert_is_false(fs.exists(FILENAME_COPY)); + fs.copy(FILENAME, FILENAME_COPY); + assert_is_true(fs.exists(FILENAME_COPY)); + assert_equals(fs.read(FILENAME), fs.read(FILENAME_COPY)); + +}, "copy a file"); + +test(function () { + assert_is_true(fs.exists(FILENAME)); + var contentBeforeMove = fs.read(FILENAME); + fs.move(FILENAME, FILENAME_MOVED); + assert_is_false(fs.exists(FILENAME)); + assert_is_true(fs.exists(FILENAME_MOVED)); + assert_equals(fs.read(FILENAME_MOVED), contentBeforeMove); + +}, "move a file"); + +test(function () { + assert_is_true(fs.exists(FILENAME_MOVED)); + assert_is_true(fs.exists(FILENAME_COPY)); + assert_is_true(fs.exists(FILENAME_EMPTY)); + + fs.remove(FILENAME_MOVED); + fs.remove(FILENAME_COPY); + fs.remove(FILENAME_EMPTY); + + assert_is_false(fs.exists(FILENAME_MOVED)); + assert_is_false(fs.exists(FILENAME_COPY)); + assert_is_false(fs.exists(FILENAME_EMPTY)); + +}, "remove a file"); + +test(function () { + assert_throws("Unable to open file '"+ ABSENT +"'", + function () { fs.open(ABSENT, "r"); }); + + assert_throws("Unable to copy file '" + ABSENT + + "' at '" + FILENAME_COPY + "'", + function () { fs.copy(ABSENT, FILENAME_COPY); }); + +}, "operations on nonexistent files throw an exception"); + +test(function () { + var data = "ÄABCÖ"; + var data_b = String.fromCharCode( + 0xC3, 0x84, 0x41, 0x42, 0x43, 0xC3, 0x96); + + var f = fs.open(FILENAME_ENC, "w"); + this.add_cleanup(function () { + f.close(); + fs.remove(FILENAME_ENC); + }); + + f.write(data); + f.close(); + + f = fs.open(FILENAME_ENC, "r"); + assert_equals(f.read(), data); + + var g = fs.open(FILENAME_ENC, "rb"); + this.add_cleanup(function () { g.close(); }); + assert_equals(g.read(), data_b); + +}, "read/write UTF-8 text by default"); + +test(function () { + var data = "ピタゴラスイッチ"; + var data_b = String.fromCharCode( + 0x83, 0x73, 0x83, 0x5e, 0x83, 0x53, 0x83, 0x89, + 0x83, 0x58, 0x83, 0x43, 0x83, 0x62, 0x83, 0x60); + + var f = fs.open(FILENAME_ENC, { mode: "w", charset: "Shift_JIS" }); + this.add_cleanup(function () { + f.close(); + fs.remove(FILENAME_ENC); + }); + + f.write(data); + f.close(); + + f = fs.open(FILENAME_ENC, { mode: "r", charset: "Shift_JIS" }); + assert_equals(f.read(), data); + + var g = fs.open(FILENAME_ENC, "rb"); + this.add_cleanup(function () { g.close(); }); + assert_equals(g.read(), data_b); + +}, "read/write Shift-JIS text with options"); + +test(function () { + var data = String.fromCharCode(0, 1, 2, 3, 4, 5); + + var f = fs.open(FILENAME_BIN, "wb"); + this.add_cleanup(function () { + f.close(); + fs.remove(FILENAME_BIN); + }); + + f.write(data); + f.close(); + + f = fs.open(FILENAME_BIN, "rb"); + assert_equals(f.read(), data); + +}, "read/write binary data"); + +test(function () { + var data = String.fromCharCode(0, 1, 2, 3, 4, 5); + + fs.write(FILENAME_BIN, data, "b"); + this.add_cleanup(function () { + fs.remove(FILENAME_BIN); + }); + + assert_equals(fs.read(FILENAME_BIN, "b"), data); + +}, "read/write binary data (shortcuts)"); diff --git a/test/module/fs/fileattrs.js b/test/module/fs/fileattrs.js new file mode 100644 index 000000000..783b8fab2 --- /dev/null +++ b/test/module/fs/fileattrs.js @@ -0,0 +1,91 @@ + +var fs = require('fs'); + +var ABSENT_DIR = "absentdir02", + ABSENT_FILE = "absentfile02", + TEST_DIR = "testdir02", + TEST_FILE = "temp-02.test", + TEST_FILE_PATH = fs.join(TEST_DIR, TEST_FILE), + TEST_CONTENT = "test content", + CONTENT_MULTIPLIER = 1024; + +test(function () { + assert_throws("Unable to read file '"+ ABSENT_FILE +"' size", + function () { fs.size(ABSENT_FILE); }); + + assert_equals(fs.lastModified(ABSENT_FILE), null); + +}, "size/date queries on nonexistent files"); + +test(function () { + // Round down to the nearest multiple of two seconds, because + // file timestamps might only have that much precision. + var before_creation = Math.floor(Date.now() / 2000) * 2000; + + var f = fs.open(TEST_FILE, "w"); + this.add_cleanup(function () { + if (f !== null) f.close(); + fs.remove(TEST_FILE); + }); + + for (var i = 0; i < CONTENT_MULTIPLIER; i++) { + f.write(TEST_CONTENT); + } + f.close(); f = null; + + // Similarly, but round _up_. + var after_creation = Math.ceil(Date.now() / 2000) * 2000; + + assert_equals(fs.size(TEST_FILE), + TEST_CONTENT.length * CONTENT_MULTIPLIER); + + var flm = fs.lastModified(TEST_FILE).getTime(); + + assert_greater_than_equal(flm, before_creation); + assert_less_than_equal(flm, after_creation); + +}, "size/date queries on existing files"); + +test(function () { + fs.makeDirectory(TEST_DIR); + this.add_cleanup(function () { fs.removeTree(TEST_DIR); }); + fs.write(TEST_FILE_PATH, TEST_CONTENT, "w"); + + assert_is_true(fs.exists(TEST_FILE_PATH)); + assert_is_true(fs.exists(TEST_DIR)); + assert_is_false(fs.exists(ABSENT_FILE)); + assert_is_false(fs.exists(ABSENT_DIR)); + + assert_is_true(fs.isDirectory(TEST_DIR)); + assert_is_false(fs.isDirectory(ABSENT_DIR)); + + + assert_is_true(fs.isFile(TEST_FILE_PATH)); + assert_is_false(fs.isFile(ABSENT_FILE)); + + var absPath = fs.absolute(TEST_FILE_PATH); + assert_is_false(fs.isAbsolute(TEST_FILE_PATH)); + assert_is_true(fs.isAbsolute(absPath)); + + assert_is_true(fs.isReadable(TEST_FILE_PATH)); + assert_is_true(fs.isWritable(TEST_FILE_PATH)); + assert_is_false(fs.isExecutable(TEST_FILE_PATH)); + + assert_is_false(fs.isReadable(ABSENT_FILE)); + assert_is_false(fs.isWritable(ABSENT_FILE)); + assert_is_false(fs.isExecutable(ABSENT_FILE)); + + assert_is_true(fs.isReadable(TEST_DIR)); + assert_is_true(fs.isWritable(TEST_DIR)); + assert_is_true(fs.isExecutable(TEST_DIR)); + + assert_is_false(fs.isReadable(ABSENT_DIR)); + assert_is_false(fs.isWritable(ABSENT_DIR)); + assert_is_false(fs.isExecutable(ABSENT_DIR)); + + assert_is_false(fs.isLink(TEST_DIR)); + assert_is_false(fs.isLink(TEST_FILE_PATH)); + assert_is_false(fs.isLink(ABSENT_DIR)); + assert_is_false(fs.isLink(ABSENT_FILE)); + +}, "file types and access modes"); diff --git a/test/module/fs/paths.js b/test/module/fs/paths.js new file mode 100644 index 000000000..0910f20bb --- /dev/null +++ b/test/module/fs/paths.js @@ -0,0 +1,72 @@ + +var fs = require('fs'); +var system = require('system'); + +var TEST_DIR = "testdir", + TEST_FILE = "testfile", + START_CWD = fs.workingDirectory; + +test(function () { + assert_is_true(fs.makeDirectory(TEST_DIR)); + this.add_cleanup(function () { fs.removeTree(TEST_DIR); }); + + assert_is_true(fs.changeWorkingDirectory(TEST_DIR)); + this.add_cleanup(function () { fs.changeWorkingDirectory(START_CWD); }); + + fs.write(TEST_FILE, TEST_FILE, "w"); + var suffix = fs.join("", TEST_DIR, TEST_FILE), + abs = fs.absolute(".." + suffix), + lastIndex = abs.lastIndexOf(suffix); + + assert_not_equals(lastIndex, -1); + assert_equals(lastIndex + suffix.length, abs.length); + +}, "manipulation of current working directory"); + +test(function () { + + fs.copyTree(phantom.libraryPath, TEST_DIR); + this.add_cleanup(function () { fs.removeTree(TEST_DIR); }); + + assert_deep_equals(fs.list(phantom.libraryPath), fs.list(TEST_DIR)); + +}, "copying a directory tree"); + +test(function () { + assert_type_of(fs.readLink, 'function'); + // TODO: test the actual functionality once we can create symlinks. +}, "fs.readLink exists"); + +generate_tests(function fs_join_test (parts, expected) { + var actual = fs.join.apply(null, parts); + assert_equals(actual, expected); +}, [ + [ "fs.join: []", [], "." ], + [ "fs.join: nonsense", [[], null], "." ], + [ "fs.join: 1 element", [""], "." ], + [ "fs.join: 2 elements", ["", "a"], "/a" ], + [ "fs.join: 3 elements", ["a", "b", "c"], "a/b/c" ], + [ "fs.join: 4 elements", ["", "a", "b", "c"], "/a/b/c" ], + [ "fs.join: empty elements", ["", "a", "", "b", "", "c"], "/a/b/c" ], + [ "fs.join: empty elements 2", ["a", "", "b", "", "c"], "a/b/c" ] +]); + +generate_tests(function fs_split_test (input, expected) { + var path = input.join(fs.separator); + var actual = fs.split(path); + assert_deep_equals(actual, expected); +}, [ + [ "fs.split: absolute", + ["", "a", "b", "c", "d"], ["", "a", "b", "c", "d"] ], + [ "fs.split: absolute, trailing", + ["", "a", "b", "c", "d", ""], ["", "a", "b", "c", "d"] ], + [ "fs.split: non-absolute", + ["a", "b", "c", "d"], ["a", "b", "c", "d"] ], + [ "fs.split: non-absolute, trailing", + ["a", "b", "c", "d", ""], ["a", "b", "c", "d"] ], + [ "fs.split: repeated separators", + ["a", "", "", "", + "b", "", + "c", "", "", + "d", "", "", ""], ["a", "b", "c", "d"] ] +]); diff --git a/test/module/webserver/basics.js b/test/module/webserver/basics.js new file mode 100644 index 000000000..4f124cc42 --- /dev/null +++ b/test/module/webserver/basics.js @@ -0,0 +1,25 @@ +test(function () { + assert_no_property(window, "WebServer", + "WebServer constructor should not be global"); + + var WebServer = require("webserver").create; + assert_type_of(WebServer, "function"); + +}, "WebServer constructor"); + +test(function () { + var server = require("webserver").create(); + + assert_not_equals(server, null); + assert_type_of(server, "object"); + assert_equals(server.objectName, "WebServer"); + + assert_own_property(server, "port"); + assert_type_of(server.port, "string"); + assert_equals(server.port, ""); + + assert_type_of(server.listenOnPort, "function"); + assert_type_of(server.newRequest, "function"); + assert_type_of(server.close, "function"); + +}, "WebServer object properties"); diff --git a/test/module/webserver/requests.js b/test/module/webserver/requests.js new file mode 100644 index 000000000..3d4246dd0 --- /dev/null +++ b/test/module/webserver/requests.js @@ -0,0 +1,145 @@ +var server, port, request_cb; +setup(function () { + server = require("webserver").create(); + + // Should be unable to listen on port 1 (FIXME: this might succeed if + // the test suite is being run with root privileges). + assert_is_false(server.listen(1, function () {})); + assert_equals(server.port, ""); + + // Find an unused port in the 1024--32767 range on which to run the + // rest of the tests. The function in "request_cb" will be called + // for each request; it is set appropriately by each test case. + for (var i = 1024; i < 32768; i++) { + if (server.listen(i, function(rq,rs){return request_cb(rq,rs);})) { + assert_equals(server.port, i.toString()); + port = server.port; + return; + } + } + assert_unreached("unable to find a free TCP port for server tests"); +}, + { "test_timeout": 1000 }); + +function arm_check_request (test, expected_postdata, expected_bindata, + expected_mimetype) { + request_cb = test.step_func(function check_request (request, response) { + try { + assert_type_of(request, "object"); + assert_own_property(request, "url"); + assert_own_property(request, "method"); + assert_own_property(request, "httpVersion"); + assert_own_property(request, "headers"); + assert_type_of(request.headers, "object"); + + assert_type_of(response, "object"); + assert_own_property(response, "statusCode"); + assert_own_property(response, "headers"); + assert_type_of(response.setHeaders, "function"); + assert_type_of(response.setHeader, "function"); + assert_type_of(response.header, "function"); + assert_type_of(response.write, "function"); + assert_type_of(response.writeHead, "function"); + + if (expected_postdata !== false) { + assert_equals(request.method, "POST"); + assert_own_property(request, "post"); + if (request.headers["Content-Type"] === + "application/x-www-form-urlencoded") { + assert_own_property(request, "postRaw"); + assert_type_of(request.postRaw, "string"); + assert_type_of(request.post, "object"); + assert_deep_equals(request.post, expected_postdata); + } else { + assert_no_property(request, "postRaw"); + assert_type_of(request.post, "string"); + assert_not_equals(request.post, expected_postdata); + } + } + + if (expected_bindata !== false) { + response.setEncoding("binary"); + response.setHeader("Content-Type", expected_mimetype); + response.write(expected_bindata); + } else { + response.write("request handled"); + } + } finally { + response.close(); + request_cb = test.unreached_func(); + } + }); +} + +async_test(function () { + var page = require("webpage").create(); + var url = "http://localhost:"+port+"/foo/bar.php?asdf=true"; + + arm_check_request(this, false, false); + page.open(url, this.step_func_done(function (status) { + assert_equals(status, "success"); + assert_equals(page.plainText, "request handled"); + })); + +}, "basic request handling"); + +async_test(function () { + var page = require("webpage").create(); + var url = "http://localhost:"+port+"/foo/bar.txt?asdf=true"; + + arm_check_request(this, + {"answer" : "42", "universe" : "expanding"}, false); + + page.open(url, "post", "universe=expanding&answer=42", + { "Content-Type" : "application/x-www-form-urlencoded" }, + this.step_func_done(function (status) { + assert_equals(status, "success"); + assert_equals(page.plainText, "request handled"); + })); + +}, "handling POST with application/x-www-form-urlencoded data"); + +async_test(function () { + var page = require("webpage").create(); + var url = "http://localhost:"+port+"/foo/bar.txt?asdf=true"; + + arm_check_request(this, + {"answer" : "42", "universe" : "expanding"}, false); + + page.open(url, "post", "universe=expanding&answer=42", + { "Content-Type" : "application/json;charset=UTF-8" }, + this.step_func_done(function (status) { + assert_equals(status, "success"); + assert_equals(page.plainText, "request handled"); + })); + +}, "handling POST with ill-formed application/json data"); + +async_test(function () { + var page = require("webpage").create(); + var url = "http://localhost:"+port+"/"; + var fs = require("fs"); + var png = fs.read(fs.join(phantom.libraryPath, "../../phantomjs.png"), "b"); + + arm_check_request(this, false, png, "image/png"); + page.open(url, "get", this.step_func_done(function (status) { + assert_equals(status, "success"); + function checkImg() { + var img = document.querySelector("img"); + if (img) { + return { w: img.width, h: img.height }; + } else { + return {}; + } + } + // XFAIL: image doesn't load properly and we receive the dimensions of + // the ?-in-a-box placeholder + assert_object_equals(page.evaluate(checkImg), { w: 200, h: 200 }); + })); + +}, "handling binary data", { + skip: true, // crash: https://github.com/ariya/phantomjs/issues/13461 + expected_fail: true // received image is corrupt: + // https://github.com/ariya/phantomjs/issues/13026 + // and perhaps others +}); diff --git a/test/module_spec.js b/test/module_spec.js deleted file mode 100644 index 92a9cceda..000000000 --- a/test/module_spec.js +++ /dev/null @@ -1,18 +0,0 @@ -describe("Module", function() { - it("has filename property containing its absolute path", function() { - module.filename.should.match(/\/.*test\/module_spec.js/); - }); - - it("has id property equal to filename", function() { - module.id.should.equal(module.filename); - }); - - it("has dirname property containing absolute path to its directory", function() { - module.dirname.should.match(/\/.*test/); - }); - - it("its require() can be used externally", function() { - var exposed = require('dummy_exposed'); - exposed.require('./dummy_file').should.equal('spec/node_modules/dummy_file'); - }); -}); diff --git a/test/regression/pjs-10690.js b/test/regression/pjs-10690.js new file mode 100644 index 000000000..e372017c6 --- /dev/null +++ b/test/regression/pjs-10690.js @@ -0,0 +1,14 @@ +// Issue 10690: the second page load used to crash on OSX. + +var url = 'http://localhost:9180/regression/pjs-10690/index.html'; +function do_test() { + var page = require('webpage').create(); + + page.open(url, this.step_func_done (function (status) { + assert_equals(status, "success"); + page.release(); + })); +} + +async_test(do_test, "load a page with a downloadable font, once"); +async_test(do_test, "load a page with a downloadable font, again"); diff --git a/test/regression/webkit-60448.js b/test/regression/webkit-60448.js new file mode 100644 index 000000000..7cf34fa71 --- /dev/null +++ b/test/regression/webkit-60448.js @@ -0,0 +1,13 @@ +var url = + "http://localhost:9180/regression/webkit-60448.html"; + +async_test(function () { + var p = require("webpage").create(); + p.open(url, this.step_func_done(function (status) { + assert_equals(status, "success"); + assert_is_true(p.evaluate(function () { + return document.getElementById("test") === null; + })); + })); +}, +"remove an inline HTML element from the document"); diff --git a/test/require/require_spec.js b/test/require/require_spec.js deleted file mode 100644 index 58f98e8b3..000000000 --- a/test/require/require_spec.js +++ /dev/null @@ -1,215 +0,0 @@ -describe("require()", function() { - it("loads 'webpage' native module", function() { - should.exist(require('webpage').create); - }); - - it("loads 'fs' native module", function() { - should.exist(require('fs').separator); - }); - - it("loads 'webserver' native module", function() { - should.exist(require('webserver').create); - }); - - it("loads 'cookiejar' native module", function() { - should.exist(require('cookiejar').create); - }); - - it("loads 'system' native module", function() { - require('system').platform.should.equal('phantomjs'); - }); - - it("doesn't expose CoffeeScript", function() { - should.not.exist(window.CoffeeScript); - }); - - it("loads JSON modules", function() { - require('./json_dummy').message.should.equal('hello'); - }); - - it("loads modules with specified extension", function() { - require('./dummy.js').should.equal('require/dummy'); - }); - - it("caches modules", function() { - require('./empty').hello = 'hola'; - require('./empty').hello.should.equal('hola'); - }); - - it("supports cycles (circular dependencies)", function() { - var a = require('./a'); - var b = require('./b'); - a.b.should.equal(b); - b.a.should.equal(a); - }); - - it("has cache object attached containing cached modules", function() { - var exposed = require('dummy_exposed'); - should.exist(require.cache); - require.cache[module.filename].should.equal(module); - require.cache[exposed.filename].should.equal(exposed); - }); - - it("throws an error with appropriate message when module not found", function() { - (function() { - require('dummy_missing'); - }).should.Throw("Cannot find module 'dummy_missing'"); - }); - - xit("maintains proper .stack when module not found", function() { - try { - require('./not_found').requireNonExistent(); - } catch (e) { - e.stack.should.match(/\n *at .*not_found\.js:2\n/); - } - }); - - xit("maintains proper .stack when an error is thrown in module's exports", function() { - try { - require('./thrower').fn(); - } catch (e) { - e.stack.should.match(/^Error: fn\n *at .*thrower\.js:2/); - } - }); - - describe("stub()", function() { - it("stubs modules in given context", function() { - require('./stubber').stubbed.should.equal('stubbed module'); - }); - - it("stubs modules in child context", function() { - require('./stubber').child.stubbed.should.equal('stubbed module'); - }); - - it("doesn't stub in parent context", function() { - (function() { - require('stubbed'); - }).should.Throw("Cannot find module 'stubbed'"); - }); - - describe("when invoked with a factory function", function() { - var count = 0; - require.stub('lazily_stubbed', function() { - ++count; - return 'lazily stubbed module'; - }); - - it("initializes the module lazily", function() { - require('lazily_stubbed').should.equal('lazily stubbed module'); - }); - - it("doesn't reinitialize the module each time it's required", function() { - require('lazily_stubbed'); - count.should.equal(1); - }); - }); - }); - - describe("when the path is relative", function() { - it("loads modules from the same directory", function() { - require('./dummy').should.equal('require/dummy'); - }); - - it("loads modules from the parent directory", function() { - require('../dummy').should.equal('spec/dummy'); - }); - - it("loads modules from a child directory", function() { - require('./dir/dummy').should.equal('dir/dummy'); - }); - - it("loads modules from a deeper directory", function() { - require('./dir/subdir/dummy').should.equal('subdir/dummy'); - }); - - it("loads modules when path has intertwined '..'", function() { - require('./dir/../dummy').should.equal('require/dummy'); - }); - - it("loads modules when path has intertwined '.'", function() { - require('./dir/./dummy').should.equal('dir/dummy'); - }); - }); - - describe("when loading from node_modules", function() { - it("first tries to load from ./node_modules", function() { - require('dummy_file').should.equal('require/node_modules/dummy_file'); - }); - - it("loads from ../node_modules", function() { - require('dummy_file2').should.equal('spec/node_modules/dummy_file2'); - }); - - it("loads from further up the directory tree", function() { - require('./dir/subdir/loader').dummyFile2.should.equal('spec/node_modules/dummy_file2'); - }); - - describe("when module is a directory", function() { - it("first tries to load the path from package.json", function() { - require('dummy_module').should.equal('require/node_modules/dummy_module'); - }); - - it("loads index.js if package.json not found", function() { - require('dummy_module2').should.equal('require/node_modules/dummy_module2'); - }); - }); - }); - - describe("when path is absolute", function() { - it("loads modules from the absolute path", function() { - require(fs.absolute('dummy')).should.equal('spec/dummy'); - }); - }); - - describe("with require.paths", function() { - describe("when require.paths.push(relative)", function() { - it("add relative path to paths", function() { - require.paths.push('./dir/subdir'); - }); - - it("loads 'loader' module in dir/subdir", function() { - require('loader').dummyFile2.should.equal('spec/node_modules/dummy_file2'); - }); - - it("loads 'loader' module in dir/subdir2 relative to require.paths", function() { - require('../subdir2/loader').should.equal('require/subdir2/loader'); - }); - - it("loads 'dummy' module from the path that takes precedence", function() { - require('../dummy').should.equal('spec/dummy'); - }); - - it("doesn't load 'loader' module in dir/subdir after require.paths.pop()", function() { - (function() { - require.paths.pop(); - require('loader'); - }).should.Throw("Cannot find module 'loader'"); - }); - }); - - describe("when require.paths.push(absolute)", function() { - it("adds absolute path to paths", function() { - require.paths.push(fs.absolute('require/dir/subdir')); - }); - - it("loads 'loader' module in dir/subdir", function() { - require('loader').dummyFile2.should.equal('spec/node_modules/dummy_file2'); - }); - - it("loads 'loader' module in dir/subdir2 relative to require.paths", function() { - require('../subdir2/loader').should.equal('require/subdir2/loader'); - }); - - it("loads 'dummy' module from the path that takes precedence", function() { - require('../dummy').should.equal('spec/dummy'); - }); - - it("doesn't load 'loader' module in dir/subdir after require.paths.pop()", function() { - (function() { - require.paths.pop(); - require('loader'); - }).should.Throw("Cannot find module 'loader'"); - }); - }); - }); -}); diff --git a/test/require/thrower.js b/test/require/thrower.js deleted file mode 100644 index 4d87f3290..000000000 --- a/test/require/thrower.js +++ /dev/null @@ -1,3 +0,0 @@ -exports.fn = function() { - throw new Error('fn'); -}; diff --git a/test/run-tests.js b/test/run-tests.js index 82b084397..d4a698e88 100644 --- a/test/run-tests.js +++ b/test/run-tests.js @@ -64,16 +64,7 @@ function expectHasPropertyString(o, name) { } // Load specs -phantom.injectJs("./phantom-spec.js"); -phantom.injectJs("./webserver-spec.js"); -phantom.injectJs("./fs-spec-01.js"); //< Filesystem Specs 01 (Basic) -phantom.injectJs("./fs-spec-02.js"); //< Filesystem Specs 02 (Attributes) -phantom.injectJs("./fs-spec-03.js"); //< Filesystem Specs 03 (Paths) -phantom.injectJs("./fs-spec-04.js"); //< Filesystem Specs 04 (Tests) -phantom.injectJs("./webkit-spec.js"); phantom.injectJs("./webpage-spec.js"); -require("./module_spec.js"); -require("./require/require_spec.js"); // Environment configuration var jasmineEnv = jasmine.getEnv(); diff --git a/test/set/690-ttf-crash/run.js b/test/set/690-ttf-crash/run.js deleted file mode 100644 index 0f6209053..000000000 --- a/test/set/690-ttf-crash/run.js +++ /dev/null @@ -1,12 +0,0 @@ -var page = require('webpage').create(); -var url = 'http://localhost:5000'; - -page.open(url, function (status) { - console.log('Page loaded once', status); - page.release(); - page = require('webpage').create(); - page.open(url, function (status) { - console.log('Page loaded twice', status); - phantom.exit(); - }); -}); diff --git a/test/set/690-ttf-crash/serve.rb b/test/set/690-ttf-crash/serve.rb deleted file mode 100644 index 6e515cc18..000000000 --- a/test/set/690-ttf-crash/serve.rb +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env ruby - -# From http://chrismdp.com/2011/12/cache-busting-ruby-http-server/ - -require 'webrick' -class NonCachingFileHandler < WEBrick::HTTPServlet::FileHandler - def prevent_caching(res) - res['ETag'] = nil - res['Last-Modified'] = Time.now + 100**4 - res['Cache-Control'] = 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0' - res['Pragma'] = 'no-cache' - res['Expires'] = Time.now - 100**4 - end - - def do_GET(req, res) - super - prevent_caching(res) - end -end - -server = WEBrick::HTTPServer.new :Port => 5000 - -server.mount '/', NonCachingFileHandler , Dir.pwd -trap('INT') { server.stop } -server.start diff --git a/test/webkit-spec.js b/test/webkit-spec.js deleted file mode 100644 index b1448f3aa..000000000 --- a/test/webkit-spec.js +++ /dev/null @@ -1,7 +0,0 @@ -describe("WebKit", function() { - it("should not crash when failing to dirty lines while removing a inline.", function () { - var p = require("webpage").create(); - p.open('../test/webkit-spec/inline-destroy-dirty-lines-crash.html'); - waits(50); - }); -}); diff --git a/test/webserver-spec.js b/test/webserver-spec.js deleted file mode 100644 index c7cf4aab2..000000000 --- a/test/webserver-spec.js +++ /dev/null @@ -1,192 +0,0 @@ -describe("WebServer constructor", function() { - it("should not exist in window", function() { - expect(window.hasOwnProperty('WebServer')).toBeFalsy(); - }); - - it("should be a function", function() { - var WebServer = require('webserver').create; - expect(typeof WebServer).toEqual('function'); - }); -}); - -var expectedPostData = false, expectedBinaryData = false; - -function checkRequest(request, response) { - expect(typeof request).toEqual('object'); - expect(request.hasOwnProperty('url')).toBeTruthy(); - expect(request.hasOwnProperty('method')).toBeTruthy(); - expect(request.hasOwnProperty('httpVersion')).toBeTruthy(); - expect(request.hasOwnProperty('headers')).toBeTruthy(); - expect(typeof request.headers).toEqual('object'); - - expect(typeof response).toEqual('object'); - expect(response.hasOwnProperty('statusCode')).toBeTruthy(); - expect(response.hasOwnProperty('headers')).toBeTruthy(); - expect(typeof response['setHeaders']).toEqual('function'); - expect(typeof response['setHeader']).toEqual('function'); - expect(typeof response['header']).toEqual('function'); - expect(typeof response['write']).toEqual('function'); - expect(typeof response['writeHead']).toEqual('function'); - - if (expectedPostData !== false) { - expect(request.method).toEqual("POST"); - expect(request.hasOwnProperty('post')).toBeTruthy(); - jasmine.log("request.post => " + JSON.stringify(request.post, null, 4)); - jasmine.log("expectedPostData => " + JSON.stringify(expectedPostData, null, 4)); - jasmine.log("request.headers => " + JSON.stringify(request.headers, null, 4)); - if (request.headers["Content-Type"] && request.headers["Content-Type"] === "application/x-www-form-urlencoded") { - expect(typeof request.post).toEqual('object'); - expect(request.post).toEqual(expectedPostData); - expect(request.hasOwnProperty('postRaw')).toBeTruthy(); - expect(typeof request.postRaw).toEqual('string'); - } else { - expect(typeof request.post).toEqual('string'); - expect(request.post).toNotEqual(expectedPostData); - expect(request.hasOwnProperty('postRaw')).toBeFalsy(); - } - expectedPostData = false; - } - - if (expectedBinaryData !== false) { - response.setEncoding('binary'); - response.write(expectedBinaryData); - expectedBinaryData = false; - } else { - response.write("request handled"); - } - response.close(); -} - -describe("WebServer object", function() { - var server = require('webserver').create(); - - it("should be creatable", function() { - expect(typeof server).toEqual('object'); - expect(server).toNotEqual(null); - }); - - it("should have objectName as 'WebServer'", function() { - expect(server.objectName).toEqual('WebServer'); - }); - - expectHasProperty(server, 'port'); - it("should have port as string", function() { - expect(typeof server.port).toEqual('string'); - }); - it("should not listen to any port by default", function() { - expect(server.port).toEqual(""); - }); - - /* TODO: - expectHasProperty(page, 'settings'); - it("should have non-empty settings", function() { - expect(page.settings).toNotEqual(null); - expect(page.settings).toNotEqual({}); - }); - */ - expectHasFunction(server, 'listenOnPort'); - expectHasFunction(server, 'newRequest'); - expectHasFunction(server, 'close'); - - it("should fail to listen to blocked ports", function() { - //NOTE: is this really blocked everywhere? - expect(server.listen(1, function(){})).toEqual(false); - expect(server.port).toEqual(""); - }); - it("should be able to listen to some port", function() { - //NOTE: this can fail if the port is already being listend on... - expect(server.listen("1337", checkRequest)).toEqual(true); - expect(server.port).toEqual("1337"); - }); - - it("should handle requests", function() { - var page = require('webpage').create(); - var url = "http://localhost:1337/foo/bar.php?asdf=true"; - var handled = false; - runs(function() { - expect(handled).toEqual(false); - page.open(url, function (status) { - expect(status == 'success').toEqual(true); - expect(page.plainText).toEqual("request handled"); - handled = true; - }); - }); - - waits(50); - - runs(function() { - expect(handled).toEqual(true); - }); - }); - - it("should handle post requests ('Content-Type' = 'application/x-www-form-urlencoded')", function() { - var page = require('webpage').create(); - var url = "http://localhost:1337/foo/bar.txt?asdf=true"; - //note: sorted by key (map) - expectedPostData = {'answer' : "42", 'universe' : "expanding"}; - var handled = false; - runs(function() { - expect(handled).toEqual(false); - page.open(url, 'post', "universe=expanding&answer=42", { "Content-Type" : "application/x-www-form-urlencoded" }, function (status) { - expect(status == 'success').toEqual(true); - expect(page.plainText).toEqual("request handled"); - handled = true; - }); - }); - - waits(50); - - runs(function() { - expect(handled).toEqual(true); - }); - }); - - it("should handle post requests ('Content-Type' = 'ANY')", function() { - var page = require('webpage').create(); - var url = "http://localhost:1337/foo/bar.txt?asdf=true"; - //note: sorted by key (map) - expectedPostData = {'answer' : "42", 'universe' : "expanding"}; - var handled = false; - runs(function() { - expect(handled).toEqual(false); - page.open(url, 'post', "universe=expanding&answer=42", { "Content-Type" : "application/json;charset=UTF-8" }, function (status) { - expect(status == 'success').toEqual(true); - expect(page.plainText).toEqual("request handled"); - handled = true; - }); - }); - - waits(50); - - runs(function() { - expect(handled).toEqual(true); - }); - }); - - xit("should handle binary data", function() { - var page = require('webpage').create(); - var url = "http://localhost:1337/"; - var fs = require('fs'); - expectedBinaryData = fs.read('phantomjs.png', 'b'); - var handled = false; - runs(function() { - expect(handled).toEqual(false); - page.open(url, 'get', function(status) { - expect(status == 'success').toEqual(true); - function checkImg() { - var img = document.querySelector('img'); - return (img) && (img.width == 200) && (img.height == 200); - } - expect(page.evaluate(checkImg)).toEqual(true); - handled = true; - }); - }); - - waits(50); - - runs(function() { - expect(handled).toEqual(true); - }); - }); - -}); diff --git a/test/www/regression/pjs-10690/Windsong.ttf b/test/www/regression/pjs-10690/Windsong.ttf new file mode 100644 index 0000000000000000000000000000000000000000..22c8e697c7b4031663a29cde4366c786e0c1e0b5 GIT binary patch literal 84852 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

L8~+In3IeG!(mZt_HDqP7vK$FqBgYz@XKOTnG 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%0AFY zEGX4>0ULKjU!0>9jh7r@0_j8#$l*avL`ktbOMC-JoQIsyGr&?N|D09t{2cvP z`Fuwn4sMAsNhO-6(wUa#<@+eLFa@)AG@~)@-Fu*{(*<`O>4Uy}y63>&4#6M*hL3Un zFDDH&_2j*f;JiASy1W2wnA*tXoo~5#6U)zj;-%XAXSObN zvXLCcs&%|7musK4(11794pk7kr#Z_CR-4|qyk9?Na6f!AU;EtE#p0J9_)nw3K`y_W zwEQDzdt_yave5lR3o#BAuu`eoS~M2{1fIb}qNF5m z30p;wJVanAM~=`{aqF<$CWU(56eI^iLVE@GsW7Q=o#ANLnR#uHF=)c}TL&!`o6YD} z!SvFAH>Ic4k#FkhbnJu4k!?YmcE@_2-+xRC2$kmhmyUUTv1q``8C5X&sx#Pgk>QV} zXn-^&iI1*)h9@Vrh9wOzOVMNT@Pt(EbIJz$NR|1o?A6079y6ftn4K}4gGAZ)Fv{y zpi@Y|upi4camAuto@S8bTF$9SKFHaK+K_(69Rjj&iqz-oiD#fS%3*>Z7H|=)Kz-_(q@^1$0(p?{Q$GZ7h+d#~Z`DE~dfNd-ozqzf-Uv7{a`gqlPJ*|Ud$H6+ z1IjDg>u_70Xy!Qb1z*7!O0k@o?}-=-zJ#4u=?18m!$HQZVw8!JzYgWg*1S~C+x>Cw~%qie3Ezu&hT}E2ktDYmK zxKfyNYDzolZknvDGGh%GVLgtQZGEND{w%QdvayjJW<8j=K_;|G4#|AGmP zbb5F$84M>)D&OWXgrp6AE&!!@)(|QL=@-MnL5DUP413fXJOAa)V$O>UuMsG#0N`>k zFBl7gPX@321&pq@1A3@W!z}*+KTqb4H?cF&!I#`xr_Bxn6Ank20EhD78 zvXW&|1#v?Jgf2*it=tgB6Y@O}K#ONC&=lmJQM66TVYAH71Z02YgP52-M{g@g<_`IE zxO#n{eBD#W4^*NmlU68owTwH|&st4_m1T5{khYp`1gnqH+7k3gDdyMGR-Out`_+01 zfH4d^)rM<<_(5kIMuTILuy+==Y(QW81?cHOeeH&mM|LmtwHKNbv4D%Z>CirCZX54w zO-BMgm=On%0bZjSTBgZy+4`<;9}<}(g{BgxB;Jf)qszS>6d`ti8Yq$mzXq(h)6+*` zP)$`l#&B9|=+^`s#}EVk9MAK`W3)xf?fdni$WaoO!b{*KZv`~QG3%=b5P>l5^+`;&z!~a%(cAg zs)quT2}TpC(q;iKytk3V9^Py;2KwBZ2;g&Pem&R>oP^Er*Ep^y!&!M8`uNTP-W=-V zvo1_;8E9|K>gf5Q?g})N;Si&Df^kBOVO9kvU{Il(F3@B|W+iIpL%rYYaL8m{)~#fu zQAgjkLJjE*2_p)~_+3AcLwG}=@uAIucKGDFIO7AG2K7KoQfRE3q!l z^mmn$Q5`)s*i(YikI#d7gZUCz-Jv1(;Y)Te$S>1VM4@!deS?|m%%^H;9NfHZ_`bWb+f&zYI(R{hjyS1IYH1|f?XbvyMiU=SqCggm{Y?DXG2&nPjY^anH8AK(ZXkq@OW zr5&?3Ku+>0#n8g{Z-|;>iiwBXZ{H9xMcHg6KL@iafGYqQrp3kTPR9;71{NY(21YPj zvXPKO9A0UHMRW?d)-|WUx>c53Y?fG5l5|ldDHBwp4Aj*>`A0^QumwY^%*DAqcz*ZPv5YZMypydi2& zt_cJG?uJMWStN1N>WA10@52&};ynHCShciq0FM^!ByM3Gy>*k`uM}+z92#@%1SQa^>qQ8ayjB2QgiOoS(05 zC$_=5g;Fu78ta2S-tn+i1I-Y6jljDncrIs_+j>wFDTw`=RK$8qZYAcG=4Bq47qM{w zqKnr|F~~;#4a^t`Y7P?r+16~;rh3R^(3k-ihtWh0!W$huA}K)H>B$AXY8twxeEn{~ z+ku`tVUkh3&Y2`U4fAvIzaesKfr|PzsiLlHE5iy;s;$%%bU@&i=3&n%K|16tg2FMb zwIL2-V@bV1+=i0sK)2U1Ci7!uh}Q82W@gB*t%=Cq^#(?0K=bj85akEWFTxxRqDp6H zwM$=VEi`+mN=q_ehwv-KTQxBJDFG0pVVU}&d@WV%M5j`~IvYn{ub(I}MWFTFH$syI zWnA^1H$W1JBIHXDMRmfQ1?o3Ma~6(jPe)r+3zhXkG9U&~ExgYHUMH|BD;wz|JN7`M zi3`a4T_c8i{Sph;dM5_ zk5Cj?paN7Qop{ToE}!#L$)FuuUKYpKo+rdGto-0LaXmBg;!SG!}pm5WWxFK~WyA1rX7u3AB9~<&JcdurwfJ zw^AehJ;jt)$5h#l5}Rz}qZ+5&&Lz%U)q4UJWUo7)21%r~R+d6#(Hj~TSW(*GzU!SF zSeK9c?du#Qz?c#+XF*wUB-lkvVTw*qS7!_sOv_>^^+TYivPNaJhR^0{MU67nt;8zcs}U;QUr<+CyoNG(aE{8b)>Jg;Bo2b z$&rCda|kuU46(iKY_^+kGOK)KNS0odEmD$NVG?9whZ>xn8wI2{F2`Tk;JYqgH=!@D z_iBG}orIPp`VsJT-O$6h2$(Y|a_7$KuCDC>Ouyn1kwTZ%zc@yfDC93YT462MU>q0DfwE!`pr0fKy z>{_oTmzu;f(`r}I>lzC9ywagj*D)bLeqv1~SGjzLy5L_I>3D_f zBDw1!U8~Y|@)&)~0Y{P=*HBL>}EuVL9a7kai9-`j8 z-W$Dioirth*4k$%2R%%*;c7*Dd$ps|?6X@r3g+2EJ~!TzU|{tzgTBQ}$sOJRwXlAc zUGhGXiF6a~N;Pj6O(ZV=AS@L0|A?7R^on?yQ1e7KJIRrr z!l5TTl*t_l`lpAJ9%q1Jb-sAhz%Bzo`>MXYnLxkC_ zGO`5YrD4KWLIy-dbJS~H?`u~PLFKIm@vTBH2bK}I2Ry+eVYecShQ=(&_oN0pO$NVC zC??X)7L|`NMTLmn0C07tL{Ag-4^6QyUYCj|16siiOO!MMEWIJp@p1RKwV-IOLL7%s@O(DyE_8BK!;^i#YTv>a*rk4ur+}f z9EjaT@De;)S^;hnKsFG!@tdJ55Y0bH&|uW4H>Ni*8WRjpGG^N1^&}4-y1=q(b}}$B z>S!}FJQ+@DQvE4U&*XeK2~%Dgf5_u#V!T$snxpBjj-0ZG^L~MArtYw{Sp#ZV(#xye zDuY>ls`g{c9q^mjDObDD_pFK0Gly>}C$|n~dnUtkdjUCUc!w$))Kfg)@#(^3Y{>jJt-;*3(z&d6bh zJS>4mP!Rp$QEaiJ)^S)HiTjqZFy^607LQusrgAiXhw*AfK~0F|6^S7g*gb=_(b0nW zg)aMHl!VV$iuB{`UQVs&qDC)GB@B+?#bmZ@*IR8G)?wDcW=0Y)9t!>tRke=oakhj9 zPap3b>I-m?s8s2r<5NR(4<#47`!X~YhY>%2qB2wMR5>h8-aYOQhmx_QI|FWefH6o>x9xRh&Avp?A7{-bwc3;kNAtF5 zEM?KEc}LsqEfhuSEP%h=J~TAbeAS?jLDFrff1;%Zk}jCn9GD`jbB5|4EuGf9?#-SAd4?na%by_Ss29#T6f zx~<$gz%tD=lTWqV0R)em?(W~tvOAsTK|npWR!V~`lc$+|9cG<{;itL>!VWhq>WsS5 zlN4pJ_)k$kE4hpSm7-$-Q88M|b}Uc7SeeTug}rkZ`dfDHgn4)aV3#|+smk*7$19_U zxjhr`g3VPUio>({U?h#EJI)gyhN;{C4;qeIt2)wRsanBt8yI=61CO7t&U9FmvTc#W zLVNunJ9eKI0W0%F}vJaH=R>q6wc&@A*qo1c!z}xLG zC5~bQSUww6G1YPwEW{Ag!EuvgX>)mX(K5I*e3}yNo~U>d`?8JL2RwpGZ#aP`YQ$h) zWF}1f#KZBlI6GZ`L7D{uW}yyfqYz|zeA|b+{VHFZ z!47NN*3J(FyS{UFFrMsIsYXuEzl~HIy7WzEFKwj+9m&94D5IDx+Vv0&7M<=OVW+C% zsyJ`db&Gy$^Va@Ct4ei#c((f_07(G-dRCsJzDtil-EId+JD!&=+DZ-+jA=7mh*75) z!q4k6Tzk8YEXhazy7>}yVAXMy8dZ3uQ6wDIBTp&%ekAYM34klXmt>!U;skgLT83k% zzFURT>IwL#kjM>ygW+3YK4%-cQ0-gpZmxZn)7okB;@B+!{PCGin6Jw;cl{_e($ShK z?pSDJZ7ME1G&H%Zz4LqR&6#*S-+ADk{jXfs!txF3drwb7CgiJRg?foj5(42OE)e$- zj}jlQ&XA8iR(+g!;LeLj=BBoc^>t((x_a;3cinmZ>`jN3m-cR-ofsP`n zHrCz7hV49Ki(@DK+P@)$l_kDPSalnLuPkc3%`&SSyP0{b3jsU=$-jTk(XNh{GNx^k_jG0nd#3 zyd7v{v;q*)_yT&)@gNL%P7l^Kpq8}~u!RrX$~+o^Y=s}PqMza8$ln06#!EYnJ^lVe zr|#Oa$G&yo+>yPHKe9Z#FKqVdJv>d?VM_&UEF#sg(9_P-I(W*dO0WwB_J`3R%P_+Y zE?w7211PM4)Y#1`-Y)o>Y_J1N3qat$Zc+umWy(f5^)AX1q)f)qdto&-O*%;G%7L5i z`OMK(SaX0WcNTmM#+2K&qrZ7^er&M6qf`W^reXo#h8X61e83@IujjVm4VNWF zxQT)@q+9E z4wIVFrlQ$fw@g^Vg(JsUEyJ8VQyn#S{5b&j=vqdZd-P@p;5IlY^MfY?!4{p z*?n6k&L6+JICcKaT@nh<)gkin$E$Dddi4H_2X}3$HfKYQ z`|iDX;g%DJ_wSjVhE>VX$C8WqT(Am;<=cw7yXkZEE!1&(KQ&7YQ*CLwBeB|$-__eC zYA|L}y_ut~u+ozTdK)1gSL*HKqQ?W$BJ!e#lr)M*kotm;r~s%9>Rp!3Nac!yPlyZx z9TQbme7QA_gK11%PX$(!GH3zym&0j1(Ba=@<0wZj~C4 z7K{!nEQq(*k^n_Sy$rVyz8v$@ih1qjLa!C?q^ySS|snq7lO=tL4ZT&YDbU$PgJE}(5DNr@)#BnK#WLBM^Vo;SK3`!Oo zsHUg#J&4>fi22TiH)^1-g z5LV?Mwu(Lhn|a{#>Yyq4+=g@nZCRIE5NBh*!ZGv<;0fPMyo$ zz7KLPVYkz)dFF|Suikm_{F$ZgQ+>Uqq~C5vc$o*ei7OqC!kV@@u+epwhwFyxbwvDL zsVQ|IH$^-h4LuS!JfviJ#g(O+N4&p2ZTu7i-uVXC&Hzqxx1`bDZcpU|1 z@|-4)tMN}Me->j$N%j!^B|e^zttAb=6@4URSUEt2nxAW@NL$>YGq@RES8&ysnZX`F zGUtTS*l^_OoBJKEQH$>Olfn10T8jPEf2cIUs@g+RZx3eM;=@-?>&C`RNhU=0pZv=` zy>_8+;%#@ld16rK!ZI$se=67mRfIaE3 zYyX*26$^f!FPBPc>EF~){Is>rYI3leAYd~%DE+y|&E_P>B?PNpDCVbbAKjLT`e^@% z?M|Lkb1z3pry<*N<=^7z*3_1R+f^BctayJ&YIYvWL4JiVjqkg8&*`T;b~EKOw;dTe zd+U8SSva-&#EHv?UFm_a#j$mK4@3jQ9{PhWp0s-GENeH^eyF2$RzV;1sbFv1TUTDB zpMr_|cGyj@1$IuIfF0f~u*^!LPgQ@ z?Evml`{}iJ;f@bwxI1qB&(u!An=GCE@$k611#QN?i8iHAgo7Wr0$XvToNms{T2yRc zu78m%zR0g#kE=mbRbKmOkuh+9+JCK8xx4S*K6B*Q*P~PUxs@_ zXtRm<02p!EL;(LJzI8R)O&8|#cqyQGX0AlS$YqMhu?W^+h)xl|@J1N~A;E(el;4Hk z02&KfMssN{MhhMe(E9;*j0qTXIl*elPJ-BT1`BUBQ+n7=1d9Nf%9!ur%*3GAXtxX2 zWXU_w4DFu7q)^<4-fuO+USV({B<+DWT3fflN*Y)*Z~g`RYl4WA1m|QmgGX8U96)! zOl&tdd?dL@@1R|f&|8+wD+DCSzQSgndR__T^<-MgUqzje$hzEcn=PUlrkg}FltBU; zDSd}BO?j|>B8H(%P9*CP{DTICF%RI~=qv#G6h~;T*g7KfEFU?LQO3b1W)$qJcqivbeCR~F5Ro3M> zDMKuE^a0bom&9dxO((W(yL}LGgdu}D%qU<{_Xq2Mh<8Zbbe7Q*iah_DTbHk~PH%9l z-F)-il@kwJ9`nOS#+6GeyD;w95Z*M15hVxvgL+k0DHXO88IBRSo}v|i$LbBiz_>cE z)^Nyv4LnIqN6e;#$Sh72e8h*>Su*nXg2K36iTlf zm5e0yRDimN`T_WWS-{;yy?;a=T7!)$j?gnjDoKYZE8Pa?LK&V7hun$*AUTO-Y}EV* zR6)WgdV49RE`!M7a}VUsAlOCO88Ku)PQM=g-j%l66E04r^LQeOtj(dbwoJ6a=D5iw zgU1Y~RF6NmFQrzg)Pj*y!fg%syuZ*r?)aJ)GXjgsy7Kbe>Rkd$duyk0j=Ai zMTXDOQ3lXkUJn>s&2BAAYs`Rb#-Ld1JCuvkQvXQAV6SaA^ulHEhRUdl>nP=;USpBX z#94QWw==v~=%Pw=j)p~1elHCf51Br2CP5?;OW3Q)gjFkQY_zr#iBH&p(f(GI6CDmf zJz+CcL)2DDrN2-h|AItC|cg|-udv&@>Q(kTI!rm~w-K*gldWPf89%r}|;sPcB zY@}&Nk-7uIYduMYv{sb>{vj;zHnrU!rK~LcmsP3ssgMvWo6|bIQ82npHUQ8TbMgN_ z^;0qq75!P_AWZC{>MAk4Wo#r9b!w(3s+B@6?6v{`SwEyQ3Zzki*@`z*tTBe7nt@rY zhX&h}p}{s38i=zU*uUUgUM0}RxltrbU?-6vt%8=qH`u@-+*_bCJf~ik8>T6TxBr@Z zkLJo<{ft&it1XVlcHx~L4EO3gKY~ts_8Oazn&RnsjJ9PB?9{y0S`diN{?Ez@@|)yk zKnom%=xjH!Oxy|48N$Ws+&48j*6MD9?da9Ab^w&vJx? zG9060nb;u7kSe!bge+FWFV(R3FhjI0lQ9Y;HS5#&_Q-I>)D^+vJSVtLu@G}zUHJxe ziJpP;VHJE#08b1MDZniB8DYm0?B!6I`J5`+Lwf+0R6c(pM&O!-7z@-ZnZ;|23IKJ$ z?IU6#`-nV?w0K#PZz=A=uZx*n@jd8YJ9Q}tCm^#ik-joNzd({(JGN+@fiAU1fOYl$ zK{^kqe4gQ1eZU_HFSyJ*0?RXrkW;UxJSsh1)@6COCHR5gzjJJS$6NvM7kOQNObcr- zOpe|xJ0)m&qu@3e3;-|g@DJ?^?-*^(jOPm`$R8j}@mDL~rru5u0)0Y6f7M2Y!_^1@ zDMw5il{EI z@ewV5OwVX3bBnuuv>42}Ae+oGnYb(Q%I{`rO)`=)I{IJ?tM|xU;(q#;GK7vwgin)Z^;N4)3*Ej?z#_ zayt{8aWHi#*Xi(39M2S;OP@adjYpG*-(>GG)0C}jKe$hLw(z~yr9Wa4=r;^-mFAlY z69$BQNt`8G0S~+iWAPPY2sEy(t=jG`=i))PS;Lf^NkQ&BsAsLP{g+6VBifWQ+E$lQ zES)Gjv&OdLJP&dT$StgYjp{7lXVe$MIwxz;dB^fb18<&x3*;VY`XkqTh0HbW)fzk| z$b7O!IP5w78Xd1MFGD{7bIn)KeJ>FLRU#d0?wbJ6crSp?5=DEMlRJ?b-gmUYeak_I zpwUgz>TBITeg&-6&2a070VM&=t@w!H zs-5)vs{z7=+pxT5l|u*TAbC-87g`o8nI}1BcM*Oi!~HngYS4AHnzW=2N7NRQ@jDEE zD?jm6Fk2TUckiz5(WktWj3lR`UMG?qvZ<1^ zR*2oo55F5rt7Jo^eXv-e>p4iwIm9*WG9+aOo7*@636)T!Ab3RNQf===yN2*8dt%>d9{jG&Gf~#8Odb38zdaZx9dMs1C zA~AN5_=r*gKO!5y>qqwkB^BFZHZmRQFOmyLCyMxF5FLSGWWg!IRrFOZ!=OtEcr&t^ z(#tp`Mcx#y!$aya#BYeLfd}L?&E9{~Y#Hoz0)U`4KR%Lr^HM(S9k=LiI~;us3fFAy zmwz1`)g*;;Ph}#-Q1pR+-I%NAqM0_5+R?2qj@?`C7&>Ks<$#p6r;ZxZq~2?{X=(sV z$Xj+SScc*uKUtvFEd_HT+?yI4Ty#;vf=ORKVEC838OX)L)7Tnu|;^I-FMSAxgT;s>^3! z#GE;D-~_)-UcFkoSKLViiGRl@+LxEM)?Qe*ok-luI+iv1SN9VI9nmAO>~VBhAFHos z-72jqnuSiKv+BY2ht{tvf@<_FB($E8{e+c3d(3UNs@-A%7;Z+#F(#;qYeYSVU=#Oc z0Tm_kX$YH=d^XW9vNP}@#l9F zN#*BH;?EELboKlMJ}f{Ur@=My^XPNa_;c&D=ss>;e}Vdm^3&VQ>uv{K0I5WL7{`3* zshV-mr%B>~L9N^-f~hZ4UQ0A+{@h4;a^yAW3V$EbaR$>7{e9dmAyE*@J5tXb&h%iz zBB=z|uw7y!<;a=WCJLO1o+AE`{A(%)XCglVDFDDTXlkmadh7%jcrb_0`H3Uw7APcL?)Rd4nb|Vi9CF`dc?XkKRwo-vFhkO^V1WMFezm4vCq$hbNmoY z-^iI?jEs(RYqHK^>es)K*bpWTEARF2+IwBSEd*X6KajCUjvPPWYW*8AnT6i=dUjsi zyrB=~&o_4q2Dt(8(k5{IsN9grIC@EIo zi;Km9n%}_GkFl^7=i+@k=b@tuHm85Ux?{JYy-%yxQii=v%l@J%;*MT@>v+`GS#sn( zk+QF?4HhvA8kT}`pJ4RgTFix@JB%tBw|8q9y}|eBS=YAK2&=Q#9!j~RRalyWssE-~9R^=DRcYv*kK zmi;}KyNWiCMdMFF%K=Z3utAN2WWK$1z-MqwcRY~#=sgo&o0Bnrw&^#HF6)2w--%@9 zTxUnMJb7r(o)&caK1LX*D^wWrQ3*VOh&_tiE~u;)gPvna!o-AO16=^H^SKyw89;+h zJ!F7G-MVg5%y^08)<%v7W3M~-1Tsy00>|iyiUXM9sL-eZS&X{N)Ro2N`~u`_r^l*0 z5_5(8VrH&)dQ0z)uKmNq`wt8a?G z!$Sx5qh377GLp~WEMt4sKzhAZpBv>DodRi=?m~o$m0gUP@G7O$i=`AvBnVfd+#&hI zy3E;X?(kJIgy@q&9+13@^MLXvBTxp=4h7PNpG?)@lf7Ezk34%FIXw7Wt+kI+Zs=Z| zCGM$~#fs@fUt1}cvFjOXVtk;h*c5=cmgp(SUZR!ck~M=w>{i-p6cy(^tAaQ*{gG!4-ZE}8jFweF`=Qph|llb zwwLPZa|;h!@H{z0{E&PW zY%!XB(P*k#1h4=-vkDoIWs@Z|O4-yP*|gMCpg)v7HF;;oz;s=Dw~^k(*8F7@j;?+Vl2Cq-gUWB# zeyY&g&&U&dR`xMg$Tl_;qfj?nf<5B56Au#4Kws5E4^Ot2eisQ*DnPHwdcmsqHp1M;f1uQChX9cmDsfAy=Zq!$ED@ zZ`e!U8;C)O`R8U0Z+t5mxk{>iu_?`MjLv8ibPrC$-Y=KYTq>{!O80x=ufOsIno=== zbSC{Kwv(hFOX!ibJ^kcYI;G+QNH@8^zXsU{y}0tbd{jIDAf|vGIOKVmgXiO zjO_2=x&dD%&4n!82k2p%tjabKMex!=Tb2f;%d9roYI}{L{Y_3)|7hbFKeorE{g1?c;v{JDrD}=1c(Hm1asHM=%lqa#im7DK4_)V~ zv!{+8fR*bb10A_2OxWrLFoWB;J$rhN)8v4}o%(@KW{v!HjkcnTlGhp_E-E$lBGy<* z1HlWSr~#~&boP)G4u`0smx6!m6(4X=5V{hpx{dV_E`nyFlDVY2cn+G6Lsks^B(y<- zDL@*JG?adZ)_6m#L1T#dyIE7r0eis>toC)LDqq;wDrO?Xg^|%+-hlZ* zIUD&!Mg9@x8fhJ`wwmohYlq$7WQ`TUPU%nI@9{KoTvD(awZ;6_i#_8VIUgMub=?8u z7+mcK9$lsHnMwRd z!on-!z)*WUC1bS--@u>$FT_&opsfsblA{ zj@6@{)zdzU^=>ot?OMYcv}eOXktEjC1>U%_LE68udVO}?!LajsQu|6pgS`DUluO1ZP z8Cy@4uJUy)PbHR)5w|E=dW&o}Q9fQQhyVJSNqtFCA}4LXE;e3P6wP=1`dA3rL~i8@ zEYTeX``AYeVT=~kN3I*}uePN^K5DSPl!^JkN_t#Su7TZM4tZOO)X=tiKCAOgVpps| zOZhtFZP5V2h_D!Rfx^G%^uBrJJ@2^to!_i&G~C!`wn(gE%0m+5aqL^lTg ztIN?X&%O4%61b&(L`gTT%bkW*mSCY~H4WQi%IuuqA?-Uu@g z88CRfVJgx;*ciKQf5Wt-?^z$FjbA4{8BEWAr~esyjA7`$J_5-42v3a|8yV_uj>D?V zv61#dDvUiu(9gKBm+;D4JK*euOM9y#smv%!QkiN5uGYXWh;i~8C+fd%F!ruDPSZbG zXT_hnK9Y(O`Uo=*7P<;O);polIy6)r)(`acR+@rd=t68sN4;Q?-A=};vDaJ3@Q7R8 z8n0LEC^4iI&7qBU_ z4Q(~iZ36(LQa(w|RNIQtFr;;uj$y`v%%TTDAGOgLTCM`3S-}ljzM9~cq|x7K!wD$! z`XEx$aR~Dvpx@ZOAV%}om=9F+BhYJh7v?snSLGKR>&4N-e~hDMDh4+wdO1Z zeY0S3H;X5pjb^yIIh=WPy=gwZ>D*xk!M5Z9-EELq#9ht^X;eQuHqzbFoS>$AD}_kN z2L`5nh%sy2Lv$Zqfu?drOF1YBny0NBn++=BYlJfjv|o+-Hj^9F3knnT&zr-ByA@{X z^P9&3O582TM8Hzb!yd6PMrkw5O>_g%1Jqb=dvh${bE1s}$U?c-1a7PJR`|$z;MQQd z)mo#3VzbRyCOD=H ziC9qDAYciv3GNyaST;4;Mqu&RFg2S*>GVb`v;PL@{rq~<(!Z%RkAqd2BNk!e2;oEP zw~cp|^I<;K-kTpG4-6nc96jvpze~wV(i)! z!V)Cuq`pgK=v^SqU4ZoAa8#WX!XN^V$p9+h`R0m~kTRL|i7g%BP)2%=S2=`G@r?oo zQzGk9xTtpZed>o4L-#_La4*z)T&}9yj#mp?2_DAU)pA+@Bc`p^nAC(z`B7Z>qg<=c zmwcg7Z!Y!nnyy;(S$;}0ZwyISoUE zIj9NN|1>21rM4Cb!6j*i4qhc4qKFRSNg1VjMTE@yzHvxHio@?M#b2yl8cwwXXb7dU zXmq8H4XeNDle~_mZD|^2G(WWRX(mhmm~cSdYa4NfxQ)04qT$y`g zBNmq6#_B0n@dS)x0@+WYA{Or9MPJ2-1HH{a;xZMU%|z9CbPawr%DLlgIDV^@MblPkr}lMdVOyA>pv+5YHN3$+^q@WxDCe8^m7ClqbL~rc?LncP z{tH~{fWo+yp~T|Ha~zi*_wCHiWebA?3x#`iT+|n}r+J>`J*|aAmr2m*z22^1&wF}q z%~LdINHg7ww_3M1KWLB?cu)ea6cDy>B7l8>g2iW+^xr!_b$v%^#kyr*G%o(o1s&jKjigu$sVq)O}`yF{wL{S zDo4pBdUWc=$6gT}#39$~SM`3qc$gdYcS(v3N{?Hv#j-VO!~l^9^C?o%N(|H0a-|Fi z0{R?B0<>Bm6T3ffBZqWg<6FugJ4nGrrMDx|YAwklrt*(6VaK_S-8UVLgviTh9@-vq zyV_1PSEH%Zdj*#>C?m6wR59P0Plg9b>gcW`M`wnq-M>9Cxm<7sJ6k+fTBD%^M@Y}; z0U-PVi4DPb$h)Hl{XW9!4(^lB^f}V?Hd!jnt)>%-Iq=sg^tyYhg)Q21f z?Z^WdcI4dYsK>$T$?%r9UcEH%%D0`II%G6Bwd$M~cXF-#fcgo27tsniqGizjzP@UI zspxl>Qt_Z2tdPKTmKm5%Rkd-(j4DT40Ypu5(!lEVL#p`vCK+>~(ZIKKa26B2NQ6a2 zP9SywC&hwg1si+JZ~P7t+Vy~`s9Z_CtSmz==|0<1)ARXx4jij#uVpG z9B)e6OFNS^XXT^Y>;b)vd*#m#vFrm2$L9MX&x<=B-%Kw-lyI83hd2#6hx6mjHQG^BnnN&zG*7!f96LoE<`}ZVquhI(K zD_eo}8KM8xR>y~x$28slT9;#+@=*8X|J@EpTIx$in>}D_6>$J+$IHvr{f1pLfIk@ISsw#>hSW` zG+lel=zVHyr8C0;+S~Cv2i9!``Gd`EK&Bx|`5tXyf>3MO4_II)piBJNvFdT^=z%>u zXG@u6+~d$3KCpKIw%qiWGm#K1!cuWd;C(H!zg-u9u2sLf0$PY;O2!<+jFB7?xFd0f z2L+?Z+c;#v3Iv`lnyU&nWGEyTRTc*>$T2}c2_v?+Jxc677Q@dtLJP5%S%BKMI%r;q zw~gO^e|OBeXiFq@Izi6G|L`K~0)dLF=JDfNvjm5L~%0$eT z^$IjCMEYX!NrT-LDg}}>KYG@!2S8biZP?$t_1m@gIpctP%SKWY!-uBsA!Dhxc-w*+ zIH@GYH4Lq;s2L&KH@!XD8twvwv4Q(bhqZ-FAs|2l6yuDPN@Kb%e^twB)tu`bt+m&F zSS*y1tvVIO8s7F5OH>1>xvtwo4EqUn)+IQ=yVUHzN|Ap>6<}I_0MMQTct-%LKu!-k?_8q|g3J?K$!J8l164QE;%@j#L2_U~z$A{Dgm2U~15uYLtlYdP<3DZ{tfN`*P z`bwo7zj_1nSL^0D$d}d+ZIDOT%xIv`@kQbtRMeZ zCov4OOR{jDMmsvN`hdKnuuMGDiA+;E{?&6H;tWzV=sf&;ZcI?=G+K9p0v&Lrv;ff( zE~|Jg@90sR0GT68IkYNI8U`N?@eaUPCjXY-qt_VqhHxw3Lh`|qPOY`2`c$x!BQT}4 zsm$?CmURh{Hrl8*=f^alI(oR{B~l>YOnV70&QADzRRCGG7_@}SLmOy$#0Y%36ue9K zswI3%qv69w155x_nz&4Sih6<`f{OhJu@y3tQF3;+x{aF3WujjD zkeoE~KS@R1o? zIx%2FoI+ROmf>~8HOx7L>I3|Pf9U z*ZR~6l1et^bVk_Nl24^U#e}%t4QD*9dK*t04!$LuF>@+z)XS=L4o01`>&pVI3p+WL zCPT(4&RqK_PmZFQM+!y7r-@%uW!Qi+iqTt=B6`b~iAqa6>b6b;z%~@3(R?((aSvC8 zX?l!?dxn8I)_E<1q4h@|)e zJ`du<4!mNA<4qV|DN`kOz@!R!w>+7GR;y^UpwbH-n&}8f?jLqIbe6WLMIhDrNRaG? zKz}F=&~aLtqpUejrK0>UyZ31l@>XV@nfhf*QN?-knbIQz7NaGU$#aZG8wsS1J$I#I zF{_pfY9MK(3+U9a^&rD*k`${3H$gix9CXwIJQYLR9wKQi19iRl%9rQ@odzAtNXsSNBya+|4mV^)K;2r$7=4{_h6jnufp%sd& z5$cyxrp4!Vio>@O(G$cKrFdU?O+5wmln7jxX}d*?1~yM0dPwq;+WG*G>|kyFUQ zIi(z&yDmAPK{Jsyg06B%AJL&zgsDQhixmNfF>GagVK`)AET zKn;hqWW?pRWlWAtPVKaide|*s)d!cdqd^VB049||8-&eHnYVhpF!nr7>P@QHNN>CF zMw$J}=XGkoB~oqon|VvRJJre?q4;fpoF}K%SleTr!*-hu0G)YHFnD0UMo^DEqNOO) z&?8zq2Xp(~z}2h3)lulceJ}Am@iv&5_}~Yt9~yiAZ@%a44_`QaM6{{GZ$0{Vn=A`dRX6a*dru+TS3{v}z2LMgU!{Ti*{V1>l2N05;3| zh8h@+O%WE^suMS8)yX>=Sd`Z-SUfn1^@0^01;Kjde>T%wG%!0iB8lX?u=k;fP;G_& z6FNZLN?aly##zrt9;rS`+<9WlSW7zWb?e@A=c&c%u>y2Pon`v0?A1dovCPq@xg+Nh zpit!j&UA%y_R@e>AOnMo)eV#itEa;hJI>?~OCf$%;t{3zJ%YrK6}!-IY=ynN*zJQ1 zhtw@z&-;og&qN&A6qFM~52@6H?9tlquvDXn8@&@3h!ChCPAJ>yl4!8dkH8@a%$JyC zqyJ=k`$Btoz~5Z-c)BdT^O>gRN@;kcQtDwiJLI`d!eG#ANpxpNItSDgOt|Vl*gcW5 z$9l$ldIQ?*;dffY%>k9lA~cPtE&BFQ+|!yr*5*sk6%uJr5tgrLz(i<=9R8k2r7IuO zd+(isPx#7<`L2hm@ge`s9SdGlySuf`<*QiAOFiL|RgD_53?BHHv-w1zqjI!aFgx5Se-ifGF*?IcGu`az%vW8- z)?usF^~%Tndpn@=2@xOa=6r-cP85kUAO(BLN~PLHw6-*P%=*@rn9r^!1gLC5mQ{ju zT+_I+Hj1_r6(xHsl63*zyzz#@d){~-+?MO?eWO->w$eg((SHDyphK`zCXeUv4{o38 z?QSbu5AIr+8R|#}aec1|mbH!W`^LH5yM+9H7-$=!$&Pxe6Al?&cwT93U?$sQ$p&{c z8puc9v{9`O$u)Z5sH>&ei^iM4%&wjxApuj#BP?)uz-16+^qa)f3DOOYhnb9)BD@S` zl;#E%BlW?fyLXM(_biTQ9jT|> zkx7=(TlixvZ-V2d`{7j6V9-v#b3+yBxxRD9j*UO=Yd7{!>xKZmMXjSKevF;(RK+OP zX=x=7f5-pK@gB7&2wQ>Fa9Y!{yeeo`vqq>F)3vc*?jv6&KLb432R-&DV4E}Qv9~Yn z-90@r&{lR@k1ouO4fJ&+op#Mqk?|hq;;2Tq21@c(`eJ2|m~7Nte+_ORXU5ybVie-a z>WNQ|f$H&(@<=EH<%nEF21n#6XM-_ZG^$8`zX4wYta|6?=)cP6J7CpTb0Q3TUYe)U znU?0|`zWv3+_77$1)V%b6{_WU=Y+Q);Rx{lTdMe@?KT#^H(QR zmlyJIijPd*`Id|1vm01d`x48~e&VIt`)9T;bwTA8b`p-?`?mA_{gbUHE@dM*IMe8O zRW8>)ZK3&avYlpFx~DnI3RaumxV&FKW^g}zGhh4M)y3kM9{5kA!9gy+o3#8Rpj;68 zmCMxc(svSB$b@YJPTm4lgr%kGGO>3j?Ct3AI#t~EN!U0aG#iB;W_OMuI#_OZp37vc z(8(B-R7>nA?ZR%R9^=SrwHn<7i6y1|S;F=Q@eCF#A)kRyn4n4cEHCj`D!d@|f%oub z>=@uVjEW%+qa9BY%DJ+{JzP1zI-gMR%A+-T_&R#0|UVt^69<@N)L$q1%US2+0vb1ONh4k?kbPksDWNBTF zp6~4JU%2^Lm^L&ea>Ma*nA2{#c1o4*5kqgz_4 zC6}3Ljs-m~RTB@WeIeAO%Lyzy@}VS6OmlT*v?T8PWUrXHei`O!IXOU*Z#eri$t6* zubP=0A;%81_4XRAF}KsBZl83=0lH1sN@y^!zEb{7YW*dNsk{nx)GbFh}`wO9683K(M z2o$97ut8pzg{1!UYVD`KX`LZ4Gjjym@fY%-O8$!Iv7#ShB^Jj(OIm=N=z%hq@F-du-uPqa4r@P# zJLImCI^VRPO3X~ZiH8p%k$@5J+or3p0NJy8iwq(Gr?r_ps}!jH+1lF%p5F$w-EV@` z2@omZxsObzs~NA?4tsu?7<4QoIOsT1IRR2a9_)wXR6T@6;w`s5F+^H<>uDs~0OpU{ z(J^yT0Ex3xa0T5pvM`p_qV2!Qwc2VfpGysJrm14*)6Y#x#aU;)D7)>#-3Jzid;BzK z;r!LfBcJ`&zmd9nDV8MKS6-$g^t%8Hb{Xn%7z@^C(hyDu+>%y9xSSkkw{Zr&KpANs zDhd)z8BEsWkOWkBH7$qDro>rIBbZpg4FKa73Ud)PEEWkl073%3Rg)nUanli>Src*_ zUipqLsUh#FUE1ZM!lc<|j_j-b6WLVzBl77UzsLIzH(x=jLH<7ZU;K;1=4kDF?QxfL z6y?@4D>XVwzXy=RPeY^?M#$lAm&2&12%D8r!~QVF=Y@ZrZr&L(Li-pGw`b6!Oi4Y@ z2uoPtmc*SlU4WN?_Y%py)H3Ug4S>ekPy!tWOd%8GV_O{3ccn-rWDDEyH9A`RT|fr# z)xPF)w37I__b5gE*I}=tm2wy@W$NdzXj`pDhr?hkQy;#`<+|yWZSXz1v(A2!Go+S(Hst?lZmj;E@+9Sq#stl3LKErL~!t~RSbWGfPn94HJyjpBylO+z`kO% z2T(qkqU|LkV!B-vOVl(I2^&bXqo*Zgi%3l&_d6p|o;A>Fn>lZ`I_b9soH0-B zpT1Sww>tpMVyJjUPl-F4+8)~3TAZ?NZn{yh=TH#3h0*D5DGA)umM;S zAVY~3KiLUKQrVi|JQ+0V;gf9(vsk<69*Jzp4!!UB;ml_&wYy-aXDI#W)XA~q2X6cB z*KRvTl1Il*l3!~w8K4j9i{a#sXYZZA@4mT9Kic}mZ{>pFz`*j`o;-2+u9MHaZF$fi zHXE`?Mi*AT1xVc&0lx?#xnYDKv*3nKO$w0kWOPG$6}!wMO#?6;nB_&bA6Gw6Yy}TI zs)$@3^{d)jUuHpR4T8W3YT971Q!{scM3AH1k+>oolGgex+ z5;3ae!zhkRc=v+0E0=JkLPPUuvyqeW{@h0TfK zL@*VHP3S`AHQz!0mL_i23D8O5gIx`<98PaDL=6EBPC~b@dqroLUqLZVcI6fN1pRS% z#zKYCPI6q8Cm<*%2^LnlP*5!+Nhx$jf+`ySdV4cTa7wyxqI{ATHA$cN)#u1h{!Q(t zwf|S$na4*_WqbToRqtyyl8_K+Iw2wf44Vi7vIGc%ERjtF!5slb5JbThaAR>n+|XeZ z9Ti4V1Q8uaaYIB<6ja8+ahr#VinyU~22$^PZg+=PnfZL?^WHzNI(5=rb(ixy=iGB| z-MY(8$9d}DV*C0-muP-X-B0anoEvC8`pw9`K%i$({9dd^Xmi+^kp{+l9d0n$63EUc zp!o!y{IHd8UsJ!Weyi1S-MY1Ft@ZV7tR2=4Xiok9`fuUgUFMEw!`9{O^Q!CcRi-{Hlc6jH|12P7{$pFE@I?3G|Bc??0Q&0dk^;;)aro(}zAEt7Jc6*&GDE{k~U zHyt|B?rk4pME*p%knz6@=rcRsTDY+1BAGX5)$52!ueUsB%HskTZ>q+w2$divDP^jdFaIgbXvs9(&Hc94~_FdN-M zgDy49A~hP)Jdm1(7Y{5|nP*FX!=inurU^38Ps+*YBassvix4v zte;umBbwVHp4&Gszj^7=wy#YhQ8;j5T@@RndR9;B+#=sv5Gl-eyhRr8=4}tpyL}2v zY&%w5T^TQ|>^AF?zNap#_C=$!9~=38#jwii?p`~8?p7RjZy=p?D=@&ly&o zaLSw#Codul)y{0HliG*u*Ng8d zR2S)I?86lX3;JXuFl|9I$f+$WY^7Y(VGnlLNm)7ScFL25Ll*oI7{<2&N&x+>kHDpjmVS9!H-P92>n zEA+7gyWhk3+`fz@g3n|Q5R6l z^n&6Bz@Dld8kqrt)>EG5{f{s8`&uTFEpr1!mJ@I4YZi~uR&U-e;WSDnYKp=YiDYRo zROrw5_(Ku9Ad(x41@$pq+a2r;ZI?N^H*}e?CFL~QB;U;JY#d@Ys>~qA!)rHZq?CUo zZn5LXmV1%B8Y(|PR z)kVsh(_5|>gQjg?bA-NXr~Eg4%91*+;9u5hRm0$Ry7#A0Wzx;>qmwxB50T&`5uGL$ zdPF4rqDW+wNR-!8(R&5ubIuouFBi$F5y|C#J`dalV?`Q0z%ZHhA!p%pP*EA8iwynr@ zV@0k<&NsmK0{B}vMr0A^V$!&&Qsm|tBDefa%yAk=0E_9)qtn)gn)f6j_U$ z*TKh=r2o_skqu9XJk7Odwu?MlA@b)YBG2VlFtjs=q18zBCS^!iEQTnX7uzL z{BHs4mK7qegW>f9BHNaWyb1R0@cI_%y+t}Z;B5!Lcfixmdqm#-o5-#yBJU3u`Rf}Z zA3QGdA^1Py|0g2yX`RU5uNB#oBl6j5kuR!6_TDV=l}F_3(>V8t?3>QX|8JIxe0zz= zcbi4Nzfj~ybaUWNk)LlA`3L;|6WJXKaV`<5pD*IJ#NRBUjDIc`$ywfa#R{B5hu;-q zh3ASDEfg!Zo1U@@#L7KQth_N|<=2T-kmTGaR-?`Ain~#)q6@_;p28kdtUy~rPgKbY zu}bqeZx^d{)AG3(T#bAPa2k#_L>v-OI5m$jDJ#lzjl%9h8vO zTLRWY61HZ#Us+d*--^|5vNj(&U_I}CDxXV%ReUIEt#@CLm)O59 zZLm)l+hERMQvQ{vBb^VqZyWxU$MmWEWuFbaDSz3g@|ATW!%p1afX>f%9+i+S?nZuJ zY~LjF?VF_s|F_yd$eDbvM@~D@-@BeY=xUOCJ35?bpW<#Hy{jz-YRN(N>0J-c^WDwp z^;*uqSR1f`mGv8~dYNJ8$pm|fOjVo9;8y*4EF==Z3 z(OqU8sNZBq>o+;4@Gea4ajn&f|DU0+L)KqqCp@c7ccJBF4tEYVairrXE?R_gc*EK*qM>8q_BiPxU74}jO}a}DcJb`VDDd9WhxJ$b zNR0%gFHkzcv_yN#JnJH%X{)y*)1Q)hw_npjJ9c5>S0x&cRiYL zMY|a*IDKjisYCn8GuD&7WNtEIz~>JHL*Ym?7SGAe%P(kDSXA7&q_jy{(`E^_r>Wpw zaLc1xRkd!@_L%B+?K^a2Wa)8Tx*p%Hdyf-(_UhfIrfMh0`yZIpgA4+b)@X>72_i`{Nx~ zUUAjcZ@xu~f#-ATeQd5LB_aC%<;hBVJIX^KgcWd9EjOX?VoyOqxy)^_V{ zK0Eom%jcu4&nMO%YcFGa^t1mLpYN<68a@XsKJfVe+sFK#GNwLaHD&H~JjIRph^3Uc zWt6c6ljk{UMm)^9_=KDB8R|JE;wQG_lP2NUCganl;MH!&uT8_Vsb8CkXH&nn4bL_k z-!=!ob{U@O4t(1c#=E_Vk9hahyy61_5t+NOd!r4)LK z33;kXVXV13j_X>hm21uBJ`JsLzoCv(s`XqMXN|iKTI;?Ct#e<|y3c(ETI243*18`; z2e}_Z>j=|IM!0W5N4uY!8u9~Zjl0LxmE)kbU~2=;D6|e9+Q3_LFs1L%%n5>?G>h8h0AB)}0F-2yY$8{T^tYI~6*@Tp8^y(c0L(A6nzy1+8`O zg$^>b&b^KAquo`|vF>J5YtJ?{_abPGdls~oFr7(%D|C>%5?be83?1P<2pvtzon;K| zX|3&7LWjA#`KG*eA?_8>LGDe^Izva3Zx`}fLd&4W-36_6zk&_|Z&$*6VcHA-gsMEX z-pmnsD24GRe&{6O< zz@#&fE4k>h#@!1Y=6+3_A?R1zG}X`%Gxso0%#pJJPK{DhmOMLXm`Q?D0I37Y{SsbSd;r$zW-~IF`fQ9Oj~WJaoz1jM8z~ZH}KlLYQk_y<%>9b%7VYd3bfqoGp#a zwnD@rPTow&to zPdL&Lw1ra+l0;>+#w~>WDKdn&V3js14FzH z@?a{oiGmCiI~*OxuH29y;zCF&Fd&27rvQ{y))Q9w!9Tw>Ap$6NCd#F-dwc=I5VwfK zJrYvv z3eaz`D-IGQX(KkUV*vs3(UApe`{c3q}H}C73kE0xJf)N!9CROoKyb zzQN8eWm#QYMw!+dyH1hl5Ale=zZxkh!5)h!c9n8KUj-TLFiOJURWLrHGf;+!WnLW& zm%^UXWB@T!1TZ7G*N;h+*s3{xBPG2LcET%%dT>dTRiS1M?xEQ3+zY+m|!P>VrL?!!A|O7Q=V-0 znDJ4@Eh0{U)Us{#*Vti6tr|_? zZljtq%k{xEMw-ecOx_v^A*suv;D-VPMx;gwP{oeWU=r*U0Wb!FDsc_NQ!esT^W|1h zv7;-n=YU;v@G(DFW#>&}hZBuz1BK-lU_Q(^paAkv>}qht2{N2Wiyc;jptWq-6h?#C zU^g~qu#=buG>2+i^{)C!9dJu2H`pCJYGiJ(Q@pS~Bpfl=;m+g5WhwTs!A=y!qpDCt z0|Y|CZ5n?SV7R;f(j!Q17BTAc`hoxiyBF-au^`wP;^YgmR7VOsRu=T5EVwn;{gI&G z4}OGAJ`{dU4@Vco#NZD*gnNUX1aL!SuD?83nkgbdQd1F{gInA*tm-!v2RaU`ZTo|4 z`o|kcSjH^_3l=~f^hU{BBcW7v*NQzDU{fRIL*qla4G0vZ#-hGD7*yx8#p!5{>ym$tCD5{=`tLmFb7+v#=LhJWOVLn3sL1hNZSD|w^#KIznu@jTvie2qU z?Ot6Z7J`gmQ+<;j3RblQdzk}?YKg{51CA3f?9dc;wN&(;&rBW)2YNTysZ6O(ffYbm273Tc5e;#&pat0d zejFcJYJQu&Zw~*ww`n{kPZ&mOj89G1x<3&mlwf9)xS1xXTt? zA;nH{RdM+!W{Mq-NK}s$c7%P>P)+5)Jus0)8k0P`#rouooKaLF_Yi7MAZFgZ8Y3y1xa$m6{wG4Jet9s{V z`am68qOnp|H_9V<#SYhChkdZ;h7`M2nyP;U#MEX2t8t=@40b@oDUhmMHDkq&=@TTB z!cNge6;ZI`rxklqenbF(bBpF+rmzq8pfAFdd5xOFo)Za%z#e5W&_EEU9|8+pMeqS& z^9K<*Fu<;kD1{waAixxM_KgL*u~3z#22~R^-;|)~6n18|`DSJJz4ZuG;1=g{Qd}-J25)M_I&`1A}kC4wEVDT7)C9AiQaquwu_s z^96fMvE$ppP8a`;*3I;LTZ|uilb`RU{@V}BK;A8JXbb2_N3SU}E~3XgvETNr`p5fn zmS>f>l5q6;=^2=D-{s@u-RLRs?xsgm)_nc(dhz(b*Z+r-|9BrQt|Fwv8wZVH;-u5r mb0~Re1LJX1*Ue2selk~nq9-!NIAfH_pEvCEjy%de>wf^3nd_1O literal 0 HcmV?d00001 diff --git a/test/set/690-ttf-crash/font.css b/test/www/regression/pjs-10690/font.css similarity index 54% rename from test/set/690-ttf-crash/font.css rename to test/www/regression/pjs-10690/font.css index b005d00bc..959d531cb 100644 --- a/test/set/690-ttf-crash/font.css +++ b/test/www/regression/pjs-10690/font.css @@ -1,9 +1,7 @@ @font-face { font-family: 'WindsongRegular'; - src: url("https://github.com/fukawi2/.fonts/raw/master/Windsong.ttf") format("truetype"); + src: url("Windsong.ttf") format("truetype"); } - h1 { font: 90px/98px "WindsongRegular", Arial, sans-serif; } - diff --git a/test/set/690-ttf-crash/index.html b/test/www/regression/pjs-10690/index.html similarity index 100% rename from test/set/690-ttf-crash/index.html rename to test/www/regression/pjs-10690/index.html diff --git a/test/set/690-ttf-crash/jquery.js b/test/www/regression/pjs-10690/jquery.js similarity index 100% rename from test/set/690-ttf-crash/jquery.js rename to test/www/regression/pjs-10690/jquery.js diff --git a/test/webkit-spec/inline-destroy-dirty-lines-crash.html b/test/www/regression/webkit-60448.html similarity index 100% rename from test/webkit-spec/inline-destroy-dirty-lines-crash.html rename to test/www/regression/webkit-60448.html