aboutsummaryrefslogtreecommitdiff
path: root/tools/addon-sdk-1.12/python-lib/cuddlefish
diff options
context:
space:
mode:
Diffstat (limited to 'tools/addon-sdk-1.12/python-lib/cuddlefish')
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/__init__.py870
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/_version.py171
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/bunch.py34
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/docs/__init__.py4
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/docs/apiparser.py392
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/docs/apirenderer.py302
-rwxr-xr-xtools/addon-sdk-1.12/python-lib/cuddlefish/docs/documentationitem.py124
-rwxr-xr-xtools/addon-sdk-1.12/python-lib/cuddlefish/docs/generate.py199
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/docs/linkrewriter.py78
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/docs/renderapi.readme.md210
-rwxr-xr-xtools/addon-sdk-1.12/python-lib/cuddlefish/docs/webdocs.py106
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/manifest.py749
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/mobile-utils/bootstrap.js55
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/mobile-utils/install.rdf39
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/options_defaults.py26
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/options_xul.py98
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/packaging.py448
-rwxr-xr-xtools/addon-sdk-1.12/python-lib/cuddlefish/preflight.py77
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/prefs.py118
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/property_parser.py110
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/rdf.py190
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/runner.py696
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/templates.py31
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/__init__.py65
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/addons/simplest-test/main.js17
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/addons/simplest-test/package.json6
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/addons/simplest-test/tests/test-minimal.js7
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-588119-files/packages/explicit-icon/explicit-icon.png0
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-588119-files/packages/explicit-icon/explicit-icon64.png0
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-588119-files/packages/explicit-icon/lib/main.js4
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-588119-files/packages/explicit-icon/package.json5
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-588119-files/packages/implicit-icon/icon.png0
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-588119-files/packages/implicit-icon/icon64.png0
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-588119-files/packages/implicit-icon/lib/main.js4
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-588119-files/packages/implicit-icon/package.json3
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-588119-files/packages/no-icon/lib/main.js4
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-588119-files/packages/no-icon/package.json3
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-588661-files/packages/bar/lib/bar-loader.js4
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-588661-files/packages/bar/package.json3
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-588661-files/packages/foo/lib/foo-loader.js4
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-588661-files/packages/foo/package.json4
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-611495-files/jspath-one/docs/main.md5
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-611495-files/jspath-one/lib/main.js8
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-611495-files/jspath-one/package.json5
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-614712-files/packages/commonjs-naming/doc/foo.md5
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-614712-files/packages/commonjs-naming/lib/foo-loader.js5
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-614712-files/packages/commonjs-naming/package.json3
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-614712-files/packages/commonjs-naming/test/test-foo.js7
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-614712-files/packages/original-naming/docs/foo.md5
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-614712-files/packages/original-naming/lib/foo-loader.js5
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-614712-files/packages/original-naming/package.json3
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-614712-files/packages/original-naming/tests/test-foo.js7
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/default-lib/doc/foo.md5
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/default-lib/lib/foo.js5
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/default-lib/lib/loader.js5
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/default-lib/package.json3
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/default-lib/test/test-foo.js7
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/default-locale/locale/emptyFolder0
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/default-locale/package.json1
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/default-root/doc/foo.md5
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/default-root/foo.js5
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/default-root/loader.js5
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/default-root/package.json3
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/default-root/test/test-foo.js7
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-dir-lib/alt-lib/foo.js5
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-dir-lib/alt-lib/loader.js5
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-dir-lib/doc/foo.md5
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-dir-lib/package.json4
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-dir-lib/test/test-foo.js7
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-lib/alt2-lib/foo.js5
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-lib/alt2-lib/loader.js5
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-lib/doc/foo.md5
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-lib/package.json4
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-lib/test/test-foo.js7
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-669274-files/packages/extra-options/docs/main.md5
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-669274-files/packages/extra-options/lib/main.js8
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-669274-files/packages/extra-options/package.json6
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-715340-files/pkg-1-pack/package.json10
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-715340-files/pkg-2-unpack/package.json10
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-715340-files/pkg-3-pack/package.json9
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/e10s-adapter-files/packages/foo/lib/bar-e10s-adapter.js11
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/e10s-adapter-files/packages/foo/lib/bar.js5
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/e10s-adapter-files/packages/foo/lib/foo.js5
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/e10s-adapter-files/packages/foo/package.json1
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/five/lib/main.js5
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/five/package.json3
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/four-deps/four-a/lib/misc.js5
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/four-deps/four-a/package.json4
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/four-deps/four-a/topfiles/main.js5
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/four/lib/main.js5
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/four/package.json3
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/one/lib/main.js9
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/one/lib/subdir/three.js6
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/one/lib/two.js8
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/one/package.json4
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/seven/data/text.data1
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/seven/lib/main.js6
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/seven/lib/unused.js5
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/seven/package.json4
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/six/lib/unused.js5
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/six/package.json3
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/six/unreachable.js5
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/lib/main.js8
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/lib/subdir/subfile.js5
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/lib/unused.js5
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/locale/fr-FR.properties5
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/package.json3
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three-deps/three-b/lib/main.js5
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three-deps/three-b/locale/fr-FR.properties6
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three-deps/three-b/package.json3
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three-deps/three-c/lib/main.js5
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three-deps/three-c/lib/sub/foo.js6
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three-deps/three-c/locale/fr-FR.properties9
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three-deps/three-c/package.json3
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three/data/msg.txt1
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three/data/subdir/submsg.txt1
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three/lib/main.js8
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three/package.json3
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three/tests/nontest.js5
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three/tests/test-one.js5
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three/tests/test-two.js5
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/preferences-files/packages/no-prefs/lib/main.js4
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/preferences-files/packages/no-prefs/package.json6
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/preferences-files/packages/simple-prefs/lib/main.js4
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/preferences-files/packages/simple-prefs/package.json50
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/doc/dev-guide-source/index.md7
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/doc/dev-guide-source/no_h1.md7
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/doc/module-source/high-level-modules.md18
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/doc/module-source/low-level-modules.md36
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/doc/module-source/sdk/aardvark-feeder.md12
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/doc/module-source/sdk/anteater/anteater.md10
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/doc/module-source/sdk/main.md0
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/doc/module-source/sdk/not_a_doc.js5
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/doc/module-source/third-party-modules.md11
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/doc/static-files/another.html5
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/doc/static-files/base.html161
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/doc/static-files/index.html27
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/docs/APIreference.html469
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/docs/APIsample.md162
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/packages/aardvark/doc/main.md0
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/packages/aardvark/lib/ignore_me3
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/packages/aardvark/lib/main.js8
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/packages/aardvark/lib/surprise.js/ignore_me_too2
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/packages/aardvark/package.json7
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/packages/anteater_files/lib/main.js8
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/packages/anteater_files/package.json8
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/packages/api-utils/lib/loader.js7
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/packages/api-utils/package.json5
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/packages/barbeque/lib/bar-module.js7
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/packages/barbeque/package.json4
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/packages/minimal/lib/main.js8
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/packages/minimal/package.json4
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/xpi-template/components/harness.js8
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/test_apiparser.py538
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/test_apirenderer.py31
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/test_generate.py181
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/test_init.py197
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/test_licenses.py88
-rwxr-xr-xtools/addon-sdk-1.12/python-lib/cuddlefish/tests/test_linker.py234
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/test_manifest.py257
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/test_packaging.py116
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/test_preflight.py147
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/test_property_parser.py93
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/test_rdf.py45
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/test_runner.py27
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/test_util.py22
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/test_version.py28
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/test_webdocs.py78
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/tests/test_xpi.py469
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/util.py23
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/version_comparator.py206
-rw-r--r--tools/addon-sdk-1.12/python-lib/cuddlefish/xpi.py155
172 files changed, 9676 insertions, 0 deletions
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/__init__.py b/tools/addon-sdk-1.12/python-lib/cuddlefish/__init__.py
new file mode 100644
index 0000000..f5a3ed6
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/__init__.py
@@ -0,0 +1,870 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import sys
+import os
+import optparse
+import webbrowser
+
+from copy import copy
+import simplejson as json
+from cuddlefish import packaging
+from cuddlefish._version import get_versions
+
+MOZRUNNER_BIN_NOT_FOUND = 'Mozrunner could not locate your binary'
+MOZRUNNER_BIN_NOT_FOUND_HELP = """
+I can't find the application binary in any of its default locations
+on your system. Please specify one using the -b/--binary option.
+"""
+
+UPDATE_RDF_FILENAME = "%s.update.rdf"
+XPI_FILENAME = "%s.xpi"
+
+usage = """
+%prog [options] command [command-specific options]
+
+Supported Commands:
+ docs - view web-based documentation
+ init - create a sample addon in an empty directory
+ test - run tests
+ run - run program
+ xpi - generate an xpi
+
+Internal Commands:
+ sdocs - export static documentation
+ testcfx - test the cfx tool
+ testex - test all example code
+ testpkgs - test all installed packages
+ testall - test whole environment
+
+Experimental and internal commands and options are not supported and may be
+changed or removed in the future.
+"""
+
+global_options = [
+ (("-v", "--verbose",), dict(dest="verbose",
+ help="enable lots of output",
+ action="store_true",
+ default=False)),
+ ]
+
+parser_groups = (
+ ("Supported Command-Specific Options", [
+ (("", "--update-url",), dict(dest="update_url",
+ help="update URL in install.rdf",
+ metavar=None,
+ default=None,
+ cmds=['xpi'])),
+ (("", "--update-link",), dict(dest="update_link",
+ help="generate update.rdf",
+ metavar=None,
+ default=None,
+ cmds=['xpi'])),
+ (("-p", "--profiledir",), dict(dest="profiledir",
+ help=("profile directory to pass to "
+ "app"),
+ metavar=None,
+ default=None,
+ cmds=['test', 'run', 'testex',
+ 'testpkgs', 'testall'])),
+ (("-b", "--binary",), dict(dest="binary",
+ help="path to app binary",
+ metavar=None,
+ default=None,
+ cmds=['test', 'run', 'testex', 'testpkgs',
+ 'testall'])),
+ (("", "--binary-args",), dict(dest="cmdargs",
+ help=("additional arguments passed to the "
+ "binary"),
+ metavar=None,
+ default=None,
+ cmds=['run', 'test'])),
+ (("", "--dependencies",), dict(dest="dep_tests",
+ help="include tests for all deps",
+ action="store_true",
+ default=False,
+ cmds=['test', 'testex', 'testpkgs',
+ 'testall'])),
+ (("", "--times",), dict(dest="iterations",
+ type="int",
+ help="number of times to run tests",
+ default=1,
+ cmds=['test', 'testex', 'testpkgs',
+ 'testall'])),
+ (("-f", "--filter",), dict(dest="filter",
+ help=("only run tests whose filenames "
+ "match FILENAME and optionally "
+ "match TESTNAME, both regexps"),
+ metavar="FILENAME[:TESTNAME]",
+ default=None,
+ cmds=['test', 'testex', 'testpkgs',
+ 'testall'])),
+ (("-g", "--use-config",), dict(dest="config",
+ help="use named config from local.json",
+ metavar=None,
+ default="default",
+ cmds=['test', 'run', 'xpi', 'testex',
+ 'testpkgs', 'testall'])),
+ (("", "--templatedir",), dict(dest="templatedir",
+ help="XULRunner app/ext. template",
+ metavar=None,
+ default=None,
+ cmds=['run', 'xpi'])),
+ (("", "--package-path",), dict(dest="packagepath", action="append",
+ help="extra directories for package search",
+ metavar=None,
+ default=[],
+ cmds=['run', 'xpi', 'test'])),
+ (("", "--extra-packages",), dict(dest="extra_packages",
+ help=("extra packages to include, "
+ "comma-separated. Default is "
+ "'addon-sdk'."),
+ metavar=None,
+ default="addon-sdk",
+ cmds=['run', 'xpi', 'test', 'testex',
+ 'testpkgs', 'testall',
+ 'testcfx'])),
+ (("", "--pkgdir",), dict(dest="pkgdir",
+ help=("package dir containing "
+ "package.json; default is "
+ "current directory"),
+ metavar=None,
+ default=None,
+ cmds=['run', 'xpi', 'test'])),
+ (("", "--static-args",), dict(dest="static_args",
+ help="extra harness options as JSON",
+ type="json",
+ metavar=None,
+ default="{}",
+ cmds=['run', 'xpi'])),
+ ]
+ ),
+
+ ("Experimental Command-Specific Options", [
+ (("-a", "--app",), dict(dest="app",
+ help=("app to run: firefox (default), fennec, "
+ "fennec-on-device, xulrunner or "
+ "thunderbird"),
+ metavar=None,
+ type="choice",
+ choices=["firefox", "fennec",
+ "fennec-on-device", "thunderbird",
+ "xulrunner"],
+ default="firefox",
+ cmds=['test', 'run', 'testex', 'testpkgs',
+ 'testall'])),
+ (("", "--no-run",), dict(dest="no_run",
+ help=("Instead of launching the "
+ "application, just show the command "
+ "for doing so. Use this to launch "
+ "the application in a debugger like "
+ "gdb."),
+ action="store_true",
+ default=False,
+ cmds=['run', 'test'])),
+ (("", "--no-strip-xpi",), dict(dest="no_strip_xpi",
+ help="retain unused modules in XPI",
+ action="store_true",
+ default=False,
+ cmds=['xpi'])),
+ (("", "--force-mobile",), dict(dest="enable_mobile",
+ help="Force compatibility with Firefox Mobile",
+ action="store_true",
+ default=False,
+ cmds=['run', 'test', 'xpi', 'testall'])),
+ (("", "--mobile-app",), dict(dest="mobile_app_name",
+ help=("Name of your Android application to "
+ "use. Possible values: 'firefox', "
+ "'firefox_beta', 'fennec_aurora', "
+ "'fennec' (for nightly)."),
+ metavar=None,
+ default=None,
+ cmds=['run', 'test', 'testall'])),
+ (("", "--harness-option",), dict(dest="extra_harness_option_args",
+ help=("Extra properties added to "
+ "harness-options.json"),
+ action="append",
+ metavar="KEY=VALUE",
+ default=[],
+ cmds=['xpi'])),
+ (("", "--stop-on-error",), dict(dest="stopOnError",
+ help="Stop running tests after the first failure",
+ action="store_true",
+ metavar=None,
+ default=False,
+ cmds=['test', 'testex', 'testpkgs'])),
+ (("", "--override-version",), dict(dest="override_version",
+ help="Pass in a version string to use in generated docs",
+ metavar=None,
+ default=False,
+ cmds=['sdocs'])),
+ ]
+ ),
+
+ ("Internal Command-Specific Options", [
+ (("", "--addons",), dict(dest="addons",
+ help=("paths of addons to install, "
+ "comma-separated"),
+ metavar=None,
+ default=None,
+ cmds=['test', 'run', 'testex', 'testpkgs',
+ 'testall'])),
+ (("", "--baseurl",), dict(dest="baseurl",
+ help=("root of static docs tree: "
+ "for example: 'http://me.com/the_docs/'"),
+ metavar=None,
+ default='',
+ cmds=['sdocs'])),
+ (("", "--test-runner-pkg",), dict(dest="test_runner_pkg",
+ help=("name of package "
+ "containing test runner "
+ "program (default is "
+ "test-harness)"),
+ default="addon-sdk",
+ cmds=['test', 'testex', 'testpkgs',
+ 'testall'])),
+ # --keydir was removed in 1.0b5, but we keep it around in the options
+ # parser to make life easier for frontends like FlightDeck which
+ # might still pass it. It can go away once the frontends are updated.
+ (("", "--keydir",), dict(dest="keydir",
+ help=("obsolete, ignored"),
+ metavar=None,
+ default=None,
+ cmds=['test', 'run', 'xpi', 'testex',
+ 'testpkgs', 'testall'])),
+ (("", "--e10s",), dict(dest="enable_e10s",
+ help="enable out-of-process Jetpacks",
+ action="store_true",
+ default=False,
+ cmds=['test', 'run', 'testex', 'testpkgs'])),
+ (("", "--logfile",), dict(dest="logfile",
+ help="log console output to file",
+ metavar=None,
+ default=None,
+ cmds=['run', 'test', 'testex', 'testpkgs'])),
+ # TODO: This should default to true once our memory debugging
+ # issues are resolved; see bug 592774.
+ (("", "--profile-memory",), dict(dest="profileMemory",
+ help=("profile memory usage "
+ "(default is false)"),
+ type="int",
+ action="store",
+ default=0,
+ cmds=['test', 'testex', 'testpkgs',
+ 'testall'])),
+ ]
+ ),
+ )
+
+def find_parent_package(cur_dir):
+ tail = True
+ while tail:
+ if os.path.exists(os.path.join(cur_dir, 'package.json')):
+ return cur_dir
+ cur_dir, tail = os.path.split(cur_dir)
+ return None
+
+def check_json(option, opt, value):
+ # We return the parsed JSON here; see bug 610816 for background on why.
+ try:
+ return json.loads(value)
+ except ValueError:
+ raise optparse.OptionValueError("Option %s must be JSON." % opt)
+
+class CfxOption(optparse.Option):
+ TYPES = optparse.Option.TYPES + ('json',)
+ TYPE_CHECKER = copy(optparse.Option.TYPE_CHECKER)
+ TYPE_CHECKER['json'] = check_json
+
+def parse_args(arguments, global_options, usage, version, parser_groups,
+ defaults=None):
+ parser = optparse.OptionParser(usage=usage.strip(), option_class=CfxOption,
+ version=version)
+
+ def name_cmp(a, b):
+ # a[0] = name sequence
+ # a[0][0] = short name (possibly empty string)
+ # a[0][1] = long name
+ names = []
+ for seq in (a, b):
+ names.append(seq[0][0][1:] if seq[0][0] else seq[0][1][2:])
+ return cmp(*names)
+
+ global_options.sort(name_cmp)
+ for names, opts in global_options:
+ parser.add_option(*names, **opts)
+
+ for group_name, options in parser_groups:
+ group = optparse.OptionGroup(parser, group_name)
+ options.sort(name_cmp)
+ for names, opts in options:
+ if 'cmds' in opts:
+ cmds = opts['cmds']
+ del opts['cmds']
+ cmds.sort()
+ if not 'help' in opts:
+ opts['help'] = ""
+ opts['help'] += " (%s)" % ", ".join(cmds)
+ group.add_option(*names, **opts)
+ parser.add_option_group(group)
+
+ if defaults:
+ parser.set_defaults(**defaults)
+
+ (options, args) = parser.parse_args(args=arguments)
+
+ if not args:
+ parser.print_help()
+ parser.exit()
+
+ return (options, args)
+
+# all tests emit progress messages to stderr, not stdout. (the mozrunner
+# console output goes to stderr and is hard to change, and
+# unittest.TextTestRunner prefers stderr, so we send everything else there
+# too, to keep all the messages in order)
+
+def test_all(env_root, defaults):
+ fail = False
+
+ if not defaults['filter']:
+ print >>sys.stderr, "Testing cfx..."
+ sys.stderr.flush()
+ result = test_cfx(env_root, defaults['verbose'])
+ if result.failures or result.errors:
+ fail = True
+
+ if not fail or not defaults.get("stopOnError"):
+ print >>sys.stderr, "Testing all examples..."
+ sys.stderr.flush()
+
+ try:
+ test_all_examples(env_root, defaults)
+ except SystemExit, e:
+ fail = (e.code != 0) or fail
+
+ if not fail or not defaults.get("stopOnError"):
+ print >>sys.stderr, "Testing all unit-test addons..."
+ sys.stderr.flush()
+
+ try:
+ test_all_testaddons(env_root, defaults)
+ except SystemExit, e:
+ fail = (e.code != 0) or fail
+
+ if not fail or not defaults.get("stopOnError"):
+ print >>sys.stderr, "Testing all packages..."
+ sys.stderr.flush()
+ try:
+ test_all_packages(env_root, defaults)
+ except SystemExit, e:
+ fail = (e.code != 0) or fail
+
+ if fail:
+ print >>sys.stderr, "Some tests were unsuccessful."
+ sys.exit(1)
+ print >>sys.stderr, "All tests were successful. Ship it!"
+ sys.exit(0)
+
+def test_cfx(env_root, verbose):
+ import cuddlefish.tests
+
+ # tests write to stderr. flush everything before and after to avoid
+ # confusion later.
+ sys.stdout.flush(); sys.stderr.flush()
+ olddir = os.getcwd()
+ os.chdir(env_root)
+ retval = cuddlefish.tests.run(verbose)
+ os.chdir(olddir)
+ sys.stdout.flush(); sys.stderr.flush()
+ return retval
+
+def test_all_testaddons(env_root, defaults):
+ addons_dir = os.path.join(env_root, "test", "addons")
+ addons = [dirname for dirname in os.listdir(addons_dir)
+ if os.path.isdir(os.path.join(addons_dir, dirname))]
+ addons.sort()
+ fail = False
+ for dirname in addons:
+ print >>sys.stderr, "Testing %s..." % dirname
+ sys.stderr.flush()
+ try:
+ run(arguments=["run",
+ "--pkgdir",
+ os.path.join(addons_dir, dirname)],
+ defaults=defaults,
+ env_root=env_root)
+ except SystemExit, e:
+ fail = (e.code != 0) or fail
+ if fail and defaults.get("stopOnError"):
+ break
+
+ if fail:
+ print >>sys.stderr, "Some test addons tests were unsuccessful."
+ sys.exit(-1)
+
+def test_all_examples(env_root, defaults):
+ examples_dir = os.path.join(env_root, "examples")
+ examples = [dirname for dirname in os.listdir(examples_dir)
+ if os.path.isdir(os.path.join(examples_dir, dirname))]
+ examples.sort()
+ fail = False
+ for dirname in examples:
+ print >>sys.stderr, "Testing %s..." % dirname
+ sys.stderr.flush()
+ try:
+ run(arguments=["test",
+ "--pkgdir",
+ os.path.join(examples_dir, dirname)],
+ defaults=defaults,
+ env_root=env_root)
+ except SystemExit, e:
+ fail = (e.code != 0) or fail
+ if fail and defaults.get("stopOnError"):
+ break
+
+ if fail:
+ print >>sys.stderr, "Some examples tests were unsuccessful."
+ sys.exit(-1)
+
+def test_all_packages(env_root, defaults):
+ packages_dir = os.path.join(env_root, "packages")
+ if os.path.isdir(packages_dir):
+ packages = [dirname for dirname in os.listdir(packages_dir)
+ if os.path.isdir(os.path.join(packages_dir, dirname))]
+ else:
+ packages = []
+ packages.append(env_root)
+ packages.sort()
+ print >>sys.stderr, "Testing all available packages: %s." % (", ".join(packages))
+ sys.stderr.flush()
+ fail = False
+ for dirname in packages:
+ print >>sys.stderr, "Testing %s..." % dirname
+ sys.stderr.flush()
+ try:
+ run(arguments=["test",
+ "--pkgdir",
+ os.path.join(packages_dir, dirname)],
+ defaults=defaults,
+ env_root=env_root)
+ except SystemExit, e:
+ fail = (e.code != 0) or fail
+ if fail and defaults.get('stopOnError'):
+ break
+ if fail:
+ print >>sys.stderr, "Some package tests were unsuccessful."
+ sys.exit(-1)
+
+def get_config_args(name, env_root):
+ local_json = os.path.join(env_root, "local.json")
+ if not (os.path.exists(local_json) and
+ os.path.isfile(local_json)):
+ if name == "default":
+ return []
+ else:
+ print >>sys.stderr, "File does not exist: %s" % local_json
+ sys.exit(1)
+ local_json = packaging.load_json_file(local_json)
+ if 'configs' not in local_json:
+ print >>sys.stderr, "'configs' key not found in local.json."
+ sys.exit(1)
+ if name not in local_json.configs:
+ if name == "default":
+ return []
+ else:
+ print >>sys.stderr, "No config found for '%s'." % name
+ sys.exit(1)
+ config = local_json.configs[name]
+ if type(config) != list:
+ print >>sys.stderr, "Config for '%s' must be a list of strings." % name
+ sys.exit(1)
+ return config
+
+def initializer(env_root, args, out=sys.stdout, err=sys.stderr):
+ from templates import PACKAGE_JSON, TEST_MAIN_JS
+ path = os.getcwd()
+ addon = os.path.basename(path)
+ # if more than two arguments
+ if len(args) > 2:
+ print >>err, 'Too many arguments.'
+ return 1
+ if len(args) == 2:
+ path = os.path.join(path,args[1])
+ try:
+ os.mkdir(path)
+ print >>out, '*', args[1], 'package directory created'
+ except OSError:
+ print >>out, '*', args[1], 'already exists, testing if directory is empty'
+ # avoid clobbering existing files, but we tolerate things like .git
+ existing = [fn for fn in os.listdir(path) if not fn.startswith(".")]
+ if existing:
+ print >>err, 'This command must be run in an empty directory.'
+ return 1
+ for d in ['lib','data','test','doc']:
+ os.mkdir(os.path.join(path,d))
+ print >>out, '*', d, 'directory created'
+ open(os.path.join(path,'README.md'),'w').write('')
+ print >>out, '* README.md written'
+ open(os.path.join(path,'package.json'),'w').write(PACKAGE_JSON % {'name':addon.lower(),
+ 'fullName':addon })
+ print >>out, '* package.json written'
+ open(os.path.join(path,'test','test-main.js'),'w').write(TEST_MAIN_JS)
+ print >>out, '* test/test-main.js written'
+ open(os.path.join(path,'lib','main.js'),'w').write('')
+ print >>out, '* lib/main.js written'
+ open(os.path.join(path,'doc','main.md'),'w').write('')
+ print >>out, '* doc/main.md written'
+ if len(args) == 1:
+ print >>out, '\nYour sample add-on is now ready.'
+ print >>out, 'Do "cfx test" to test it and "cfx run" to try it. Have fun!'
+ else:
+ print >>out, '\nYour sample add-on is now ready in the \'' + args[1] + '\' directory.'
+ print >>out, 'Change to that directory, then do "cfx test" to test it, \nand "cfx run" to try it. Have fun!'
+ return 0
+
+def buildJID(target_cfg):
+ if "id" in target_cfg:
+ jid = target_cfg["id"]
+ else:
+ import uuid
+ jid = str(uuid.uuid4())
+ if not ("@" in jid or jid.startswith("{")):
+ jid = jid + "@jetpack"
+ return jid
+
+def run(arguments=sys.argv[1:], target_cfg=None, pkg_cfg=None,
+ defaults=None, env_root=os.environ.get('CUDDLEFISH_ROOT'),
+ stdout=sys.stdout):
+ versions = get_versions()
+ sdk_version = versions["version"]
+ display_version = "Add-on SDK %s (%s)" % (sdk_version, versions["full"])
+ parser_kwargs = dict(arguments=arguments,
+ global_options=global_options,
+ parser_groups=parser_groups,
+ usage=usage,
+ version=display_version,
+ defaults=defaults)
+
+ (options, args) = parse_args(**parser_kwargs)
+
+ config_args = get_config_args(options.config, env_root);
+
+ # reparse configs with arguments from local.json
+ if config_args:
+ parser_kwargs['arguments'] += config_args
+ (options, args) = parse_args(**parser_kwargs)
+
+ command = args[0]
+
+ if command == "init":
+ initializer(env_root, args)
+ return
+ if command == "testpkgs":
+ test_all_packages(env_root, defaults=options.__dict__)
+ return
+ elif command == "testaddons":
+ test_all_testaddons(env_root, defaults=options.__dict__)
+ return
+ elif command == "testex":
+ test_all_examples(env_root, defaults=options.__dict__)
+ return
+ elif command == "testall":
+ test_all(env_root, defaults=options.__dict__)
+ return
+ elif command == "testcfx":
+ if options.filter:
+ print >>sys.stderr, "The filter option is not valid with the testcfx command"
+ return
+ test_cfx(env_root, options.verbose)
+ return
+ elif command == "docs":
+ from cuddlefish.docs import generate
+ if len(args) > 1:
+ docs_home = generate.generate_named_file(env_root, filename_and_path=args[1])
+ else:
+ docs_home = generate.generate_local_docs(env_root)
+ webbrowser.open(docs_home)
+ return
+ elif command == "sdocs":
+ from cuddlefish.docs import generate
+ filename=""
+ if options.override_version:
+ filename = generate.generate_static_docs(env_root, override_version=options.override_version)
+ else:
+ filename = generate.generate_static_docs(env_root)
+ print >>stdout, "Wrote %s." % filename
+ return
+ elif command not in ["xpi", "test", "run"]:
+ print >>sys.stderr, "Unknown command: %s" % command
+ print >>sys.stderr, "Try using '--help' for assistance."
+ sys.exit(1)
+
+ target_cfg_json = None
+ if not target_cfg:
+ if not options.pkgdir:
+ options.pkgdir = find_parent_package(os.getcwd())
+ if not options.pkgdir:
+ print >>sys.stderr, ("cannot find 'package.json' in the"
+ " current directory or any parent.")
+ sys.exit(1)
+ else:
+ options.pkgdir = os.path.abspath(options.pkgdir)
+ if not os.path.exists(os.path.join(options.pkgdir, 'package.json')):
+ print >>sys.stderr, ("cannot find 'package.json' in"
+ " %s." % options.pkgdir)
+ sys.exit(1)
+
+ target_cfg_json = os.path.join(options.pkgdir, 'package.json')
+ target_cfg = packaging.get_config_in_dir(options.pkgdir)
+
+ # At this point, we're either building an XPI or running Jetpack code in
+ # a Mozilla application (which includes running tests).
+
+ use_main = False
+ inherited_options = ['verbose', 'enable_e10s']
+ enforce_timeouts = False
+
+ if command == "xpi":
+ use_main = True
+ elif command == "test":
+ if 'tests' not in target_cfg:
+ target_cfg['tests'] = []
+ inherited_options.extend(['iterations', 'filter', 'profileMemory',
+ 'stopOnError'])
+ enforce_timeouts = True
+ elif command == "run":
+ use_main = True
+ else:
+ assert 0, "shouldn't get here"
+
+ if use_main and 'main' not in target_cfg:
+ # If the user supplies a template dir, then the main
+ # program may be contained in the template.
+ if not options.templatedir:
+ print >>sys.stderr, "package.json does not have a 'main' entry."
+ sys.exit(1)
+
+ if not pkg_cfg:
+ pkg_cfg = packaging.build_config(env_root, target_cfg, options.packagepath)
+
+ target = target_cfg.name
+
+ # TODO: Consider keeping a cache of dynamic UUIDs, based
+ # on absolute filesystem pathname, in the root directory
+ # or something.
+ if command in ('xpi', 'run'):
+ from cuddlefish.preflight import preflight_config
+ if target_cfg_json:
+ config_was_ok, modified = preflight_config(target_cfg,
+ target_cfg_json)
+ if not config_was_ok:
+ if modified:
+ # we need to re-read package.json . The safest approach
+ # is to re-run the "cfx xpi"/"cfx run" command.
+ print >>sys.stderr, ("package.json modified: please re-run"
+ " 'cfx %s'" % command)
+ else:
+ print >>sys.stderr, ("package.json needs modification:"
+ " please update it and then re-run"
+ " 'cfx %s'" % command)
+ sys.exit(1)
+ # if we make it this far, we have a JID
+ else:
+ assert command == "test"
+
+ jid = buildJID(target_cfg)
+
+ targets = [target]
+ if command == "test":
+ targets.append(options.test_runner_pkg)
+
+ extra_packages = []
+ if options.extra_packages:
+ extra_packages = options.extra_packages.split(",")
+ if extra_packages:
+ targets.extend(extra_packages)
+ target_cfg.extra_dependencies = extra_packages
+
+ deps = packaging.get_deps_for_targets(pkg_cfg, targets)
+
+ from cuddlefish.manifest import build_manifest, ModuleNotFoundError, \
+ BadChromeMarkerError
+ # Figure out what loader files should be scanned. This is normally
+ # computed inside packaging.generate_build_for_target(), by the first
+ # dependent package that defines a "loader" property in its package.json.
+ # This property is interpreted as a filename relative to the top of that
+ # file, and stored as a path in build.loader . generate_build_for_target()
+ # cannot be called yet (it needs the list of used_deps that
+ # build_manifest() computes, but build_manifest() needs the list of
+ # loader files that it computes). We could duplicate or factor out this
+ # build.loader logic, but that would be messy, so instead we hard-code
+ # the choice of loader for manifest-generation purposes. In practice,
+ # this means that alternative loaders probably won't work with
+ # --strip-xpi.
+ assert packaging.DEFAULT_LOADER == "addon-sdk"
+ assert pkg_cfg.packages["addon-sdk"].loader == "lib/sdk/loader/cuddlefish.js"
+ cuddlefish_js_path = os.path.join(pkg_cfg.packages["addon-sdk"].root_dir,
+ "lib", "sdk", "loader", "cuddlefish.js")
+ loader_modules = [("addon-sdk", "lib", "sdk/loader/cuddlefish", cuddlefish_js_path)]
+ scan_tests = command == "test"
+ test_filter_re = None
+ if scan_tests and options.filter:
+ test_filter_re = options.filter
+ if ":" in options.filter:
+ test_filter_re = options.filter.split(":")[0]
+ try:
+ manifest = build_manifest(target_cfg, pkg_cfg, deps,
+ scan_tests, test_filter_re,
+ loader_modules)
+ except ModuleNotFoundError, e:
+ print str(e)
+ sys.exit(1)
+ except BadChromeMarkerError, e:
+ # An error had already been displayed on stderr in manifest code
+ sys.exit(1)
+ used_deps = manifest.get_used_packages()
+ if command == "test":
+ # The test runner doesn't appear to link against any actual packages,
+ # because it loads everything at runtime (invisible to the linker).
+ # If we believe that, we won't set up URI mappings for anything, and
+ # tests won't be able to run.
+ used_deps = deps
+ for xp in extra_packages:
+ if xp not in used_deps:
+ used_deps.append(xp)
+
+ build = packaging.generate_build_for_target(
+ pkg_cfg, target, used_deps,
+ include_dep_tests=options.dep_tests,
+ is_running_tests=(command == "test")
+ )
+
+ harness_options = {
+ 'jetpackID': jid,
+ 'staticArgs': options.static_args,
+ 'name': target,
+ }
+
+ harness_options.update(build)
+
+ extra_environment = {}
+ if command == "test":
+ # This should be contained in the test runner package.
+ # maybe just do: target_cfg.main = 'test-harness/run-tests'
+ harness_options['main'] = 'sdk/test/runner'
+ harness_options['mainPath'] = manifest.get_manifest_entry("addon-sdk", "lib", "sdk/test/runner").get_path()
+ else:
+ harness_options['main'] = target_cfg.get('main')
+ harness_options['mainPath'] = manifest.top_path
+ extra_environment["CFX_COMMAND"] = command
+
+ for option in inherited_options:
+ harness_options[option] = getattr(options, option)
+
+ harness_options['metadata'] = packaging.get_metadata(pkg_cfg, used_deps)
+
+ harness_options['sdkVersion'] = sdk_version
+
+ packaging.call_plugins(pkg_cfg, used_deps)
+
+ retval = 0
+
+ if options.templatedir:
+ app_extension_dir = os.path.abspath(options.templatedir)
+ else:
+ mydir = os.path.dirname(os.path.abspath(__file__))
+ app_extension_dir = os.path.join(mydir, "../../app-extension")
+
+
+ if target_cfg.get('preferences'):
+ harness_options['preferences'] = target_cfg.get('preferences')
+
+ harness_options['manifest'] = manifest.get_harness_options_manifest()
+ harness_options['allTestModules'] = manifest.get_all_test_modules()
+ if len(harness_options['allTestModules']) == 0 and command == "test":
+ sys.exit(0)
+
+ from cuddlefish.rdf import gen_manifest, RDFUpdate
+
+ manifest_rdf = gen_manifest(template_root_dir=app_extension_dir,
+ target_cfg=target_cfg,
+ jid=jid,
+ update_url=options.update_url,
+ bootstrap=True,
+ enable_mobile=options.enable_mobile)
+
+ if command == "xpi" and options.update_link:
+ if not options.update_link.startswith("https"):
+ raise optparse.OptionValueError("--update-link must start with 'https': %s" % options.update_link)
+ rdf_name = UPDATE_RDF_FILENAME % target_cfg.name
+ print >>stdout, "Exporting update description to %s." % rdf_name
+ update = RDFUpdate()
+ update.add(manifest_rdf, options.update_link)
+ open(rdf_name, "w").write(str(update))
+
+ # ask the manifest what files were used, so we can construct an XPI
+ # without the rest. This will include the loader (and everything it
+ # uses) because of the "loader_modules" starting points we passed to
+ # build_manifest earlier
+ used_files = None
+ if command == "xpi":
+ used_files = set(manifest.get_used_files())
+
+ if options.no_strip_xpi:
+ used_files = None # disables the filter, includes all files
+
+ if command == 'xpi':
+ from cuddlefish.xpi import build_xpi
+ extra_harness_options = {}
+ for kv in options.extra_harness_option_args:
+ key,value = kv.split("=", 1)
+ extra_harness_options[key] = value
+ xpi_path = XPI_FILENAME % target_cfg.name
+ print >>stdout, "Exporting extension to %s." % xpi_path
+ build_xpi(template_root_dir=app_extension_dir,
+ manifest=manifest_rdf,
+ xpi_path=xpi_path,
+ harness_options=harness_options,
+ limit_to=used_files,
+ extra_harness_options=extra_harness_options)
+ else:
+ from cuddlefish.runner import run_app
+
+ if options.profiledir:
+ options.profiledir = os.path.expanduser(options.profiledir)
+ options.profiledir = os.path.abspath(options.profiledir)
+
+ if options.addons is not None:
+ options.addons = options.addons.split(",")
+
+ try:
+ retval = run_app(harness_root_dir=app_extension_dir,
+ manifest_rdf=manifest_rdf,
+ harness_options=harness_options,
+ app_type=options.app,
+ binary=options.binary,
+ profiledir=options.profiledir,
+ verbose=options.verbose,
+ enforce_timeouts=enforce_timeouts,
+ logfile=options.logfile,
+ addons=options.addons,
+ args=options.cmdargs,
+ extra_environment=extra_environment,
+ norun=options.no_run,
+ used_files=used_files,
+ enable_mobile=options.enable_mobile,
+ mobile_app_name=options.mobile_app_name)
+ except ValueError, e:
+ print ""
+ print "A given cfx option has an inappropriate value:"
+ print >>sys.stderr, " " + " \n ".join(str(e).split("\n"))
+ retval = -1
+ except Exception, e:
+ if str(e).startswith(MOZRUNNER_BIN_NOT_FOUND):
+ print >>sys.stderr, MOZRUNNER_BIN_NOT_FOUND_HELP.strip()
+ retval = -1
+ else:
+ raise
+ sys.exit(retval)
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/_version.py b/tools/addon-sdk-1.12/python-lib/cuddlefish/_version.py
new file mode 100644
index 0000000..be1ee60
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/_version.py
@@ -0,0 +1,171 @@
+
+# This file helps to compute a version number in source trees obtained from
+# git-archive tarball (such as those provided by githubs download-from-tag
+# feature). Distribution tarballs (build by setup.py sdist) and build
+# directories (produced by setup.py build) will contain a much shorter file
+# that just contains the computed version number.
+
+# This file is released into the public domain. Generated by versioneer-0.6
+# (https://github.com/warner/python-versioneer)
+
+# these strings will be replaced by git during git-archive
+git_refnames = " (HEAD, 1.12rc3, 1.12, origin/stabilization, stabilization)"
+git_full = "9c31911385c15801523dd6ca27bcc2a5748ff476"
+
+
+import subprocess
+
+def run_command(args, cwd=None, verbose=False):
+ try:
+ # remember shell=False, so use git.cmd on windows, not just git
+ p = subprocess.Popen(args, stdout=subprocess.PIPE, cwd=cwd)
+ except EnvironmentError, e:
+ if verbose:
+ print "unable to run %s" % args[0]
+ print e
+ return None
+ stdout = p.communicate()[0].strip()
+ if p.returncode != 0:
+ if verbose:
+ print "unable to run %s (error)" % args[0]
+ return None
+ return stdout
+
+
+import sys
+import re
+import os.path
+
+def get_expanded_variables(versionfile_source):
+ # the code embedded in _version.py can just fetch the value of these
+ # variables. When used from setup.py, we don't want to import
+ # _version.py, so we do it with a regexp instead. This function is not
+ # used from _version.py.
+ variables = {}
+ try:
+ for line in open(versionfile_source,"r").readlines():
+ if line.strip().startswith("git_refnames ="):
+ mo = re.search(r'=\s*"(.*)"', line)
+ if mo:
+ variables["refnames"] = mo.group(1)
+ if line.strip().startswith("git_full ="):
+ mo = re.search(r'=\s*"(.*)"', line)
+ if mo:
+ variables["full"] = mo.group(1)
+ except EnvironmentError:
+ pass
+ return variables
+
+def versions_from_expanded_variables(variables, tag_prefix):
+ refnames = variables["refnames"].strip()
+ if refnames.startswith("$Format"):
+ return {} # unexpanded, so not in an unpacked git-archive tarball
+ refs = set([r.strip() for r in refnames.strip("()").split(",")])
+ for ref in list(refs):
+ if not re.search(r'\d', ref):
+ refs.discard(ref)
+ # Assume all version tags have a digit. git's %d expansion
+ # behaves like git log --decorate=short and strips out the
+ # refs/heads/ and refs/tags/ prefixes that would let us
+ # distinguish between branches and tags. By ignoring refnames
+ # without digits, we filter out many common branch names like
+ # "release" and "stabilization", as well as "HEAD" and "master".
+ for ref in sorted(refs):
+ # sorting will prefer e.g. "2.0" over "2.0rc1"
+ if ref.startswith(tag_prefix):
+ r = ref[len(tag_prefix):]
+ return { "version": r,
+ "full": variables["full"].strip() }
+ # no suitable tags, so we use the full revision id
+ return { "version": variables["full"].strip(),
+ "full": variables["full"].strip() }
+
+def versions_from_vcs(tag_prefix, versionfile_source, verbose=False):
+ # this runs 'git' from the root of the source tree. That either means
+ # someone ran a setup.py command (and this code is in versioneer.py, thus
+ # the containing directory is the root of the source tree), or someone
+ # ran a project-specific entry point (and this code is in _version.py,
+ # thus the containing directory is somewhere deeper in the source tree).
+ # This only gets called if the git-archive 'subst' variables were *not*
+ # expanded, and _version.py hasn't already been rewritten with a short
+ # version string, meaning we're inside a checked out source tree.
+
+ try:
+ here = os.path.abspath(__file__)
+ except NameError:
+ # some py2exe/bbfreeze/non-CPython implementations don't do __file__
+ return {} # not always correct
+
+ # versionfile_source is the relative path from the top of the source tree
+ # (where the .git directory might live) to this file. Invert this to find
+ # the root from __file__.
+ root = here
+ for i in range(len(versionfile_source.split("/"))):
+ root = os.path.dirname(root)
+ if not os.path.exists(os.path.join(root, ".git")):
+ return {}
+
+ GIT = "git"
+ if sys.platform == "win32":
+ GIT = "git.cmd"
+ stdout = run_command([GIT, "describe", "--tags", "--dirty", "--always"],
+ cwd=root)
+ if stdout is None:
+ return {}
+ if not stdout.startswith(tag_prefix):
+ if verbose:
+ print "tag '%s' doesn't start with prefix '%s'" % (stdout, tag_prefix)
+ return {}
+ tag = stdout[len(tag_prefix):]
+ stdout = run_command([GIT, "rev-parse", "HEAD"], cwd=root)
+ if stdout is None:
+ return {}
+ full = stdout.strip()
+ if tag.endswith("-dirty"):
+ full += "-dirty"
+ return {"version": tag, "full": full}
+
+
+def versions_from_parentdir(parentdir_prefix, versionfile_source, verbose=False):
+ try:
+ here = os.path.abspath(__file__)
+ # versionfile_source is the relative path from the top of the source
+ # tree (where the .git directory might live) to _version.py, when
+ # this is used by the runtime. Invert this to find the root from
+ # __file__.
+ root = here
+ for i in range(len(versionfile_source.split("/"))):
+ root = os.path.dirname(root)
+ except NameError:
+ # try a couple different things to handle py2exe, bbfreeze, and
+ # non-CPython implementations which don't do __file__. This code
+ # either lives in versioneer.py (used by setup.py) or _version.py
+ # (used by the runtime). In the versioneer.py case, sys.argv[0] will
+ # be setup.py, in the root of the source tree. In the _version.py
+ # case, we have no idea what sys.argv[0] is (some
+ # application-specific runner).
+ root = os.path.dirname(os.path.abspath(sys.argv[0]))
+ # Source tarballs conventionally unpack into a directory that includes
+ # both the project name and a version string.
+ dirname = os.path.basename(root)
+ if not dirname.startswith(parentdir_prefix):
+ if verbose:
+ print "dirname '%s' doesn't start with prefix '%s'" % (dirname, parentdir_prefix)
+ return None
+ return {"version": dirname[len(parentdir_prefix):], "full": ""}
+
+tag_prefix = ""
+parentdir_prefix = "addon-sdk-"
+versionfile_source = "python-lib/cuddlefish/_version.py"
+
+def get_versions():
+ variables = { "refnames": git_refnames, "full": git_full }
+ ver = versions_from_expanded_variables(variables, tag_prefix)
+ if not ver:
+ ver = versions_from_vcs(tag_prefix, versionfile_source)
+ if not ver:
+ ver = versions_from_parentdir(parentdir_prefix, versionfile_source)
+ if not ver:
+ ver = {"version": "unknown", "full": ""}
+ return ver
+
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/bunch.py b/tools/addon-sdk-1.12/python-lib/cuddlefish/bunch.py
new file mode 100644
index 0000000..5efa79f
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/bunch.py
@@ -0,0 +1,34 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+# Taken from Paver's paver.options module.
+
+class Bunch(dict):
+ """A dictionary that provides attribute-style access."""
+
+ def __repr__(self):
+ keys = self.keys()
+ keys.sort()
+ args = ', '.join(['%s=%r' % (key, self[key]) for key in keys])
+ return '%s(%s)' % (self.__class__.__name__, args)
+
+ def __getitem__(self, key):
+ item = dict.__getitem__(self, key)
+ if callable(item):
+ return item()
+ return item
+
+ def __getattr__(self, name):
+ try:
+ return self[name]
+ except KeyError:
+ raise AttributeError(name)
+
+ __setattr__ = dict.__setitem__
+
+ def __delattr__(self, name):
+ try:
+ del self[name]
+ except KeyError:
+ raise AttributeError(name)
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/docs/__init__.py b/tools/addon-sdk-1.12/python-lib/cuddlefish/docs/__init__.py
new file mode 100644
index 0000000..5501cd4
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/docs/__init__.py
@@ -0,0 +1,4 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/docs/apiparser.py b/tools/addon-sdk-1.12/python-lib/cuddlefish/docs/apiparser.py
new file mode 100644
index 0000000..b6ccf22
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/docs/apiparser.py
@@ -0,0 +1,392 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import sys, re, textwrap
+
+VERSION = 4
+
+class ParseError(Exception):
+ # args[1] is the line number that caused the problem
+ def __init__(self, why, lineno):
+ self.why = why
+ self.lineno = lineno
+ def __str__(self):
+ return ("ParseError: the JS API docs were unparseable on line %d: %s" %
+ (self.lineno, self.why))
+
+class Accumulator:
+ def __init__(self, holder, firstline):
+ self.holder = holder
+ self.firstline = firstline
+ self.otherlines = []
+ def addline(self, line):
+ self.otherlines.append(line)
+ def finish(self):
+ # take a list of strings like:
+ # "initial stuff" (this is in firstline)
+ # " more stuff" (this is in lines[0])
+ # " yet more stuff"
+ # " indented block"
+ # " indented block"
+ # " nonindented stuff" (lines[-1])
+ #
+ # calculate the indentation level by looking at all but the first
+ # line, and removing the whitespace they all have in common. Then
+ # join the results with newlines and return a single string.
+ pieces = []
+ if self.firstline:
+ pieces.append(self.firstline)
+ if self.otherlines:
+ pieces.append(textwrap.dedent("\n".join(self.otherlines)))
+ self.holder["description"] = "\n".join(pieces)
+
+
+class APIParser:
+ def parse(self, lines, lineno):
+ api = {"line_number": lineno + 1}
+# assign the name from the first line, of the form "<api name="API_NAME">"
+ title_line = lines[lineno].rstrip("\n")
+ api["name"] = self._parse_title_line(title_line, lineno + 1)
+ lineno += 1
+# finished with the first line, assigned the name
+ working_set = self._initialize_working_set()
+ props = []
+ currentPropHolder = api
+# fetch the next line, of the form "@tag [name] {datatype} description"
+# and parse it into tag, info, description
+ tag, info, firstline = self._parseTypeLine(lines[lineno], lineno + 1)
+ api["type"] = tag
+# if this API element is a property then datatype must be set
+ if tag == 'property':
+ api['datatype'] = info['datatype']
+ # info is ignored
+ currentAccumulator = Accumulator(api, firstline)
+ lineno += 1
+ while (lineno) < len(lines):
+ line = lines[lineno].rstrip("\n")
+ # accumulate any multiline descriptive text belonging to
+ # the preceding "@" section
+ if self._is_description_line(line):
+ currentAccumulator.addline(line)
+ else:
+ currentAccumulator.finish()
+ if line.startswith("<api"):
+ # then we should recursively handle a nested element
+ nested_api, lineno = self.parse(lines, lineno)
+ self._update_working_set(nested_api, working_set)
+ elif line.startswith("</api"):
+ # then we have finished parsing this api element
+ currentAccumulator.finish()
+ if props and currentPropHolder:
+ currentPropHolder["props"] = props
+ self._assemble_api_element(api, working_set)
+ return api, lineno
+ else:
+ # then we are looking at a subcomponent of an <api> element
+ tag, info, desc = self._parseTypeLine(line, lineno + 1)
+ currentAccumulator = Accumulator(info, desc)
+ if tag == "prop":
+ # build up props[]
+ props.append(info)
+ elif tag == "returns":
+ # close off the @prop list
+ if props and currentPropHolder:
+ currentPropHolder["props"] = props
+ props = []
+ api["returns"] = info
+ currentPropHolder = info
+ elif tag == "param":
+ # close off the @prop list
+ if props and currentPropHolder:
+ currentPropHolder["props"] = props
+ props = []
+ working_set["params"].append(info)
+ currentPropHolder = info
+ elif tag == "argument":
+ # close off the @prop list
+ if props and currentPropHolder:
+ currentPropHolder["props"] = props
+ props = []
+ working_set["arguments"].append(info)
+ currentPropHolder = info
+ else:
+ raise ParseError("unknown '@' section header %s in \
+ '%s'" % (tag, line), lineno + 1)
+ lineno += 1
+ raise ParseError("closing </api> tag not found for <api name=\"" +
+ api["name"] + "\">", lineno + 1)
+
+ def _parse_title_line(self, title_line, lineno):
+ if "name" not in title_line:
+ raise ParseError("Opening <api> tag must have a name attribute.",
+ lineno)
+ m = re.search("name=['\"]{0,1}([-\w\.]*?)['\"]", title_line)
+ if not m:
+ raise ParseError("No value for name attribute found in "
+ "opening <api> tag.", lineno)
+ return m.group(1)
+
+ def _is_description_line(self, line):
+ return not ( (line.lstrip().startswith("@")) or
+ (line.lstrip().startswith("<api")) or
+ (line.lstrip().startswith("</api")) )
+
+ def _initialize_working_set(self):
+ # working_set accumulates api elements
+ # that might belong to a parent api element
+ working_set = {}
+ working_set["constructors"] = []
+ working_set["methods"] = []
+ working_set["properties"] = []
+ working_set["params"] = []
+ working_set["events"] = []
+ working_set["arguments"] = []
+ return working_set
+
+ def _update_working_set(self, nested_api, working_set):
+ # add this api element to whichever list is appropriate
+ if nested_api["type"] == "constructor":
+ working_set["constructors"].append(nested_api)
+ if nested_api["type"] == "method":
+ working_set["methods"].append(nested_api)
+ if nested_api["type"] == "property":
+ working_set["properties"].append(nested_api)
+ if nested_api["type"] == "event":
+ working_set["events"].append(nested_api)
+
+ def _assemble_signature(self, api_element, params):
+ signature = api_element["name"] + "("
+ if len(params) > 0:
+ signature += params[0]["name"]
+ for param in params[1:]:
+ signature += ", " + param["name"]
+ signature += ")"
+ api_element["signature"] = signature
+
+ def _assemble_api_element(self, api_element, working_set):
+ # if any of this working set's lists are non-empty,
+ # add it to the current api element
+ if (api_element["type"] == "constructor") or \
+ (api_element["type"] == "function") or \
+ (api_element["type"] == "method"):
+ self._assemble_signature(api_element, working_set["params"])
+ if len(working_set["params"]) > 0:
+ api_element["params"] = working_set["params"]
+ if len(working_set["properties"]) > 0:
+ api_element["properties"] = working_set["properties"]
+ if len(working_set["constructors"]) > 0:
+ api_element["constructors"] = working_set["constructors"]
+ if len(working_set["methods"]) > 0:
+ api_element["methods"] = working_set["methods"]
+ if len(working_set["events"]) > 0:
+ api_element["events"] = working_set["events"]
+ if len(working_set["arguments"]) > 0:
+ api_element["arguments"] = working_set["arguments"]
+
+ def _validate_info(self, tag, info, line, lineno):
+ if tag == 'property':
+ if not 'datatype' in info:
+ raise ParseError("No type found for @property.", lineno)
+ elif tag == "param":
+ if info.get("required", False) and "default" in info:
+ raise ParseError(
+ "required parameters should not have defaults: '%s'"
+ % line, lineno)
+ elif tag == "prop":
+ if "datatype" not in info:
+ raise ParseError("@prop lines must include {type}: '%s'" %
+ line, lineno)
+ if "name" not in info:
+ raise ParseError("@prop lines must provide a name: '%s'" %
+ line, lineno)
+
+ def _parseTypeLine(self, line, lineno):
+ # handle these things:
+ # @method
+ # @returns description
+ # @returns {string} description
+ # @param NAME {type} description
+ # @param NAME
+ # @prop NAME {type} description
+ # @prop NAME
+ # returns:
+ # tag: type of api element
+ # info: linenumber, required, default, name, datatype
+ # description
+
+ info = {"line_number": lineno}
+ line = line.rstrip("\n")
+ pieces = line.split()
+
+ if not pieces:
+ raise ParseError("line is too short: '%s'" % line, lineno)
+ if not pieces[0].startswith("@"):
+ raise ParseError("type line should start with @: '%s'" % line,
+ lineno)
+ tag = pieces[0][1:]
+ skip = 1
+
+ expect_name = tag in ("param", "prop")
+
+ if len(pieces) == 1:
+ description = ""
+ else:
+ if pieces[1].startswith("{"):
+ # NAME is missing, pieces[1] is TYPE
+ pass
+ else:
+ if expect_name:
+ info["required"] = not pieces[1].startswith("[")
+ name = pieces[1].strip("[ ]")
+ if "=" in name:
+ name, info["default"] = name.split("=")
+ info["name"] = name
+ skip += 1
+
+ if len(pieces) > skip and pieces[skip].startswith("{"):
+ info["datatype"] = pieces[skip].strip("{ }")
+ skip += 1
+
+ # we've got the metadata, now extract the description
+ pieces = line.split(None, skip)
+ if len(pieces) > skip:
+ description = pieces[skip]
+ else:
+ description = ""
+ self._validate_info(tag, info, line, lineno)
+ return tag, info, description
+
+def parse_hunks(text):
+ # return a list of tuples. Each is one of:
+ # ("raw", string) : non-API blocks
+ # ("api-json", dict) : API blocks
+ yield ("version", VERSION)
+ lines = text.splitlines(True)
+ line_number = 0
+ markdown_string = ""
+ while line_number < len(lines):
+ line = lines[line_number]
+ if line.startswith("<api"):
+ if len(markdown_string) > 0:
+ yield ("markdown", markdown_string)
+ markdown_string = ""
+ api, line_number = APIParser().parse(lines, line_number)
+ # this business with 'leftover' is a horrible thing to do,
+ # and exists only to collect the \n after the closing /api tag.
+ # It's not needed probably, except to help keep compatibility
+ # with the previous behaviour
+ leftover = lines[line_number].lstrip("</api>")
+ if len(leftover) > 0:
+ markdown_string += leftover
+ line_number = line_number + 1
+ yield ("api-json", api)
+ else:
+ markdown_string += line
+ line_number = line_number + 1
+ if len(markdown_string) > 0:
+ yield ("markdown", markdown_string)
+
+class TestRenderer:
+ # render docs for test purposes
+
+ def getm(self, d, key):
+ return d.get(key, "<MISSING>")
+
+ def join_lines(self, text):
+ return " ".join([line.strip() for line in text.split("\n")])
+
+ def render_prop(self, p):
+ s = "props[%s]: " % self.getm(p, "name")
+ pieces = []
+ for k in ("type", "description", "required", "default"):
+ if k in p:
+ pieces.append("%s=%s" % (k, self.join_lines(str(p[k]))))
+ return s + ", ".join(pieces)
+
+ def render_param(self, p):
+ pieces = []
+ for k in ("name", "type", "description", "required", "default"):
+ if k in p:
+ pieces.append("%s=%s" % (k, self.join_lines(str(p[k]))))
+ yield ", ".join(pieces)
+ for prop in p.get("props", []):
+ yield " " + self.render_prop(prop)
+
+ def render_method(self, method):
+ yield "name= %s" % self.getm(method, "name")
+ yield "type= %s" % self.getm(method, "type")
+ yield "description= %s" % self.getm(method, "description")
+ signature = method.get("signature")
+ if signature:
+ yield "signature= %s" % self.getm(method, "signature")
+ params = method.get("params", [])
+ if params:
+ yield "parameters:"
+ for p in params:
+ for pline in self.render_param(p):
+ yield " " + pline
+ r = method.get("returns", None)
+ if r:
+ yield "returns:"
+ if "type" in r:
+ yield " type= %s" % r["type"]
+ if "description" in r:
+ yield " description= %s" % self.join_lines(r["description"])
+ props = r.get("props", [])
+ for p in props:
+ yield " " + self.render_prop(p)
+
+ def format_api(self, api):
+ for mline in self.render_method(api):
+ yield mline
+ constructors = api.get("constructors", [])
+ if constructors:
+ yield "constructors:"
+ for m in constructors:
+ for mline in self.render_method(m):
+ yield " " + mline
+ methods = api.get("methods", [])
+ if methods:
+ yield "methods:"
+ for m in methods:
+ for mline in self.render_method(m):
+ yield " " + mline
+ properties = api.get("properties", [])
+ if properties:
+ yield "properties:"
+ for p in properties:
+ yield " " + self.render_prop(p)
+
+ def render_docs(self, docs_json, outf=sys.stdout):
+
+ for (t,data) in docs_json:
+ if t == "api-json":
+ for line in self.format_api(data):
+ line = line.rstrip("\n")
+ outf.write("API: " + line + "\n")
+ else:
+ for line in str(data).split("\n"):
+ outf.write("MD :" + line + "\n")
+
+def hunks_to_dict(docs_json):
+ exports = {}
+ for (t,data) in docs_json:
+ if t != "api-json":
+ continue
+ if data["name"]:
+ exports[data["name"]] = data
+ return exports
+
+if __name__ == "__main__":
+ json = False
+ if sys.argv[1] == "--json":
+ json = True
+ del sys.argv[1]
+ docs_text = open(sys.argv[1]).read()
+ docs_parsed = list(parse_hunks(docs_text))
+ if json:
+ import simplejson
+ print simplejson.dumps(docs_parsed, indent=2)
+ else:
+ TestRenderer().render_docs(docs_parsed)
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/docs/apirenderer.py b/tools/addon-sdk-1.12/python-lib/cuddlefish/docs/apirenderer.py
new file mode 100644
index 0000000..36e46ef
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/docs/apirenderer.py
@@ -0,0 +1,302 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import sys, os
+import markdown
+import apiparser
+
+# list of all the 'class' and 'id' attributes assigned to
+# <div> and <span> tags by the renderer.
+API_REFERENCE = 'api_reference'
+MODULE_API_DOCS_CLASS = 'module_api_docs'
+MODULE_API_DOCS_ID = '_module_api_docs'
+API_HEADER = 'api_header'
+API_NAME = 'api_name'
+API_COMPONENT_GROUP = 'api_component_group'
+API_COMPONENT = 'api_component'
+DATATYPE = 'datatype'
+RETURNS = 'returns'
+PARAMETER_SET = 'parameter_set'
+MODULE_DESCRIPTION = 'module_description'
+
+HTML_HEADER = '''
+<!DOCTYPE html>\n
+<html>\n
+<head>\n
+ <meta http-equiv="Content-type" content="text/html; charset=utf-8" />\n
+ <base target="_blank"/>\n
+ <link rel="stylesheet" type="text/css" media="all"\n
+ href="../../../css/base.css" />\n
+ <link rel="stylesheet" type="text/css" media="all"\n
+ href="../../../css/apidocs.css" />\n
+ <title>Add-on SDK Documentation</title>\n
+ <style type="text/css">\n
+ body {\n
+ border: 50px solid #FFFFFF;\n
+ }\n
+ </style>\n
+\n
+ <script type="text/javascript">\n
+ function rewrite_links() {\n
+ var images = document.getElementsByTagName("img");\n
+ for (var i = 0; i < images.length; i++) {\n
+ var before = images[i].src.split("packages/")[0];\n
+ var after = images[i].src.split("/docs")[1];\n
+ images[i].src = before + after;\n
+ }\n
+ }\n
+ </script>\n
+</head>\n
+\n
+<body onload = "rewrite_links()">\n'''
+
+HTML_FOOTER = '''
+</body>\n
+\n
+</html>\n'''
+
+def indent(text_in):
+ text_out = ''
+ lines = text_in.splitlines(True)
+ indentation_level = 0
+ indentation_depth = 2
+ for line in lines:
+ if (line.startswith('<div')):
+ text_out += ((' ' * indentation_depth) * indentation_level) + line
+ if not '</div>' in line:
+ indentation_level += 1
+ else:
+ if (line.startswith('</div>')):
+ indentation_level -= 1
+ text_out += ((' ' * indentation_depth) * indentation_level) + line
+ return text_out
+
+def tag_wrap_id(text, classname, id, tag = 'div'):
+ return ''.join(['\n<'+ tag + ' id="', id, '" class="', \
+ classname, '">\n', text + '\n</' + tag +'>\n'])
+
+def tag_wrap(text, classname, tag = 'div', inline = False):
+ if inline:
+ return ''.join(['\n<' + tag + ' class="', classname, '">', \
+ text, '</'+ tag + '>\n'])
+ else:
+ return ''.join(['\n<' + tag + ' class="', classname, '">', \
+ text, '\n</'+ tag + '>\n'])
+
+def tag_wrap_inline(text, classname, tag = 'div'):
+ return ''.join(['\n<' + tag + ' class="', classname, '">', \
+ text, '</'+ tag + '>\n'])
+
+def span_wrap(text, classname):
+ return ''.join(['<span class="', classname, '">', \
+ text, '</span>'])
+
+class API_Renderer(object):
+ def __init__(self, json, tag):
+ self.name = json.get('name', None)
+ self.tag = tag
+ self.description = json.get('description', '')
+ self.json = json
+
+ def render_name(self):
+ raise Exception('not implemented in this class')
+
+ def render_description(self):
+ return markdown.markdown(self.description)
+
+ def render_subcomponents(self):
+ raise Exception('not implemented in this class')
+
+ def get_tag(self):
+ return self.tag
+
+class Class_Doc(API_Renderer):
+ def __init__(self, json, tag):
+ API_Renderer.__init__(self, json, tag)
+
+ def render_name(self):
+ return self.name
+
+ def render_subcomponents(self):
+ return render_object_contents(self.json, 'h5', 'h6')
+
+class Event_Doc(API_Renderer):
+ def __init__(self, json, tag):
+ API_Renderer.__init__(self, json, tag)
+ self.arguments_json = json.get('arguments', None)
+
+ def render_name(self):
+ return self.name
+
+ def render_subcomponents(self):
+ if not self.arguments_json:
+ return ''
+ text = ''.join([render_comp(Argument_Doc(argument_json, 'div')) \
+ for argument_json in self.arguments_json])
+ return tag_wrap(text, PARAMETER_SET)
+
+class Argument_Doc(API_Renderer):
+ def __init__(self, json, tag):
+ API_Renderer.__init__(self, json, tag)
+ self.datatype = json.get('datatype', None)
+
+ def render_name(self):
+ return span_wrap(self.datatype, DATATYPE)
+
+ def render_subcomponents(self):
+ return ''
+
+class Function_Doc(API_Renderer):
+ def __init__(self, json, tag):
+ API_Renderer.__init__(self, json, tag)
+ self.signature = json['signature']
+ self.returns = json.get('returns', None)
+ self.parameters_json = json.get('params', None)
+
+ def render_name(self):
+ return self.signature
+
+ def render_subcomponents(self):
+ return self._render_parameters() + self._render_returns()
+
+ def _render_parameters(self):
+ if not self.parameters_json:
+ return ''
+ text = ''.join([render_comp(Parameter_Doc(parameter_json, 'div')) \
+ for parameter_json in self.parameters_json])
+ return tag_wrap(text, PARAMETER_SET)
+
+ def _render_returns(self):
+ if not self.returns:
+ return ''
+ text = 'Returns: ' + span_wrap(self.returns['datatype'], DATATYPE)
+ text += markdown.markdown(self.returns['description'])
+ return tag_wrap(text, RETURNS)
+
+class Property_Doc(API_Renderer):
+ def __init__(self, json, tag):
+ API_Renderer.__init__(self, json, tag)
+ self.datatype = json.get('datatype', None)
+ self.required = json.get('required', True)
+ self.default = json.get('default', False)
+
+ def render_name(self):
+ rendered = self.name
+ if self.default:
+ rendered = rendered + " = " + self.default
+ if self.datatype:
+ rendered = rendered + ' : ' + span_wrap(self.datatype, DATATYPE)
+ if not self.required:
+ rendered = '[ ' + rendered + ' ]'
+ return rendered
+
+ def render_subcomponents(self):
+ return render_object_contents(self.json)
+
+class Parameter_Doc(Property_Doc):
+ def __init__(self, json, tag):
+ Property_Doc.__init__(self, json, tag)
+ self.properties_json = json.get('props', None)
+
+ def render_subcomponents(self):
+ if not self.properties_json:
+ return ''
+ text = ''.join([render_comp(Property_Doc(property_json, 'div')) \
+ for property_json in self.properties_json])
+ return text
+
+def render_object_contents(json, tag = 'div', comp_tag = 'div'):
+ ctors = json.get('constructors', None)
+ text = render_comp_group(ctors, 'Constructors', Function_Doc, tag, comp_tag)
+ methods = json.get('methods', None)
+ text += render_comp_group(methods, 'Methods', Function_Doc, tag, comp_tag)
+ properties = json.get('properties', None)
+ text += render_comp_group(properties, 'Properties', Property_Doc, tag, comp_tag)
+ events = json.get('events', None)
+ text += render_comp_group(events, 'Events', Event_Doc, tag, comp_tag)
+ return text
+
+def render_comp(component):
+ # a component is wrapped inside a single div marked 'API_COMPONENT'
+ # containing:
+ # 1) the component name, marked 'API_NAME'
+ text = tag_wrap(component.render_name(), API_NAME, component.get_tag(), True)
+ # 2) the component description
+ text += component.render_description()
+ # 3) the component contents
+ text += component.render_subcomponents()
+ return tag_wrap(text, API_COMPONENT)
+
+def render_comp_group(group, group_name, ctor, tag = 'div', comp_tag = 'div'):
+ if not group:
+ return ''
+ # component group is a list of components in a single div called
+ # 'API_COMPONENT_GROUP' containing:
+ # 1) a title for the group marked with 'API_HEADER'
+ text = tag_wrap(group_name, API_HEADER, tag, True)
+ # 2) each component
+ text += ''.join([render_comp(ctor(api, comp_tag)) for api in group])
+ return tag_wrap(text, API_COMPONENT_GROUP)
+
+def render_descriptions(descriptions_md):
+ text = ''.join([description_md for description_md in descriptions_md])
+ return tag_wrap(markdown.markdown(text), MODULE_DESCRIPTION)
+
+def render_api_reference(api_docs):
+ if (len(api_docs) == 0):
+ return ''
+ # at the top level api reference is in a single div marked 'API_REFERENCE',
+ # containing:
+ # 1) a title 'API Reference' marked with 'API_HEADER'
+ text = tag_wrap('API Reference', API_HEADER, 'h2', True)
+ # 2) a component group called 'Classes' containing any class elements
+ classes = [api for api in api_docs if api['type'] == 'class']
+ text += render_comp_group(classes, 'Classes', Class_Doc, 'h3', 'h4')
+ # 3) a component group called 'Functions' containing any global functions
+ functions = [api for api in api_docs if api['type'] == 'function']
+ text += render_comp_group(functions, 'Functions', Function_Doc, 'h3', 'h4')
+ # 4) a component group called 'Properties' containing any global properties
+ properties = [api for api in api_docs if api['type'] == 'property']
+ text += render_comp_group(properties, 'Properties', Property_Doc, 'h3', 'h4')
+ # 5) a component group called 'Events' containing any global events
+ events = [api for api in api_docs if api['type'] == 'event']
+ text += render_comp_group(events, 'Events', Event_Doc, 'h3', 'h4')
+ return tag_wrap(text, API_REFERENCE)
+
+# take the JSON output of apiparser
+# return the HTML DIV containing the rendered component
+def json_to_div(json, markdown_filename):
+ module_name, ext = os.path.splitext(os.path.basename(markdown_filename))
+ descriptions = [hunk[1] for hunk in json if hunk[0]=='markdown']
+ api_docs = [hunk[1] for hunk in json if hunk[0]=='api-json']
+ text = "<h1>" + module_name + "</h1>"
+ text += render_descriptions(descriptions)
+ text += render_api_reference(api_docs)
+ text = tag_wrap_id(text, MODULE_API_DOCS_CLASS, \
+ module_name + MODULE_API_DOCS_ID)
+ return text.encode('utf8')
+
+# take the JSON output of apiparser
+# return standalone HTML containing the rendered component
+def json_to_html(json, markdown_filename):
+ return indent(HTML_HEADER + \
+ json_to_div(json, markdown_filename) + HTML_FOOTER)
+
+# take the name of a Markdown file
+# return the HTML DIV containing the rendered component
+def md_to_div(markdown_filename):
+ markdown_contents = open(markdown_filename).read().decode('utf8')
+ json = list(apiparser.parse_hunks(markdown_contents))
+ return json_to_div(json, markdown_filename)
+
+# take the name of a Markdown file
+# return standalone HTML containing the rendered component
+def md_to_html(markdown_filename):
+ return indent(HTML_HEADER + md_to_div(markdown_filename) + HTML_FOOTER)
+
+if __name__ == '__main__':
+ if (len(sys.argv) == 0):
+ print 'Supply the name of a docs file to parse'
+ else:
+ print md_to_html(sys.argv[1])
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/docs/documentationitem.py b/tools/addon-sdk-1.12/python-lib/cuddlefish/docs/documentationitem.py
new file mode 100755
index 0000000..c72133a
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/docs/documentationitem.py
@@ -0,0 +1,124 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import sys, os, re
+
+class DocumentationItemInfo(object):
+ def __init__(self, env_root, md_path, filename):
+ self.env_root = env_root
+ # full path to MD file, without filename
+ self.source_path = md_path
+ # MD filename
+ self.source_filename = filename
+
+ def env_root(self):
+ return self.env_root
+
+ def source_path(self):
+ return self.source_path
+
+ def source_filename(self):
+ return self.source_filename
+
+ def base_filename(self):
+ return self.source_filename[:-len(".md")]
+
+ def source_path_and_filename(self):
+ return os.sep.join([self.source_path, self.source_filename])
+
+ def source_path_relative_from_env_root(self):
+ return self.source_path[len(self.env_root) + 1:]
+
+class DevGuideItemInfo(DocumentationItemInfo):
+ def __init__(self, env_root, devguide_root, md_path, filename):
+ DocumentationItemInfo.__init__(self, env_root, md_path, filename)
+ self.devguide_root = devguide_root
+
+ def source_path_relative_from_devguide_root(self):
+ return self.source_path[len(self.devguide_root) + 1:]
+
+ def destination_path(self):
+ root_pieces = self.devguide_root.split(os.sep)
+ root_pieces[-1] = "dev-guide"
+ return os.sep.join([os.sep.join(root_pieces), self.source_path_relative_from_devguide_root()])
+
+class ModuleInfo(DocumentationItemInfo):
+ def __init__(self, env_root, module_root, md_path, filename):
+ DocumentationItemInfo.__init__(self, env_root, md_path, filename)
+ self.module_root = module_root
+
+ def js_module_path(self):
+ return os.path.join(self.env_root, "lib", self.source_path_relative_from_module_root(), self.source_filename[:-len(".md")] + ".js")
+
+ def source_path_relative_from_module_root(self):
+ return self.source_path[len(self.module_root) + 1:]
+
+ def destination_path(self):
+ if self.level() == "third-party":
+ return os.sep.join([self.env_root, "doc", "modules", "packages"])
+ root_pieces = self.module_root.split(os.sep)
+ root_pieces[-1] = "modules"
+ relative_pieces = self.source_path_relative_from_module_root().split(os.sep)
+ return os.sep.join(root_pieces + relative_pieces)
+
+ def relative_url(self):
+ if self.level() == "third-party":
+ relative_pieces = ["packages"]
+ else:
+ relative_pieces = self.source_path_relative_from_module_root().split(os.sep)
+ return "/".join(relative_pieces) + "/" + self.base_filename() + ".html"
+
+ def name(self):
+ if os.sep.join([self.module_root, "sdk"]) == self.source_path or self.level() == "third-party":
+ return self.source_filename[:-3]
+ else:
+ path_from_root_pieces = self.source_path_relative_from_module_root().split(os.sep)
+ return "/".join(["/".join(path_from_root_pieces[1:]), self.source_filename[:-len(".md")]])
+
+ def level(self):
+ if self.source_path_relative_from_env_root().startswith("packages"):
+ return "third-party"
+ else:
+ if os.sep.join([self.module_root, "sdk"]) == self.source_path:
+ return "high"
+ else:
+ return "low"
+
+def get_modules_in_package(env_root, package_docs_dir, module_list, ignore_files_in_root):
+ for (dirpath, dirnames, filenames) in os.walk(package_docs_dir):
+ for filename in filenames:
+ # ignore files in the root
+ if ignore_files_in_root and package_docs_dir == dirpath:
+ continue
+ if filename.endswith(".md"):
+ module_list.append(ModuleInfo(env_root, package_docs_dir, dirpath, filename))
+
+def get_module_list(env_root):
+ module_list = []
+ # get the built-in modules
+ module_root = os.sep.join([env_root, "doc", "module-source"])
+ get_modules_in_package(env_root, module_root, module_list, True)
+ # get the third-party modules
+ packages_root = os.sep.join([env_root, "packages"])
+ if os.path.exists(packages_root):
+ for entry in os.listdir(packages_root):
+ if os.path.isdir(os.sep.join([packages_root, entry])):
+ package_docs = os.sep.join([packages_root, entry, "docs"])
+ if os.path.exists(package_docs):
+ get_modules_in_package(env_root, package_docs, module_list, False)
+ module_list.sort(key=lambda x: x.name())
+ return module_list
+
+def get_devguide_list(env_root):
+ devguide_list = []
+ devguide_root = os.sep.join([env_root, "doc", "dev-guide-source"])
+ for (dirpath, dirnames, filenames) in os.walk(devguide_root):
+ for filename in filenames:
+ if filename.endswith(".md"):
+ devguide_list.append(DevGuideItemInfo(env_root, devguide_root, dirpath, filename))
+ return devguide_list
+
+if __name__ == "__main__":
+ module_list = get_module_list(sys.argv[1])
+ print [module_info.name for module_info in module_list]
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/docs/generate.py b/tools/addon-sdk-1.12/python-lib/cuddlefish/docs/generate.py
new file mode 100755
index 0000000..b498f00
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/docs/generate.py
@@ -0,0 +1,199 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import os
+import sys
+import shutil
+import hashlib
+import tarfile
+import StringIO
+
+from cuddlefish._version import get_versions
+from cuddlefish.docs import apiparser
+from cuddlefish.docs import apirenderer
+from cuddlefish.docs import webdocs
+from documentationitem import get_module_list
+from documentationitem import get_devguide_list
+from documentationitem import ModuleInfo
+from documentationitem import DevGuideItemInfo
+from linkrewriter import rewrite_links
+import simplejson as json
+
+DIGEST = "status.md5"
+TGZ_FILENAME = "addon-sdk-docs.tgz"
+
+def get_sdk_docs_path(env_root):
+ return os.path.join(env_root, "doc")
+
+def get_base_url(env_root):
+ sdk_docs_path = get_sdk_docs_path(env_root).lstrip("/")
+ return "file://"+"/"+"/".join(sdk_docs_path.split(os.sep))+"/"
+
+def clean_generated_docs(docs_dir):
+ status_file = os.path.join(docs_dir, "status.md5")
+ if os.path.exists(status_file):
+ os.remove(status_file)
+ index_file = os.path.join(docs_dir, "index.html")
+ if os.path.exists(index_file):
+ os.remove(index_file)
+ dev_guide_dir = os.path.join(docs_dir, "dev-guide")
+ if os.path.exists(dev_guide_dir):
+ shutil.rmtree(dev_guide_dir)
+ api_doc_dir = os.path.join(docs_dir, "modules")
+ if os.path.exists(api_doc_dir):
+ shutil.rmtree(api_doc_dir)
+
+def generate_static_docs(env_root, override_version=get_versions()["version"]):
+ clean_generated_docs(get_sdk_docs_path(env_root))
+ generate_docs(env_root, override_version, stdout=StringIO.StringIO())
+ tgz = tarfile.open(TGZ_FILENAME, 'w:gz')
+ tgz.add(get_sdk_docs_path(env_root), "doc")
+ tgz.close()
+ return TGZ_FILENAME
+
+def generate_local_docs(env_root):
+ return generate_docs(env_root, get_versions()["version"], get_base_url(env_root))
+
+def generate_named_file(env_root, filename_and_path):
+ web_docs = webdocs.WebDocs(env_root, get_versions()["version"], get_base_url(env_root))
+ abs_path = os.path.abspath(filename_and_path)
+ path, filename = os.path.split(abs_path)
+ if abs_path.startswith(os.path.join(env_root, 'doc', 'module-source')):
+ module_root = os.sep.join([env_root, "doc", "module-source"])
+ module_info = ModuleInfo(module_root, path, filename)
+ write_module_doc(env_root, web_docs, module_info, False)
+ elif abs_path.startswith(os.path.join(get_sdk_docs_path(env_root), 'dev-guide-source')):
+ devguide_root = os.sep.join([env_root, "doc", "dev-guide-source"])
+ devguideitem_info = DevGuideItemInfo(devguide_root, path, filename)
+ write_devguide_doc(env_root, web_docs, devguideitem_info, False)
+ else:
+ raise ValueError("Not a valid path to a documentation file")
+
+def generate_docs(env_root, version=get_versions()["version"], base_url=None, stdout=sys.stdout):
+ docs_dir = get_sdk_docs_path(env_root)
+ # if the generated docs don't exist, generate everything
+ if not os.path.exists(os.path.join(docs_dir, "dev-guide")):
+ print >>stdout, "Generating documentation..."
+ generate_docs_from_scratch(env_root, version, base_url)
+ current_status = calculate_current_status(env_root)
+ open(os.path.join(docs_dir, DIGEST), "w").write(current_status)
+ else:
+ current_status = calculate_current_status(env_root)
+ previous_status_file = os.path.join(docs_dir, DIGEST)
+ docs_are_up_to_date = False
+ if os.path.exists(previous_status_file):
+ docs_are_up_to_date = current_status == open(previous_status_file, "r").read()
+ # if the docs are not up to date, generate everything
+ if not docs_are_up_to_date:
+ print >>stdout, "Regenerating documentation..."
+ generate_docs_from_scratch(env_root, version, base_url)
+ open(os.path.join(docs_dir, DIGEST), "w").write(current_status)
+ return get_base_url(env_root) + "index.html"
+
+# this function builds a hash of the name and last modification date of:
+# * every file in "doc/sdk" which ends in ".md"
+# * every file in "doc/dev-guide-source" which ends in ".md"
+# * every file in "doc/static-files" which does not start with "."
+def calculate_current_status(env_root):
+ docs_dir = get_sdk_docs_path(env_root)
+ current_status = hashlib.md5()
+ module_src_dir = os.path.join(env_root, "doc", "module-source")
+ for (dirpath, dirnames, filenames) in os.walk(module_src_dir):
+ for filename in filenames:
+ if filename.endswith(".md"):
+ current_status.update(filename)
+ current_status.update(str(os.path.getmtime(os.path.join(dirpath, filename))))
+ guide_src_dir = os.path.join(docs_dir, "dev-guide-source")
+ for (dirpath, dirnames, filenames) in os.walk(guide_src_dir):
+ for filename in filenames:
+ if filename.endswith(".md"):
+ current_status.update(filename)
+ current_status.update(str(os.path.getmtime(os.path.join(dirpath, filename))))
+ package_dir = os.path.join(env_root, "packages")
+ for (dirpath, dirnames, filenames) in os.walk(package_dir):
+ for filename in filenames:
+ if filename.endswith(".md"):
+ current_status.update(filename)
+ current_status.update(str(os.path.getmtime(os.path.join(dirpath, filename))))
+ base_html_file = os.path.join(docs_dir, "static-files", "base.html")
+ current_status.update(base_html_file)
+ current_status.update(str(os.path.getmtime(os.path.join(dirpath, base_html_file))))
+ return current_status.digest()
+
+def generate_docs_from_scratch(env_root, version, base_url):
+ docs_dir = get_sdk_docs_path(env_root)
+ web_docs = webdocs.WebDocs(env_root, version, base_url)
+ must_rewrite_links = True
+ if base_url:
+ must_rewrite_links = False
+ clean_generated_docs(docs_dir)
+
+ # py2.5 doesn't have ignore=, so we delete tempfiles afterwards. If we
+ # required >=py2.6, we could use ignore=shutil.ignore_patterns("*~")
+ for (dirpath, dirnames, filenames) in os.walk(docs_dir):
+ for n in filenames:
+ if n.endswith("~"):
+ os.unlink(os.path.join(dirpath, n))
+
+ # generate api docs for all modules
+ if not os.path.exists(os.path.join(docs_dir, "modules")):
+ os.mkdir(os.path.join(docs_dir, "modules"))
+ module_root = os.sep.join([env_root, "doc", "module-source"])
+ module_list = get_module_list(env_root)
+ [write_module_doc(env_root, web_docs, module_info, must_rewrite_links) for module_info in module_list]
+
+ # generate third-party module index
+ third_party_index_file = os.sep.join([env_root, "doc", "module-source", "third-party-modules.md"])
+ third_party_module_list = [module_info for module_info in module_list if module_info.level() == "third-party"]
+ write_module_index(env_root, web_docs, third_party_index_file, third_party_module_list, must_rewrite_links)
+
+
+ # generate high-level module index
+ high_level_index_file = os.sep.join([env_root, "doc", "module-source", "high-level-modules.md"])
+ high_level_module_list = [module_info for module_info in module_list if module_info.level() == "high"]
+ write_module_index(env_root, web_docs, high_level_index_file, high_level_module_list, must_rewrite_links)
+
+ # generate low-level module index
+ low_level_index_file = os.sep.join([env_root, "doc", "module-source", "low-level-modules.md"])
+ low_level_module_list = [module_info for module_info in module_list if module_info.level() == "low"]
+ write_module_index(env_root, web_docs, low_level_index_file, low_level_module_list, must_rewrite_links)
+
+ # generate dev-guide docs
+ devguide_list = get_devguide_list(env_root)
+ [write_devguide_doc(env_root, web_docs, devguide_info, must_rewrite_links) for devguide_info in devguide_list]
+
+ # make /md/dev-guide/welcome.html the top level index file
+ doc_html = web_docs.create_guide_page(os.path.join(docs_dir, 'dev-guide-source', 'index.md'))
+ write_file(env_root, doc_html, docs_dir, 'index', False)
+
+def write_module_index(env_root, web_docs, source_file, module_list, must_rewrite_links):
+ doc_html = web_docs.create_module_index(source_file, module_list)
+ base_filename, extension = os.path.splitext(os.path.basename(source_file))
+ destination_path = os.sep.join([env_root, "doc", "modules"])
+ write_file(env_root, doc_html, destination_path, base_filename, must_rewrite_links)
+
+def write_module_doc(env_root, web_docs, module_info, must_rewrite_links):
+ doc_html = web_docs.create_module_page(module_info.source_path_and_filename())
+ write_file(env_root, doc_html, module_info.destination_path(), module_info.base_filename(), must_rewrite_links)
+
+def write_devguide_doc(env_root, web_docs, devguide_info, must_rewrite_links):
+ doc_html = web_docs.create_guide_page(devguide_info.source_path_and_filename())
+ write_file(env_root, doc_html, devguide_info.destination_path(), devguide_info.base_filename(), must_rewrite_links)
+
+def write_file(env_root, doc_html, dest_dir, filename, must_rewrite_links):
+ if not os.path.exists(dest_dir):
+ os.makedirs(dest_dir)
+ dest_path_html = os.path.join(dest_dir, filename) + ".html"
+ replace_file(env_root, dest_path_html, doc_html, must_rewrite_links)
+ return dest_path_html
+
+def replace_file(env_root, dest_path, file_contents, must_rewrite_links):
+ if os.path.exists(dest_path):
+ os.remove(dest_path)
+ # before we copy the final version, we'll rewrite the links
+ # I'll do this last, just because we know definitely what the dest_path is at this point
+ if must_rewrite_links and dest_path.endswith(".html"):
+ file_contents = rewrite_links(env_root, get_sdk_docs_path(env_root), file_contents, dest_path)
+ open(dest_path, "w").write(file_contents)
+
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/docs/linkrewriter.py b/tools/addon-sdk-1.12/python-lib/cuddlefish/docs/linkrewriter.py
new file mode 100644
index 0000000..11b0f11
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/docs/linkrewriter.py
@@ -0,0 +1,78 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import os
+import sys
+import HTMLParser
+import urlparse
+
+def rewrite_links(env_root, sdk_docs_path, page, dest_path):
+ dest_path_depth = len(dest_path.split(os.sep)) -1 # because dest_path includes filename
+ docs_root_depth = len(sdk_docs_path.split(os.sep))
+ relative_depth = dest_path_depth - docs_root_depth
+ linkRewriter = LinkRewriter("../" * relative_depth)
+ return linkRewriter.rewrite_links(page)
+
+class LinkRewriter(HTMLParser.HTMLParser):
+ def __init__(self, link_prefix):
+ HTMLParser.HTMLParser.__init__(self)
+ self.stack = []
+ self.link_prefix = link_prefix
+
+ def rewrite_links(self, page):
+ self.feed(page)
+ self.close()
+ page = ''.join(self.stack)
+ self.stack = []
+ return page
+
+ def handle_decl(self, decl):
+ self.stack.append("<!" + decl + ">")
+
+ def handle_comment(self, decl):
+ self.stack.append("<!--" + decl + "-->")
+
+ def handle_starttag(self, tag, attrs):
+ self.stack.append(self.__html_start_tag(tag, self._rewrite_link(attrs)))
+
+ def handle_entityref(self, name):
+ self.stack.append("&" + name + ";")
+
+ def handle_endtag(self, tag):
+ self.stack.append(self.__html_end_tag(tag))
+
+ def handle_startendtag(self, tag, attrs):
+ self.stack.append(self.__html_startend_tag(tag, self._rewrite_link(attrs)))
+
+ def _update_attribute(self, attr_name, attrs):
+ attr_value = attrs.get(attr_name, '')
+ if attr_value:
+ parsed = urlparse.urlparse(attr_value)
+ if not parsed.scheme:
+ attrs[attr_name] = self.link_prefix + attr_value
+
+ def _rewrite_link(self, attrs):
+ attrs = dict(attrs)
+ self._update_attribute('href', attrs)
+ self._update_attribute('src', attrs)
+ self._update_attribute('action', attrs)
+ return attrs
+
+ def handle_data(self, data):
+ self.stack.append(data)
+
+ def __html_start_tag(self, tag, attrs):
+ return '<%s%s>' % (tag, self.__html_attrs(attrs))
+
+ def __html_startend_tag(self, tag, attrs):
+ return '<%s%s/>' % (tag, self.__html_attrs(attrs))
+
+ def __html_end_tag(self, tag):
+ return '</%s>' % (tag)
+
+ def __html_attrs(self, attrs):
+ _attrs = ''
+ if attrs:
+ _attrs = ' %s' % (' '.join([('%s="%s"' % (k,v)) for k,v in dict(attrs).iteritems()]))
+ return _attrs
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/docs/renderapi.readme.md b/tools/addon-sdk-1.12/python-lib/cuddlefish/docs/renderapi.readme.md
new file mode 100644
index 0000000..627c2a6
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/docs/renderapi.readme.md
@@ -0,0 +1,210 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+
+This document describes the structure of the HTML generated by the renderapi.py
+tool, both for use in the API docs shown by "cfx docs" and as exported by
+"cfx sdocs". The particular HTML id and class attributes embedded in the files,
+as well as their organization, represent the interface between the tool and any
+front-end code wanting to style the docs in some particular way.
+
+renderapi generates two sorts of files:
+
+- a file called "<module-name>.div": this is the contents of the parsed
+Markdown file rendered inside a well-defined DIV tag
+
+- a file called "<module-name>.html": this is the DIV from above inserted into
+a simple HTML template that references a sample CSS file which styles the
+contents of the DIV. This CSS file is the same as the one used by the SDK
+itself.
+
+DIV tags
+--------
+The following class and id attributes are used in the DIV:
+
+renderapi uses a number of class attributes and a single id attribute in the DIV:
+
+id attribute <module_name>"_module_api_docs"
+class attribute "api_reference"
+class attribute "module_api_docs"
+class attribute "api_header"
+class attribute "api_name"
+class attribute "api_component_group"
+class attribute "api_component"
+class attribute "datatype"
+class attribute "returns"
+class attribute "parameter_set"
+class attribute "module_description"
+
+DIV structure
+-------------
+The top level DIV is marked with the id attribute and the "module_api_docs" class
+attribute:
+
+ <div id='tabs_module_api_docs' class='module_api_docs'>
+ //module doc contents
+ </div>
+
+
+Inside this:
+
+- the first item is an <h1> heading containing the name of the module:
+
+- all "markdown" hunks (that is, all descriptive text not occurring
+inside <api></api> tags) are rendered inside a DIV marked with the
+"module-description" class attribute
+
+- all <api></api> content is rendered, enclosed in a single tag marked
+with the "api_reference" class attribute:
+
+ <div id='tabs_module_api_docs' class='module_api_docs'>
+ <div class='module_description'>
+ //descriptions
+ </div>
+ <div class='api_reference'>
+ //api reference
+ </div>
+ </div>
+
+If there is no <api></api> content, then the "api-reference" section is absent.
+
+### API Reference structure ###
+
+The first item in API reference is an <h2> heading title marked with the
+"api_header" attribute. This might have the text content "API Reference"
+(but you should not rely on that):
+
+ <div class='api_reference'>
+
+ <h2 class='api_header'>API Reference</h2>
+
+ //api contents
+
+ </div>
+
+After the title come one or more component groups.
+
+#### Component Group ####
+
+A component group is marked with the "api_component_group" attribute. The
+component group is a collection of some sort of component: for example, a group
+of classes, a group of functions, or a group of events.
+
+Each component group starts off with a header marked with the
+"api_header" attribute and is followed by one or more sections marked with the
+"api_component" attribute.
+At the top level (that is, when they are directly under the "API Reference"
+heading), the "api_header" items are <h3> headings, otherwise they are divs.
+
+ <div class='api_reference'>
+
+ <h2 class='api_header'>API Reference</h2>
+
+ <div class='api_component_group'>
+
+ <h3 class='api_header'>Classes</h3>
+
+ <div class='api_component'>
+ // the first class
+ </div>
+
+ <div class='api_component'>
+ // another class
+ </div>
+
+ </div>
+
+ <div class='api_component_group'>
+ //some different components
+
+ <h3 class='api_header'>Functions</h3>
+
+ <div class='api_component'>
+ the first function
+ </div>
+
+ <div class='api_component'>
+ another function
+ </div>
+
+ </div>
+
+ </div>
+
+#### Component ####
+
+API components represent actual objects in the API like classes, functions,
+properties and events.
+
+Each component starts with a section marked with the
+"api_name" tag, which includes the name of the component in the API: for
+example "postMessage(message)".
+
+Components at the top level (i.e., directly under h3 headings) are <h4>
+headings, otherwise they are divs.
+
+After the name, the component's contents are listed. Different sorts of
+components may have different sorts of contents: for example, a function might
+have parameters. If the component is composite then it may contain its own
+component group. For example, a class may contain methods and properties,
+which might be grouped together.
+
+ <div class='api_component'>
+
+ <h4 class='api_name'>Panel</h4>
+
+ <div class='api_component_group'>
+
+ <div class='api_header'>
+ Methods
+ </div>
+
+ <div class='api_component'>
+ show()
+ </div>
+
+ </div>
+
+ </div>
+
+Other attributes
+-----------------------------
+
+### Datatype ###
+All primitive data types, like "string" and "number", are marked with the
+"datatype" class attribute:
+
+ <div class="api_component">
+
+ <div class="api_name">
+ label : <span class="datatype">string</span>
+ </div>
+
+ <p>A required string description of the widget used for accessibility,
+ title bars, and error reporting.</p>
+
+ </div>
+
+### Returns ###
+
+Functions mark return values with the "returns" class attribute.
+
+ <div class="api_component">
+
+ <div class="api_name">
+ get()
+ </div>
+
+ Make a `GET` request.
+
+ <div class="returns">
+ Returns: <span class="datatype">Request</span>
+ </div>
+
+ </div>
+
+### Parameter_set ###
+
+Functions that take parameters mark them with the parameter_set class
+attribute.
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/docs/webdocs.py b/tools/addon-sdk-1.12/python-lib/cuddlefish/docs/webdocs.py
new file mode 100755
index 0000000..f761282
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/docs/webdocs.py
@@ -0,0 +1,106 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import os, re, errno
+import markdown
+import cgi
+
+from cuddlefish import packaging
+from cuddlefish.docs import apirenderer
+from cuddlefish._version import get_versions
+from documentationitem import get_module_list
+
+INDEX_PAGE = '/doc/static-files/base.html'
+BASE_URL_INSERTION_POINT = '<base '
+VERSION_INSERTION_POINT = '<div id="version">'
+MODULE_INDEX_INSERTION_POINT = '<ul id="module-index">'
+THIRD_PARTY_MODULE_SUMMARIES = '<ul id="third-party-module-summaries">'
+HIGH_LEVEL_MODULE_SUMMARIES = '<ul id="high-level-module-summaries">'
+LOW_LEVEL_MODULE_SUMMARIES = '<ul id="low-level-module-summaries">'
+CONTENT_ID = '<div id="main-content">'
+TITLE_ID = '<title>'
+DEFAULT_TITLE = 'Add-on SDK Documentation'
+
+def tag_wrap(text, tag, attributes={}):
+ result = '\n<' + tag
+ for name in attributes.keys():
+ result += ' ' + name + '=' + '"' + attributes[name] + '"'
+ result +='>' + text + '</'+ tag + '>\n'
+ return result
+
+def insert_after(target, insertion_point_id, text_to_insert):
+ insertion_point = target.find(insertion_point_id) + len(insertion_point_id)
+ return target[:insertion_point] + text_to_insert + target[insertion_point:]
+
+class WebDocs(object):
+ def __init__(self, root, version=get_versions()["version"], base_url = None):
+ self.root = root
+ self.version = version
+ self.pkg_cfg = packaging.build_pkg_cfg(root)
+ self.packages_json = packaging.build_pkg_index(self.pkg_cfg)
+ self.base_page = self._create_base_page(root, base_url)
+
+ def create_guide_page(self, path):
+ md_content = unicode(open(path, 'r').read(), 'utf8')
+ guide_content = markdown.markdown(md_content)
+ return self._create_page(guide_content)
+
+ def create_module_index(self, path, module_list):
+ md_content = unicode(open(path, 'r').read(), 'utf8')
+ index_content = markdown.markdown(md_content)
+ module_list_content = self._make_module_text(module_list)
+ index_content = insert_after(index_content, MODULE_INDEX_INSERTION_POINT, module_list_content)
+ return self._create_page(index_content)
+
+ def create_module_page(self, path):
+ module_content = apirenderer.md_to_div(path)
+ return self._create_page(module_content)
+
+ def _create_page(self, page_content):
+ page = self._insert_title(self.base_page, page_content)
+ page = insert_after(page, CONTENT_ID, page_content)
+ return page.encode('utf8')
+
+ def _make_module_text(self, module_list):
+ module_text = ''
+ for module in module_list:
+ module_link = tag_wrap(module.name(), 'a', \
+ {'href': "/".join(["modules", module.relative_url()])})
+ module_list_item = tag_wrap(module_link, "li")
+ module_text += module_list_item
+ return module_text
+
+ def _create_base_page(self, root, base_url):
+ base_page = unicode(open(root + INDEX_PAGE, 'r').read(), 'utf8')
+ if base_url:
+ base_tag = 'href="' + base_url + '"'
+ base_page = insert_after(base_page, BASE_URL_INSERTION_POINT, base_tag)
+ base_page = insert_after(base_page, VERSION_INSERTION_POINT, "Version " + self.version)
+ module_list = get_module_list(root)
+
+ third_party_module_list = [module_info for module_info in module_list if module_info.level() == "third-party"]
+ third_party_module_text = self._make_module_text(third_party_module_list)
+ base_page = insert_after(base_page, \
+ THIRD_PARTY_MODULE_SUMMARIES, third_party_module_text)
+
+ high_level_module_list = [module_info for module_info in module_list if module_info.level() == "high"]
+ high_level_module_text = self._make_module_text(high_level_module_list)
+ base_page = insert_after(base_page, \
+ HIGH_LEVEL_MODULE_SUMMARIES, high_level_module_text)
+
+ low_level_module_list = [module_info for module_info in module_list if module_info.level() == "low"]
+ low_level_module_text = self._make_module_text(low_level_module_list)
+ base_page = insert_after(base_page, \
+ LOW_LEVEL_MODULE_SUMMARIES, low_level_module_text)
+ return base_page
+
+ def _insert_title(self, target, content):
+ match = re.search('<h1>.*</h1>', content)
+ if match:
+ title = match.group(0)[len('<h1>'):-len('</h1>')] + ' - ' + \
+ DEFAULT_TITLE
+ else:
+ title = DEFAULT_TITLE
+ target = insert_after(target, TITLE_ID, title)
+ return target
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/manifest.py b/tools/addon-sdk-1.12/python-lib/cuddlefish/manifest.py
new file mode 100644
index 0000000..b1f9367
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/manifest.py
@@ -0,0 +1,749 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+import os, sys, re, hashlib
+import simplejson as json
+SEP = os.path.sep
+from cuddlefish.util import filter_filenames, filter_dirnames
+
+# Load new layout mapping hashtable
+path = os.path.join(os.environ.get('CUDDLEFISH_ROOT'), "mapping.json")
+data = open(path, 'r').read()
+NEW_LAYOUT_MAPPING = json.loads(data)
+
+def js_zipname(packagename, modulename):
+ return "%s-lib/%s.js" % (packagename, modulename)
+def docs_zipname(packagename, modulename):
+ return "%s-docs/%s.md" % (packagename, modulename)
+def datamap_zipname(packagename):
+ return "%s-data.json" % packagename
+def datafile_zipname(packagename, datapath):
+ return "%s-data/%s" % (packagename, datapath)
+
+def to_json(o):
+ return json.dumps(o, indent=1).encode("utf-8")+"\n"
+
+class ModuleNotFoundError(Exception):
+ def __init__(self, requirement_type, requirement_name,
+ used_by, line_number, looked_in):
+ Exception.__init__(self)
+ self.requirement_type = requirement_type # "require" or "define"
+ self.requirement_name = requirement_name # string, what they require()d
+ self.used_by = used_by # string, full path to module which did require()
+ self.line_number = line_number # int, 1-indexed line number of first require()
+ self.looked_in = looked_in # list of full paths to potential .js files
+ def __str__(self):
+ what = "%s(%s)" % (self.requirement_type, self.requirement_name)
+ where = self.used_by
+ if self.line_number is not None:
+ where = "%s:%d" % (self.used_by, self.line_number)
+ searched = "Looked for it in:\n %s\n" % "\n ".join(self.looked_in)
+ return ("ModuleNotFoundError: unable to satisfy: %s from\n"
+ " %s:\n" % (what, where)) + searched
+
+class BadModuleIdentifier(Exception):
+ pass
+class BadSection(Exception):
+ pass
+class UnreachablePrefixError(Exception):
+ pass
+
+class ManifestEntry:
+ def __init__(self):
+ self.docs_filename = None
+ self.docs_hash = None
+ self.requirements = {}
+ self.datamap = None
+
+ def get_path(self):
+ path = "%s/%s/%s" % \
+ (self.packageName, self.sectionName, self.moduleName)
+ if not path.endswith(".js"):
+ path += ".js"
+ return path
+
+ def get_entry_for_manifest(self):
+ entry = { "packageName": self.packageName,
+ "sectionName": self.sectionName,
+ "moduleName": self.moduleName,
+ "jsSHA256": self.js_hash,
+ "docsSHA256": self.docs_hash,
+ "requirements": {},
+ }
+ for req in self.requirements:
+ if isinstance(self.requirements[req], ManifestEntry):
+ them = self.requirements[req] # this is another ManifestEntry
+ them_path = them.get_path()
+ entry["requirements"][req] = {"path": them_path}
+ else:
+ # something magic. The manifest entry indicates that they're
+ # allowed to require() it
+ entry["requirements"][req] = self.requirements[req]
+ assert isinstance(entry["requirements"][req], dict)
+ return entry
+
+ def add_js(self, js_filename):
+ self.js_filename = js_filename
+ self.js_hash = hash_file(js_filename)
+ def add_docs(self, docs_filename):
+ self.docs_filename = docs_filename
+ self.docs_hash = hash_file(docs_filename)
+ def add_requirement(self, reqname, reqdata):
+ self.requirements[reqname] = reqdata
+ def add_data(self, datamap):
+ self.datamap = datamap
+
+ def get_js_zipname(self):
+ return js_zipname(self.packagename, self.modulename)
+ def get_docs_zipname(self):
+ if self.docs_hash:
+ return docs_zipname(self.packagename, self.modulename)
+ return None
+ # self.js_filename
+ # self.docs_filename
+
+
+def hash_file(fn):
+ return hashlib.sha256(open(fn,"rb").read()).hexdigest()
+
+def get_datafiles(datadir):
+ # yields pathnames relative to DATADIR, ignoring some files
+ for dirpath, dirnames, filenames in os.walk(datadir):
+ filenames = list(filter_filenames(filenames))
+ # this tells os.walk to prune the search
+ dirnames[:] = filter_dirnames(dirnames)
+ for filename in filenames:
+ fullname = os.path.join(dirpath, filename)
+ assert fullname.startswith(datadir+SEP), "%s%s not in %s" % (datadir, SEP, fullname)
+ yield fullname[len(datadir+SEP):]
+
+
+class DataMap:
+ # one per package
+ def __init__(self, pkg):
+ self.pkg = pkg
+ self.name = pkg.name
+ self.files_to_copy = []
+ datamap = {}
+ datadir = os.path.join(pkg.root_dir, "data")
+ for dataname in get_datafiles(datadir):
+ absname = os.path.join(datadir, dataname)
+ zipname = datafile_zipname(pkg.name, dataname)
+ datamap[dataname] = hash_file(absname)
+ self.files_to_copy.append( (zipname, absname) )
+ self.data_manifest = to_json(datamap)
+ self.data_manifest_hash = hashlib.sha256(self.data_manifest).hexdigest()
+ self.data_manifest_zipname = datamap_zipname(pkg.name)
+ self.data_uri_prefix = "%s/data/" % (self.name)
+
+class BadChromeMarkerError(Exception):
+ pass
+
+class ModuleInfo:
+ def __init__(self, package, section, name, js, docs):
+ self.package = package
+ self.section = section
+ self.name = name
+ self.js = js
+ self.docs = docs
+
+ def __hash__(self):
+ return hash( (self.package.name, self.section, self.name,
+ self.js, self.docs) )
+ def __eq__(self, them):
+ if them.__class__ is not self.__class__:
+ return False
+ if ((them.package.name, them.section, them.name, them.js, them.docs) !=
+ (self.package.name, self.section, self.name, self.js, self.docs) ):
+ return False
+ return True
+
+ def __repr__(self):
+ return "ModuleInfo [%s %s %s] (%s, %s)" % (self.package.name,
+ self.section,
+ self.name,
+ self.js, self.docs)
+
+class ManifestBuilder:
+ def __init__(self, target_cfg, pkg_cfg, deps, extra_modules,
+ stderr=sys.stderr):
+ self.manifest = {} # maps (package,section,module) to ManifestEntry
+ self.target_cfg = target_cfg # the entry point
+ self.pkg_cfg = pkg_cfg # all known packages
+ self.deps = deps # list of package names to search
+ self.used_packagenames = set()
+ self.stderr = stderr
+ self.extra_modules = extra_modules
+ self.modules = {} # maps ModuleInfo to URI in self.manifest
+ self.datamaps = {} # maps package name to DataMap instance
+ self.files = [] # maps manifest index to (absfn,absfn) js/docs pair
+ self.test_modules = [] # for runtime
+
+ def build(self, scan_tests, test_filter_re):
+ # process the top module, which recurses to process everything it
+ # reaches
+ if "main" in self.target_cfg:
+ top_mi = self.find_top(self.target_cfg)
+ top_me = self.process_module(top_mi)
+ self.top_path = top_me.get_path()
+ self.datamaps[self.target_cfg.name] = DataMap(self.target_cfg)
+ if scan_tests:
+ mi = self._find_module_in_package("addon-sdk", "lib", "sdk/test/runner", [])
+ self.process_module(mi)
+ # also scan all test files in all packages that we use. By making
+ # a copy of self.used_packagenames first, we refrain from
+ # processing tests in packages that our own tests depend upon. If
+ # we're running tests for package A, and either modules in A or
+ # tests in A depend upon modules from package B, we *don't* want
+ # to run tests for package B.
+ test_modules = []
+ dirnames = self.target_cfg["tests"]
+ if isinstance(dirnames, basestring):
+ dirnames = [dirnames]
+ dirnames = [os.path.join(self.target_cfg.root_dir, d)
+ for d in dirnames]
+ for d in dirnames:
+ for filename in os.listdir(d):
+ if filename.startswith("test-") and filename.endswith(".js"):
+ testname = filename[:-3] # require(testname)
+ if test_filter_re:
+ if not re.search(test_filter_re, testname):
+ continue
+ tmi = ModuleInfo(self.target_cfg, "tests", testname,
+ os.path.join(d, filename), None)
+ # scan the test's dependencies
+ tme = self.process_module(tmi)
+ test_modules.append( (testname, tme) )
+ # also add it as an artificial dependency of unit-test-finder, so
+ # the runtime dynamic load can work.
+ test_finder = self.get_manifest_entry("addon-sdk", "lib",
+ "sdk/deprecated/unit-test-finder")
+ for (testname,tme) in test_modules:
+ test_finder.add_requirement(testname, tme)
+ # finally, tell the runtime about it, so they won't have to
+ # search for all tests. self.test_modules will be passed
+ # through the harness-options.json file in the
+ # .allTestModules property.
+ self.test_modules.append(testname)
+
+ # include files used by the loader
+ for em in self.extra_modules:
+ (pkgname, section, modname, js) = em
+ mi = ModuleInfo(self.pkg_cfg.packages[pkgname], section, modname,
+ js, None)
+ self.process_module(mi)
+
+
+ def get_module_entries(self):
+ return frozenset(self.manifest.values())
+ def get_data_entries(self):
+ return frozenset(self.datamaps.values())
+
+ def get_used_packages(self):
+ used = set()
+ for index in self.manifest:
+ (package, section, module) = index
+ used.add(package)
+ return sorted(used)
+
+ def get_used_files(self):
+ # returns all .js files that we reference, plus data/ files. You will
+ # need to add the loader, off-manifest files that it needs, and
+ # generated metadata.
+ for datamap in self.datamaps.values():
+ for (zipname, absname) in datamap.files_to_copy:
+ yield absname
+
+ for me in self.get_module_entries():
+ yield me.js_filename
+
+ def get_all_test_modules(self):
+ return self.test_modules
+
+ def get_harness_options_manifest(self):
+ manifest = {}
+ for me in self.get_module_entries():
+ path = me.get_path()
+ manifest[path] = me.get_entry_for_manifest()
+ return manifest
+
+ def get_manifest_entry(self, package, section, module):
+ index = (package, section, module)
+ if index not in self.manifest:
+ m = self.manifest[index] = ManifestEntry()
+ m.packageName = package
+ m.sectionName = section
+ m.moduleName = module
+ self.used_packagenames.add(package)
+ return self.manifest[index]
+
+ def uri_name_from_path(self, pkg, fn):
+ # given a filename like .../pkg1/lib/bar/foo.js, and a package
+ # specification (with a .root_dir like ".../pkg1" and a .lib list of
+ # paths where .lib[0] is like "lib"), return the appropriate NAME
+ # that can be put into a URI like resource://JID-pkg1-lib/NAME . This
+ # will throw an exception if the file is outside of the lib/
+ # directory, since that means we can't construct a URI that points to
+ # it.
+ #
+ # This should be a lot easier, and shouldn't fail when the file is in
+ # the root of the package. Both should become possible when the XPI
+ # is rearranged and our URI scheme is simplified.
+ fn = os.path.abspath(fn)
+ pkglib = pkg.lib[0]
+ libdir = os.path.abspath(os.path.join(pkg.root_dir, pkglib))
+ # AARGH, section and name! we need to reverse-engineer a
+ # ModuleInfo instance that will produce a URI (in the form
+ # PREFIX/PKGNAME-SECTION/JS) that will map to the existing file.
+ # Until we fix URI generation to get rid of "sections", this is
+ # limited to files in the same .directories.lib as the rest of
+ # the package uses. So if the package's main files are in lib/,
+ # but the main.js is in the package root, there is no URI we can
+ # construct that will point to it, and we must fail.
+ #
+ # This will become much easier (and the failure case removed)
+ # when we get rid of sections and change the URIs to look like
+ # (PREFIX/PKGNAME/PATH-TO-JS).
+
+ # AARGH 2, allowing .lib to be a list is really getting in the
+ # way. That needs to go away eventually too.
+ if not fn.startswith(libdir):
+ raise UnreachablePrefixError("Sorry, but the 'main' file (%s) in package %s is outside that package's 'lib' directory (%s), so I cannot construct a URI to reach it."
+ % (fn, pkg.name, pkglib))
+ name = fn[len(libdir):].lstrip(SEP)[:-len(".js")]
+ return name
+
+
+ def parse_main(self, root_dir, main, check_lib_dir=None):
+ # 'main' can be like one of the following:
+ # a: ./lib/main.js b: ./lib/main c: lib/main
+ # we require it to be a path to the file, though, and ignore the
+ # .directories stuff. So just "main" is insufficient if you really
+ # want something in a "lib/" subdirectory.
+ if main.endswith(".js"):
+ main = main[:-len(".js")]
+ if main.startswith("./"):
+ main = main[len("./"):]
+ # package.json must always use "/", but on windows we'll replace that
+ # with "\" before using it as an actual filename
+ main = os.sep.join(main.split("/"))
+ paths = [os.path.join(root_dir, main+".js")]
+ if check_lib_dir is not None:
+ paths.append(os.path.join(root_dir, check_lib_dir, main+".js"))
+ return paths
+
+ def find_top_js(self, target_cfg):
+ for libdir in target_cfg.lib:
+ for n in self.parse_main(target_cfg.root_dir, target_cfg.main,
+ libdir):
+ if os.path.exists(n):
+ return n
+ raise KeyError("unable to find main module '%s.js' in top-level package" % target_cfg.main)
+
+ def find_top(self, target_cfg):
+ top_js = self.find_top_js(target_cfg)
+ n = os.path.join(target_cfg.root_dir, "README.md")
+ if os.path.exists(n):
+ top_docs = n
+ else:
+ top_docs = None
+ name = self.uri_name_from_path(target_cfg, top_js)
+ return ModuleInfo(target_cfg, "lib", name, top_js, top_docs)
+
+ def process_module(self, mi):
+ pkg = mi.package
+ #print "ENTERING", pkg.name, mi.name
+ # mi.name must be fully-qualified
+ assert (not mi.name.startswith("./") and
+ not mi.name.startswith("../"))
+ # create and claim the manifest row first
+ me = self.get_manifest_entry(pkg.name, mi.section, mi.name)
+
+ me.add_js(mi.js)
+ if mi.docs:
+ me.add_docs(mi.docs)
+
+ js_lines = open(mi.js,"r").readlines()
+ requires, problems, locations = scan_module(mi.js,js_lines,self.stderr)
+ if problems:
+ # the relevant instructions have already been written to stderr
+ raise BadChromeMarkerError()
+
+ # We update our requirements on the way out of the depth-first
+ # traversal of the module graph
+
+ for reqname in sorted(requires.keys()):
+ # If requirement is chrome or a pseudo-module (starts with @) make
+ # path a requirement name.
+ if reqname == "chrome" or reqname.startswith("@"):
+ me.add_requirement(reqname, {"path": reqname})
+ else:
+ # when two modules require() the same name, do they get a
+ # shared instance? This is a deep question. For now say yes.
+
+ # find_req_for() returns an entry to put in our
+ # 'requirements' dict, and will recursively process
+ # everything transitively required from here. It will also
+ # populate the self.modules[] cache. Note that we must
+ # tolerate cycles in the reference graph.
+ looked_in = [] # populated by subroutines
+ them_me = self.find_req_for(mi, reqname, looked_in)
+ if them_me is None:
+ if mi.section == "tests":
+ # tolerate missing modules in tests, because
+ # test-securable-module.js, and the modules/red.js
+ # that it imports, both do that intentionally
+ continue
+ lineno = locations.get(reqname) # None means define()
+ if lineno is None:
+ reqtype = "define"
+ else:
+ reqtype = "require"
+ err = ModuleNotFoundError(reqtype, reqname,
+ mi.js, lineno, looked_in)
+ raise err
+ else:
+ me.add_requirement(reqname, them_me)
+
+ return me
+ #print "LEAVING", pkg.name, mi.name
+
+ def find_req_for(self, from_module, reqname, looked_in):
+ # handle a single require(reqname) statement from from_module .
+ # Return a uri that exists in self.manifest
+ # Populate looked_in with places we looked.
+ def BAD(msg):
+ return BadModuleIdentifier(msg + " in require(%s) from %s" %
+ (reqname, from_module))
+
+ if not reqname:
+ raise BAD("no actual modulename")
+
+ # Allow things in tests/*.js to require both test code and real code.
+ # But things in lib/*.js can only require real code.
+ if from_module.section == "tests":
+ lookfor_sections = ["tests", "lib"]
+ elif from_module.section == "lib":
+ lookfor_sections = ["lib"]
+ else:
+ raise BadSection(from_module.section)
+ modulename = from_module.name
+
+ #print " %s require(%s))" % (from_module, reqname)
+
+ if reqname.startswith("./") or reqname.startswith("../"):
+ # 1: they want something relative to themselves, always from
+ # their own package
+ them = modulename.split("/")[:-1]
+ bits = reqname.split("/")
+ while bits[0] in (".", ".."):
+ if not bits:
+ raise BAD("no actual modulename")
+ if bits[0] == "..":
+ if not them:
+ raise BAD("too many ..")
+ them.pop()
+ bits.pop(0)
+ bits = them+bits
+ lookfor_pkg = from_module.package.name
+ lookfor_mod = "/".join(bits)
+ return self._get_module_from_package(lookfor_pkg,
+ lookfor_sections, lookfor_mod,
+ looked_in)
+
+ # non-relative import. Might be a short name (requiring a search
+ # through "library" packages), or a fully-qualified one.
+
+ # Search for a module in new layout.
+ # First normalize require argument in order to easily find a mapping
+ normalized = reqname
+ if normalized.endswith(".js"):
+ normalized = normalized[:-len(".js")]
+ if normalized.startswith("addon-kit/"):
+ normalized = normalized[len("addon-kit/"):]
+ if normalized.startswith("api-utils/"):
+ normalized = normalized[len("api-utils/"):]
+ if normalized in NEW_LAYOUT_MAPPING:
+ reqname = NEW_LAYOUT_MAPPING[normalized]
+
+ if "/" in reqname:
+ # 2: PKG/MOD: find PKG, look inside for MOD
+ bits = reqname.split("/")
+ lookfor_pkg = bits[0]
+ lookfor_mod = "/".join(bits[1:])
+ mi = self._get_module_from_package(lookfor_pkg,
+ lookfor_sections, lookfor_mod,
+ looked_in)
+ if mi: # caution, 0==None
+ return mi
+ else:
+ # 3: try finding PKG, if found, use its main.js entry point
+ lookfor_pkg = reqname
+ mi = self._get_entrypoint_from_package(lookfor_pkg, looked_in)
+ if mi:
+ return mi
+
+ # 4: search packages for MOD or MODPARENT/MODCHILD. We always search
+ # their own package first, then the list of packages defined by their
+ # .dependencies list
+ from_pkg = from_module.package.name
+ return self._search_packages_for_module(from_pkg,
+ lookfor_sections, reqname,
+ looked_in)
+
+ def _handle_module(self, mi):
+ if not mi:
+ return None
+
+ # we tolerate cycles in the reference graph, which means we need to
+ # populate the self.modules cache before recursing into
+ # process_module() . We must also check the cache first, so recursion
+ # can terminate.
+ if mi in self.modules:
+ return self.modules[mi]
+
+ # this creates the entry
+ new_entry = self.get_manifest_entry(mi.package.name, mi.section, mi.name)
+ # and populates the cache
+ self.modules[mi] = new_entry
+ self.process_module(mi)
+ return new_entry
+
+ def _get_module_from_package(self, pkgname, sections, modname, looked_in):
+ if pkgname not in self.pkg_cfg.packages:
+ return None
+ mi = self._find_module_in_package(pkgname, sections, modname,
+ looked_in)
+ return self._handle_module(mi)
+
+ def _get_entrypoint_from_package(self, pkgname, looked_in):
+ if pkgname not in self.pkg_cfg.packages:
+ return None
+ pkg = self.pkg_cfg.packages[pkgname]
+ main = pkg.get("main", None)
+ if not main:
+ return None
+ for js in self.parse_main(pkg.root_dir, main):
+ looked_in.append(js)
+ if os.path.exists(js):
+ section = "lib"
+ name = self.uri_name_from_path(pkg, js)
+ docs = None
+ mi = ModuleInfo(pkg, section, name, js, docs)
+ return self._handle_module(mi)
+ return None
+
+ def _search_packages_for_module(self, from_pkg, sections, reqname,
+ looked_in):
+ searchpath = [] # list of package names
+ searchpath.append(from_pkg) # search self first
+ us = self.pkg_cfg.packages[from_pkg]
+ if 'dependencies' in us:
+ # only look in dependencies
+ searchpath.extend(us['dependencies'])
+ else:
+ # they didn't declare any dependencies (or they declared an empty
+ # list, but we'll treat that as not declaring one, because it's
+ # easier), so look in all deps, sorted alphabetically, so
+ # addon-kit comes first. Note that self.deps includes all
+ # packages found by traversing the ".dependencies" lists in each
+ # package.json, starting from the main addon package, plus
+ # everything added by --extra-packages
+ searchpath.extend(sorted(self.deps))
+ for pkgname in searchpath:
+ mi = self._find_module_in_package(pkgname, sections, reqname,
+ looked_in)
+ if mi:
+ return self._handle_module(mi)
+ return None
+
+ def _find_module_in_package(self, pkgname, sections, name, looked_in):
+ # require("a/b/c") should look at ...\a\b\c.js on windows
+ filename = os.sep.join(name.split("/"))
+ # normalize filename, make sure that we do not add .js if it already has
+ # it.
+ if not filename.endswith(".js"):
+ filename += ".js"
+ basename = filename[:-3]
+
+ pkg = self.pkg_cfg.packages[pkgname]
+ if isinstance(sections, basestring):
+ sections = [sections]
+ for section in sections:
+ for sdir in pkg.get(section, []):
+ js = os.path.join(pkg.root_dir, sdir, filename)
+ looked_in.append(js)
+ if os.path.exists(js):
+ docs = None
+ maybe_docs = os.path.join(pkg.root_dir, "docs",
+ basename+".md")
+ if section == "lib" and os.path.exists(maybe_docs):
+ docs = maybe_docs
+ return ModuleInfo(pkg, section, name, js, docs)
+ return None
+
+def build_manifest(target_cfg, pkg_cfg, deps, scan_tests,
+ test_filter_re=None, extra_modules=[]):
+ """
+ Perform recursive dependency analysis starting from entry_point,
+ building up a manifest of modules that need to be included in the XPI.
+ Each entry will map require() names to the URL of the module that will
+ be used to satisfy that dependency. The manifest will be used by the
+ runtime's require() code.
+
+ This returns a ManifestBuilder object, with two public methods. The
+ first, get_module_entries(), returns a set of ManifestEntry objects, each
+ of which can be asked for the following:
+
+ * its contribution to the harness-options.json '.manifest'
+ * the local disk name
+ * the name in the XPI at which it should be placed
+
+ The second is get_data_entries(), which returns a set of DataEntry
+ objects, each of which has:
+
+ * local disk name
+ * name in the XPI
+
+ note: we don't build the XPI here, but our manifest is passed to the
+ code which does, so it knows what to copy into the XPI.
+ """
+
+ mxt = ManifestBuilder(target_cfg, pkg_cfg, deps, extra_modules)
+ mxt.build(scan_tests, test_filter_re)
+ return mxt
+
+
+
+COMMENT_PREFIXES = ["//", "/*", "*", "dump("]
+
+REQUIRE_RE = r"(?<!goog[.])(?<![\'\"])\brequire\s*\(\s*[\'\"]([^\'\"]+?)[\'\"]\s*\)"
+#REQUIRE_RE = r"(?<![\'\"])require\s*\(\s*[\'\"]([^\'\"]+?)[\'\"]\s*\)"
+
+# detect the define idiom of the form:
+# define("module name", ["dep1", "dep2", "dep3"], function() {})
+# by capturing the contents of the list in a group.
+DEF_RE = re.compile(r"(require|define)\s*\(\s*([\'\"][^\'\"]+[\'\"]\s*,)?\s*\[([^\]]+)\]")
+
+# Out of the async dependencies, do not allow quotes in them.
+DEF_RE_ALLOWED = re.compile(r"^[\'\"][^\'\"]+[\'\"]$")
+
+def scan_requirements_with_grep(fn, lines):
+ requires = {}
+ first_location = {}
+ for (lineno0, line) in enumerate(lines):
+ for clause in line.split(";"):
+ clause = clause.strip()
+ iscomment = False
+ for commentprefix in COMMENT_PREFIXES:
+ if clause.startswith(commentprefix):
+ iscomment = True
+ if iscomment:
+ continue
+ mo = re.search(REQUIRE_RE, clause)
+ if mo:
+ modname = mo.group(1)
+ requires[modname] = {}
+ if modname not in first_location:
+ first_location[modname] = lineno0+1
+
+ # define() can happen across multiple lines, so join everyone up.
+ wholeshebang = "\n".join(lines)
+ for match in DEF_RE.finditer(wholeshebang):
+ # this should net us a list of string literals separated by commas
+ for strbit in match.group(3).split(","):
+ strbit = strbit.strip()
+ # There could be a trailing comma netting us just whitespace, so
+ # filter that out. Make sure that only string values with
+ # quotes around them are allowed, and no quotes are inside
+ # the quoted value.
+ if strbit and DEF_RE_ALLOWED.match(strbit):
+ modname = strbit[1:-1]
+ if modname not in ["exports"]:
+ requires[modname] = {}
+ # joining all the lines means we lose line numbers, so we
+ # can't fill first_location[]
+
+ return requires, first_location
+
+CHROME_ALIASES = [
+ (re.compile(r"Components\.classes"), "Cc"),
+ (re.compile(r"Components\.interfaces"), "Ci"),
+ (re.compile(r"Components\.utils"), "Cu"),
+ (re.compile(r"Components\.results"), "Cr"),
+ (re.compile(r"Components\.manager"), "Cm"),
+ ]
+OTHER_CHROME = re.compile(r"Components\.[a-zA-Z]")
+
+def scan_for_bad_chrome(fn, lines, stderr):
+ problems = False
+ old_chrome = set() # i.e. "Cc" when we see "Components.classes"
+ old_chrome_lines = [] # list of (lineno, line.strip()) tuples
+ for lineno,line in enumerate(lines):
+ # note: this scanner is not obligated to spot all possible forms of
+ # chrome access. The scanner is detecting voluntary requests for
+ # chrome. Runtime tools will enforce allowance or denial of access.
+ line = line.strip()
+ iscomment = False
+ for commentprefix in COMMENT_PREFIXES:
+ if line.startswith(commentprefix):
+ iscomment = True
+ break
+ if iscomment:
+ continue
+ old_chrome_in_this_line = set()
+ for (regexp,alias) in CHROME_ALIASES:
+ if regexp.search(line):
+ old_chrome_in_this_line.add(alias)
+ if not old_chrome_in_this_line:
+ if OTHER_CHROME.search(line):
+ old_chrome_in_this_line.add("components")
+ old_chrome.update(old_chrome_in_this_line)
+ if old_chrome_in_this_line:
+ old_chrome_lines.append( (lineno+1, line) )
+
+ if old_chrome:
+ print >>stderr, """
+The following lines from file %(fn)s:
+%(lines)s
+use 'Components' to access chrome authority. To do so, you need to add a
+line somewhat like the following:
+
+ const {%(needs)s} = require("chrome");
+
+Then you can use any shortcuts to its properties that you import from the
+'chrome' module ('Cc', 'Ci', 'Cm', 'Cr', and 'Cu' for the 'classes',
+'interfaces', 'manager', 'results', and 'utils' properties, respectively. And
+`components` for `Components` object itself).
+""" % { "fn": fn, "needs": ",".join(sorted(old_chrome)),
+ "lines": "\n".join([" %3d: %s" % (lineno,line)
+ for (lineno, line) in old_chrome_lines]),
+ }
+ problems = True
+ return problems
+
+def scan_module(fn, lines, stderr=sys.stderr):
+ filename = os.path.basename(fn)
+ requires, locations = scan_requirements_with_grep(fn, lines)
+ if filename == "cuddlefish.js":
+ # this is the loader: don't scan for chrome
+ problems = False
+ else:
+ problems = scan_for_bad_chrome(fn, lines, stderr)
+ return requires, problems, locations
+
+
+
+if __name__ == '__main__':
+ for fn in sys.argv[1:]:
+ requires, problems, locations = scan_module(fn, open(fn).readlines())
+ print
+ print "---", fn
+ if problems:
+ print "PROBLEMS"
+ sys.exit(1)
+ print "requires: %s" % (",".join(sorted(requires.keys())))
+ print "locations: %s" % locations
+
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/mobile-utils/bootstrap.js b/tools/addon-sdk-1.12/python-lib/cuddlefish/mobile-utils/bootstrap.js
new file mode 100644
index 0000000..752c962
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/mobile-utils/bootstrap.js
@@ -0,0 +1,55 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+const Cr = Components.results;
+
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+const DEBUG = false;
+
+let log = DEBUG ? dump : function (){};
+
+
+function startup(data, reason) {
+ // This code allow to make all stdIO work
+ try {
+ Components.utils.import("resource://gre/modules/ctypes.jsm");
+ let libdvm = ctypes.open("libdvm.so");
+ let dvmStdioConverterStartup;
+ // Starting with Android ICS, dalvik uses C++.
+ // So that the symbol isn't a simple C one
+ try {
+ dvmStdioConverterStartup = libdvm.declare("_Z24dvmStdioConverterStartupv", ctypes.default_abi, ctypes.bool);
+ }
+ catch(e) {
+ // Otherwise, before ICS, it was a pure C library
+ dvmStdioConverterStartup = libdvm.declare("dvmStdioConverterStartup", ctypes.default_abi, ctypes.void_t);
+ }
+ dvmStdioConverterStartup();
+ log("MU: console redirected to adb logcat.\n");
+ } catch(e) {
+ Cu.reportError("MU: unable to execute jsctype hack: "+e);
+ }
+
+ try {
+ let QuitObserver = {
+ observe: function (aSubject, aTopic, aData) {
+ Services.obs.removeObserver(QuitObserver, "quit-application", false);
+ dump("MU: APPLICATION-QUIT\n");
+ }
+ };
+ Services.obs.addObserver(QuitObserver, "quit-application", false);
+ log("MU: ready to watch firefox exit.\n");
+ } catch(e) {
+ log("MU: unable to register quit-application observer: " + e + "\n");
+ }
+}
+
+function install() {}
+function shutdown() {}
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/mobile-utils/install.rdf b/tools/addon-sdk-1.12/python-lib/cuddlefish/mobile-utils/install.rdf
new file mode 100644
index 0000000..0b81a0e
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/mobile-utils/install.rdf
@@ -0,0 +1,39 @@
+<?xml version="1.0"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+ <Description about="urn:mozilla:install-manifest">
+ <em:id>mobile-utils@mozilla.com</em:id>
+ <em:version>1.0</em:version>
+ <em:type>2</em:type>
+ <em:bootstrap>true</em:bootstrap>
+
+ <!-- Fennec-XUL -->
+ <em:targetApplication>
+ <Description>
+ <em:id>{a23983c0-fd0e-11dc-95ff-0800200c9a66}</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <!-- Fennec-NativeUI -->
+ <em:targetApplication>
+ <Description>
+ <em:id>{aa3c5121-dab2-40e2-81ca-7ea25febc110}</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <!-- Front End MetaData -->
+ <em:name>Mobile Addon-SDK utility addon</em:name>
+ <em:description>Allow better integration with cfx tool.</em:description>
+ <em:creator>Mozilla Corporation</em:creator>
+
+ </Description>
+</RDF>
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/options_defaults.py b/tools/addon-sdk-1.12/python-lib/cuddlefish/options_defaults.py
new file mode 100644
index 0000000..5931a26
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/options_defaults.py
@@ -0,0 +1,26 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+def parse_options_defaults(options, jetpack_id):
+ # this returns a unicode string
+ pref_list = []
+
+ for pref in options:
+ if ('value' in pref):
+ value = pref["value"]
+
+ if isinstance(value, float):
+ continue
+ elif isinstance(value, bool):
+ value = str(pref["value"]).lower()
+ elif isinstance(value, str): # presumably ASCII
+ value = "\"" + unicode(pref["value"]) + "\""
+ elif isinstance(value, unicode):
+ value = "\"" + pref["value"] + "\""
+ else:
+ value = str(pref["value"])
+
+ pref_list.append("pref(\"extensions." + jetpack_id + "." + pref["name"] + "\", " + value + ");")
+
+ return "\n".join(pref_list) + "\n"
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/options_xul.py b/tools/addon-sdk-1.12/python-lib/cuddlefish/options_xul.py
new file mode 100644
index 0000000..99fa1a4
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/options_xul.py
@@ -0,0 +1,98 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from xml.dom.minidom import Document
+
+VALID_PREF_TYPES = ['bool', 'boolint', 'integer', 'string', 'color', 'file',
+ 'directory', 'control', 'menulist', 'radio']
+
+class Error(Exception):
+ pass
+
+class BadPrefTypeError(Error):
+ pass
+
+class MissingPrefAttr(Error):
+ pass
+
+def validate_prefs(options):
+ for pref in options:
+ # Make sure there is a 'title'
+ if ("title" not in pref):
+ raise MissingPrefAttr("The '%s' pref requires a 'title'" % (pref["name"]))
+
+ # Make sure that the pref type is a valid inline pref type
+ if (pref["type"] not in VALID_PREF_TYPES):
+ raise BadPrefTypeError('%s is not a valid inline pref type' % (pref["type"]))
+
+ # Make sure the 'control' type has a 'label'
+ if (pref["type"] == "control"):
+ if ("label" not in pref):
+ raise MissingPrefAttr("The 'control' inline pref type requires a 'label'")
+
+ # Make sure the 'menulist' type has a 'menulist'
+ if (pref["type"] == "menulist" or pref["type"] == "radio"):
+ if ("options" not in pref):
+ raise MissingPrefAttr("The 'menulist' and the 'radio' inline pref types requires a 'options'")
+
+ # Make sure each option has a 'value' and a 'label'
+ for item in pref["options"]:
+ if ("value" not in item):
+ raise MissingPrefAttr("'options' requires a 'value'")
+ if ("label" not in item):
+ raise MissingPrefAttr("'options' requires a 'label'")
+
+ # TODO: Check that pref["type"] matches default value type
+
+def parse_options(options, jetpack_id):
+ doc = Document()
+ root = doc.createElement("vbox")
+ root.setAttribute("xmlns", "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul")
+ doc.appendChild(root)
+
+ for pref in options:
+ setting = doc.createElement("setting")
+ setting.setAttribute("pref-name", pref["name"])
+ setting.setAttribute("data-jetpack-id", jetpack_id)
+ setting.setAttribute("pref", "extensions." + jetpack_id + "." + pref["name"])
+ setting.setAttribute("type", pref["type"])
+ setting.setAttribute("title", pref["title"])
+
+ if ("description" in pref):
+ setting.appendChild(doc.createTextNode(pref["description"]))
+
+ if (pref["type"] == "control"):
+ button = doc.createElement("button")
+ button.setAttribute("pref-name", pref["name"])
+ button.setAttribute("data-jetpack-id", jetpack_id)
+ button.setAttribute("label", pref["label"])
+ button.setAttribute("oncommand", "Services.obs.notifyObservers(null, '" +
+ jetpack_id + "-cmdPressed', '" +
+ pref["name"] + "');");
+ setting.appendChild(button)
+ elif (pref["type"] == "boolint"):
+ setting.setAttribute("on", pref["on"])
+ setting.setAttribute("off", pref["off"])
+ elif (pref["type"] == "menulist"):
+ menulist = doc.createElement("menulist")
+ menupopup = doc.createElement("menupopup")
+ for item in pref["options"]:
+ menuitem = doc.createElement("menuitem")
+ menuitem.setAttribute("value", item["value"])
+ menuitem.setAttribute("label", item["label"])
+ menupopup.appendChild(menuitem)
+ menulist.appendChild(menupopup)
+ setting.appendChild(menulist)
+ elif (pref["type"] == "radio"):
+ radiogroup = doc.createElement("radiogroup")
+ for item in pref["options"]:
+ radio = doc.createElement("radio")
+ radio.setAttribute("value", item["value"])
+ radio.setAttribute("label", item["label"])
+ radiogroup.appendChild(radio)
+ setting.appendChild(radiogroup)
+
+ root.appendChild(setting)
+
+ return doc.toprettyxml(indent=" ")
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/packaging.py b/tools/addon-sdk-1.12/python-lib/cuddlefish/packaging.py
new file mode 100644
index 0000000..9e08b66
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/packaging.py
@@ -0,0 +1,448 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import os
+import sys
+import re
+import copy
+
+import simplejson as json
+from cuddlefish.bunch import Bunch
+
+MANIFEST_NAME = 'package.json'
+DEFAULT_LOADER = 'addon-sdk'
+
+# Is different from root_dir when running tests
+env_root = os.environ.get('CUDDLEFISH_ROOT')
+
+DEFAULT_PROGRAM_MODULE = 'main'
+
+DEFAULT_ICON = 'icon.png'
+DEFAULT_ICON64 = 'icon64.png'
+
+METADATA_PROPS = ['name', 'description', 'keywords', 'author', 'version',
+ 'contributors', 'license', 'homepage', 'icon', 'icon64',
+ 'main', 'directories']
+
+RESOURCE_HOSTNAME_RE = re.compile(r'^[a-z0-9_\-]+$')
+
+class Error(Exception):
+ pass
+
+class MalformedPackageError(Error):
+ pass
+
+class MalformedJsonFileError(Error):
+ pass
+
+class DuplicatePackageError(Error):
+ pass
+
+class PackageNotFoundError(Error):
+ def __init__(self, missing_package, reason):
+ self.missing_package = missing_package
+ self.reason = reason
+ def __str__(self):
+ return "%s (%s)" % (self.missing_package, self.reason)
+
+class BadChromeMarkerError(Error):
+ pass
+
+def validate_resource_hostname(name):
+ """
+ Validates the given hostname for a resource: URI.
+
+ For more information, see:
+
+ https://bugzilla.mozilla.org/show_bug.cgi?id=566812#c13
+
+ Examples:
+
+ >>> validate_resource_hostname('blarg')
+
+ >>> validate_resource_hostname('bl arg')
+ Traceback (most recent call last):
+ ...
+ ValueError: Error: the name of your package contains an invalid character.
+ Package names can contain only lower-case letters, numbers, underscores, and dashes.
+ Current package name: bl arg
+
+ >>> validate_resource_hostname('BLARG')
+ Traceback (most recent call last):
+ ...
+ ValueError: Error: the name of your package contains upper-case letters.
+ Package names can contain only lower-case letters, numbers, underscores, and dashes.
+ Current package name: BLARG
+
+ >>> validate_resource_hostname('foo@bar')
+ Traceback (most recent call last):
+ ...
+ ValueError: Error: the name of your package contains an invalid character.
+ Package names can contain only lower-case letters, numbers, underscores, and dashes.
+ Current package name: foo@bar
+ """
+
+ # See https://bugzilla.mozilla.org/show_bug.cgi?id=568131 for details.
+ if not name.islower():
+ raise ValueError("""Error: the name of your package contains upper-case letters.
+Package names can contain only lower-case letters, numbers, underscores, and dashes.
+Current package name: %s""" % name)
+
+ if not RESOURCE_HOSTNAME_RE.match(name):
+ raise ValueError("""Error: the name of your package contains an invalid character.
+Package names can contain only lower-case letters, numbers, underscores, and dashes.
+Current package name: %s""" % name)
+
+def find_packages_with_module(pkg_cfg, name):
+ # TODO: Make this support more than just top-level modules.
+ filename = "%s.js" % name
+ packages = []
+ for cfg in pkg_cfg.packages.itervalues():
+ if 'lib' in cfg:
+ matches = [dirname for dirname in resolve_dirs(cfg, cfg.lib)
+ if os.path.exists(os.path.join(dirname, filename))]
+ if matches:
+ packages.append(cfg.name)
+ return packages
+
+def resolve_dirs(pkg_cfg, dirnames):
+ for dirname in dirnames:
+ yield resolve_dir(pkg_cfg, dirname)
+
+def resolve_dir(pkg_cfg, dirname):
+ return os.path.join(pkg_cfg.root_dir, dirname)
+
+def get_metadata(pkg_cfg, deps):
+ metadata = Bunch()
+ for pkg_name in deps:
+ cfg = pkg_cfg.packages[pkg_name]
+ metadata[pkg_name] = Bunch()
+ for prop in METADATA_PROPS:
+ if cfg.get(prop):
+ metadata[pkg_name][prop] = cfg[prop]
+ return metadata
+
+def set_section_dir(base_json, name, base_path, dirnames, allow_root=False):
+ resolved = compute_section_dir(base_json, base_path, dirnames, allow_root)
+ if resolved:
+ base_json[name] = os.path.abspath(resolved)
+
+def compute_section_dir(base_json, base_path, dirnames, allow_root):
+ # PACKAGE_JSON.lib is highest priority
+ # then PACKAGE_JSON.directories.lib
+ # then lib/ (if it exists)
+ # then . (but only if allow_root=True)
+ for dirname in dirnames:
+ if base_json.get(dirname):
+ return os.path.join(base_path, base_json[dirname])
+ if "directories" in base_json:
+ for dirname in dirnames:
+ if dirname in base_json.directories:
+ return os.path.join(base_path, base_json.directories[dirname])
+ for dirname in dirnames:
+ if os.path.isdir(os.path.join(base_path, dirname)):
+ return os.path.join(base_path, dirname)
+ if allow_root:
+ return os.path.abspath(base_path)
+ return None
+
+def normalize_string_or_array(base_json, key):
+ if base_json.get(key):
+ if isinstance(base_json[key], basestring):
+ base_json[key] = [base_json[key]]
+
+def load_json_file(path):
+ data = open(path, 'r').read()
+ try:
+ return Bunch(json.loads(data))
+ except ValueError, e:
+ raise MalformedJsonFileError('%s when reading "%s"' % (str(e),
+ path))
+
+def get_config_in_dir(path):
+ package_json = os.path.join(path, MANIFEST_NAME)
+ if not (os.path.exists(package_json) and
+ os.path.isfile(package_json)):
+ raise MalformedPackageError('%s not found in "%s"' % (MANIFEST_NAME,
+ path))
+ base_json = load_json_file(package_json)
+
+ if 'name' not in base_json:
+ base_json.name = os.path.basename(path)
+
+ # later processing steps will expect to see the following keys in the
+ # base_json that we return:
+ #
+ # name: name of the package
+ # lib: list of directories with .js files
+ # test: list of directories with test-*.js files
+ # doc: list of directories with documentation .md files
+ # data: list of directories with bundled arbitrary data files
+ # packages: ?
+
+ if (not base_json.get('tests') and
+ os.path.isdir(os.path.join(path, 'test'))):
+ base_json['tests'] = 'test'
+
+ set_section_dir(base_json, 'lib', path, ['lib'], True)
+ set_section_dir(base_json, 'tests', path, ['test', 'tests'], False)
+ set_section_dir(base_json, 'doc', path, ['doc', 'docs'])
+ set_section_dir(base_json, 'data', path, ['data'])
+ set_section_dir(base_json, 'packages', path, ['packages'])
+ set_section_dir(base_json, 'locale', path, ['locale'])
+
+ if (not base_json.get('icon') and
+ os.path.isfile(os.path.join(path, DEFAULT_ICON))):
+ base_json['icon'] = DEFAULT_ICON
+
+ if (not base_json.get('icon64') and
+ os.path.isfile(os.path.join(path, DEFAULT_ICON64))):
+ base_json['icon64'] = DEFAULT_ICON64
+
+ for key in ['lib', 'tests', 'dependencies', 'packages']:
+ # TODO: lib/tests can be an array?? consider interaction with
+ # compute_section_dir above
+ normalize_string_or_array(base_json, key)
+
+ if 'main' not in base_json and 'lib' in base_json:
+ for dirname in base_json['lib']:
+ program = os.path.join(path, dirname,
+ '%s.js' % DEFAULT_PROGRAM_MODULE)
+ if os.path.exists(program):
+ base_json['main'] = DEFAULT_PROGRAM_MODULE
+ break
+
+ base_json.root_dir = path
+
+ if "dependencies" in base_json:
+ deps = base_json["dependencies"]
+ deps = [x for x in deps if x not in ["addon-kit", "api-utils"]]
+ deps.append("addon-sdk")
+ base_json["dependencies"] = deps
+
+ return base_json
+
+def _is_same_file(a, b):
+ if hasattr(os.path, 'samefile'):
+ return os.path.samefile(a, b)
+ return a == b
+
+def build_config(root_dir, target_cfg, packagepath=[]):
+ dirs_to_scan = [env_root] # root is addon-sdk dir, diff from root_dir in tests
+
+ def add_packages_from_config(pkgconfig):
+ if 'packages' in pkgconfig:
+ for package_dir in resolve_dirs(pkgconfig, pkgconfig.packages):
+ dirs_to_scan.append(package_dir)
+
+ add_packages_from_config(target_cfg)
+
+ packages_dir = os.path.join(root_dir, 'packages')
+ if os.path.exists(packages_dir) and os.path.isdir(packages_dir):
+ dirs_to_scan.append(packages_dir)
+ dirs_to_scan.extend(packagepath)
+
+ packages = Bunch({target_cfg.name: target_cfg})
+
+ while dirs_to_scan:
+ packages_dir = dirs_to_scan.pop()
+ if os.path.exists(os.path.join(packages_dir, "package.json")):
+ package_paths = [packages_dir]
+ else:
+ package_paths = [os.path.join(packages_dir, dirname)
+ for dirname in os.listdir(packages_dir)
+ if not dirname.startswith('.')]
+ package_paths = [dirname for dirname in package_paths
+ if os.path.isdir(dirname)]
+
+ for path in package_paths:
+ pkgconfig = get_config_in_dir(path)
+ if pkgconfig.name in packages:
+ otherpkg = packages[pkgconfig.name]
+ if not _is_same_file(otherpkg.root_dir, path):
+ raise DuplicatePackageError(path, otherpkg.root_dir)
+ else:
+ packages[pkgconfig.name] = pkgconfig
+ add_packages_from_config(pkgconfig)
+
+ return Bunch(packages=packages)
+
+def get_deps_for_targets(pkg_cfg, targets):
+ visited = []
+ deps_left = [[dep, None] for dep in list(targets)]
+
+ while deps_left:
+ [dep, required_by] = deps_left.pop()
+ if dep not in visited:
+ visited.append(dep)
+ if dep not in pkg_cfg.packages:
+ required_reason = ("required by '%s'" % (required_by)) \
+ if required_by is not None \
+ else "specified as target"
+ raise PackageNotFoundError(dep, required_reason)
+ dep_cfg = pkg_cfg.packages[dep]
+ deps_left.extend([[i, dep] for i in dep_cfg.get('dependencies', [])])
+ deps_left.extend([[i, dep] for i in dep_cfg.get('extra_dependencies', [])])
+
+ return visited
+
+def generate_build_for_target(pkg_cfg, target, deps,
+ include_tests=True,
+ include_dep_tests=False,
+ is_running_tests=False,
+ default_loader=DEFAULT_LOADER):
+
+ build = Bunch(# Contains section directories for all packages:
+ packages=Bunch(),
+ locale=Bunch()
+ )
+
+ def add_section_to_build(cfg, section, is_code=False,
+ is_data=False):
+ if section in cfg:
+ dirnames = cfg[section]
+ if isinstance(dirnames, basestring):
+ # This is just for internal consistency within this
+ # function, it has nothing to do w/ a non-canonical
+ # configuration dict.
+ dirnames = [dirnames]
+ for dirname in resolve_dirs(cfg, dirnames):
+ # ensure that package name is valid
+ try:
+ validate_resource_hostname(cfg.name)
+ except ValueError, err:
+ print err
+ sys.exit(1)
+ # ensure that this package has an entry
+ if not cfg.name in build.packages:
+ build.packages[cfg.name] = Bunch()
+ # detect duplicated sections
+ if section in build.packages[cfg.name]:
+ raise KeyError("package's section already defined",
+ cfg.name, section)
+ # Register this section (lib, data, tests)
+ build.packages[cfg.name][section] = dirname
+
+ def add_locale_to_build(cfg):
+ # Bug 730776: Ignore locales for addon-kit, that are only for unit tests
+ if not is_running_tests and cfg.name == "addon-sdk":
+ return
+
+ path = resolve_dir(cfg, cfg['locale'])
+ files = os.listdir(path)
+ for filename in files:
+ fullpath = os.path.join(path, filename)
+ if os.path.isfile(fullpath) and filename.endswith('.properties'):
+ language = filename[:-len('.properties')]
+
+ from property_parser import parse_file, MalformedLocaleFileError
+ try:
+ content = parse_file(fullpath)
+ except MalformedLocaleFileError, msg:
+ print msg[0]
+ sys.exit(1)
+
+ # Merge current locales into global locale hashtable.
+ # Locale files only contains one big JSON object
+ # that act as an hastable of:
+ # "keys to translate" => "translated keys"
+ if language in build.locale:
+ merge = (build.locale[language].items() +
+ content.items())
+ build.locale[language] = Bunch(merge)
+ else:
+ build.locale[language] = content
+
+ def add_dep_to_build(dep):
+ dep_cfg = pkg_cfg.packages[dep]
+ add_section_to_build(dep_cfg, "lib", is_code=True)
+ add_section_to_build(dep_cfg, "data", is_data=True)
+ if include_tests and include_dep_tests:
+ add_section_to_build(dep_cfg, "tests", is_code=True)
+ if 'locale' in dep_cfg:
+ add_locale_to_build(dep_cfg)
+ if ("loader" in dep_cfg) and ("loader" not in build):
+ build.loader = "%s/%s" % (dep,
+ dep_cfg.loader)
+
+ target_cfg = pkg_cfg.packages[target]
+
+ if include_tests and not include_dep_tests:
+ add_section_to_build(target_cfg, "tests", is_code=True)
+
+ for dep in deps:
+ add_dep_to_build(dep)
+
+ if 'loader' not in build:
+ add_dep_to_build(DEFAULT_LOADER)
+
+ if 'icon' in target_cfg:
+ build['icon'] = os.path.join(target_cfg.root_dir, target_cfg.icon)
+ del target_cfg['icon']
+
+ if 'icon64' in target_cfg:
+ build['icon64'] = os.path.join(target_cfg.root_dir, target_cfg.icon64)
+ del target_cfg['icon64']
+
+ if ('preferences' in target_cfg):
+ build['preferences'] = target_cfg.preferences
+
+ return build
+
+def _get_files_in_dir(path):
+ data = {}
+ files = os.listdir(path)
+ for filename in files:
+ fullpath = os.path.join(path, filename)
+ if os.path.isdir(fullpath):
+ data[filename] = _get_files_in_dir(fullpath)
+ else:
+ try:
+ info = os.stat(fullpath)
+ data[filename] = ("file", dict(size=info.st_size))
+ except OSError:
+ pass
+ return ("directory", data)
+
+def build_pkg_index(pkg_cfg):
+ pkg_cfg = copy.deepcopy(pkg_cfg)
+ for pkg in pkg_cfg.packages:
+ root_dir = pkg_cfg.packages[pkg].root_dir
+ files = _get_files_in_dir(root_dir)
+ pkg_cfg.packages[pkg].files = files
+ try:
+ readme = open(root_dir + '/README.md').read()
+ pkg_cfg.packages[pkg].readme = readme
+ except IOError:
+ pass
+ del pkg_cfg.packages[pkg].root_dir
+ return pkg_cfg.packages
+
+def build_pkg_cfg(root):
+ pkg_cfg = build_config(root, Bunch(name='dummy'))
+ del pkg_cfg.packages['dummy']
+ return pkg_cfg
+
+def call_plugins(pkg_cfg, deps):
+ for dep in deps:
+ dep_cfg = pkg_cfg.packages[dep]
+ dirnames = dep_cfg.get('python-lib', [])
+ for dirname in resolve_dirs(dep_cfg, dirnames):
+ sys.path.append(dirname)
+ module_names = dep_cfg.get('python-plugins', [])
+ for module_name in module_names:
+ module = __import__(module_name)
+ module.init(root_dir=dep_cfg.root_dir)
+
+def call_cmdline_tool(env_root, pkg_name):
+ pkg_cfg = build_config(env_root, Bunch(name='dummy'))
+ if pkg_name not in pkg_cfg.packages:
+ print "This tool requires the '%s' package." % pkg_name
+ sys.exit(1)
+ cfg = pkg_cfg.packages[pkg_name]
+ for dirname in resolve_dirs(cfg, cfg['python-lib']):
+ sys.path.append(dirname)
+ module_name = cfg.get('python-cmdline-tool')
+ module = __import__(module_name)
+ module.run()
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/preflight.py b/tools/addon-sdk-1.12/python-lib/cuddlefish/preflight.py
new file mode 100755
index 0000000..8b500ec
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/preflight.py
@@ -0,0 +1,77 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import os, sys
+import base64
+import simplejson as json
+
+def create_jid():
+ """Return 'jid1-XYZ', where 'XYZ' is a randomly-generated string. (in the
+ previous jid0- series, the string securely identified a specific public
+ key). To get a suitable add-on ID, append '@jetpack' to this string.
+ """
+ # per https://developer.mozilla.org/en/Install_Manifests#id all XPI id
+ # values must either be in the form of a 128-bit GUID (crazy braces
+ # and all) or in the form of an email address (crazy @ and all).
+ # Firefox will refuse to install an add-on with an id that doesn't
+ # match one of these forms. The actual regexp is at:
+ # http://mxr.mozilla.org/mozilla-central/source/toolkit/mozapps/extensions/XPIProvider.jsm#130
+ # So the JID needs an @-suffix, and the only legal punctuation is
+ # "-._". So we start with a base64 encoding, and replace the
+ # punctuation (+/) with letters (AB), losing a few bits of integrity.
+
+ # even better: windows has a maximum path length limitation of 256
+ # characters:
+ # http://msdn.microsoft.com/en-us/library/aa365247%28VS.85%29.aspx
+ # (unless all paths are prefixed with "\\?\", I kid you not). The
+ # typical install will put add-on code in a directory like:
+ # C:\Documents and Settings\<username>\Application Data\Mozilla\Firefox\Profiles\232353483.default\extensions\$JID\...
+ # (which is 108 chars long without the $JID).
+ # Then the unpacked XPI contains packaged resources like:
+ # resources/$JID-api-utils-lib/main.js (35 chars plus the $JID)
+ #
+ # We create a random 80 bit string, base64 encode that (with
+ # AB instead of +/ to be path-safe), then bundle it into
+ # "jid1-XYZ@jetpack". This gives us 27 characters. The resulting
+ # main.js will have a path length of 211 characters, leaving us 45
+ # characters of margin.
+ #
+ # 80 bits is enough to generate one billion JIDs and still maintain lower
+ # than a one-in-a-million chance of accidental collision. (1e9 JIDs is 30
+ # bits, square for the "birthday-paradox" to get 60 bits, add 20 bits for
+ # the one-in-a-million margin to get 80 bits)
+
+ # if length were no issue, we'd prefer to use this:
+ h = os.urandom(80/8)
+ s = base64.b64encode(h, "AB").strip("=")
+ jid = "jid1-" + s
+ return jid
+
+def preflight_config(target_cfg, filename, stderr=sys.stderr):
+ modified = False
+ config = json.load(open(filename, 'r'))
+
+ if "id" not in config:
+ print >>stderr, ("No 'id' in package.json: creating a new ID for you.")
+ jid = create_jid()
+ config["id"] = jid
+ modified = True
+
+ if modified:
+ i = 0
+ backup = filename + ".backup"
+ while os.path.exists(backup):
+ if i > 1000:
+ raise ValueError("I'm having problems finding a good name"
+ " for the backup file. Please move %s out"
+ " of the way and try again."
+ % (filename + ".backup"))
+ backup = filename + ".backup-%d" % i
+ i += 1
+ os.rename(filename, backup)
+ new_json = json.dumps(config, indent=4)
+ open(filename, 'w').write(new_json+"\n")
+ return False, True
+
+ return True, False
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/prefs.py b/tools/addon-sdk-1.12/python-lib/cuddlefish/prefs.py
new file mode 100644
index 0000000..f3a82fe
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/prefs.py
@@ -0,0 +1,118 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DEFAULT_COMMON_PREFS = {
+ # allow debug output via dump to be printed to the system console
+ # (setting it here just in case, even though PlainTextConsole also
+ # sets this preference)
+ 'browser.dom.window.dump.enabled': True,
+ # warn about possibly incorrect code
+ 'javascript.options.strict': True,
+ 'javascript.options.showInConsole': True,
+
+ 'extensions.checkCompatibility.nightly' : False,
+
+ # Disable extension updates and notifications.
+ 'extensions.update.enabled' : False,
+ 'extensions.update.notifyUser' : False,
+
+ # From:
+ # http://hg.mozilla.org/mozilla-central/file/1dd81c324ac7/build/automation.py.in#l372
+ # Only load extensions from the application and user profile.
+ # AddonManager.SCOPE_PROFILE + AddonManager.SCOPE_APPLICATION
+ 'extensions.enabledScopes' : 5,
+ # Disable metadata caching for installed add-ons by default
+ 'extensions.getAddons.cache.enabled' : False,
+ # Disable intalling any distribution add-ons
+ 'extensions.installDistroAddons' : False,
+ # Allow installing extensions dropped into the profile folder
+ 'extensions.autoDisableScopes' : 10,
+
+ # Disable app update
+ 'app.update.enabled' : False,
+
+ # Point update checks to a nonexistent local URL for fast failures.
+ 'extensions.update.url' : 'http://localhost/extensions-dummy/updateURL',
+ 'extensions.blocklist.url' : 'http://localhost/extensions-dummy/blocklistURL',
+ # Make sure opening about:addons won't hit the network.
+ 'extensions.webservice.discoverURL' : 'http://localhost/extensions-dummy/discoveryURL'
+}
+
+DEFAULT_FENNEC_PREFS = {
+ 'browser.console.showInPanel': True,
+ 'browser.firstrun.show.uidiscovery': False
+}
+
+# When launching a temporary new Firefox profile, use these preferences.
+DEFAULT_FIREFOX_PREFS = {
+ 'browser.startup.homepage' : 'about:blank',
+ 'startup.homepage_welcome_url' : 'about:blank',
+ 'devtools.errorconsole.enabled' : True,
+
+ # Disable the feedback extension
+ 'extensions.testpilot.runStudies' : False,
+
+ # From:
+ # http://hg.mozilla.org/mozilla-central/file/1dd81c324ac7/build/automation.py.in#l388
+ # Make url-classifier updates so rare that they won't affect tests.
+ 'urlclassifier.updateinterval' : 172800,
+ # Point the url-classifier to a nonexistent local URL for fast failures.
+ 'browser.safebrowsing.provider.0.gethashURL' : 'http://localhost/safebrowsing-dummy/gethash',
+ 'browser.safebrowsing.provider.0.keyURL' : 'http://localhost/safebrowsing-dummy/newkey',
+ 'browser.safebrowsing.provider.0.updateURL' : 'http://localhost/safebrowsing-dummy/update',
+ }
+
+# When launching a temporary new Thunderbird profile, use these preferences.
+# Note that these were taken from:
+# http://mxr.mozilla.org/comm-central/source/mail/test/mozmill/runtest.py
+DEFAULT_THUNDERBIRD_PREFS = {
+ # say no to slow script warnings
+ 'dom.max_chrome_script_run_time': 200,
+ 'dom.max_script_run_time': 0,
+ # do not ask about being the default mail client
+ 'mail.shell.checkDefaultClient': False,
+ # disable non-gloda indexing daemons
+ 'mail.winsearch.enable': False,
+ 'mail.winsearch.firstRunDone': True,
+ 'mail.spotlight.enable': False,
+ 'mail.spotlight.firstRunDone': True,
+ # disable address books for undisclosed reasons
+ 'ldap_2.servers.osx.position': 0,
+ 'ldap_2.servers.oe.position': 0,
+ # disable the first use junk dialog
+ 'mailnews.ui.junk.firstuse': False,
+ # other unknown voodoo
+ # -- dummied up local accounts to stop the account wizard
+ 'mail.account.account1.server' : "server1",
+ 'mail.account.account2.identities' : "id1",
+ 'mail.account.account2.server' : "server2",
+ 'mail.accountmanager.accounts' : "account1,account2",
+ 'mail.accountmanager.defaultaccount' : "account2",
+ 'mail.accountmanager.localfoldersserver' : "server1",
+ 'mail.identity.id1.fullName' : "Tinderbox",
+ 'mail.identity.id1.smtpServer' : "smtp1",
+ 'mail.identity.id1.useremail' : "tinderbox@invalid.com",
+ 'mail.identity.id1.valid' : True,
+ 'mail.root.none-rel' : "[ProfD]Mail",
+ 'mail.root.pop3-rel' : "[ProfD]Mail",
+ 'mail.server.server1.directory-rel' : "[ProfD]Mail/Local Folders",
+ 'mail.server.server1.hostname' : "Local Folders",
+ 'mail.server.server1.name' : "Local Folders",
+ 'mail.server.server1.type' : "none",
+ 'mail.server.server1.userName' : "nobody",
+ 'mail.server.server2.check_new_mail' : False,
+ 'mail.server.server2.directory-rel' : "[ProfD]Mail/tinderbox",
+ 'mail.server.server2.download_on_biff' : True,
+ 'mail.server.server2.hostname' : "tinderbox",
+ 'mail.server.server2.login_at_startup' : False,
+ 'mail.server.server2.name' : "tinderbox@invalid.com",
+ 'mail.server.server2.type' : "pop3",
+ 'mail.server.server2.userName' : "tinderbox",
+ 'mail.smtp.defaultserver' : "smtp1",
+ 'mail.smtpserver.smtp1.hostname' : "tinderbox",
+ 'mail.smtpserver.smtp1.username' : "tinderbox",
+ 'mail.smtpservers' : "smtp1",
+ 'mail.startup.enabledMailCheckOnce' : True,
+ 'mailnews.start_page_override.mstone' : "ignore",
+ }
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/property_parser.py b/tools/addon-sdk-1.12/python-lib/cuddlefish/property_parser.py
new file mode 100644
index 0000000..5d75717
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/property_parser.py
@@ -0,0 +1,110 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import re
+import codecs
+
+class MalformedLocaleFileError(Exception):
+ pass
+
+def parse_file(path):
+ return parse(read_file(path), path)
+
+def read_file(path):
+ try:
+ return codecs.open( path, "r", "utf-8" ).readlines()
+ except UnicodeDecodeError, e:
+ raise MalformedLocaleFileError(
+ 'Following locale file is not a valid ' +
+ 'UTF-8 file: %s\n%s"' % (path, str(e)))
+
+COMMENT = re.compile(r'\s*#')
+EMPTY = re.compile(r'^\s+$')
+KEYVALUE = re.compile(r"\s*([^=:]+)(=|:)\s*(.*)")
+
+def parse(lines, path=None):
+ lines = iter(lines)
+ lineNo = 1
+ pairs = dict()
+ for line in lines:
+ if COMMENT.match(line) or EMPTY.match(line) or len(line) == 0:
+ continue
+ m = KEYVALUE.match(line)
+ if not m:
+ raise MalformedLocaleFileError(
+ 'Following locale file is not a valid .properties file: %s\n'
+ 'Line %d is incorrect:\n%s' % (path, lineNo, line))
+
+ # All spaces are strip. Spaces at the beginning are stripped
+ # by the regular expression. We have to strip spaces at the end.
+ key = m.group(1).rstrip()
+ val = m.group(3).rstrip()
+
+ # `key` can be empty when key is only made of spaces
+ if not key:
+ raise MalformedLocaleFileError(
+ 'Following locale file is not a valid .properties file: %s\n'
+ 'Key is invalid on line %d is incorrect:\n%s' %
+ (path, lineNo, line))
+
+ # Multiline value: keep reading lines, while lines end with backslash
+ # and strip spaces at the beginning of lines except the last line
+ # that doesn't end up with backslash, we strip all spaces for this one.
+ if val.endswith("\\"):
+ val = val[:-1]
+ try:
+ # remove spaces before/after and especially the \n at EOL
+ line = lines.next().strip()
+ while line.endswith("\\"):
+ val += line[:-1].lstrip()
+ line = lines.next()
+ lineNo += 1
+ val += line.strip()
+ except StopIteration:
+ raise MalformedLocaleFileError(
+ 'Following locale file is not a valid .properties file: %s\n'
+ 'Unexpected EOF in multiline sequence at line %d:\n%s' %
+ (path, lineNo, line))
+ # Save this new pair
+ pairs[key] = val
+ lineNo += 1
+
+ normalize_plural(path, pairs)
+ return pairs
+
+# Plural forms in properties files are defined like this:
+# key = other form
+# key[one] = one form
+# key[...] = ...
+# Parse them and merge each key into one object containing all forms:
+# key: {
+# other: "other form",
+# one: "one form",
+# ...: ...
+# }
+PLURAL_FORM = re.compile(r'^(.*)\[(zero|one|two|few|many|other)\]$')
+def normalize_plural(path, pairs):
+ for key in list(pairs.keys()):
+ m = PLURAL_FORM.match(key)
+ if not m:
+ continue
+ main_key = m.group(1)
+ plural_form = m.group(2)
+ # Allows not specifying a generic key (i.e a key without [form])
+ if not main_key in pairs:
+ pairs[main_key] = {}
+ # Ensure that we always have the [other] form
+ if not main_key + "[other]" in pairs:
+ raise MalformedLocaleFileError(
+ 'Following locale file is not a valid UTF-8 file: %s\n'
+ 'This plural form doesn\'t have a matching `%s[other]` form:\n'
+ '%s\n'
+ 'You have to defined following key:\n%s'
+ % (path, main_key, key, main_key))
+ # convert generic form into an object if it is still a string
+ if isinstance(pairs[main_key], unicode):
+ pairs[main_key] = {"other": pairs[main_key]}
+ # then, add this new plural form
+ pairs[main_key][plural_form] = pairs[key]
+ del pairs[key]
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/rdf.py b/tools/addon-sdk-1.12/python-lib/cuddlefish/rdf.py
new file mode 100644
index 0000000..3f58d88
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/rdf.py
@@ -0,0 +1,190 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import os
+import xml.dom.minidom
+import StringIO
+
+RDF_NS = "http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+EM_NS = "http://www.mozilla.org/2004/em-rdf#"
+
+class RDF(object):
+ def __str__(self):
+ # real files have an .encoding attribute and use it when you
+ # write() unicode into them: they read()/write() unicode and
+ # put encoded bytes in the backend file. StringIO objects
+ # read()/write() unicode and put unicode in the backing store,
+ # so we must encode the output of getvalue() to get a
+ # bytestring. (cStringIO objects are weirder: they effectively
+ # have .encoding hardwired to "ascii" and put only bytes in
+ # the backing store, so we can't use them here).
+ #
+ # The encoding= argument to dom.writexml() merely sets the XML header's
+ # encoding= attribute. It still writes unencoded unicode to the output file,
+ # so we have to encode it for real afterwards.
+ #
+ # Also see: https://bugzilla.mozilla.org/show_bug.cgi?id=567660
+
+ buf = StringIO.StringIO()
+ self.dom.writexml(buf, encoding="utf-8")
+ return buf.getvalue().encode('utf-8')
+
+class RDFUpdate(RDF):
+ def __init__(self):
+ impl = xml.dom.minidom.getDOMImplementation()
+ self.dom = impl.createDocument(RDF_NS, "RDF", None)
+ self.dom.documentElement.setAttribute("xmlns", RDF_NS)
+ self.dom.documentElement.setAttribute("xmlns:em", EM_NS)
+
+ def _make_node(self, name, value, parent):
+ elem = self.dom.createElement(name)
+ elem.appendChild(self.dom.createTextNode(value))
+ parent.appendChild(elem)
+ return elem
+
+ def add(self, manifest, update_link):
+ desc = self.dom.createElement("Description")
+ desc.setAttribute(
+ "about",
+ "urn:mozilla:extension:%s" % manifest.get("em:id")
+ )
+ self.dom.documentElement.appendChild(desc)
+
+ updates = self.dom.createElement("em:updates")
+ desc.appendChild(updates)
+
+ seq = self.dom.createElement("Seq")
+ updates.appendChild(seq)
+
+ li = self.dom.createElement("li")
+ seq.appendChild(li)
+
+ li_desc = self.dom.createElement("Description")
+ li.appendChild(li_desc)
+
+ self._make_node("em:version", manifest.get("em:version"),
+ li_desc)
+
+ apps = manifest.dom.documentElement.getElementsByTagName(
+ "em:targetApplication"
+ )
+
+ for app in apps:
+ target_app = self.dom.createElement("em:targetApplication")
+ li_desc.appendChild(target_app)
+
+ ta_desc = self.dom.createElement("Description")
+ target_app.appendChild(ta_desc)
+
+ for name in ["em:id", "em:minVersion", "em:maxVersion"]:
+ elem = app.getElementsByTagName(name)[0]
+ self._make_node(name, elem.firstChild.nodeValue, ta_desc)
+
+ self._make_node("em:updateLink", update_link, ta_desc)
+
+class RDFManifest(RDF):
+ def __init__(self, path):
+ self.dom = xml.dom.minidom.parse(path)
+
+ def set(self, property, value):
+ elements = self.dom.documentElement.getElementsByTagName(property)
+ if not elements:
+ raise ValueError("Element with value not found: %s" % property)
+ if not elements[0].firstChild:
+ elements[0].appendChild(self.dom.createTextNode(value))
+ else:
+ elements[0].firstChild.nodeValue = value
+
+ def get(self, property, default=None):
+ elements = self.dom.documentElement.getElementsByTagName(property)
+ if not elements:
+ return default
+ return elements[0].firstChild.nodeValue
+
+ def remove(self, property):
+ elements = self.dom.documentElement.getElementsByTagName(property)
+ if not elements:
+ return True
+ else:
+ for i in elements:
+ i.parentNode.removeChild(i);
+
+ return True;
+
+def gen_manifest(template_root_dir, target_cfg, jid,
+ update_url=None, bootstrap=True, enable_mobile=False):
+ install_rdf = os.path.join(template_root_dir, "install.rdf")
+ manifest = RDFManifest(install_rdf)
+ dom = manifest.dom
+
+ manifest.set("em:id", jid)
+ manifest.set("em:version",
+ target_cfg.get('version', '1.0'))
+ manifest.set("em:name",
+ target_cfg.get('fullName', target_cfg['name']))
+ manifest.set("em:description",
+ target_cfg.get("description", ""))
+ manifest.set("em:creator",
+ target_cfg.get("author", ""))
+ manifest.set("em:bootstrap", str(bootstrap).lower())
+ # XPIs remain packed by default, but package.json can override that. The
+ # RDF format accepts "true" as True, anything else as False. We expect
+ # booleans in the .json file, not strings.
+ manifest.set("em:unpack", "true" if target_cfg.get("unpack") else "false")
+
+ for contributor in target_cfg.get("contributors", [ ]):
+ elem = dom.createElement("em:contributor");
+ elem.appendChild(dom.createTextNode(contributor))
+ dom.documentElement.getElementsByTagName("Description")[0].appendChild(elem)
+
+ if update_url:
+ manifest.set("em:updateURL", update_url)
+ else:
+ manifest.remove("em:updateURL")
+
+ if target_cfg.get("preferences"):
+ manifest.set("em:optionsType", "2")
+ else:
+ manifest.remove("em:optionsType")
+
+ if enable_mobile:
+ target_app = dom.createElement("em:targetApplication")
+ dom.documentElement.getElementsByTagName("Description")[0].appendChild(target_app)
+
+ ta_desc = dom.createElement("Description")
+ target_app.appendChild(ta_desc)
+
+ elem = dom.createElement("em:id")
+ elem.appendChild(dom.createTextNode("{aa3c5121-dab2-40e2-81ca-7ea25febc110}"))
+ ta_desc.appendChild(elem)
+
+ elem = dom.createElement("em:minVersion")
+ elem.appendChild(dom.createTextNode("17.0"))
+ ta_desc.appendChild(elem)
+
+ elem = dom.createElement("em:maxVersion")
+ elem.appendChild(dom.createTextNode("18.*"))
+ ta_desc.appendChild(elem)
+
+ if target_cfg.get("homepage"):
+ manifest.set("em:homepageURL", target_cfg.get("homepage"))
+ else:
+ manifest.remove("em:homepageURL")
+
+ return manifest
+
+if __name__ == "__main__":
+ print "Running smoke test."
+ root = os.path.join(os.path.dirname(__file__), '../../app-extension')
+ manifest = gen_manifest(root, {'name': 'test extension'},
+ 'fakeid', 'http://foo.com/update.rdf')
+ update = RDFUpdate()
+ update.add(manifest, "https://foo.com/foo.xpi")
+ exercise_str = str(manifest) + str(update)
+ for tagname in ["em:targetApplication", "em:version", "em:id"]:
+ if not len(update.dom.getElementsByTagName(tagname)):
+ raise Exception("tag does not exist: %s" % tagname)
+ if not update.dom.getElementsByTagName(tagname)[0].firstChild:
+ raise Exception("tag has no children: %s" % tagname)
+ print "Success!"
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/runner.py b/tools/addon-sdk-1.12/python-lib/cuddlefish/runner.py
new file mode 100644
index 0000000..337a998
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/runner.py
@@ -0,0 +1,696 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import os
+import sys
+import time
+import tempfile
+import atexit
+import shlex
+import subprocess
+import re
+
+import mozrunner
+from cuddlefish.prefs import DEFAULT_COMMON_PREFS
+from cuddlefish.prefs import DEFAULT_FIREFOX_PREFS
+from cuddlefish.prefs import DEFAULT_THUNDERBIRD_PREFS
+from cuddlefish.prefs import DEFAULT_FENNEC_PREFS
+
+# Used to remove noise from ADB output
+CLEANUP_ADB = re.compile(r'^(I|E)/(stdout|stderr|GeckoConsole)\s*\(\s*\d+\):\s*(.*)$')
+# Used to filter only messages send by `console` module
+FILTER_ONLY_CONSOLE_FROM_ADB = re.compile(r'^I/(stdout|stderr)\s*\(\s*\d+\):\s*((info|warning|error|debug): .*)$')
+
+# Maximum time we'll wait for tests to finish, in seconds.
+# The purpose of this timeout is to recover from infinite loops. It should be
+# longer than the amount of time any test run takes, including those on slow
+# machines running slow (debug) versions of Firefox.
+RUN_TIMEOUT = 30 * 60 # 30 minutes
+
+# Maximum time we'll wait for tests to emit output, in seconds.
+# The purpose of this timeout is to recover from hangs. It should be longer
+# than the amount of time any test takes to report results.
+OUTPUT_TIMEOUT = 60 # one minute
+
+def follow_file(filename):
+ """
+ Generator that yields the latest unread content from the given
+ file, or None if no new content is available.
+
+ For example:
+
+ >>> f = open('temp.txt', 'w')
+ >>> f.write('hello')
+ >>> f.flush()
+ >>> tail = follow_file('temp.txt')
+ >>> tail.next()
+ 'hello'
+ >>> tail.next() is None
+ True
+ >>> f.write('there')
+ >>> f.flush()
+ >>> tail.next()
+ 'there'
+ >>> f.close()
+ >>> os.remove('temp.txt')
+ """
+
+ last_pos = 0
+ last_size = 0
+ while True:
+ newstuff = None
+ if os.path.exists(filename):
+ size = os.stat(filename).st_size
+ if size > last_size:
+ last_size = size
+ f = open(filename, 'r')
+ f.seek(last_pos)
+ newstuff = f.read()
+ last_pos = f.tell()
+ f.close()
+ yield newstuff
+
+# subprocess.check_output only appeared in python2.7, so this code is taken
+# from python source code for compatibility with py2.5/2.6
+class CalledProcessError(Exception):
+ def __init__(self, returncode, cmd, output=None):
+ self.returncode = returncode
+ self.cmd = cmd
+ self.output = output
+ def __str__(self):
+ return "Command '%s' returned non-zero exit status %d" % (self.cmd, self.returncode)
+
+def check_output(*popenargs, **kwargs):
+ if 'stdout' in kwargs:
+ raise ValueError('stdout argument not allowed, it will be overridden.')
+ process = subprocess.Popen(stdout=subprocess.PIPE, *popenargs, **kwargs)
+ output, unused_err = process.communicate()
+ retcode = process.poll()
+ if retcode:
+ cmd = kwargs.get("args")
+ if cmd is None:
+ cmd = popenargs[0]
+ raise CalledProcessError(retcode, cmd, output=output)
+ return output
+
+
+class FennecProfile(mozrunner.Profile):
+ preferences = {}
+ names = ['fennec']
+
+class FennecRunner(mozrunner.Runner):
+ profile_class = FennecProfile
+
+ names = ['fennec']
+
+ __DARWIN_PATH = '/Applications/Fennec.app/Contents/MacOS/fennec'
+
+ def __init__(self, binary=None, **kwargs):
+ if sys.platform == 'darwin' and binary and binary.endswith('.app'):
+ # Assume it's a Fennec app dir.
+ binary = os.path.join(binary, 'Contents/MacOS/fennec')
+
+ self.__real_binary = binary
+
+ mozrunner.Runner.__init__(self, **kwargs)
+
+ def find_binary(self):
+ if not self.__real_binary:
+ if sys.platform == 'darwin':
+ if os.path.exists(self.__DARWIN_PATH):
+ return self.__DARWIN_PATH
+ self.__real_binary = mozrunner.Runner.find_binary(self)
+ return self.__real_binary
+
+
+class RemoteFennecRunner(mozrunner.Runner):
+ profile_class = FennecProfile
+
+ names = ['fennec']
+
+ _REMOTE_PATH = '/mnt/sdcard/jetpack-profile'
+ _INTENT_PREFIX = 'org.mozilla.'
+
+ _adb_path = None
+
+ def __init__(self, binary=None, **kwargs):
+ # Check that we have a binary set
+ if not binary:
+ raise ValueError("You have to define `--binary` option set to the "
+ "path to your ADB executable.")
+ # Ensure that binary refer to a valid ADB executable
+ output = subprocess.Popen([binary], stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE).communicate()
+ output = "".join(output)
+ if not ("Android Debug Bridge" in output):
+ raise ValueError("`--binary` option should be the path to your "
+ "ADB executable.")
+ self.binary = binary
+
+ mobile_app_name = kwargs['cmdargs'][0]
+ self.profile = kwargs['profile']
+ self._adb_path = binary
+
+ # This pref has to be set to `false` otherwise, we do not receive
+ # output of adb commands!
+ subprocess.call([self._adb_path, "shell",
+ "setprop log.redirect-stdio false"])
+
+ # Android apps are launched by their "intent" name,
+ # Automatically detect already installed firefox by using `pm` program
+ # or use name given as cfx `--mobile-app` argument.
+ intents = self.getIntentNames()
+ if not intents:
+ raise ValueError("Unable to found any Firefox "
+ "application on your device.")
+ elif mobile_app_name:
+ if not mobile_app_name in intents:
+ raise ValueError("Unable to found Firefox application "
+ "with intent name '%s'\n"
+ "Available ones are: %s" %
+ (mobile_app_name, ", ".join(intents)))
+ self._intent_name = self._INTENT_PREFIX + mobile_app_name
+ else:
+ if "firefox" in intents:
+ self._intent_name = self._INTENT_PREFIX + "firefox"
+ elif "firefox_beta" in intents:
+ self._intent_name = self._INTENT_PREFIX + "firefox_beta"
+ elif "firefox_nightly" in intents:
+ self._intent_name = self._INTENT_PREFIX + "firefox_nightly"
+ else:
+ self._intent_name = self._INTENT_PREFIX + intents[0]
+
+ print "Launching mobile application with intent name " + self._intent_name
+
+ # First try to kill firefox if it is already running
+ pid = self.getProcessPID(self._intent_name)
+ if pid != None:
+ print "Killing running Firefox instance ..."
+ subprocess.call([self._adb_path, "shell",
+ "am force-stop " + self._intent_name])
+ time.sleep(2)
+ if self.getProcessPID(self._intent_name) != None:
+ raise Exception("Unable to automatically kill running Firefox" +
+ " instance. Please close it manually before " +
+ "executing cfx.")
+
+ print "Pushing the addon to your device"
+
+ # Create a clean empty profile on the sd card
+ subprocess.call([self._adb_path, "shell", "rm -r " + self._REMOTE_PATH])
+ subprocess.call([self._adb_path, "shell", "mkdir " + self._REMOTE_PATH])
+
+ # Push the profile folder created by mozrunner to the device
+ # (we can't simply use `adb push` as it doesn't copy empty folders)
+ localDir = self.profile.profile
+ remoteDir = self._REMOTE_PATH
+ for root, dirs, files in os.walk(localDir, followlinks='true'):
+ relRoot = os.path.relpath(root, localDir)
+ # Note about os.path usage below:
+ # Local files may be using Windows `\` separators but
+ # remote are always `/`, so we need to convert local ones to `/`
+ for file in files:
+ localFile = os.path.join(root, file)
+ remoteFile = remoteDir.replace("/", os.sep)
+ if relRoot != ".":
+ remoteFile = os.path.join(remoteFile, relRoot)
+ remoteFile = os.path.join(remoteFile, file)
+ remoteFile = "/".join(remoteFile.split(os.sep))
+ subprocess.Popen([self._adb_path, "push", localFile, remoteFile],
+ stderr=subprocess.PIPE).wait()
+ for dir in dirs:
+ targetDir = remoteDir.replace("/", os.sep)
+ if relRoot != ".":
+ targetDir = os.path.join(targetDir, relRoot)
+ targetDir = os.path.join(targetDir, dir)
+ targetDir = "/".join(targetDir.split(os.sep))
+ # `-p` option is not supported on all devices!
+ subprocess.call([self._adb_path, "shell", "mkdir " + targetDir])
+
+ @property
+ def command(self):
+ """Returns the command list to run."""
+ return [self._adb_path,
+ "shell",
+ "am start " +
+ "-a android.activity.MAIN " +
+ "-n " + self._intent_name + "/" + self._intent_name + ".App " +
+ "--es args \"-profile " + self._REMOTE_PATH + "\""
+ ]
+
+ def start(self):
+ subprocess.call(self.command)
+
+ def getProcessPID(self, processName):
+ p = subprocess.Popen([self._adb_path, "shell", "ps"],
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ line = p.stdout.readline()
+ while line:
+ columns = line.split()
+ pid = columns[1]
+ name = columns[-1]
+ line = p.stdout.readline()
+ if processName in name:
+ return pid
+ return None
+
+ def getIntentNames(self):
+ p = subprocess.Popen([self._adb_path, "shell", "pm list packages"],
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ names = []
+ for line in p.stdout.readlines():
+ line = re.sub("(^package:)|\s", "", line)
+ if self._INTENT_PREFIX in line:
+ names.append(line.replace(self._INTENT_PREFIX, ""))
+ return names
+
+
+class XulrunnerAppProfile(mozrunner.Profile):
+ preferences = {}
+ names = []
+
+class XulrunnerAppRunner(mozrunner.Runner):
+ """
+ Runner for any XULRunner app. Can use a Firefox binary in XULRunner
+ mode to execute the app, or can use XULRunner itself. Expects the
+ app's application.ini to be passed in as one of the items in
+ 'cmdargs' in the constructor.
+
+ This class relies a lot on the particulars of mozrunner.Runner's
+ implementation, and does some unfortunate acrobatics to get around
+ some of the class' limitations/assumptions.
+ """
+
+ profile_class = XulrunnerAppProfile
+
+ # This is a default, and will be overridden in the instance if
+ # Firefox is used in XULRunner mode.
+ names = ['xulrunner']
+
+ # Default location of XULRunner on OS X.
+ __DARWIN_PATH = "/Library/Frameworks/XUL.framework/xulrunner-bin"
+ __LINUX_PATH = "/usr/bin/xulrunner"
+
+ # What our application.ini's path looks like if it's part of
+ # an "installed" XULRunner app on OS X.
+ __DARWIN_APP_INI_SUFFIX = '.app/Contents/Resources/application.ini'
+
+ def __init__(self, binary=None, **kwargs):
+ if sys.platform == 'darwin' and binary and binary.endswith('.app'):
+ # Assume it's a Firefox app dir.
+ binary = os.path.join(binary, 'Contents/MacOS/firefox-bin')
+
+ self.__app_ini = None
+ self.__real_binary = binary
+
+ mozrunner.Runner.__init__(self, **kwargs)
+
+ # See if we're using a genuine xulrunner-bin from the XULRunner SDK,
+ # or if we're being asked to use Firefox in XULRunner mode.
+ self.__is_xulrunner_sdk = 'xulrunner' in self.binary
+
+ if sys.platform == 'linux2' and not self.env.get('LD_LIBRARY_PATH'):
+ self.env['LD_LIBRARY_PATH'] = os.path.dirname(self.binary)
+
+ newargs = []
+ for item in self.cmdargs:
+ if 'application.ini' in item:
+ self.__app_ini = item
+ else:
+ newargs.append(item)
+ self.cmdargs = newargs
+
+ if not self.__app_ini:
+ raise ValueError('application.ini not found in cmdargs')
+ if not os.path.exists(self.__app_ini):
+ raise ValueError("file does not exist: '%s'" % self.__app_ini)
+
+ if (sys.platform == 'darwin' and
+ self.binary == self.__DARWIN_PATH and
+ self.__app_ini.endswith(self.__DARWIN_APP_INI_SUFFIX)):
+ # If the application.ini is in an app bundle, then
+ # it could be inside an "installed" XULRunner app.
+ # If this is the case, use the app's actual
+ # binary instead of the XUL framework's, so we get
+ # a proper app icon, etc.
+ new_binary = '/'.join(self.__app_ini.split('/')[:-2] +
+ ['MacOS', 'xulrunner'])
+ if os.path.exists(new_binary):
+ self.binary = new_binary
+
+ @property
+ def command(self):
+ """Returns the command list to run."""
+
+ if self.__is_xulrunner_sdk:
+ return [self.binary, self.__app_ini, '-profile',
+ self.profile.profile]
+ else:
+ return [self.binary, '-app', self.__app_ini, '-profile',
+ self.profile.profile]
+
+ def __find_xulrunner_binary(self):
+ if sys.platform == 'darwin':
+ if os.path.exists(self.__DARWIN_PATH):
+ return self.__DARWIN_PATH
+ if sys.platform == 'linux2':
+ if os.path.exists(self.__LINUX_PATH):
+ return self.__LINUX_PATH
+ return None
+
+ def find_binary(self):
+ # This gets called by the superclass constructor. It will
+ # always get called, even if a binary was passed into the
+ # constructor, because we want to have full control over
+ # what the exact setting of self.binary is.
+
+ if not self.__real_binary:
+ self.__real_binary = self.__find_xulrunner_binary()
+ if not self.__real_binary:
+ dummy_profile = {}
+ runner = mozrunner.FirefoxRunner(profile=dummy_profile)
+ self.__real_binary = runner.find_binary()
+ self.names = runner.names
+ return self.__real_binary
+
+def run_app(harness_root_dir, manifest_rdf, harness_options,
+ app_type, binary=None, profiledir=None, verbose=False,
+ enforce_timeouts=False,
+ logfile=None, addons=None, args=None, extra_environment={},
+ norun=None,
+ used_files=None, enable_mobile=False,
+ mobile_app_name=None):
+ if binary:
+ binary = os.path.expanduser(binary)
+
+ if addons is None:
+ addons = []
+ else:
+ addons = list(addons)
+
+ cmdargs = []
+ preferences = dict(DEFAULT_COMMON_PREFS)
+
+ # For now, only allow running on Mobile with --force-mobile argument
+ if app_type in ["fennec", "fennec-on-device"] and not enable_mobile:
+ print """
+ WARNING: Firefox Mobile support is still experimental.
+ If you would like to run an addon on this platform, use --force-mobile flag:
+
+ cfx --force-mobile"""
+ return 0
+
+ if app_type == "fennec-on-device":
+ profile_class = FennecProfile
+ preferences.update(DEFAULT_FENNEC_PREFS)
+ runner_class = RemoteFennecRunner
+ # We pass the intent name through command arguments
+ cmdargs.append(mobile_app_name)
+ elif enable_mobile or app_type == "fennec":
+ profile_class = FennecProfile
+ preferences.update(DEFAULT_FENNEC_PREFS)
+ runner_class = FennecRunner
+ elif app_type == "xulrunner":
+ profile_class = XulrunnerAppProfile
+ runner_class = XulrunnerAppRunner
+ cmdargs.append(os.path.join(harness_root_dir, 'application.ini'))
+ elif app_type == "firefox":
+ profile_class = mozrunner.FirefoxProfile
+ preferences.update(DEFAULT_FIREFOX_PREFS)
+ runner_class = mozrunner.FirefoxRunner
+ elif app_type == "thunderbird":
+ profile_class = mozrunner.ThunderbirdProfile
+ preferences.update(DEFAULT_THUNDERBIRD_PREFS)
+ runner_class = mozrunner.ThunderbirdRunner
+ else:
+ raise ValueError("Unknown app: %s" % app_type)
+ if sys.platform == 'darwin' and app_type != 'xulrunner':
+ cmdargs.append('-foreground')
+
+ if args:
+ cmdargs.extend(shlex.split(args))
+
+ # TODO: handle logs on remote device
+ if app_type != "fennec-on-device":
+ # tempfile.gettempdir() was constant, preventing two simultaneous "cfx
+ # run"/"cfx test" on the same host. On unix it points at /tmp (which is
+ # world-writeable), enabling a symlink attack (e.g. imagine some bad guy
+ # does 'ln -s ~/.ssh/id_rsa /tmp/harness_result'). NamedTemporaryFile
+ # gives us a unique filename that fixes both problems. We leave the
+ # (0-byte) file in place until the browser-side code starts writing to
+ # it, otherwise the symlink attack becomes possible again.
+ fileno,resultfile = tempfile.mkstemp(prefix="harness-result-")
+ os.close(fileno)
+ harness_options['resultFile'] = resultfile
+
+ def maybe_remove_logfile():
+ if os.path.exists(logfile):
+ os.remove(logfile)
+
+ logfile_tail = None
+
+ # We always buffer output through a logfile for two reasons:
+ # 1. On Windows, it's the only way to print console output to stdout/err.
+ # 2. It enables us to keep track of the last time output was emitted,
+ # so we can raise an exception if the test runner hangs.
+ if not logfile:
+ fileno,logfile = tempfile.mkstemp(prefix="harness-log-")
+ os.close(fileno)
+ logfile_tail = follow_file(logfile)
+ atexit.register(maybe_remove_logfile)
+
+ logfile = os.path.abspath(os.path.expanduser(logfile))
+ maybe_remove_logfile()
+
+ if app_type != "fennec-on-device":
+ harness_options['logFile'] = logfile
+
+ env = {}
+ env.update(os.environ)
+ env['MOZ_NO_REMOTE'] = '1'
+ env['XPCOM_DEBUG_BREAK'] = 'stack'
+ env['NS_TRACE_MALLOC_DISABLE_STACKS'] = '1'
+ env.update(extra_environment)
+ if norun:
+ cmdargs.append("-no-remote")
+
+ # Create the addon XPI so mozrunner will copy it to the profile it creates.
+ # We delete it below after getting mozrunner to create the profile.
+ from cuddlefish.xpi import build_xpi
+ xpi_path = tempfile.mktemp(suffix='cfx-tmp.xpi')
+ build_xpi(template_root_dir=harness_root_dir,
+ manifest=manifest_rdf,
+ xpi_path=xpi_path,
+ harness_options=harness_options,
+ limit_to=used_files)
+ addons.append(xpi_path)
+
+ starttime = last_output_time = time.time()
+
+ # Redirect runner output to a file so we can catch output not generated
+ # by us.
+ # In theory, we could do this using simple redirection on all platforms
+ # other than Windows, but this way we only have a single codepath to
+ # maintain.
+ fileno,outfile = tempfile.mkstemp(prefix="harness-stdout-")
+ os.close(fileno)
+ outfile_tail = follow_file(outfile)
+ def maybe_remove_outfile():
+ if os.path.exists(outfile):
+ os.remove(outfile)
+ atexit.register(maybe_remove_outfile)
+ outf = open(outfile, "w")
+ popen_kwargs = { 'stdout': outf, 'stderr': outf}
+
+ profile = None
+
+ if app_type == "fennec-on-device":
+ # Install a special addon when we run firefox on mobile device
+ # in order to be able to kill it
+ mydir = os.path.dirname(os.path.abspath(__file__))
+ addon_dir = os.path.join(mydir, "mobile-utils")
+ addons.append(addon_dir)
+
+ # the XPI file is copied into the profile here
+ profile = profile_class(addons=addons,
+ profile=profiledir,
+ preferences=preferences)
+
+ # Delete the temporary xpi file
+ os.remove(xpi_path)
+
+ runner = runner_class(profile=profile,
+ binary=binary,
+ env=env,
+ cmdargs=cmdargs,
+ kp_kwargs=popen_kwargs)
+
+ sys.stdout.flush(); sys.stderr.flush()
+
+ if app_type == "fennec-on-device":
+ if not enable_mobile:
+ print >>sys.stderr, """
+ WARNING: Firefox Mobile support is still experimental.
+ If you would like to run an addon on this platform, use --force-mobile flag:
+
+ cfx --force-mobile"""
+ return 0
+
+ # In case of mobile device, we need to get stdio from `adb logcat` cmd:
+
+ # First flush logs in order to avoid catching previous ones
+ subprocess.call([binary, "logcat", "-c"])
+
+ # Launch adb command
+ runner.start()
+
+ # We can immediatly remove temporary profile folder
+ # as it has been uploaded to the device
+ profile.cleanup()
+ # We are not going to use the output log file
+ outf.close()
+
+ # Then we simply display stdout of `adb logcat`
+ p = subprocess.Popen([binary, "logcat", "stderr:V stdout:V GeckoConsole:V *:S"], stdout=subprocess.PIPE)
+ while True:
+ line = p.stdout.readline()
+ if line == '':
+ break
+ # mobile-utils addon contains an application quit event observer
+ # that will print this string:
+ if "APPLICATION-QUIT" in line:
+ break
+
+ if verbose:
+ # if --verbose is given, we display everything:
+ # All JS Console messages, stdout and stderr.
+ m = CLEANUP_ADB.match(line)
+ if not m:
+ print line.rstrip()
+ continue
+ print m.group(3)
+ else:
+ # Otherwise, display addons messages dispatched through
+ # console.[info, log, debug, warning, error](msg)
+ m = FILTER_ONLY_CONSOLE_FROM_ADB.match(line)
+ if m:
+ print m.group(2)
+
+ print >>sys.stderr, "Program terminated successfully."
+ return 0
+
+
+ print >>sys.stderr, "Using binary at '%s'." % runner.binary
+
+ # Ensure cfx is being used with Firefox 4.0+.
+ # TODO: instead of dying when Firefox is < 4, warn when Firefox is outside
+ # the minVersion/maxVersion boundaries.
+ version_output = check_output(runner.command + ["-v"])
+ # Note: this regex doesn't handle all valid versions in the Toolkit Version
+ # Format <https://developer.mozilla.org/en/Toolkit_version_format>, just the
+ # common subset that we expect Mozilla apps to use.
+ mo = re.search(r"Mozilla (Firefox|Iceweasel|Fennec)\b[^ ]* ((\d+)\.\S*)",
+ version_output)
+ if not mo:
+ # cfx may be used with Thunderbird, SeaMonkey or an exotic Firefox
+ # version.
+ print """
+ WARNING: cannot determine Firefox version; please ensure you are running
+ a Mozilla application equivalent to Firefox 4.0 or greater.
+ """
+ elif mo.group(1) == "Fennec":
+ # For now, only allow running on Mobile with --force-mobile argument
+ if not enable_mobile:
+ print """
+ WARNING: Firefox Mobile support is still experimental.
+ If you would like to run an addon on this platform, use --force-mobile flag:
+
+ cfx --force-mobile"""
+ return
+ else:
+ version = mo.group(3)
+ if int(version) < 4:
+ print """
+ cfx requires Firefox 4 or greater and is unable to find a compatible
+ binary. Please install a newer version of Firefox or provide the path to
+ your existing compatible version with the --binary flag:
+
+ cfx --binary=PATH_TO_FIREFOX_BINARY"""
+ return
+
+ # Set the appropriate extensions.checkCompatibility preference to false,
+ # so the tests run even if the SDK is not marked as compatible with the
+ # version of Firefox on which they are running, and we don't have to
+ # ensure we update the maxVersion before the version of Firefox changes
+ # every six weeks.
+ #
+ # The regex we use here is effectively the same as BRANCH_REGEX from
+ # /toolkit/mozapps/extensions/content/extensions.js, which toolkit apps
+ # use to determine whether or not to load an incompatible addon.
+ #
+ br = re.search(r"^([^\.]+\.[0-9]+[a-z]*).*", mo.group(2), re.I)
+ if br:
+ prefname = 'extensions.checkCompatibility.' + br.group(1)
+ profile.preferences[prefname] = False
+ # Calling profile.set_preferences here duplicates the list of prefs
+ # in prefs.js, since the profile calls self.set_preferences in its
+ # constructor, but that is ok, because it doesn't change the set of
+ # preferences that are ultimately registered in Firefox.
+ profile.set_preferences(profile.preferences)
+
+ print >>sys.stderr, "Using profile at '%s'." % profile.profile
+ sys.stderr.flush()
+
+ if norun:
+ print "To launch the application, enter the following command:"
+ print " ".join(runner.command) + " " + (" ".join(runner.cmdargs))
+ return 0
+
+ runner.start()
+
+ done = False
+ result = None
+ try:
+ while not done:
+ time.sleep(0.05)
+ for tail in (logfile_tail, outfile_tail):
+ if tail:
+ new_chars = tail.next()
+ if new_chars:
+ last_output_time = time.time()
+ sys.stderr.write(new_chars)
+ sys.stderr.flush()
+ if os.path.exists(resultfile):
+ result = open(resultfile).read()
+ if result:
+ if result in ['OK', 'FAIL']:
+ done = True
+ else:
+ sys.stderr.write("Hrm, resultfile (%s) contained something weird (%d bytes)\n" % (resultfile, len(result)))
+ sys.stderr.write("'"+result+"'\n")
+ if enforce_timeouts:
+ if time.time() - last_output_time > OUTPUT_TIMEOUT:
+ raise Exception("Test output exceeded timeout (%ds)." %
+ OUTPUT_TIMEOUT)
+ if time.time() - starttime > RUN_TIMEOUT:
+ raise Exception("Test run exceeded timeout (%ds)." %
+ RUN_TIMEOUT)
+ except:
+ runner.stop()
+ raise
+ else:
+ runner.wait(10)
+ finally:
+ outf.close()
+ if profile:
+ profile.cleanup()
+
+ print >>sys.stderr, "Total time: %f seconds" % (time.time() - starttime)
+
+ if result == 'OK':
+ print >>sys.stderr, "Program terminated successfully."
+ return 0
+ else:
+ print >>sys.stderr, "Program terminated unsuccessfully."
+ return -1
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/templates.py b/tools/addon-sdk-1.12/python-lib/cuddlefish/templates.py
new file mode 100644
index 0000000..808f03d
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/templates.py
@@ -0,0 +1,31 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#Template used by test-main.js
+TEST_MAIN_JS = '''\
+var main = require("main");
+
+exports["test main"] = function(assert) {
+ assert.pass("Unit test running!");
+};
+
+exports["test main async"] = function(assert, done) {
+ assert.pass("async Unit test running!");
+ done();
+};
+
+require("test").run(exports);
+'''
+
+#Template used by package.json
+PACKAGE_JSON = '''\
+{
+ "name": "%(name)s",
+ "fullName": "%(fullName)s",
+ "description": "a basic add-on",
+ "author": "",
+ "license": "MPL 2.0",
+ "version": "0.1"
+}
+'''
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/__init__.py b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/__init__.py
new file mode 100644
index 0000000..c1a7fd2
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/__init__.py
@@ -0,0 +1,65 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import os
+import unittest
+import doctest
+import glob
+
+env_root = os.environ['CUDDLEFISH_ROOT']
+
+def get_tests():
+ import cuddlefish
+ import cuddlefish.tests
+
+ tests = []
+ packages = [cuddlefish, cuddlefish.tests]
+ for package in packages:
+ path = os.path.abspath(package.__path__[0])
+ pynames = glob.glob(os.path.join(path, '*.py'))
+ for filename in pynames:
+ basename = os.path.basename(filename)
+ module_name = os.path.splitext(basename)[0]
+ full_name = "%s.%s" % (package.__name__, module_name)
+ module = __import__(full_name, fromlist=[package.__name__])
+
+ loader = unittest.TestLoader()
+ suite = loader.loadTestsFromModule(module)
+ for test in suite:
+ tests.append(test)
+
+ finder = doctest.DocTestFinder()
+ doctests = finder.find(module)
+ for test in doctests:
+ if len(test.examples) > 0:
+ tests.append(doctest.DocTestCase(test))
+
+ md_dir = os.path.join(env_root, 'dev-guide')
+ doctest_opts = (doctest.NORMALIZE_WHITESPACE |
+ doctest.REPORT_UDIFF)
+ for dirpath, dirnames, filenames in os.walk(md_dir):
+ for filename in filenames:
+ if filename.endswith('.md'):
+ absname = os.path.join(dirpath, filename)
+ tests.append(doctest.DocFileTest(
+ absname,
+ module_relative=False,
+ optionflags=doctest_opts
+ ))
+
+ return tests
+
+def run(verbose=False):
+ if verbose:
+ verbosity = 2
+ else:
+ verbosity = 1
+
+ tests = get_tests()
+ suite = unittest.TestSuite(tests)
+ runner = unittest.TextTestRunner(verbosity=verbosity)
+ return runner.run(suite)
+
+if __name__ == '__main__':
+ run()
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/addons/simplest-test/main.js b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/addons/simplest-test/main.js
new file mode 100644
index 0000000..980ea88
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/addons/simplest-test/main.js
@@ -0,0 +1,17 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const { Cc, Ci } = require("chrome");
+
+exports.main = function(options, callbacks) {
+ // Close Firefox window. Firefox should quit.
+ require("api-utils/window-utils").activeBrowserWindow.close();
+
+ // But not on Mac where it stay alive! We have to request application quit.
+ if (require("api-utils/runtime").OS == "Darwin") {
+ let appStartup = Cc['@mozilla.org/toolkit/app-startup;1'].
+ getService(Ci.nsIAppStartup);
+ appStartup.quit(appStartup.eAttemptQuit);
+ }
+}
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/addons/simplest-test/package.json b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/addons/simplest-test/package.json
new file mode 100644
index 0000000..afbc158
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/addons/simplest-test/package.json
@@ -0,0 +1,6 @@
+{
+ "id": "simplest-test",
+ "directories": {
+ "lib": "."
+ }
+}
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/addons/simplest-test/tests/test-minimal.js b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/addons/simplest-test/tests/test-minimal.js
new file mode 100644
index 0000000..533cd34
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/addons/simplest-test/tests/test-minimal.js
@@ -0,0 +1,7 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+exports.minimalTest = function(test) {
+ test.assert(true);
+};
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-588119-files/packages/explicit-icon/explicit-icon.png b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-588119-files/packages/explicit-icon/explicit-icon.png
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-588119-files/packages/explicit-icon/explicit-icon.png
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-588119-files/packages/explicit-icon/explicit-icon64.png b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-588119-files/packages/explicit-icon/explicit-icon64.png
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-588119-files/packages/explicit-icon/explicit-icon64.png
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-588119-files/packages/explicit-icon/lib/main.js b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-588119-files/packages/explicit-icon/lib/main.js
new file mode 100644
index 0000000..b7e0a1d
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-588119-files/packages/explicit-icon/lib/main.js
@@ -0,0 +1,4 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-588119-files/packages/explicit-icon/package.json b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-588119-files/packages/explicit-icon/package.json
new file mode 100644
index 0000000..8d56d74
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-588119-files/packages/explicit-icon/package.json
@@ -0,0 +1,5 @@
+{
+ "loader": "lib/main.js",
+ "icon": "explicit-icon.png",
+ "icon64": "explicit-icon64.png"
+}
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-588119-files/packages/implicit-icon/icon.png b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-588119-files/packages/implicit-icon/icon.png
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-588119-files/packages/implicit-icon/icon.png
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-588119-files/packages/implicit-icon/icon64.png b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-588119-files/packages/implicit-icon/icon64.png
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-588119-files/packages/implicit-icon/icon64.png
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-588119-files/packages/implicit-icon/lib/main.js b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-588119-files/packages/implicit-icon/lib/main.js
new file mode 100644
index 0000000..b7e0a1d
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-588119-files/packages/implicit-icon/lib/main.js
@@ -0,0 +1,4 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-588119-files/packages/implicit-icon/package.json b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-588119-files/packages/implicit-icon/package.json
new file mode 100644
index 0000000..3f0e241
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-588119-files/packages/implicit-icon/package.json
@@ -0,0 +1,3 @@
+{
+ "loader": "lib/main.js"
+}
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-588119-files/packages/no-icon/lib/main.js b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-588119-files/packages/no-icon/lib/main.js
new file mode 100644
index 0000000..b7e0a1d
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-588119-files/packages/no-icon/lib/main.js
@@ -0,0 +1,4 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-588119-files/packages/no-icon/package.json b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-588119-files/packages/no-icon/package.json
new file mode 100644
index 0000000..3f0e241
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-588119-files/packages/no-icon/package.json
@@ -0,0 +1,3 @@
+{
+ "loader": "lib/main.js"
+}
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-588661-files/packages/bar/lib/bar-loader.js b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-588661-files/packages/bar/lib/bar-loader.js
new file mode 100644
index 0000000..b7e0a1d
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-588661-files/packages/bar/lib/bar-loader.js
@@ -0,0 +1,4 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-588661-files/packages/bar/package.json b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-588661-files/packages/bar/package.json
new file mode 100644
index 0000000..e83a9d4
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-588661-files/packages/bar/package.json
@@ -0,0 +1,3 @@
+{
+ "loader": "lib/bar-loader.js"
+}
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-588661-files/packages/foo/lib/foo-loader.js b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-588661-files/packages/foo/lib/foo-loader.js
new file mode 100644
index 0000000..b7e0a1d
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-588661-files/packages/foo/lib/foo-loader.js
@@ -0,0 +1,4 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-588661-files/packages/foo/package.json b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-588661-files/packages/foo/package.json
new file mode 100644
index 0000000..4648df6
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-588661-files/packages/foo/package.json
@@ -0,0 +1,4 @@
+{
+ "loader": "lib/foo-loader.js",
+ "dependencies": ["bar"]
+}
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-611495-files/jspath-one/docs/main.md b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-611495-files/jspath-one/docs/main.md
new file mode 100644
index 0000000..54518d3
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-611495-files/jspath-one/docs/main.md
@@ -0,0 +1,5 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+minimal docs
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-611495-files/jspath-one/lib/main.js b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-611495-files/jspath-one/lib/main.js
new file mode 100644
index 0000000..aeda0e7
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-611495-files/jspath-one/lib/main.js
@@ -0,0 +1,8 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+exports.main = function(options, callbacks) {
+ console.log("minimal");
+ callbacks.quit();
+};
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-611495-files/jspath-one/package.json b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-611495-files/jspath-one/package.json
new file mode 100644
index 0000000..45d409a
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-611495-files/jspath-one/package.json
@@ -0,0 +1,5 @@
+{
+ "name": "jspath-one",
+ "author": "Jon Smith",
+ "description": "A package w/ a main module; can be built into an extension."
+}
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-614712-files/packages/commonjs-naming/doc/foo.md b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-614712-files/packages/commonjs-naming/doc/foo.md
new file mode 100644
index 0000000..c92ddb8
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-614712-files/packages/commonjs-naming/doc/foo.md
@@ -0,0 +1,5 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+I am documentation for foo.
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-614712-files/packages/commonjs-naming/lib/foo-loader.js b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-614712-files/packages/commonjs-naming/lib/foo-loader.js
new file mode 100644
index 0000000..2daeb35
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-614712-files/packages/commonjs-naming/lib/foo-loader.js
@@ -0,0 +1,5 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-614712-files/packages/commonjs-naming/package.json b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-614712-files/packages/commonjs-naming/package.json
new file mode 100644
index 0000000..ccc61b2
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-614712-files/packages/commonjs-naming/package.json
@@ -0,0 +1,3 @@
+{
+ "loader": "lib/foo-loader.js"
+}
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-614712-files/packages/commonjs-naming/test/test-foo.js b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-614712-files/packages/commonjs-naming/test/test-foo.js
new file mode 100644
index 0000000..bd0cfa9
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-614712-files/packages/commonjs-naming/test/test-foo.js
@@ -0,0 +1,7 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+exports.testThing = function(test) {
+ test.assertEqual(2, 1 + 1);
+};
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-614712-files/packages/original-naming/docs/foo.md b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-614712-files/packages/original-naming/docs/foo.md
new file mode 100644
index 0000000..c92ddb8
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-614712-files/packages/original-naming/docs/foo.md
@@ -0,0 +1,5 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+I am documentation for foo.
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-614712-files/packages/original-naming/lib/foo-loader.js b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-614712-files/packages/original-naming/lib/foo-loader.js
new file mode 100644
index 0000000..2daeb35
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-614712-files/packages/original-naming/lib/foo-loader.js
@@ -0,0 +1,5 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-614712-files/packages/original-naming/package.json b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-614712-files/packages/original-naming/package.json
new file mode 100644
index 0000000..ccc61b2
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-614712-files/packages/original-naming/package.json
@@ -0,0 +1,3 @@
+{
+ "loader": "lib/foo-loader.js"
+}
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-614712-files/packages/original-naming/tests/test-foo.js b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-614712-files/packages/original-naming/tests/test-foo.js
new file mode 100644
index 0000000..bd0cfa9
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-614712-files/packages/original-naming/tests/test-foo.js
@@ -0,0 +1,7 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+exports.testThing = function(test) {
+ test.assertEqual(2, 1 + 1);
+};
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/default-lib/doc/foo.md b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/default-lib/doc/foo.md
new file mode 100644
index 0000000..c92ddb8
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/default-lib/doc/foo.md
@@ -0,0 +1,5 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+I am documentation for foo.
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/default-lib/lib/foo.js b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/default-lib/lib/foo.js
new file mode 100644
index 0000000..2daeb35
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/default-lib/lib/foo.js
@@ -0,0 +1,5 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/default-lib/lib/loader.js b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/default-lib/lib/loader.js
new file mode 100644
index 0000000..2daeb35
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/default-lib/lib/loader.js
@@ -0,0 +1,5 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/default-lib/package.json b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/default-lib/package.json
new file mode 100644
index 0000000..c2d22aa
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/default-lib/package.json
@@ -0,0 +1,3 @@
+{
+ "loader": "lib/loader.js"
+}
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/default-lib/test/test-foo.js b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/default-lib/test/test-foo.js
new file mode 100644
index 0000000..bd0cfa9
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/default-lib/test/test-foo.js
@@ -0,0 +1,7 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+exports.testThing = function(test) {
+ test.assertEqual(2, 1 + 1);
+};
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/default-locale/locale/emptyFolder b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/default-locale/locale/emptyFolder
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/default-locale/locale/emptyFolder
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/default-locale/package.json b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/default-locale/package.json
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/default-locale/package.json
@@ -0,0 +1 @@
+{}
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/default-root/doc/foo.md b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/default-root/doc/foo.md
new file mode 100644
index 0000000..c92ddb8
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/default-root/doc/foo.md
@@ -0,0 +1,5 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+I am documentation for foo.
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/default-root/foo.js b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/default-root/foo.js
new file mode 100644
index 0000000..2daeb35
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/default-root/foo.js
@@ -0,0 +1,5 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/default-root/loader.js b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/default-root/loader.js
new file mode 100644
index 0000000..2daeb35
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/default-root/loader.js
@@ -0,0 +1,5 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/default-root/package.json b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/default-root/package.json
new file mode 100644
index 0000000..100249f
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/default-root/package.json
@@ -0,0 +1,3 @@
+{
+ "loader": "loader.js"
+}
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/default-root/test/test-foo.js b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/default-root/test/test-foo.js
new file mode 100644
index 0000000..bd0cfa9
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/default-root/test/test-foo.js
@@ -0,0 +1,7 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+exports.testThing = function(test) {
+ test.assertEqual(2, 1 + 1);
+};
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-dir-lib/alt-lib/foo.js b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-dir-lib/alt-lib/foo.js
new file mode 100644
index 0000000..2daeb35
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-dir-lib/alt-lib/foo.js
@@ -0,0 +1,5 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-dir-lib/alt-lib/loader.js b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-dir-lib/alt-lib/loader.js
new file mode 100644
index 0000000..2daeb35
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-dir-lib/alt-lib/loader.js
@@ -0,0 +1,5 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-dir-lib/doc/foo.md b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-dir-lib/doc/foo.md
new file mode 100644
index 0000000..c92ddb8
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-dir-lib/doc/foo.md
@@ -0,0 +1,5 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+I am documentation for foo.
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-dir-lib/package.json b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-dir-lib/package.json
new file mode 100644
index 0000000..f79250e
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-dir-lib/package.json
@@ -0,0 +1,4 @@
+{
+ "directories": { "lib": "./alt-lib" },
+ "loader": "alt-lib/loader.js"
+}
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-dir-lib/test/test-foo.js b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-dir-lib/test/test-foo.js
new file mode 100644
index 0000000..bd0cfa9
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-dir-lib/test/test-foo.js
@@ -0,0 +1,7 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+exports.testThing = function(test) {
+ test.assertEqual(2, 1 + 1);
+};
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-lib/alt2-lib/foo.js b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-lib/alt2-lib/foo.js
new file mode 100644
index 0000000..2daeb35
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-lib/alt2-lib/foo.js
@@ -0,0 +1,5 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-lib/alt2-lib/loader.js b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-lib/alt2-lib/loader.js
new file mode 100644
index 0000000..2daeb35
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-lib/alt2-lib/loader.js
@@ -0,0 +1,5 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-lib/doc/foo.md b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-lib/doc/foo.md
new file mode 100644
index 0000000..c92ddb8
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-lib/doc/foo.md
@@ -0,0 +1,5 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+I am documentation for foo.
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-lib/package.json b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-lib/package.json
new file mode 100644
index 0000000..b017baa
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-lib/package.json
@@ -0,0 +1,4 @@
+{
+ "lib": "./alt2-lib",
+ "loader": "alt2-lib/loader.js"
+}
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-lib/test/test-foo.js b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-lib/test/test-foo.js
new file mode 100644
index 0000000..bd0cfa9
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-lib/test/test-foo.js
@@ -0,0 +1,7 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+exports.testThing = function(test) {
+ test.assertEqual(2, 1 + 1);
+};
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-669274-files/packages/extra-options/docs/main.md b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-669274-files/packages/extra-options/docs/main.md
new file mode 100644
index 0000000..54518d3
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-669274-files/packages/extra-options/docs/main.md
@@ -0,0 +1,5 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+minimal docs
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-669274-files/packages/extra-options/lib/main.js b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-669274-files/packages/extra-options/lib/main.js
new file mode 100644
index 0000000..aeda0e7
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-669274-files/packages/extra-options/lib/main.js
@@ -0,0 +1,8 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+exports.main = function(options, callbacks) {
+ console.log("minimal");
+ callbacks.quit();
+};
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-669274-files/packages/extra-options/package.json b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-669274-files/packages/extra-options/package.json
new file mode 100644
index 0000000..de868f7
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-669274-files/packages/extra-options/package.json
@@ -0,0 +1,6 @@
+{
+ "name": "extra-options",
+ "author": "Jon Smith",
+ "description": "A package w/ a main module; can be built into an extension.",
+ "loader": "lib/main.js"
+}
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-715340-files/pkg-1-pack/package.json b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-715340-files/pkg-1-pack/package.json
new file mode 100644
index 0000000..d18aa3d
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-715340-files/pkg-1-pack/package.json
@@ -0,0 +1,10 @@
+{
+ "name": "empty",
+ "license": "MPL 2.0",
+ "author": "",
+ "version": "0.1",
+ "fullName": "empty",
+ "id": "jid1-80fr8b6qeRlQSQ",
+ "unpack": false,
+ "description": "test unpack= support"
+}
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-715340-files/pkg-2-unpack/package.json b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-715340-files/pkg-2-unpack/package.json
new file mode 100644
index 0000000..9bcf1eb
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-715340-files/pkg-2-unpack/package.json
@@ -0,0 +1,10 @@
+{
+ "name": "empty",
+ "license": "MPL 2.0",
+ "author": "",
+ "version": "0.1",
+ "fullName": "empty",
+ "id": "jid1-80fr8b6qeRlQSQ",
+ "unpack": true,
+ "description": "test unpack= support"
+}
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-715340-files/pkg-3-pack/package.json b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-715340-files/pkg-3-pack/package.json
new file mode 100644
index 0000000..45d1d91
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/bug-715340-files/pkg-3-pack/package.json
@@ -0,0 +1,9 @@
+{
+ "name": "empty",
+ "license": "MPL 2.0",
+ "author": "",
+ "version": "0.1",
+ "fullName": "empty",
+ "id": "jid1-80fr8b6qeRlQSQ",
+ "description": "test unpack= support"
+}
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/e10s-adapter-files/packages/foo/lib/bar-e10s-adapter.js b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/e10s-adapter-files/packages/foo/lib/bar-e10s-adapter.js
new file mode 100644
index 0000000..ad54ae7
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/e10s-adapter-files/packages/foo/lib/bar-e10s-adapter.js
@@ -0,0 +1,11 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+if (this.sendMessage) {
+} else {
+ require('bar');
+
+ exports.register = function(process) {
+ };
+}
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/e10s-adapter-files/packages/foo/lib/bar.js b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/e10s-adapter-files/packages/foo/lib/bar.js
new file mode 100644
index 0000000..fe9e4fb
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/e10s-adapter-files/packages/foo/lib/bar.js
@@ -0,0 +1,5 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+require('chrome');
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/e10s-adapter-files/packages/foo/lib/foo.js b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/e10s-adapter-files/packages/foo/lib/foo.js
new file mode 100644
index 0000000..55633d1
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/e10s-adapter-files/packages/foo/lib/foo.js
@@ -0,0 +1,5 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+require('bar');
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/e10s-adapter-files/packages/foo/package.json b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/e10s-adapter-files/packages/foo/package.json
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/e10s-adapter-files/packages/foo/package.json
@@ -0,0 +1 @@
+{}
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/five/lib/main.js b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/five/lib/main.js
new file mode 100644
index 0000000..e32a30f
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/five/lib/main.js
@@ -0,0 +1,5 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+exports.main = "'main' mainly reigns in main(.js)";
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/five/package.json b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/five/package.json
new file mode 100644
index 0000000..98e4b85
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/five/package.json
@@ -0,0 +1,3 @@
+{ "name": "five",
+ "main": "./lib/main"
+}
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/four-deps/four-a/lib/misc.js b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/four-deps/four-a/lib/misc.js
new file mode 100644
index 0000000..7e1ce7e
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/four-deps/four-a/lib/misc.js
@@ -0,0 +1,5 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+exports.main = 42;
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/four-deps/four-a/package.json b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/four-deps/four-a/package.json
new file mode 100644
index 0000000..3010fae
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/four-deps/four-a/package.json
@@ -0,0 +1,4 @@
+{ "name": "four-a",
+ "directories": {"lib": "lib"},
+ "main": "./topfiles/main.js"
+}
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/four-deps/four-a/topfiles/main.js b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/four-deps/four-a/topfiles/main.js
new file mode 100644
index 0000000..7e1ce7e
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/four-deps/four-a/topfiles/main.js
@@ -0,0 +1,5 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+exports.main = 42;
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/four/lib/main.js b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/four/lib/main.js
new file mode 100644
index 0000000..b95f7bd
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/four/lib/main.js
@@ -0,0 +1,5 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var a = require("four-a");
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/four/package.json b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/four/package.json
new file mode 100644
index 0000000..53180b9
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/four/package.json
@@ -0,0 +1,3 @@
+{ "name": "four",
+ "main": "main"
+}
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/one/lib/main.js b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/one/lib/main.js
new file mode 100644
index 0000000..88fd751
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/one/lib/main.js
@@ -0,0 +1,9 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var panel = require("panel");
+var two = require("two.js");
+var a = require("./two");
+var b = require("sdk/tabs.js");
+var c = require("./subdir/three");
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/one/lib/subdir/three.js b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/one/lib/subdir/three.js
new file mode 100644
index 0000000..b594f3c
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/one/lib/subdir/three.js
@@ -0,0 +1,6 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+exports.foo = 1;
+var main = require("../main");
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/one/lib/two.js b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/one/lib/two.js
new file mode 100644
index 0000000..9765219
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/one/lib/two.js
@@ -0,0 +1,8 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+// this ought to find our sibling, not packages/development-mode/lib/main.js
+var main = require("main");
+exports.foo = 1;
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/one/package.json b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/one/package.json
new file mode 100644
index 0000000..edd2b17
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/one/package.json
@@ -0,0 +1,4 @@
+{ "name": "one",
+ "id": "jid1@jetpack",
+ "main": "main"
+}
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/seven/data/text.data b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/seven/data/text.data
new file mode 100644
index 0000000..1269488
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/seven/data/text.data
@@ -0,0 +1 @@
+data
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/seven/lib/main.js b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/seven/lib/main.js
new file mode 100644
index 0000000..b860821
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/seven/lib/main.js
@@ -0,0 +1,6 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var self = require("self"); // trigger inclusion of data
+exports.main = function () { console.log("main"); };
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/seven/lib/unused.js b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/seven/lib/unused.js
new file mode 100644
index 0000000..a7b1c14
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/seven/lib/unused.js
@@ -0,0 +1,5 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+exports.unused = "just pretend I'm not here";
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/seven/package.json b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/seven/package.json
new file mode 100644
index 0000000..922c77d
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/seven/package.json
@@ -0,0 +1,4 @@
+{
+ "name": "seven",
+ "id": "jid7"
+}
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/six/lib/unused.js b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/six/lib/unused.js
new file mode 100644
index 0000000..ada31ef
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/six/lib/unused.js
@@ -0,0 +1,5 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+exports.unused = "I am.";
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/six/package.json b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/six/package.json
new file mode 100644
index 0000000..906b249
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/six/package.json
@@ -0,0 +1,3 @@
+{ "name": "six",
+ "main": "./unreachable"
+}
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/six/unreachable.js b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/six/unreachable.js
new file mode 100644
index 0000000..e8b229c
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/six/unreachable.js
@@ -0,0 +1,5 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+exports.main = "I am outside lib/ and cannot be reached, yet";
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/lib/main.js b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/lib/main.js
new file mode 100644
index 0000000..cee7380
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/lib/main.js
@@ -0,0 +1,8 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+exports.main = 42;
+require("./subdir/subfile");
+require("self"); // trigger inclusion of our data/ directory
+
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/lib/subdir/subfile.js b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/lib/subdir/subfile.js
new file mode 100644
index 0000000..aec24d0
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/lib/subdir/subfile.js
@@ -0,0 +1,5 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+exports.main = "I should be included in a subdir";
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/lib/unused.js b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/lib/unused.js
new file mode 100644
index 0000000..36c4a4e
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/lib/unused.js
@@ -0,0 +1,5 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+exports.main = "unused, linker should not include me in the XPI";
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/locale/fr-FR.properties b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/locale/fr-FR.properties
new file mode 100644
index 0000000..980ac46
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/locale/fr-FR.properties
@@ -0,0 +1,5 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+Yes= Oui
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/package.json b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/package.json
new file mode 100644
index 0000000..6b796fc
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/package.json
@@ -0,0 +1,3 @@
+{ "name": "three-a",
+ "main": "./lib/main.js"
+}
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three-deps/three-b/lib/main.js b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three-deps/three-b/lib/main.js
new file mode 100644
index 0000000..7e1ce7e
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three-deps/three-b/lib/main.js
@@ -0,0 +1,5 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+exports.main = 42;
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three-deps/three-b/locale/fr-FR.properties b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three-deps/three-b/locale/fr-FR.properties
new file mode 100644
index 0000000..c1bf146
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three-deps/three-b/locale/fr-FR.properties
@@ -0,0 +1,6 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+No= Non
+one= un
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three-deps/three-b/package.json b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three-deps/three-b/package.json
new file mode 100644
index 0000000..c0ff5ee
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three-deps/three-b/package.json
@@ -0,0 +1,3 @@
+{ "name": "three-b",
+ "main": "./lib/main"
+}
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three-deps/three-c/lib/main.js b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three-deps/three-c/lib/main.js
new file mode 100644
index 0000000..7e1ce7e
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three-deps/three-c/lib/main.js
@@ -0,0 +1,5 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+exports.main = 42;
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three-deps/three-c/lib/sub/foo.js b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three-deps/three-c/lib/sub/foo.js
new file mode 100644
index 0000000..5878496
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three-deps/three-c/lib/sub/foo.js
@@ -0,0 +1,6 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+exports.foo = "you found me down here";
+
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three-deps/three-c/locale/fr-FR.properties b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three-deps/three-c/locale/fr-FR.properties
new file mode 100644
index 0000000..dac3f13
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three-deps/three-c/locale/fr-FR.properties
@@ -0,0 +1,9 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+No= Nein
+What?= Quoi?
+plural=other
+plural[one]=one
+uft8_value=é
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three-deps/three-c/package.json b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three-deps/three-c/package.json
new file mode 100644
index 0000000..169c914
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three-deps/three-c/package.json
@@ -0,0 +1,3 @@
+{ "name": "three-c",
+ "main": "lib/main"
+}
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three/data/msg.txt b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three/data/msg.txt
new file mode 100644
index 0000000..3b18e51
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three/data/msg.txt
@@ -0,0 +1 @@
+hello world
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three/data/subdir/submsg.txt b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three/data/subdir/submsg.txt
new file mode 100644
index 0000000..d2cfe80
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three/data/subdir/submsg.txt
@@ -0,0 +1 @@
+hello subdir
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three/lib/main.js b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three/lib/main.js
new file mode 100644
index 0000000..4f59443
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three/lib/main.js
@@ -0,0 +1,8 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var a = require("three-a");
+var b = require("three-b");
+var c = require("three-c");
+var c3 = require("three-c/sub/foo");
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three/package.json b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three/package.json
new file mode 100644
index 0000000..cbfbc5b
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three/package.json
@@ -0,0 +1,3 @@
+{ "name": "three",
+ "main": "main"
+}
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three/tests/nontest.js b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three/tests/nontest.js
new file mode 100644
index 0000000..edbc08e
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three/tests/nontest.js
@@ -0,0 +1,5 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// dummy
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three/tests/test-one.js b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three/tests/test-one.js
new file mode 100644
index 0000000..edbc08e
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three/tests/test-one.js
@@ -0,0 +1,5 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// dummy
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three/tests/test-two.js b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three/tests/test-two.js
new file mode 100644
index 0000000..edbc08e
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/linker-files/three/tests/test-two.js
@@ -0,0 +1,5 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// dummy
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/preferences-files/packages/no-prefs/lib/main.js b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/preferences-files/packages/no-prefs/lib/main.js
new file mode 100644
index 0000000..b7e0a1d
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/preferences-files/packages/no-prefs/lib/main.js
@@ -0,0 +1,4 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/preferences-files/packages/no-prefs/package.json b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/preferences-files/packages/no-prefs/package.json
new file mode 100644
index 0000000..f1d51b0
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/preferences-files/packages/no-prefs/package.json
@@ -0,0 +1,6 @@
+{
+ "id": "jid1-fZHqN9JfrDBa8A",
+ "fullName": "No Prefs Test",
+ "author": "Erik Vold",
+ "loader": "lib/main.js"
+}
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/preferences-files/packages/simple-prefs/lib/main.js b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/preferences-files/packages/simple-prefs/lib/main.js
new file mode 100644
index 0000000..b7e0a1d
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/preferences-files/packages/simple-prefs/lib/main.js
@@ -0,0 +1,4 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/preferences-files/packages/simple-prefs/package.json b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/preferences-files/packages/simple-prefs/package.json
new file mode 100644
index 0000000..a6bf06b
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/preferences-files/packages/simple-prefs/package.json
@@ -0,0 +1,50 @@
+{
+ "id": "jid1-fZHqN9JfrDBa8A",
+ "fullName": "Simple Prefs Test",
+ "author": "Erik Vold",
+ "preferences": [{
+ "name": "test",
+ "type": "bool",
+ "title": "tëst",
+ "value": false
+ },
+ {
+ "name": "test2",
+ "type": "string",
+ "title": "tëst",
+ "value": "ünicødé"
+ },
+ {
+ "name": "test3",
+ "type": "menulist",
+ "title": "\"><test",
+ "value": "1",
+ "options": [
+ {
+ "value": "0",
+ "label": "label1"
+ },
+ {
+ "value": "1",
+ "label": "label2"
+ }
+ ]
+ },
+ {
+ "name": "test4",
+ "type": "radio",
+ "title": "tëst",
+ "value": "red",
+ "options": [
+ {
+ "value": "red",
+ "label": "rouge"
+ },
+ {
+ "value": "blue",
+ "label": "bleu"
+ }
+ ]
+ }],
+ "loader": "lib/main.js"
+}
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/doc/dev-guide-source/index.md b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/doc/dev-guide-source/index.md
new file mode 100644
index 0000000..0f03259
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/doc/dev-guide-source/index.md
@@ -0,0 +1,7 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+# An Imposing Title #
+
+*Some words!*
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/doc/dev-guide-source/no_h1.md b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/doc/dev-guide-source/no_h1.md
new file mode 100644
index 0000000..9859f33
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/doc/dev-guide-source/no_h1.md
@@ -0,0 +1,7 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+## A heading ##
+
+*Some words!*
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/doc/module-source/high-level-modules.md b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/doc/module-source/high-level-modules.md
new file mode 100644
index 0000000..8745e13
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/doc/module-source/high-level-modules.md
@@ -0,0 +1,18 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+# High-Level APIs #
+
+Modules in this section implement high-level APIs for
+building add-ons:
+
+* creating user interfaces
+* interacting with the web
+* interacting with the browser
+
+Unless the documentation explicitly says otherwise, all these modules are
+"supported": meaning that they are relatively stable, and that we'll avoid
+making incompatible changes to them unless absolutely necessary.
+
+<ul id="module-index"></ul>
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/doc/module-source/low-level-modules.md b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/doc/module-source/low-level-modules.md
new file mode 100644
index 0000000..bc44007
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/doc/module-source/low-level-modules.md
@@ -0,0 +1,36 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+# Low-Level APIs #
+
+Modules in this section implement low-level APIs. These
+modules fall roughly into three categories:
+
+* fundamental utilities such as
+[collection](modules/sdk/util/collection.html) and
+[url](modules/sdk/url.html). Many add-ons are likely to
+want to use modules from this category.
+
+* building blocks for higher level modules, such as
+[events](modules/sdk/events/core.html),
+[worker](modules/sdk/content/worker.html), and
+[api-utils](modules/sdk/deprecated/api-utils.html). You're more
+likely to use these if you are building your own modules that
+implement new APIs, thus extending the SDK itself.
+
+* privileged modules that expose powerful low-level capabilities
+such as [window/utils](modules/sdk/window/utils.html) and
+[xhr](modules/sdk/net/xhr.html). You can use these
+modules in your add-on if you need to, but should be aware that
+the cost of privileged access is the need to take more elaborate
+security precautions. In many cases these modules have simpler,
+more restricted analogs among the "High-Level APIs" (for
+example, [windows](modules/sdk/windows.html) or
+[request](modules/sdk/request.html)).
+
+These modules are still in active development, and we expect to
+make incompatible changes to them in future releases.
+
+
+<ul id="module-index"></ul>
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/doc/module-source/sdk/aardvark-feeder.md b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/doc/module-source/sdk/aardvark-feeder.md
new file mode 100644
index 0000000..3845955
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/doc/module-source/sdk/aardvark-feeder.md
@@ -0,0 +1,12 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+The `aardvark-feeder` module simplifies feeding aardvarks.
+
+<api name="feed">
+@function
+ Feed the aardvark.
+@param food {string}
+ The food. Aardvarks will eat anything.
+</api>
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/doc/module-source/sdk/anteater/anteater.md b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/doc/module-source/sdk/anteater/anteater.md
new file mode 100644
index 0000000..c480ce3
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/doc/module-source/sdk/anteater/anteater.md
@@ -0,0 +1,10 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+The `anteater` module should not be used by anyone.
+
+<api name="release">
+@function
+ Releases the anteater. Do not call this function.
+</api>
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/doc/module-source/sdk/main.md b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/doc/module-source/sdk/main.md
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/doc/module-source/sdk/main.md
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/doc/module-source/sdk/not_a_doc.js b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/doc/module-source/sdk/not_a_doc.js
new file mode 100644
index 0000000..f4abc3b
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/doc/module-source/sdk/not_a_doc.js
@@ -0,0 +1,5 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+not a doc \ No newline at end of file
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/doc/module-source/third-party-modules.md b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/doc/module-source/third-party-modules.md
new file mode 100644
index 0000000..33e957c
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/doc/module-source/third-party-modules.md
@@ -0,0 +1,11 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+# Third-party APIs #
+
+This section include modules you've added to the SDK. The tutorial on
+[adding menu items to Firefox](dev-guide/tutorials/adding-menus.html)
+explains how to add third-party modules to your SDK installation.
+
+<ul id="module-index"></ul>
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/doc/static-files/another.html b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/doc/static-files/another.html
new file mode 100644
index 0000000..d40a2e8
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/doc/static-files/another.html
@@ -0,0 +1,5 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+another file \ No newline at end of file
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/doc/static-files/base.html b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/doc/static-files/base.html
new file mode 100644
index 0000000..3dd3df1
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/doc/static-files/base.html
@@ -0,0 +1,161 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
+"http://www.w3.org/TR/html4/strict.dtd">
+<html lang="en">
+<head>
+ <base >
+ <meta http-equiv="Content-type" content="text/html; charset=utf-8">
+ <script type="text/javascript" src="static-files/syntaxhighlighter/scripts/shCore.js"></script>
+ <script type="text/javascript" src="static-files/syntaxhighlighter/scripts/shBrushCss.js"></script>
+ <script type="text/javascript" src="static-files/syntaxhighlighter/scripts/shBrushXml.js"></script>
+ <script type="text/javascript" src="static-files/syntaxhighlighter/scripts/shBrushJScript.js"></script>
+ <link rel="stylesheet" type="text/css" media="all" href="static-files/css/base.css">
+ <link rel="stylesheet" type="text/css" media="all" href="static-files/css/header.css">
+ <link rel="stylesheet" type="text/css" media="all" href="static-files/css/footer.css">
+ <link rel="stylesheet" type="text/css" media="all" href="static-files/css/sdk-docs.css">
+ <link rel="stylesheet" type="text/css" media="all" href="static-files/css/api-reference.css">
+ <link rel="stylesheet" type="text/css" href="static-files/syntaxhighlighter/styles/shCore.css">
+ <link rel="stylesheet" type="text/css" href="static-files/syntaxhighlighter/styles/shThemeDefault.css">
+ <!--[if IE]>
+ <style type="text/css">
+ .package-summary .module,
+ .package-entry .module,
+ .package-detail .module {
+ display: block;
+ }
+ </style>
+ <![endif]-->
+
+ <link rel="shortcut icon" type="image/x-icon" href="static-files/media/favicon.png">
+ <title></title>
+</head>
+<body>
+
+<header id="global-header">
+ <div class="funnel">
+ <a id="mozilla-tab" href="http://www.mozilla.org/?ref=logo">Mozilla</a>
+ <div class="menu">
+ <p>
+ <a href="https://builder.addons.mozilla.org/">Add-on Builder</a>
+ </p>
+ <p>
+ <a href="https://addons.mozilla.org/en-US/developers/">Developer Hub</a>
+ </p>
+ </div>
+</header>
+
+
+<header id="site-header">
+ <div class="funnel">
+ <h1>
+ <a href="dev-guide/welcome.html">Add-on SDK<span></span></a>
+ </h1>
+ <div id="version"></div>
+ </div>
+</header>
+
+ <div id="container">
+
+ <div id="columns">
+
+ <div id="main-content-column" class="column">
+ <div id="toc"></div>
+ <div id="main-content"></div>
+ </div>
+
+ <div id="sidebar" class="column">
+ <div class="sidebar-section" id="addon-development">
+ <h2 class="sidebar-section-header">Developer Guide</h2>
+ <ul class="sidebar-section-contents" id="default-section-contents">
+
+ <li class="sidebar-subsection">
+ <a href="dev-guide/tutorials/installation.html"><h3>Installation</h3></a>
+ </li>
+ <li class="sidebar-subsection">
+ <a href="dev-guide/tutorials/tutorials.html"><h3 class="sidebar-subsection-header">Tutorials</h3></a>
+ </li>
+
+ <li class="sidebar-subsection">
+ <a href="dev-guide/guides/index.html"><h3 class="sidebar-subsection-header">Guides</h3></a>
+ </li>
+
+ <li class="sidebar-subsection" id="third-party-packages-subsection">
+ <a href="dev-guide/third-party-apis.html"><h3 class="sidebar-subsection-header">Third-Party APIs</h3></a>
+ <div class="sidebar-subsection-contents">
+ <ul id="third-party-package-summaries"></ul>
+ </div>
+ </li>
+
+ <li class="sidebar-subsection">
+ <a href="dev-guide/high-level-apis.html"><h3 class="sidebar-subsection-header">High-Level APIs</h3></a>
+ <div class="sidebar-subsection-contents">
+ <ul id="high-level-module-summaries"></ul>
+ </div>
+ </li>
+
+ <li class="sidebar-subsection">
+ <h3 class="sidebar-subsection-header">Tools Reference</h3>
+ <div class="sidebar-subsection-contents">
+ <a href="dev-guide/console.html">console</a>
+ <a href="dev-guide/cfx-tool.html">cfx</a>
+ <a href="dev-guide/package-spec.html">package.json</a>
+ </div>
+ </li>
+
+ <li class="sidebar-subsection">
+ <a href="dev-guide/low-level-apis.html"><h3 class="sidebar-subsection-header">Low-Level APIs</h3></a>
+ <div class="sidebar-subsection-contents">
+ <ul id="low-level-module-summaries"></ul>
+ </div>
+ </li>
+
+ </ul>
+ </div>
+
+ <ul class="sidebar-section" id="appendices">
+ <li><a href="https://wiki.mozilla.org/Labs/Jetpack/Release_Notes"><h3>Release Notes</h3></a></li>
+ <li><a href="https://wiki.mozilla.org/Labs/Jetpack"><h3>Jetpack Wiki</h3></a></li>
+ <li><a href="dev-guide/glossary.html"><h3>Glossary</h3></a></li>
+ <li><a href="dev-guide/credits.html"><h3>Credits</h3></a></li>
+
+ </ul>
+<!--end of sidebar column-->
+ </div>
+<!--end of 'columns'-->
+<div class="clearfooter"></div>
+</div>
+</div>
+
+<div role="contentinfo" id="footer">
+ <div class="section">
+ <img alt="" src="static-files/media/footer-logo-med.png" class="footerlogo">
+ <div id="social-footer">
+ <ul>
+ <li>get to know <b>add-ons</b></li>
+ <li><a href="https://addons.mozilla.org/en-US/firefox/about">About</a></li>
+ <li><a href="http://blog.mozilla.com/addons">Blog</a></li>
+ <li class="footer-devhub-link"><a href="https://addons.mozilla.org/en-US/developers/">Developer Hub</a></li>
+ <li><a href="https://addons.mozilla.org/en-US/firefox/faq">FAQ</a></li>
+ <li><a href="https://forums.addons.mozilla.org">Forum</a></li>
+ </ul>
+ </div>
+
+ <div id="copyright">
+ <p id="footer-links">
+ <a href="http://mozilla.com/privacy-policy.html">Privacy Policy</a> &nbsp;|&nbsp;
+ <a href="http://mozilla.com/about/legal.html">Legal Notices</a> &nbsp;|&nbsp;
+ <a href="http://mozilla.com/legal/fraud-report/index.html">Report Trademark Abuse</a>
+ &nbsp;|&nbsp;<a href="https://addons.mozilla.org/z/en-US/developers/" class="mobile-link">View Mobile Site</a>
+ </p>
+ <p>
+ Except where otherwise <a href="http://mozilla.com/about/legal.html#site">noted</a>, content on this site is licensed under the <br> <a href="http://creativecommons.org/licenses/by-sa/3.0/"> Creative Commons Attribution Share-Alike License v3.0 </a> or any later version.
+ </p>
+ </div>
+ </div>
+</div>
+
+<script type="text/javascript" src="static-files/js/jquery.js"></script>
+<script type="text/javascript" src="static-files/js/main.js"></script>
+
+</body>
+
+</html>
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/doc/static-files/index.html b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/doc/static-files/index.html
new file mode 100644
index 0000000..1909a18
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/doc/static-files/index.html
@@ -0,0 +1,27 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<html>
+
+<head>
+
+ <title></title>
+
+</head>
+
+<body>
+
+<div id="left-column">
+
+<ul><li id="high-level-package-summaries"></li></ul>
+
+<ul><li id="low-level-package-summaries"></li></ul>
+
+</div>
+
+<div id="right-column"></div>
+
+</body>
+
+</html>
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/docs/APIreference.html b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/docs/APIreference.html
new file mode 100644
index 0000000..e302641
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/docs/APIreference.html
@@ -0,0 +1,469 @@
+
+<!DOCTYPE html>
+
+<html>
+
+<head>
+
+ <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
+
+ <base target="_blank"/>
+
+ <link rel="stylesheet" type="text/css" media="all"
+
+ href="../../../css/base.css" />
+
+ <link rel="stylesheet" type="text/css" media="all"
+
+ href="../../../css/apidocs.css" />
+
+ <title>Add-on SDK Documentation</title>
+
+ <style type="text/css">
+
+ body {
+
+ border: 50px solid #FFFFFF;
+
+ }
+
+ </style>
+
+
+
+ <script type="text/javascript">
+
+ function rewrite_links() {
+
+ var images = document.getElementsByTagName("img");
+
+ for (var i = 0; i < images.length; i++) {
+
+ var before = images[i].src.split("packages/")[0];
+
+ var after = images[i].src.split("/docs")[1];
+
+ images[i].src = before + after;
+
+ }
+
+ }
+
+ </script>
+
+</head>
+
+
+
+<body onload = "rewrite_links()">
+
+<div id="APIsample_module_api_docs" class="module_api_docs">
+ <h1>APIsample</h1>
+ <div class="module_description"><!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+ <h1>Title</h1>
+ <p>Some text here</p>
+ <p>This text appears between the API blocks.</p>
+ <p>Wooo, more text.</p>
+ <p>Some more text here, at the end of the file.</p>
+ </div>
+
+ <div class="api_reference">
+ <h2 class="api_header">API Reference</h2>
+
+ <div class="api_component_group">
+ <h3 class="api_header">Classes</h3>
+
+ <div class="api_component">
+ <h4 class="api_name">empty-class</h4>
+ <p>This class contains nothing.</p>
+ </div>
+
+ <div class="api_component">
+ <h4 class="api_name">only-one-ctor</h4>
+ <p>This class contains only one constructor.</p>
+ <div class="api_component_group">
+ <h5 class="api_header">Constructors</h5>
+
+ <div class="api_component">
+ <h6 class="api_name">one-constructor(options)</h6>
+
+ <div class="parameter_set">
+ <div class="api_component">
+ <div class="api_name">[ options ]</div>
+ <p>An object-bag of goodies.</p>
+ </div>
+
+ </div>
+
+ </div>
+
+ </div>
+
+ </div>
+
+ <div class="api_component">
+ <h4 class="api_name">two-ctors</h4>
+ <p>This class contains two constructors.</p>
+ <div class="api_component_group">
+ <h5 class="api_header">Constructors</h5>
+
+ <div class="api_component">
+ <h6 class="api_name">one-constructor(options)</h6>
+ <p>The first constructor.</p>
+ <div class="parameter_set">
+ <div class="api_component">
+ <div class="api_name">[ options ]</div>
+ <p>An object-bag of goodies.</p>
+ </div>
+
+ </div>
+
+ </div>
+
+ <div class="api_component">
+ <h6 class="api_name">another-constructor(options)</h6>
+ <p>The second constructor.</p>
+ <div class="parameter_set">
+ <div class="api_component">
+ <div class="api_name">[ options ]</div>
+ <p>An object-bag of goodies.</p>
+ </div>
+
+ </div>
+
+ </div>
+
+ </div>
+
+ </div>
+
+ <div class="api_component">
+ <h4 class="api_name">ctor-and-method</h4>
+ <p>This class contains one constructor and one method.</p>
+ <div class="api_component_group">
+ <h5 class="api_header">Constructors</h5>
+
+ <div class="api_component">
+ <h6 class="api_name">one-constructor(options)</h6>
+ <p>The first constructor.</p>
+ <div class="parameter_set">
+ <div class="api_component">
+ <div class="api_name">[ options ]</div>
+ <p>An object-bag of goodies.</p>
+ </div>
+
+ </div>
+
+ </div>
+
+ </div>
+
+ <div class="api_component_group">
+ <h5 class="api_header">Methods</h5>
+
+ <div class="api_component">
+ <h6 class="api_name">a-method(options)</h6>
+ <p>Does things.</p>
+ <div class="parameter_set">
+ <div class="api_component">
+ <div class="api_name">[ options ]</div>
+ <p>An argument.</p>
+ </div>
+
+ </div>
+
+ </div>
+
+ </div>
+
+ </div>
+
+ <div class="api_component">
+ <h4 class="api_name">ctor-method-prop-event</h4>
+ <p>This class contains one constructor, one method, one property and an event.</p>
+ <div class="api_component_group">
+ <h5 class="api_header">Constructors</h5>
+
+ <div class="api_component">
+ <h6 class="api_name">one-constructor(options)</h6>
+ <p>The first constructor.</p>
+ <div class="parameter_set">
+ <div class="api_component">
+ <div class="api_name">[ options ]</div>
+ <p>An object-bag of goodies.</p>
+ </div>
+
+ </div>
+
+ </div>
+
+ </div>
+
+ <div class="api_component_group">
+ <h5 class="api_header">Methods</h5>
+
+ <div class="api_component">
+ <h6 class="api_name">a-method(options)</h6>
+ <p>Does things.</p>
+ <div class="parameter_set">
+ <div class="api_component">
+ <div class="api_name">[ options ]</div>
+ <p>An argument.</p>
+ </div>
+
+ </div>
+
+ </div>
+
+ </div>
+
+ <div class="api_component_group">
+ <h5 class="api_header">Properties</h5>
+
+ <div class="api_component">
+ <h6 class="api_name">a-property : <span class="datatype">bool</span></h6>
+ <p>Represents stuff.</p>
+ </div>
+
+ </div>
+
+ <div class="api_component_group">
+ <h5 class="api_header">Events</h5>
+
+ <div class="api_component">
+ <h6 class="api_name">message</h6>
+ <p>Event emitted when the content script sends a message to the add-on.</p>
+ <div class="parameter_set">
+ <div class="api_component">
+ <div class="api_name"><span class="datatype">JSON</span></div>
+ <p>The message itself as a JSON-serialized object.</p>
+ </div>
+
+ </div>
+
+ </div>
+
+ </div>
+
+ </div>
+
+ </div>
+
+ <div class="api_component_group">
+ <h3 class="api_header">Functions</h3>
+
+ <div class="api_component">
+ <h4 class="api_name">test(argOne, argTwo, argThree, options)</h4>
+ <p>This is a function which does nothing in particular.</p>
+ <div class="parameter_set">
+ <div class="api_component">
+ <div class="api_name">argOne : <span class="datatype">string</span></div>
+ <p>This is the first argument.</p>
+ </div>
+
+ <div class="api_component">
+ <div class="api_name">[ argTwo : <span class="datatype">bool</span> ]</div>
+ <p>This is the second argument.</p>
+ </div>
+
+ <div class="api_component">
+ <div class="api_name">[ argThree = default : <span class="datatype">uri</span> ]</div>
+ <p>This is the third and final argument. And this is
+ a test of the ability to do multiple lines of
+ text.</p>
+ </div>
+
+ <div class="api_component">
+ <div class="api_name">[ options ]</div>
+ <p>Options Bag</p>
+ <div class="api_component">
+ <div class="api_name">[ style : <span class="datatype">string</span> ]</div>
+ <p>Some style information.</p>
+ </div>
+
+ <div class="api_component">
+ <div class="api_name">[ secondToLastOption = True : <span class="datatype">bool</span> ]</div>
+ <p>The last property.</p>
+ </div>
+
+ <div class="api_component">
+ <div class="api_name">[ lastOption : <span class="datatype">uri</span> ]</div>
+ <p>And this time we have
+ A multiline description
+ Written as haiku</p>
+ </div>
+
+ </div>
+
+ </div>
+
+ <div class="returns">Returns: <span class="datatype">object</span>
+ </div>
+
+ </div>
+
+ <div class="api_component">
+ <h4 class="api_name">append(options)</h4>
+ <p>This is a list of options to specify modifications to your slideBar instance.</p>
+ <div class="parameter_set">
+ <div class="api_component">
+ <div class="api_name">options</div>
+ <p>Pass in all of your options here.</p>
+ <div class="api_component">
+ <div class="api_name">[ icon : <span class="datatype">uri</span> ]</div>
+ <p>The HREF of an icon to show as the method of accessing your features slideBar</p>
+ </div>
+
+ <div class="api_component">
+ <div class="api_name">[ html : <span class="datatype">string/xml</span> ]</div>
+ <p>The content of the feature, either as an HTML string,
+ or an E4X document fragment.</p>
+ </div>
+
+ <div class="api_component">
+ <div class="api_name">[ url : <span class="datatype">uri</span> ]</div>
+ <p>The url to load into the content area of the feature</p>
+ </div>
+
+ <div class="api_component">
+ <div class="api_name">[ width : <span class="datatype">int</span> ]</div>
+ <p>Width of the content area and the selected slide size</p>
+ </div>
+
+ <div class="api_component">
+ <div class="api_name">[ persist : <span class="datatype">bool</span> ]</div>
+ <p>Default slide behavior when being selected as follows:
+ If true: blah; If false: double blah.</p>
+ </div>
+
+ <div class="api_component">
+ <div class="api_name">[ autoReload : <span class="datatype">bool</span> ]</div>
+ <p>Automatically reload content on select</p>
+ </div>
+
+ <div class="api_component">
+ <div class="api_name">[ onClick : <span class="datatype">function</span> ]</div>
+ <p>Callback when the icon is clicked</p>
+ </div>
+
+ <div class="api_component">
+ <div class="api_name">[ onSelect : <span class="datatype">function</span> ]</div>
+ <p>Callback when the feature is selected</p>
+ </div>
+
+ <div class="api_component">
+ <div class="api_name">[ onReady : <span class="datatype">function</span> ]</div>
+ <p>Callback when featured is loaded</p>
+ </div>
+
+ </div>
+
+ </div>
+
+ </div>
+
+ <div class="api_component">
+ <h4 class="api_name">cool-func.dot(howMuch, double, options, onemore, options2)</h4>
+
+ <div class="parameter_set">
+ <div class="api_component">
+ <div class="api_name">howMuch : <span class="datatype">string</span></div>
+ <p>How much cool it is.</p>
+ </div>
+
+ <div class="api_component">
+ <div class="api_name">[ double = true : <span class="datatype">bool</span> ]</div>
+ <p>In case you just really need to double it.</p>
+ </div>
+
+ <div class="api_component">
+ <div class="api_name">[ options ]</div>
+ <p>An object-bag of goodies.</p>
+ <div class="api_component">
+ <div class="api_name">callback : <span class="datatype">function</span></div>
+ <p>The callback</p>
+ </div>
+
+ <div class="api_component">
+ <div class="api_name">[ random : <span class="datatype">bool</span> ]</div>
+ <p>Do something random?</p>
+ </div>
+
+ </div>
+
+ <div class="api_component">
+ <div class="api_name">[ onemore : <span class="datatype">bool</span> ]</div>
+ <p>One more paramater</p>
+ </div>
+
+ <div class="api_component">
+ <div class="api_name">[ options2 ]</div>
+ <p>This is a full description of something
+ that really sucks. Because I now have a multiline
+ description of this thingy.</p>
+ <div class="api_component">
+ <div class="api_name">monkey : <span class="datatype">string</span></div>
+ <p>You heard me right</p>
+ </div>
+
+ <div class="api_component">
+ <div class="api_name">[ freak = true : <span class="datatype">bool</span> ]</div>
+ <p>Yes, you are a freak.</p>
+ </div>
+
+ </div>
+
+ </div>
+
+ <div class="returns">Returns: <span class="datatype">string</span><p>A value telling you just how cool you are.
+ A boa-constructor!
+ This description can go on for a while, and can even contain
+ some <strong>realy</strong> fancy things. Like <code>code</code>, or even
+ ~~~~{.javascript}
+ // Some code!
+ ~~~~</p>
+ </div>
+
+ </div>
+
+ <div class="api_component">
+ <h4 class="api_name">random()</h4>
+ <p>A function that returns a random integer between 0 and 10.</p>
+ <div class="returns">Returns: <span class="datatype">int</span><p>The random number.</p>
+ </div>
+
+ </div>
+
+ </div>
+
+ <div class="api_component_group">
+ <h3 class="api_header">Events</h3>
+
+ <div class="api_component">
+ <h4 class="api_name">open</h4>
+ <p>A module-level event called open.</p>
+ <div class="parameter_set">
+ <div class="api_component">
+ <div class="api_name"><span class="datatype">bool</span></div>
+ <p>Yes, it's open.</p>
+ </div>
+
+ </div>
+
+ </div>
+
+ </div>
+
+ </div>
+
+</div>
+
+</body>
+
+
+
+</html>
+
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/docs/APIsample.md b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/docs/APIsample.md
new file mode 100644
index 0000000..c5090c3
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/docs/APIsample.md
@@ -0,0 +1,162 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+# Title #
+
+Some text here
+
+<api name="test">
+@function
+This is a function which does nothing in particular.
+@returns {object}
+ @prop firststring {string} First string
+ @prop firsturl {url} First URL
+@param argOne {string} This is the first argument.
+@param [argTwo] {bool} This is the second argument.
+@param [argThree=default] {uri}
+ This is the third and final argument. And this is
+ a test of the ability to do multiple lines of
+ text.
+@param [options] Options Bag
+ @prop [style] {string} Some style information.
+ @prop [secondToLastOption=True] {bool} The last property.
+ @prop [lastOption] {uri}
+ And this time we have
+ A multiline description
+ Written as haiku
+</api>
+
+This text appears between the API blocks.
+
+<api name="append">
+@function
+This is a list of options to specify modifications to your slideBar instance.
+@param options
+ Pass in all of your options here.
+ @prop [icon] {uri} The HREF of an icon to show as the method of accessing your features slideBar
+ @prop [html] {string/xml}
+ The content of the feature, either as an HTML string,
+ or an E4X document fragment.
+ @prop [url] {uri} The url to load into the content area of the feature
+ @prop [width] {int} Width of the content area and the selected slide size
+ @prop [persist] {bool}
+ Default slide behavior when being selected as follows:
+ If true: blah; If false: double blah.
+ @prop [autoReload] {bool} Automatically reload content on select
+ @prop [onClick] {function} Callback when the icon is clicked
+ @prop [onSelect] {function} Callback when the feature is selected
+ @prop [onReady] {function} Callback when featured is loaded
+</api>
+
+Wooo, more text.
+
+<api name="cool-func.dot">
+@function
+@returns {string} A value telling you just how cool you are.
+A boa-constructor!
+This description can go on for a while, and can even contain
+some **realy** fancy things. Like `code`, or even
+~~~~{.javascript}
+// Some code!
+~~~~
+@param howMuch {string} How much cool it is.
+@param [double=true] {bool}
+ In case you just really need to double it.
+@param [options] An object-bag of goodies.
+ @prop callback {function} The callback
+ @prop [random] {bool} Do something random?
+@param [onemore] {bool} One more paramater
+@param [options2]
+ This is a full description of something
+ that really sucks. Because I now have a multiline
+ description of this thingy.
+ @prop monkey {string} You heard me right
+ @prop [freak=true] {bool}
+ Yes, you are a freak.
+</api>
+
+<api name="random">
+@function
+A function that returns a random integer between 0 and 10.
+@returns {int} The random number.
+</api>
+
+<api name="empty-class">
+@class
+This class contains nothing.
+</api>
+
+<api name="only-one-ctor">
+@class
+This class contains only one constructor.
+<api name="one-constructor">
+@constructor
+@param [options] An object-bag of goodies.
+</api>
+</api>
+
+<api name="two-ctors">
+@class
+This class contains two constructors.
+<api name="one-constructor">
+@constructor
+The first constructor.
+@param [options] An object-bag of goodies.
+</api>
+<api name="another-constructor">
+@constructor
+The second constructor.
+@param [options] An object-bag of goodies.
+</api>
+</api>
+
+<api name="ctor-and-method">
+@class
+This class contains one constructor and one method.
+<api name="one-constructor">
+@constructor
+The first constructor.
+@param [options] An object-bag of goodies.
+</api>
+<api name="a-method">
+@method
+Does things.
+@param [options] An argument.
+</api>
+</api>
+
+<api name="ctor-method-prop-event">
+@class
+This class contains one constructor, one method, one property and an event.
+<api name="one-constructor">
+@constructor
+The first constructor.
+@param [options] An object-bag of goodies.
+</api>
+<api name="a-method">
+@method
+Does things.
+@param [options] An argument.
+</api>
+<api name="a-property">
+@property {bool}
+Represents stuff.
+</api>
+<api name="message">
+@event
+Event emitted when the content script sends a message to the add-on.
+@argument {JSON}
+The message itself as a JSON-serialized object.
+</api>
+</api>
+
+<api name="open">
+@event
+A module-level event called open.
+@argument {bool}
+Yes, it's open.
+</api>
+
+Some more text here, at the end of the file.
+
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/packages/aardvark/doc/main.md b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/packages/aardvark/doc/main.md
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/packages/aardvark/doc/main.md
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/packages/aardvark/lib/ignore_me b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/packages/aardvark/lib/ignore_me
new file mode 100644
index 0000000..014242c
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/packages/aardvark/lib/ignore_me
@@ -0,0 +1,3 @@
+The docs processor should tolerate (by ignoring) random non-.js files in lib
+directories, such as those left around by editors, version-control systems,
+or OS metadata like .DS_Store . This file exercises that tolerance.
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/packages/aardvark/lib/main.js b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/packages/aardvark/lib/main.js
new file mode 100644
index 0000000..0264872
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/packages/aardvark/lib/main.js
@@ -0,0 +1,8 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+exports.main = function(options, callbacks) {
+ console.log("1 + 1 =", require("bar-module").add(1, 1));
+ callbacks.quit();
+};
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/packages/aardvark/lib/surprise.js/ignore_me_too b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/packages/aardvark/lib/surprise.js/ignore_me_too
new file mode 100644
index 0000000..066f9b5
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/packages/aardvark/lib/surprise.js/ignore_me_too
@@ -0,0 +1,2 @@
+The docs processor should also ignore directories named *.js, and their
+contents.
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/packages/aardvark/package.json b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/packages/aardvark/package.json
new file mode 100644
index 0000000..afb5698
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/packages/aardvark/package.json
@@ -0,0 +1,7 @@
+{
+ "author": "Jon Smith",
+ "description": "A package w/ a main module; can be built into an extension.",
+ "keywords": ["potato"],
+ "version": "1.0",
+ "dependencies": ["addon-sdk", "barbeque"]
+}
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/packages/anteater_files/lib/main.js b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/packages/anteater_files/lib/main.js
new file mode 100644
index 0000000..0264872
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/packages/anteater_files/lib/main.js
@@ -0,0 +1,8 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+exports.main = function(options, callbacks) {
+ console.log("1 + 1 =", require("bar-module").add(1, 1));
+ callbacks.quit();
+};
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/packages/anteater_files/package.json b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/packages/anteater_files/package.json
new file mode 100644
index 0000000..9684581
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/packages/anteater_files/package.json
@@ -0,0 +1,8 @@
+{
+ "name": "anteater",
+ "author": "Jon Smith",
+ "description": "A package w/ a main module; can be built into an extension.",
+ "keywords": ["potato"],
+ "version": "1.0",
+ "dependencies": ["addon-sdk", "barbeque"]
+}
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/packages/api-utils/lib/loader.js b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/packages/api-utils/lib/loader.js
new file mode 100644
index 0000000..361846d
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/packages/api-utils/lib/loader.js
@@ -0,0 +1,7 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// This module will be imported by the XPCOM harness/boostrapper
+// via Components.utils.import() and is responsible for creating a
+// CommonJS module loader.
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/packages/api-utils/package.json b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/packages/api-utils/package.json
new file mode 100644
index 0000000..64eb065
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/packages/api-utils/package.json
@@ -0,0 +1,5 @@
+{
+ "description": "A foundational package that provides a CommonJS module loader implementation.",
+ "keywords": ["potato", "jetpack-low-level"],
+ "loader": "lib/loader.js"
+}
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/packages/barbeque/lib/bar-module.js b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/packages/barbeque/lib/bar-module.js
new file mode 100644
index 0000000..ff982ae
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/packages/barbeque/lib/bar-module.js
@@ -0,0 +1,7 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+exports.add = function add(a, b) {
+ return a + b;
+};
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/packages/barbeque/package.json b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/packages/barbeque/package.json
new file mode 100644
index 0000000..62e3c12
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/packages/barbeque/package.json
@@ -0,0 +1,4 @@
+{
+ "keywords": ["potato", "jetpack-low-level"],
+ "description": "A package used by 'aardvark' as a library."
+}
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/packages/minimal/lib/main.js b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/packages/minimal/lib/main.js
new file mode 100644
index 0000000..aeda0e7
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/packages/minimal/lib/main.js
@@ -0,0 +1,8 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+exports.main = function(options, callbacks) {
+ console.log("minimal");
+ callbacks.quit();
+};
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/packages/minimal/package.json b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/packages/minimal/package.json
new file mode 100644
index 0000000..530f3c2
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/packages/minimal/package.json
@@ -0,0 +1,4 @@
+{
+ "author": "Jon Smith",
+ "description": "A package w/ a main module; can be built into an extension."
+}
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/xpi-template/components/harness.js b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/xpi-template/components/harness.js
new file mode 100644
index 0000000..a20bf3f
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/static-files/xpi-template/components/harness.js
@@ -0,0 +1,8 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// This file contains XPCOM code that bootstraps an SDK-based add-on
+// by loading its harness-options.json, registering all its resource
+// directories, executing its loader, and then executing its program's
+// main() function.
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/test_apiparser.py b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/test_apiparser.py
new file mode 100644
index 0000000..c24f5c3
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/test_apiparser.py
@@ -0,0 +1,538 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+import os
+import unittest
+from cuddlefish.docs.apiparser import parse_hunks, ParseError
+
+tests_path = os.path.abspath(os.path.dirname(__file__))
+static_files_path = os.path.join(tests_path, "static-files")
+
+class ParserTests(unittest.TestCase):
+ def pathname(self, filename):
+ return os.path.join(static_files_path, "docs", filename)
+
+ def parse_text(self, text):
+ return list(parse_hunks(text))
+
+ def parse(self, pathname):
+ return self.parse_text(open(pathname).read())
+
+ def test_parser(self):
+ parsed = self.parse(self.pathname("APIsample.md"))
+ #for i,h in enumerate(parsed):
+ # print i, h
+ self.assertEqual(parsed[0],
+ ("version", 4))
+ self.assertEqual(parsed[1],
+ ("markdown", """\
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+# Title #
+
+Some text here
+
+"""))
+
+ self.assertEqual(parsed[2][0], "api-json")
+ p_test = parsed[2][1]
+ self.assertEqual(p_test["name"], "test")
+ self.assertEqual(p_test["type"], "function")
+ self.assertEqual(p_test["signature"], "test(argOne, argTwo, \
+argThree, options)")
+ self.assertEqual(p_test["description"],
+ "This is a function which does nothing in \
+particular.")
+ r = p_test["returns"]
+ self.assertEqual(r["datatype"], "object")
+ self.assertEqual(r["description"], "")
+ self.assertEqual(len(r["props"]), 2)
+ self.assertEqual(r["props"][0]["datatype"], "string")
+ self.assertEqual(r["props"][0]["description"], "First string")
+ self.assertEqual(r["props"][1]["datatype"], "url")
+ self.assertEqual(r["props"][1]["description"], "First URL")
+
+ self.assertEqual(p_test["params"][0],
+ {"name": "argOne",
+ "required": True,
+ "datatype": "string",
+ "description": "This is the first argument.",
+ "line_number": 15,
+ })
+
+ self.assertEqual(p_test["params"][1],
+ {"name": "argTwo",
+ "required": False,
+ "datatype": "bool",
+ "description": "This is the second argument.",
+ "line_number": 16,
+ })
+
+ self.assertEqual(p_test["params"][2],
+ {"name": "argThree",
+ "required": False,
+ "default": "default",
+ "datatype": "uri",
+ "line_number": 17,
+ "description": """\
+This is the third and final argument. And this is
+a test of the ability to do multiple lines of
+text.""",
+ })
+ p3 = p_test["params"][3]
+ self.assertEqual(p3["name"], "options")
+ self.assertEqual(p3["required"], False)
+ self.failIf("type" in p3)
+ self.assertEqual(p3["description"], "Options Bag")
+ self.assertEqual(p3["props"][0],
+ {"name": "style",
+ "required": False,
+ "datatype": "string",
+ "description": "Some style information.",
+ "line_number": 22,
+ })
+ self.assertEqual(p3["props"][1],
+ {"name": "secondToLastOption",
+ "required": False,
+ "default": "True",
+ "datatype": "bool",
+ "description": "The last property.",
+ "line_number": 23,
+ })
+ self.assertEqual(p3["props"][2]["name"], "lastOption")
+ self.assertEqual(p3["props"][2]["required"], False)
+ self.assertEqual(p3["props"][2]["datatype"], "uri")
+ self.assertEqual(p3["props"][2]["description"], """\
+And this time we have
+A multiline description
+Written as haiku""")
+
+ self.assertEqual(parsed[3][0], "markdown")
+ self.assertEqual(parsed[3][1], "\n\nThis text appears between the \
+API blocks.\n\n")
+
+ self.assertEqual(parsed[4][0], "api-json")
+ p_test = parsed[4][1]
+
+ expected = {'line_number': 32,
+ 'name': 'append',
+ 'params': [{'props':[{'line_number': 37,
+ 'required': False,
+ 'datatype': 'uri',
+ 'name': 'icon',
+ 'description': 'The HREF of an icon to show as the \
+method of accessing your features slideBar'},
+ {'line_number': 38,
+ 'required': False,
+ 'datatype': 'string/xml',
+ 'name': 'html',
+ 'description': 'The content of the feature, either \
+as an HTML string,\nor an E4X document fragment.'},
+ {'line_number': 41,
+ 'required': False,
+ 'datatype': 'uri',
+ 'name': 'url',
+ 'description': 'The url to load into the content area \
+of the feature'},
+ {'line_number': 42,
+ 'required': False,
+ 'datatype': 'int',
+ 'name': 'width',
+ 'description': 'Width of the content area and the \
+selected slide size'},
+ {'line_number': 43,
+ 'required': False,
+ 'datatype': 'bool',
+ 'name': 'persist',
+ 'description': 'Default slide behavior when being \
+selected as follows:\nIf true: blah; If false: double blah.'},
+ {'line_number': 46,
+ 'required': False,
+ 'datatype': 'bool',
+ 'name': 'autoReload',
+ 'description': 'Automatically reload content on \
+select'},
+ {'line_number': 47,
+ 'required': False,
+ 'datatype': 'function',
+ 'name': 'onClick',
+ 'description': 'Callback when the icon is \
+clicked'},
+ {'line_number': 48,
+ 'required': False,
+ 'datatype': 'function',
+ 'name': 'onSelect',
+ 'description': 'Callback when the feature is selected'},
+ {'line_number': 49,
+ 'required': False,
+ 'datatype': 'function',
+ 'name': 'onReady',
+ 'description':
+ 'Callback when featured is loaded'}],
+ 'line_number': 35,
+ 'required': True,
+ 'name': 'options',
+ 'description': 'Pass in all of your options here.'}],
+ 'signature': 'append(options)',
+ 'type': 'function',
+ 'description': 'This is a list of options to specify modifications to your \
+slideBar instance.'}
+ self.assertEqual(p_test, expected)
+
+ self.assertEqual(parsed[6][0], "api-json")
+ p_test = parsed[6][1]
+ self.assertEqual(p_test["name"], "cool-func.dot")
+ self.assertEqual(p_test["signature"], "cool-func.dot(howMuch, double, \
+options, onemore, options2)")
+ self.assertEqual(p_test["returns"]["description"],
+ """\
+A value telling you just how cool you are.
+A boa-constructor!
+This description can go on for a while, and can even contain
+some **realy** fancy things. Like `code`, or even
+~~~~{.javascript}
+// Some code!
+~~~~""")
+ self.assertEqual(p_test["params"][2]["props"][0],
+ {"name": "callback",
+ "required": True,
+ "datatype": "function",
+ "line_number": 67,
+ "description": "The callback",
+ })
+ self.assertEqual(p_test["params"][2]["props"][1],
+ {"name": "random",
+ "required": False,
+ "datatype": "bool",
+ "line_number": 68,
+ "description": "Do something random?",
+ })
+
+ p_test = parsed[8][1]
+ self.assertEqual(p_test["signature"],"random()")
+
+ # tests for classes
+ #1) empty class
+ p_test = parsed[10][1]
+ self.assertEqual(len(p_test), 4)
+ self.assertEqual(p_test["name"], "empty-class")
+ self.assertEqual(p_test["description"], "This class contains nothing.")
+ self.assertEqual(p_test["type"], "class")
+ # 2) class with just one ctor
+ p_test = parsed[12][1]
+ self.assertEqual(len(p_test), 5)
+ self.assertEqual(p_test["name"], "only-one-ctor")
+ self.assertEqual(p_test["description"], "This class contains only \
+one constructor.")
+ self.assertEqual(p_test["type"], "class")
+ constructors = p_test["constructors"]
+ self.assertEqual(len(constructors), 1)
+ self._test_class_constructor(constructors[0], "one-constructor")
+ # 3) class with 2 ctors
+ p_test = parsed[14][1]
+ self.assertEqual(len(p_test), 5)
+ self.assertEqual(p_test["name"], "two-ctors")
+ self.assertEqual(p_test["description"], "This class contains two \
+constructors.")
+ self.assertEqual(p_test["type"], "class")
+ constructors = p_test["constructors"]
+ self.assertEqual(len(constructors), 2)
+ self._test_class_constructor(constructors[0], "one-constructor")
+ self._test_class_constructor(constructors[1], "another-constructor")
+ # 4) class with ctor + method
+ p_test = parsed[16][1]
+ self.assertEqual(len(p_test), 6)
+ self.assertEqual(p_test["name"], "ctor-and-method")
+ self.assertEqual(p_test["description"], "This class contains one \
+constructor and one method.")
+ self.assertEqual(p_test["type"], "class")
+ constructors = p_test["constructors"]
+ self.assertEqual(len(constructors), 1)
+ self._test_class_constructor(constructors[0], "one-constructor")
+ methods = p_test["methods"]
+ self.assertEqual(len(methods), 1)
+ self._test_class_method(methods[0])
+ # 5) class with ctor + method + property
+ p_test = parsed[18][1]
+ self.assertEqual(len(p_test), 8)
+ self.assertEqual(p_test["name"], "ctor-method-prop-event")
+ self.assertEqual(p_test["description"], "This class contains one \
+constructor, one method, one property and an event.")
+ self.assertEqual(p_test["type"], "class")
+ constructors = p_test["constructors"]
+ self.assertEqual(len(constructors), 1)
+ self._test_class_constructor(constructors[0], "one-constructor")
+ methods = p_test["methods"]
+ self.assertEqual(len(methods), 1)
+ self._test_class_method(methods[0])
+ properties = p_test["properties"]
+ self.assertEqual(len(properties), 1)
+ self._test_class_property(properties[0])
+ events = p_test["events"]
+ self.assertEqual(len(events), 1)
+ self._test_class_event(events[0])
+
+ self.assertEqual(parsed[-1][0], "markdown")
+ self.assertEqual(parsed[-1][1], "\n\nSome more text here, \
+at the end of the file.\n\n")
+
+ def _test_class_constructor(self, constructor, name):
+ self.assertEqual(constructor["type"], "constructor")
+ self.assertEqual(constructor["name"], name)
+ params = constructor["params"]
+ self.assertEqual(len(params), 1)
+ self.assertEqual(params[0]["name"], "options")
+ self.assertEqual(params[0]["description"], "An object-bag of goodies.")
+
+ def _test_class_method(self, method):
+ self.assertEqual(method["type"], "method")
+ self.assertEqual(method["name"], "a-method")
+ self.assertEqual(method["description"], "Does things.")
+ params = method["params"]
+ self.assertEqual(len(params), 1)
+ self.assertEqual(params[0]["name"], "options")
+ self.assertEqual(params[0]["description"], "An argument.")
+
+ def _test_class_property(self, prop):
+ self.assertEqual(prop["type"], "property")
+ self.assertEqual(prop["name"], "a-property")
+ self.assertEqual(prop["description"], "Represents stuff.")
+ self.assertEqual(prop["datatype"], "bool")
+
+ def _test_class_event(self, event):
+ self.assertEqual(event["type"], "event")
+ self.assertEqual(event["name"], "message")
+ self.assertEqual(event["description"], "Event emitted when the \
+content script sends a message to the add-on.")
+ arguments = event["arguments"]
+ self.assertEqual(len(arguments), 1)
+ argument = arguments[0]
+ self.assertEqual(argument["datatype"], "JSON")
+ self.assertEqual(argument["description"], "The message itself as a \
+JSON-serialized object.")
+
+ def test_missing_return_propname(self):
+ md = '''\
+<api name="test">
+@method
+This is a function which does nothing in particular.
+@returns {object}
+ @prop {string} First string, but the property name is missing
+ @prop {url} First URL, same problem
+@param argOne {string} This is the first argument.
+</api>
+'''
+ self.assertRaises(ParseError, self.parse_text, md)
+
+ def test_missing_return_proptype(self):
+ md = '''\
+<api name="test">
+@method
+This is a function which does nothing in particular.
+@returns {object}
+ @prop untyped It is an error to omit the type of a return property.
+@param argOne {string} This is the first argument.
+@param [argTwo=True] {bool} This is the second argument.
+</api>
+'''
+ self.assertRaises(ParseError, self.parse_text, md)
+
+ def test_return_propnames(self):
+ md = '''\
+<api name="test">
+@method
+This is a function which does nothing in particular.
+@returns {object}
+ @prop firststring {string} First string.
+ @prop [firsturl] {url} First URL, not always provided.
+@param argOne {string} This is the first argument.
+@param [argTwo=True] {bool} This is the second argument.
+</api>
+'''
+ parsed = self.parse_text(md)
+ r = parsed[1][1]["returns"]
+ self.assertEqual(r["props"][0]["name"], "firststring")
+ self.assertEqual(r["props"][0],
+ {"name": "firststring",
+ "datatype": "string",
+ "description": "First string.",
+ "required": True,
+ "line_number": 5, # 1-indexed
+ })
+ self.assertEqual(r["props"][1],
+ {"name": "firsturl",
+ "datatype": "url",
+ "description": "First URL, not always provided.",
+ "required": False,
+ "line_number": 6,
+ })
+
+ def test_return_description_1(self):
+ md = '''\
+<api name="test">
+@method
+This is a function which does nothing in particular.
+@returns {object} A one-line description.
+ @prop firststring {string} First string.
+ @prop [firsturl] {url} First URL, not always provided.
+@param argOne {string} This is the first argument.
+@param [argTwo=True] {bool} This is the second argument.
+</api>
+'''
+ parsed = self.parse_text(md)
+ r = parsed[1][1]["returns"]
+ self.assertEqual(r["description"], "A one-line description.")
+
+ def test_return_description_2(self):
+ md = '''\
+<api name="test">
+@method
+This is a function which does nothing in particular.
+@returns {object} A six-line description
+ which is consistently indented by two spaces
+ except for this line
+ and preserves the following empty line
+
+ from which a two-space indentation will be removed.
+ @prop firststring {string} First string.
+ @prop [firsturl] {url} First URL, not always provided.
+@param argOne {string} This is the first argument.
+@param [argTwo=True] {bool} This is the second argument.
+</api>
+'''
+ parsed = self.parse_text(md)
+ r = parsed[1][1]["returns"]
+ self.assertEqual(r["description"],
+ "A six-line description\n"
+ "which is consistently indented by two spaces\n"
+ " except for this line\n"
+ "and preserves the following empty line\n"
+ "\n"
+ "from which a two-space indentation will be removed.")
+
+ def test_return_description_3(self):
+ md = '''\
+<api name="test">
+@method
+This is a function which does nothing in particular.
+@returns A one-line untyped description.
+@param argOne {string} This is the first argument.
+@param [argTwo=True] {bool} This is the second argument.
+</api>
+'''
+ parsed = self.parse_text(md)
+ r = parsed[1][1]["returns"]
+ self.assertEqual(r["description"], "A one-line untyped description.")
+
+ # if the return value was supposed to be an array, the correct syntax
+ # would not have any @prop tags:
+ # @returns {array}
+ # Array consists of two elements, a string and a url...
+
+ def test_return_array(self):
+ md = '''\
+<api name="test">
+@method
+This is a function which returns an array.
+@returns {array}
+ Array consists of two elements, a string and a url.
+@param argOne {string} This is the first argument.
+@param [argTwo=True] {bool} This is the second argument.
+</api>
+'''
+ parsed = self.parse_text(md)
+ r = parsed[1][1]["returns"]
+ self.assertEqual(r["description"],
+ "Array consists of two elements, a string and a url.")
+
+ def test_bad_default_on_required_parameter(self):
+ md = '''\
+<api name="test">
+@method
+This is a function which does nothing in particular.
+@returns something
+@param argOne=ILLEGAL {string} Mandatory parameters do not take defaults.
+@param [argTwo=Chicago] {string} This is the second argument.
+</api>
+'''
+ self.assertRaises(ParseError, self.parse_text, md)
+
+ def test_missing_apitype(self):
+ md = '''\
+<api name="test">
+Sorry, you must have a @method or something before the description.
+Putting it after the description is not good enough
+@method
+@returns something
+</api>
+'''
+ self.assertRaises(ParseError, self.parse_text, md)
+
+ def test_missing_param_propname(self):
+ md = '''\
+<api name="test">
+@method
+This is a function which does nothing in particular.
+@param p1 {object} This is a parameter.
+ @prop {string} Oops, props must have a name.
+</api>
+'''
+ self.assertRaises(ParseError, self.parse_text, md)
+
+ def test_missing_param_proptype(self):
+ md = '''\
+<api name="test">
+@method
+This is a function which does nothing in particular.
+@param p1 {object} This is a parameter.
+ @prop name Oops, props must have a type.
+</api>
+'''
+ self.assertRaises(ParseError, self.parse_text, md)
+
+ def test_property(self):
+ md = '''\
+<api name="test">
+@property {foo}
+An object property named test of type foo.
+</api>
+'''
+ parsed = self.parse_text(md)
+ self.assertEqual(parsed[1][0], 'api-json')
+ actual_api_json_obj = parsed[1][1]
+ expected_api_json_obj = {
+ 'line_number': 1,
+ 'datatype': 'foo',
+ 'type': 'property',
+ 'name': 'test',
+ 'description': "An object property named test of type foo."
+ }
+ self.assertEqual(actual_api_json_obj, expected_api_json_obj)
+
+ def test_property_no_type(self):
+ md = '''\
+<api name="test">
+@property
+This property needs to specify a type!
+</api>
+'''
+ self.assertRaises(ParseError, self.parse_text, md)
+
+ def test_missing_api_closing_tag(self):
+ md = '''\
+<api name="test">
+@class
+This is a class with a missing closing tag.
+<api name="doStuff"
+@method
+This method does stuff.
+</api>
+'''
+ self.assertRaises(ParseError, self.parse_text, md)
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/test_apirenderer.py b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/test_apirenderer.py
new file mode 100644
index 0000000..24a1c7c
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/test_apirenderer.py
@@ -0,0 +1,31 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+import os
+import unittest
+from cuddlefish.docs.apirenderer import md_to_html
+
+tests_path = os.path.abspath(os.path.dirname(__file__))
+static_files_path = os.path.join(tests_path, "static-files")
+
+class ParserTests(unittest.TestCase):
+ def pathname(self, filename):
+ return os.path.join(static_files_path, "docs", filename)
+
+ def render_markdown(self, pathname):
+ return md_to_html(pathname)
+
+ def test_renderer(self):
+ test = self.render_markdown(self.pathname("APIsample.md"))
+ reference = open(self.pathname("APIreference.html")).read()
+ test_lines = test.splitlines(True)
+ reference_lines = reference.splitlines(True)
+ for x in range(len(test_lines)):
+ self.assertEqual(test_lines[x], reference_lines[x],
+ "line %d: expected '%s', got '%s'"
+ % (x+1, reference_lines[x], test_lines[x]))
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/test_generate.py b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/test_generate.py
new file mode 100644
index 0000000..901c889
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/test_generate.py
@@ -0,0 +1,181 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import os
+import shutil
+import unittest
+import StringIO
+import tarfile
+import HTMLParser
+import urlparse
+import urllib
+
+from cuddlefish.docs import generate
+from cuddlefish.tests import env_root
+
+INITIAL_FILESET = [ ["static-files", "base.html"], \
+ ["dev-guide", "index.html"], \
+ ["modules", "sdk", "aardvark-feeder.html"], \
+ ["modules", "sdk", "anteater", "anteater.html"]]
+
+EXTENDED_FILESET = [ ["static-files", "base.html"], \
+ ["dev-guide", "extra.html"], \
+ ["dev-guide", "index.html"], \
+ ["modules", "sdk", "aardvark-feeder.html"], \
+ ["modules", "sdk", "anteater", "anteater.html"]]
+
+EXTRAFILE = ["dev-guide", "extra.html"]
+
+def get_test_root():
+ return os.path.join(env_root, "python-lib", "cuddlefish", "tests", "static-files")
+
+def get_sdk_docs_root():
+ return os.path.join(get_test_root(), "sdk-docs")
+
+def get_base_url_path():
+ return os.path.join(get_sdk_docs_root(), "doc")
+
+def url_from_path(path):
+ path = path.lstrip("/")
+ return "file://"+"/"+"/".join(path.split(os.sep))+"/"
+
+def get_base_url():
+ return url_from_path(get_base_url_path())
+
+class Link_Checker(HTMLParser.HTMLParser):
+ def __init__(self, tester, filename, base_url):
+ HTMLParser.HTMLParser.__init__(self)
+ self.tester = tester
+ self.filename = filename
+ self.base_url = base_url
+ self.errors = []
+
+ def handle_starttag(self, tag, attrs):
+ link = self.find_link(attrs)
+ if link:
+ self.validate_link(link)
+
+ def handle_startendtag(self, tag, attrs):
+ link = self.find_link(attrs)
+ if link:
+ self.validate_link(link)
+
+ def find_link(self, attrs):
+ attrs = dict(attrs)
+ href = attrs.get('href', '')
+ if href:
+ parsed = urlparse.urlparse(href)
+ if not parsed.scheme:
+ return href
+ src = attrs.get('src', '')
+ if src:
+ parsed = urlparse.urlparse(src)
+ if not parsed.scheme:
+ return src
+
+ def validate_link(self, link):
+ parsed = urlparse.urlparse(link)
+ # there should not be any file:// URLs
+ self.tester.assertNotEqual(parsed.scheme, "file")
+ # any other absolute URLs will not be checked
+ if parsed.scheme:
+ return
+ current_path_as_url = url_from_path(os.path.dirname(self.filename))
+ # otherwise try to open the file at: baseurl + path
+ absolute_url = current_path_as_url + parsed.path
+ try:
+ urllib.urlopen(absolute_url)
+ except IOError:
+ self.errors.append(self.filename + "\n " + absolute_url)
+
+class Generate_Docs_Tests(unittest.TestCase):
+
+ def test_generate_static_docs(self):
+
+ def cleanup():
+ shutil.rmtree(get_base_url_path())
+ tgz.close()
+ os.remove(tar_filename)
+ generate.clean_generated_docs(os.path.join(env_root, "doc"))
+
+ # make sure we start clean
+ if os.path.exists(get_base_url_path()):
+ shutil.rmtree(get_base_url_path())
+ # generate a doc tarball, and extract it
+ base_url = get_base_url()
+ tar_filename = generate.generate_static_docs(env_root)
+ tgz = tarfile.open(tar_filename)
+ tgz.extractall(get_sdk_docs_root())
+ broken_links = []
+ # get each HTML file...
+ for root, subFolders, filenames in os.walk(get_sdk_docs_root()):
+ for filename in filenames:
+ if not filename.endswith(".html"):
+ continue
+ if root.endswith("static-files"):
+ continue
+ filename = os.path.join(root, filename)
+ # ...and feed it to the link checker
+ linkChecker = Link_Checker(self, filename, base_url)
+ linkChecker.feed(open(filename, "r").read())
+ broken_links.extend(linkChecker.errors)
+ if broken_links:
+ print
+ print "The following links are broken:"
+ for broken_link in sorted(broken_links):
+ print " "+ broken_link
+
+ cleanup()
+ self.fail("%d links are broken" % len(broken_links))
+
+ cleanup()
+
+ def test_generate_docs(self):
+ test_root = get_test_root()
+ docs_root = os.path.join(test_root, "doc")
+ generate.clean_generated_docs(docs_root)
+ new_digest = self.check_generate_regenerate_cycle(test_root, INITIAL_FILESET)
+ # touching an MD file under sdk **does** cause a regenerate
+ os.utime(os.path.join(test_root, "doc", "module-source", "sdk", "aardvark-feeder.md"), None)
+ new_digest = self.check_generate_regenerate_cycle(test_root, INITIAL_FILESET, new_digest)
+ # touching a non MD file under sdk **does not** cause a regenerate
+ os.utime(os.path.join(test_root, "doc", "module-source", "sdk", "not_a_doc.js"), None)
+ self.check_generate_is_skipped(test_root, INITIAL_FILESET, new_digest)
+ # touching a non MD file under static-files **does not** cause a regenerate
+ os.utime(os.path.join(docs_root, "static-files", "another.html"), None)
+ new_digest = self.check_generate_is_skipped(test_root, INITIAL_FILESET, new_digest)
+ # touching an MD file under dev-guide **does** cause a regenerate
+ os.utime(os.path.join(docs_root, "dev-guide-source", "index.md"), None)
+ new_digest = self.check_generate_regenerate_cycle(test_root, INITIAL_FILESET, new_digest)
+ # adding a file **does** cause a regenerate
+ open(os.path.join(docs_root, "dev-guide-source", "extra.md"), "w").write("some content")
+ new_digest = self.check_generate_regenerate_cycle(test_root, EXTENDED_FILESET, new_digest)
+ # deleting a file **does** cause a regenerate
+ os.remove(os.path.join(docs_root, "dev-guide-source", "extra.md"))
+ new_digest = self.check_generate_regenerate_cycle(test_root, INITIAL_FILESET, new_digest)
+ # remove the files
+ generate.clean_generated_docs(docs_root)
+
+ def check_generate_is_skipped(self, test_root, files_to_expect, initial_digest):
+ generate.generate_docs(test_root, stdout=StringIO.StringIO())
+ docs_root = os.path.join(test_root, "doc")
+ for file_to_expect in files_to_expect:
+ self.assertTrue(os.path.exists(os.path.join(docs_root, *file_to_expect)))
+ self.assertTrue(initial_digest == open(os.path.join(docs_root, "status.md5"), "r").read())
+
+ def check_generate_regenerate_cycle(self, test_root, files_to_expect, initial_digest = None):
+ # test that if we generate, files are getting generated
+ generate.generate_docs(test_root, stdout=StringIO.StringIO())
+ docs_root = os.path.join(test_root, "doc")
+ for file_to_expect in files_to_expect:
+ self.assertTrue(os.path.exists(os.path.join(docs_root, *file_to_expect)), os.path.join(docs_root, *file_to_expect) + "not found")
+ if initial_digest:
+ self.assertTrue(initial_digest != open(os.path.join(docs_root, "status.md5"), "r").read())
+ # and that if we regenerate, nothing changes...
+ new_digest = open(os.path.join(docs_root, "status.md5"), "r").read()
+ self.check_generate_is_skipped(test_root, files_to_expect, new_digest)
+ return new_digest
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/test_init.py b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/test_init.py
new file mode 100644
index 0000000..3558048
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/test_init.py
@@ -0,0 +1,197 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import os, unittest, shutil
+from StringIO import StringIO
+from cuddlefish import initializer
+from cuddlefish.templates import TEST_MAIN_JS, PACKAGE_JSON
+
+tests_path = os.path.abspath(os.path.dirname(__file__))
+
+class TestInit(unittest.TestCase):
+
+ def run_init_in_subdir(self, dirname, f, *args, **kwargs):
+ top = os.path.abspath(os.getcwd())
+ basedir = os.path.abspath(os.path.join(".test_tmp",self.id(),dirname))
+ if os.path.isdir(basedir):
+ assert basedir.startswith(top)
+ shutil.rmtree(basedir)
+ os.makedirs(basedir)
+ try:
+ os.chdir(basedir)
+ return f(basedir, *args, **kwargs)
+ finally:
+ os.chdir(top)
+
+ def do_test_init(self,basedir):
+ # Let's init the addon, no error admited
+ f = open(".ignoreme","w")
+ f.write("stuff")
+ f.close()
+
+ out, err = StringIO(), StringIO()
+ init_run = initializer(None, ["init"], out, err)
+ out, err = out.getvalue(), err.getvalue()
+ self.assertEqual(init_run, 0)
+ self.assertTrue("* lib directory created" in out)
+ self.assertTrue("* data directory created" in out)
+ self.assertTrue("Have fun!" in out)
+ self.assertEqual(err,"")
+ self.assertTrue(len(os.listdir(basedir))>0)
+ main_js = os.path.join(basedir,"lib","main.js")
+ package_json = os.path.join(basedir,"package.json")
+ test_main_js = os.path.join(basedir,"test","test-main.js")
+ self.assertTrue(os.path.exists(main_js))
+ self.assertTrue(os.path.exists(package_json))
+ self.assertTrue(os.path.exists(test_main_js))
+ self.assertEqual(open(main_js,"r").read(),"")
+ self.assertEqual(open(package_json,"r").read(),
+ PACKAGE_JSON % {"name":"tmp_addon_sample",
+ "fullName": "tmp_addon_SAMPLE" })
+ self.assertEqual(open(test_main_js,"r").read(),TEST_MAIN_JS)
+
+ # Let's check that the addon is initialized
+ out, err = StringIO(), StringIO()
+ init_run = initializer(None, ["init"], out, err)
+ out, err = out.getvalue(), err.getvalue()
+ self.failIfEqual(init_run,0)
+ self.assertTrue("This command must be run in an empty directory." in err)
+
+ def test_initializer(self):
+ self.run_init_in_subdir("tmp_addon_SAMPLE",self.do_test_init)
+
+ def do_test_args(self, basedir):
+ # check that running it with spurious arguments will fail
+ out,err = StringIO(), StringIO()
+ init_run = initializer(None, ["init", "specified-dirname", "extra-arg"], out, err)
+ out, err = out.getvalue(), err.getvalue()
+ self.failIfEqual(init_run, 0)
+ self.assertTrue("Too many arguments" in err)
+
+ def test_args(self):
+ self.run_init_in_subdir("tmp_addon_sample", self.do_test_args)
+
+ def _test_existing_files(self, basedir):
+ f = open("pay_attention_to_me","w")
+ f.write("stuff")
+ f.close()
+ out,err = StringIO(), StringIO()
+ rc = initializer(None, ["init"], out, err)
+ out, err = out.getvalue(), err.getvalue()
+ self.assertEqual(rc, 1)
+ self.failUnless("This command must be run in an empty directory" in err,
+ err)
+ self.failIf(os.path.exists("lib"))
+
+ def test_existing_files(self):
+ self.run_init_in_subdir("existing_files", self._test_existing_files)
+
+ def test_init_subdir(self):
+ parent = os.path.abspath(os.path.join(".test_tmp", self.id()))
+ basedir = os.path.join(parent, "init-basedir")
+ if os.path.exists(parent):
+ shutil.rmtree(parent)
+ os.makedirs(parent)
+
+ # if the basedir exists and is not empty, init should refuse
+ os.makedirs(basedir)
+ f = open(os.path.join(basedir, "boo"), "w")
+ f.write("stuff")
+ f.close()
+ out, err = StringIO(), StringIO()
+ rc = initializer(None, ["init", basedir], out, err)
+ out, err = out.getvalue(), err.getvalue()
+ self.assertEqual(rc, 1)
+ self.assertTrue("testing if directory is empty" in out, out)
+ self.assertTrue("This command must be run in an empty directory." in err,
+ err)
+
+ # a .dotfile should be tolerated
+ os.rename(os.path.join(basedir, "boo"), os.path.join(basedir, ".phew"))
+ out, err = StringIO(), StringIO()
+ rc = initializer(None, ["init", basedir], out, err)
+ out, err = out.getvalue(), err.getvalue()
+ self.assertEqual(rc, 0)
+ self.assertTrue("* data directory created" in out, out)
+ self.assertTrue("Have fun!" in out)
+ self.assertEqual(err,"")
+ self.assertTrue(os.listdir(basedir))
+ main_js = os.path.join(basedir,"lib","main.js")
+ package_json = os.path.join(basedir,"package.json")
+ self.assertTrue(os.path.exists(main_js))
+ self.assertTrue(os.path.exists(package_json))
+ shutil.rmtree(basedir)
+
+ # init should create directories that don't exist already
+ out, err = StringIO(), StringIO()
+ rc = initializer(None, ["init", basedir], out, err)
+ out, err = out.getvalue(), err.getvalue()
+ self.assertEqual(rc, 0)
+ self.assertTrue("* data directory created" in out)
+ self.assertTrue("Have fun!" in out)
+ self.assertEqual(err,"")
+ self.assertTrue(os.listdir(basedir))
+ main_js = os.path.join(basedir,"lib","main.js")
+ package_json = os.path.join(basedir,"package.json")
+ self.assertTrue(os.path.exists(main_js))
+ self.assertTrue(os.path.exists(package_json))
+
+
+class TestCfxQuits(unittest.TestCase):
+
+ def run_cfx(self, addon_name, command):
+ old_cwd = os.getcwd()
+ addon_path = os.path.join(tests_path,
+ "addons", addon_name)
+ os.chdir(addon_path)
+ import sys
+ old_stdout = sys.stdout
+ old_stderr = sys.stderr
+ sys.stdout = out = StringIO()
+ sys.stderr = err = StringIO()
+ try:
+ import cuddlefish
+ args = list(command)
+ # Pass arguments given to cfx so that cfx can find firefox path
+ # if --binary option is given:
+ args.extend(sys.argv[1:])
+ cuddlefish.run(arguments=args)
+ except SystemExit, e:
+ if "code" in e:
+ rc = e.code
+ elif "args" in e and len(e.args)>0:
+ rc = e.args[0]
+ else:
+ rc = 0
+ finally:
+ sys.stdout = old_stdout
+ sys.stderr = old_stderr
+ os.chdir(old_cwd)
+ out.flush()
+ err.flush()
+ return rc, out.getvalue(), err.getvalue()
+
+ # this method doesn't exists in python 2.5,
+ # implements our own
+ def assertIn(self, member, container):
+ """Just like self.assertTrue(a in b), but with a nicer default message."""
+ if member not in container:
+ standardMsg = '"%s" not found in "%s"' % (member,
+ container)
+ self.fail(standardMsg)
+
+ def test_run(self):
+ rc, out, err = self.run_cfx("simplest-test", ["run"])
+ self.assertEqual(rc, 0)
+ self.assertIn("Program terminated successfully.", err)
+
+ def test_test(self):
+ rc, out, err = self.run_cfx("simplest-test", ["test"])
+ self.assertEqual(rc, 0)
+ self.assertIn("1 of 1 tests passed.", err)
+ self.assertIn("Program terminated successfully.", err)
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/test_licenses.py b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/test_licenses.py
new file mode 100644
index 0000000..60b5957
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/test_licenses.py
@@ -0,0 +1,88 @@
+
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import unittest
+import os.path
+
+parent = os.path.dirname
+test_dir = parent(os.path.abspath(__file__))
+sdk_root = parent(parent(parent(test_dir)))
+
+def from_sdk_top(fn):
+ return os.path.abspath(os.path.join(sdk_root, fn))
+
+MPL2_URL = "http://mozilla.org/MPL/2.0/"
+
+# These files all come with their own license headers
+skip = [
+ "python-lib/cuddlefish/_version.py", # generated, public domain
+ "doc/static-files/js/jquery.js", # MIT/GPL dual
+ "examples/annotator/data/jquery-1.4.2.min.js", # MIT/GPL dual
+ "examples/reddit-panel/data/jquery-1.4.4.min.js", # MIT/GPL dual
+ "examples/library-detector/data/library-detector.js", # MIT
+ "python-lib/mozrunner/killableprocess.py", # MIT? BSDish?
+ "python-lib/mozrunner/winprocess.py", # MIT
+ "packages/api-utils/tests/test-querystring.js", # MIT
+ "packages/api-utils/lib/promise.js", # MIT
+ "packages/api-utils/tests/test-promise.js", # MIT
+ ]
+absskip = [from_sdk_top(os.path.join(*fn.split("/"))) for fn in skip]
+
+class Licenses(unittest.TestCase):
+ def test(self):
+ # Examine most SDK files to check if they've got an MPL2 license
+ # header. We exclude some files that are known to include different
+ # licenses.
+ self.missing = []
+ self.scan_file(from_sdk_top(os.path.join("python-lib", "jetpack_sdk_env.py")))
+ self.scan(os.path.join("python-lib", "cuddlefish"), [".js", ".py"],
+ skipdirs=["sdk-docs"], # test_generate.py makes this
+ )
+ self.scan(os.path.join("python-lib", "mozrunner"), [".py"])
+
+ for sdk_package in ["addon-kit", "api-utils", "test-harness"]:
+ self.scan(os.path.join("packages", sdk_package),
+ [".js", ".py", ".md"])
+ self.scan("examples", [".js", ".css", ".html", ".md"])
+ self.scan("bin", [".bat", ".ps1"])
+ for fn in [os.path.join("bin", "activate"),
+ os.path.join("bin", "cfx"),
+ os.path.join("bin", "integration-scripts", "buildbot-run-cfx-helper"),
+ os.path.join("bin", "integration-scripts", "integration-check"),
+ ]:
+ self.scan_file(from_sdk_top(fn))
+ self.scan("doc", [".js", ".css", ".md"], skipdirs=["syntaxhighlighter"])
+
+ if self.missing:
+ print
+ print "The following files are missing an MPL2 header:"
+ for fn in sorted(self.missing):
+ print " "+fn
+ self.fail("%d files are missing an MPL2 header" % len(self.missing))
+
+ def scan(self, start, extensions=[], skipdirs=[]):
+ # scan a whole subdirectory
+ start = from_sdk_top(start)
+ for root, dirs, files in os.walk(start):
+ for d in skipdirs:
+ if d in dirs:
+ dirs.remove(d)
+ for fn in files:
+ ext = os.path.splitext(fn)[1]
+ if extensions and ext not in extensions:
+ continue
+ absfn = os.path.join(root, fn)
+ if absfn in absskip:
+ continue
+ self.scan_file(absfn)
+
+ def scan_file(self, fn):
+ # scan a single file
+ if not MPL2_URL in open(fn, "r").read():
+ relfile = fn[len(sdk_root)+1:]
+ self.missing.append(relfile)
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/test_linker.py b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/test_linker.py
new file mode 100755
index 0000000..792af6f
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/test_linker.py
@@ -0,0 +1,234 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import os.path
+import shutil
+import zipfile
+from StringIO import StringIO
+import simplejson as json
+import unittest
+import cuddlefish
+from cuddlefish import packaging, manifest
+
+def up(path, generations=1):
+ for i in range(generations):
+ path = os.path.dirname(path)
+ return path
+
+ROOT = up(os.path.abspath(__file__), 4)
+def get_linker_files_dir(name):
+ return os.path.join(up(os.path.abspath(__file__)), "linker-files", name)
+
+class Basic(unittest.TestCase):
+ def get_pkg(self, name):
+ d = get_linker_files_dir(name)
+ return packaging.get_config_in_dir(d)
+
+ def test_deps(self):
+ target_cfg = self.get_pkg("one")
+ pkg_cfg = packaging.build_config(ROOT, target_cfg)
+ deps = packaging.get_deps_for_targets(pkg_cfg, ["one"])
+ self.failUnlessEqual(deps, ["one"])
+ deps = packaging.get_deps_for_targets(pkg_cfg,
+ [target_cfg.name, "addon-sdk"])
+ self.failUnlessEqual(deps, ["addon-sdk", "one"])
+
+ def test_manifest(self):
+ target_cfg = self.get_pkg("one")
+ pkg_cfg = packaging.build_config(ROOT, target_cfg)
+ deps = packaging.get_deps_for_targets(pkg_cfg,
+ [target_cfg.name, "addon-sdk"])
+ self.failUnlessEqual(deps, ["addon-sdk", "one"])
+ # target_cfg.dependencies is not provided, so we'll search through
+ # all known packages (everything in 'deps').
+ m = manifest.build_manifest(target_cfg, pkg_cfg, deps, scan_tests=False)
+ m = m.get_harness_options_manifest()
+
+ def assertReqIs(modname, reqname, path):
+ reqs = m["one/lib/%s.js" % modname]["requirements"]
+ self.failUnlessEqual(reqs[reqname]["path"], path)
+ assertReqIs("main", "panel", "addon-sdk/lib/sdk/panel.js")
+ assertReqIs("main", "two.js", "one/lib/two.js")
+ assertReqIs("main", "./two", "one/lib/two.js")
+ assertReqIs("main", "sdk/tabs.js", "addon-sdk/lib/sdk/tabs.js")
+ assertReqIs("main", "./subdir/three", "one/lib/subdir/three.js")
+ assertReqIs("two", "main", "one/lib/main.js")
+ assertReqIs("subdir/three", "../main", "one/lib/main.js")
+
+ target_cfg.dependencies = []
+ # now, because .dependencies *is* provided, we won't search 'deps',
+ # so we'll get a link error
+ self.assertRaises(manifest.ModuleNotFoundError,
+ manifest.build_manifest,
+ target_cfg, pkg_cfg, deps, scan_tests=False)
+
+ def test_main_in_deps(self):
+ target_cfg = self.get_pkg("three")
+ package_path = [get_linker_files_dir("three-deps")]
+ pkg_cfg = packaging.build_config(ROOT, target_cfg,
+ packagepath=package_path)
+ deps = packaging.get_deps_for_targets(pkg_cfg,
+ [target_cfg.name, "addon-sdk"])
+ self.failUnlessEqual(deps, ["addon-sdk", "three"])
+ m = manifest.build_manifest(target_cfg, pkg_cfg, deps, scan_tests=False)
+ m = m.get_harness_options_manifest()
+ def assertReqIs(modname, reqname, path):
+ reqs = m["three/lib/%s.js" % modname]["requirements"]
+ self.failUnlessEqual(reqs[reqname]["path"], path)
+ assertReqIs("main", "three-a", "three-a/lib/main.js")
+ assertReqIs("main", "three-b", "three-b/lib/main.js")
+ assertReqIs("main", "three-c", "three-c/lib/main.js")
+
+ def test_relative_main_in_top(self):
+ target_cfg = self.get_pkg("five")
+ package_path = []
+ pkg_cfg = packaging.build_config(ROOT, target_cfg,
+ packagepath=package_path)
+ deps = packaging.get_deps_for_targets(pkg_cfg,
+ [target_cfg.name, "addon-sdk"])
+ self.failUnlessEqual(deps, ["addon-sdk", "five"])
+ # all we care about is that this next call doesn't raise an exception
+ m = manifest.build_manifest(target_cfg, pkg_cfg, deps, scan_tests=False)
+ m = m.get_harness_options_manifest()
+ reqs = m["five/lib/main.js"]["requirements"]
+ self.failUnlessEqual(reqs, {});
+
+ def test_unreachable_relative_main_in_top(self):
+ target_cfg = self.get_pkg("six")
+ package_path = []
+ pkg_cfg = packaging.build_config(ROOT, target_cfg,
+ packagepath=package_path)
+ deps = packaging.get_deps_for_targets(pkg_cfg,
+ [target_cfg.name, "addon-sdk"])
+ self.failUnlessEqual(deps, ["addon-sdk", "six"])
+ self.assertRaises(manifest.UnreachablePrefixError,
+ manifest.build_manifest,
+ target_cfg, pkg_cfg, deps, scan_tests=False)
+
+ def test_unreachable_in_deps(self):
+ target_cfg = self.get_pkg("four")
+ package_path = [get_linker_files_dir("four-deps")]
+ pkg_cfg = packaging.build_config(ROOT, target_cfg,
+ packagepath=package_path)
+ deps = packaging.get_deps_for_targets(pkg_cfg,
+ [target_cfg.name, "addon-sdk"])
+ self.failUnlessEqual(deps, ["addon-sdk", "four"])
+ self.assertRaises(manifest.UnreachablePrefixError,
+ manifest.build_manifest,
+ target_cfg, pkg_cfg, deps, scan_tests=False)
+
+class Contents(unittest.TestCase):
+
+ def run_in_subdir(self, dirname, f, *args, **kwargs):
+ top = os.path.abspath(os.getcwd())
+ basedir = os.path.abspath(os.path.join(".test_tmp",self.id(),dirname))
+ if os.path.isdir(basedir):
+ assert basedir.startswith(top)
+ shutil.rmtree(basedir)
+ os.makedirs(basedir)
+ try:
+ os.chdir(basedir)
+ return f(basedir, *args, **kwargs)
+ finally:
+ os.chdir(top)
+
+ def assertIn(self, what, inside_what):
+ self.failUnless(what in inside_what, inside_what)
+
+ def test_jetpackID(self):
+ # this uses "id": "jid7", to which a @jetpack should be appended
+ seven = get_linker_files_dir("seven")
+ def _test(basedir):
+ stdout = StringIO()
+ shutil.copytree(seven, "seven")
+ os.chdir("seven")
+ try:
+ # regrettably, run() always finishes with sys.exit()
+ cuddlefish.run(["xpi", "--no-strip-xpi"],
+ stdout=stdout)
+ except SystemExit, e:
+ self.failUnlessEqual(e.args[0], 0)
+ zf = zipfile.ZipFile("seven.xpi", "r")
+ hopts = json.loads(zf.read("harness-options.json"))
+ self.failUnlessEqual(hopts["jetpackID"], "jid7@jetpack")
+ self.run_in_subdir("x", _test)
+
+ def test_jetpackID_suffix(self):
+ # this uses "id": "jid1@jetpack", so no suffix should be appended
+ one = get_linker_files_dir("one")
+ def _test(basedir):
+ stdout = StringIO()
+ shutil.copytree(one, "one")
+ os.chdir("one")
+ try:
+ # regrettably, run() always finishes with sys.exit()
+ cuddlefish.run(["xpi", "--no-strip-xpi"],
+ stdout=stdout)
+ except SystemExit, e:
+ self.failUnlessEqual(e.args[0], 0)
+ zf = zipfile.ZipFile("one.xpi", "r")
+ hopts = json.loads(zf.read("harness-options.json"))
+ self.failUnlessEqual(hopts["jetpackID"], "jid1@jetpack")
+ self.run_in_subdir("x", _test)
+
+ def test_strip_default(self):
+ seven = get_linker_files_dir("seven")
+ # now run 'cfx xpi' in that directory, except put the generated .xpi
+ # elsewhere
+ def _test(basedir):
+ stdout = StringIO()
+ shutil.copytree(seven, "seven")
+ os.chdir("seven")
+ try:
+ # regrettably, run() always finishes with sys.exit()
+ cuddlefish.run(["xpi"], # --strip-xpi is now the default
+ stdout=stdout)
+ except SystemExit, e:
+ self.failUnlessEqual(e.args[0], 0)
+ zf = zipfile.ZipFile("seven.xpi", "r")
+ names = zf.namelist()
+ # the first problem found in bug 664840 was that cuddlefish.js
+ # (the loader) was stripped out on windows, due to a /-vs-\ bug
+ self.assertIn("resources/addon-sdk/lib/sdk/loader/cuddlefish.js", names)
+ # the second problem found in bug 664840 was that an addon
+ # without an explicit tests/ directory would copy all files from
+ # the package into a bogus JID-PKGNAME-tests/ directory, so check
+ # for that
+ testfiles = [fn for fn in names if "seven/tests" in fn]
+ self.failUnlessEqual([], testfiles)
+ # the third problem was that data files were being stripped from
+ # the XPI. Note that data/ is only supposed to be included if a
+ # module that actually gets used does a require("self") .
+ self.assertIn("resources/seven/data/text.data",
+ names)
+ self.failIf("seven/lib/unused.js"
+ in names, names)
+ self.run_in_subdir("x", _test)
+
+ def test_no_strip(self):
+ seven = get_linker_files_dir("seven")
+ def _test(basedir):
+ stdout = StringIO()
+ shutil.copytree(seven, "seven")
+ os.chdir("seven")
+ try:
+ # regrettably, run() always finishes with sys.exit()
+ cuddlefish.run(["xpi", "--no-strip-xpi"],
+ stdout=stdout)
+ except SystemExit, e:
+ self.failUnlessEqual(e.args[0], 0)
+ zf = zipfile.ZipFile("seven.xpi", "r")
+ names = zf.namelist()
+ self.assertIn("resources/addon-sdk/lib/sdk/loader/cuddlefish.js", names)
+ testfiles = [fn for fn in names if "seven/tests" in fn]
+ self.failUnlessEqual([], testfiles)
+ self.assertIn("resources/seven/data/text.data",
+ names)
+ self.failUnless("resources/seven/lib/unused.js"
+ in names, names)
+ self.run_in_subdir("x", _test)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/test_manifest.py b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/test_manifest.py
new file mode 100644
index 0000000..1bced12
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/test_manifest.py
@@ -0,0 +1,257 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+import unittest
+from StringIO import StringIO
+from cuddlefish.manifest import scan_module
+
+class Extra:
+ def failUnlessKeysAre(self, d, keys):
+ self.failUnlessEqual(sorted(d.keys()), sorted(keys))
+
+class Require(unittest.TestCase, Extra):
+ def scan(self, text):
+ lines = StringIO(text).readlines()
+ requires, problems, locations = scan_module("fake.js", lines)
+ self.failUnlessEqual(problems, False)
+ return requires
+
+ def scan_locations(self, text):
+ lines = StringIO(text).readlines()
+ requires, problems, locations = scan_module("fake.js", lines)
+ self.failUnlessEqual(problems, False)
+ return requires, locations
+
+ def test_modules(self):
+ mod = """var foo = require('one');"""
+ requires = self.scan(mod)
+ self.failUnlessKeysAre(requires, ["one"])
+
+ mod = """var foo = require(\"one\");"""
+ requires = self.scan(mod)
+ self.failUnlessKeysAre(requires, ["one"])
+
+ mod = """var foo=require( 'one' ) ; """
+ requires = self.scan(mod)
+ self.failUnlessKeysAre(requires, ["one"])
+
+ mod = """var foo = require('o'+'ne'); // tricky, denied"""
+ requires = self.scan(mod)
+ self.failUnlessKeysAre(requires, [])
+
+ mod = """require('one').immediately.do().stuff();"""
+ requires, locations = self.scan_locations(mod)
+ self.failUnlessKeysAre(requires, ["one"])
+ self.failUnlessEqual(locations, {"one": 1})
+
+ # these forms are commented out, and thus ignored
+
+ mod = """// var foo = require('one');"""
+ requires = self.scan(mod)
+ self.failUnlessKeysAre(requires, [])
+
+ mod = """/* var foo = require('one');"""
+ requires = self.scan(mod)
+ self.failUnlessKeysAre(requires, [])
+
+ mod = """ * var foo = require('one');"""
+ requires = self.scan(mod)
+ self.failUnlessKeysAre(requires, [])
+
+ mod = """ ' var foo = require('one');"""
+ requires = self.scan(mod)
+ self.failUnlessKeysAre(requires, ["one"])
+
+ mod = """ \" var foo = require('one');"""
+ requires = self.scan(mod)
+ self.failUnlessKeysAre(requires, ["one"])
+
+ # multiple requires
+
+ mod = """const foo = require('one');
+ const foo = require('two');"""
+ requires, locations = self.scan_locations(mod)
+ self.failUnlessKeysAre(requires, ["one", "two"])
+ self.failUnlessEqual(locations["one"], 1)
+ self.failUnlessEqual(locations["two"], 2)
+
+ mod = """const foo = require('repeated');
+ const bar = require('repeated');
+ const baz = require('repeated');"""
+ requires, locations = self.scan_locations(mod)
+ self.failUnlessKeysAre(requires, ["repeated"])
+ self.failUnlessEqual(locations["repeated"], 1) # first occurrence
+
+ mod = """const foo = require('one'); const foo = require('two');"""
+ requires = self.scan(mod)
+ self.failUnlessKeysAre(requires, ["one", "two"])
+
+ # define calls
+
+ mod = """define('one', ['two', 'numbers/three'], function(t, th) {});"""
+ requires = self.scan(mod)
+ self.failUnlessKeysAre(requires, ["two", "numbers/three"])
+
+ mod = """define(
+ ['odd',
+ "numbers/four"], function() {});"""
+ requires = self.scan(mod)
+ self.failUnlessKeysAre(requires, ["odd", "numbers/four"])
+
+ mod = """define(function(require, exports, module) {
+ var a = require("some/module/a"),
+ b = require('b/v1');
+ exports.a = a;
+ //This is a fakeout: require('bad');
+ /* And another var bad = require('bad2'); */
+ require('foo').goFoo();
+ });"""
+ requires = self.scan(mod)
+ self.failUnlessKeysAre(requires, ["some/module/a", "b/v1", "foo"])
+
+ mod = """define (
+ "foo",
+ ["bar"], function (bar) {
+ var me = require("me");
+ }
+ )"""
+ requires = self.scan(mod)
+ self.failUnlessKeysAre(requires, ["bar", "me"])
+
+ mod = """define(['se' + 'ven', 'eight', nine], function () {});"""
+ requires = self.scan(mod)
+ self.failUnlessKeysAre(requires, ["eight"])
+
+ # async require calls
+
+ mod = """require(['one'], function(one) {var o = require("one");});"""
+ requires = self.scan(mod)
+ self.failUnlessKeysAre(requires, ["one"])
+
+ mod = """require([ 'one' ], function(one) {var t = require("two");});"""
+ requires = self.scan(mod)
+ self.failUnlessKeysAre(requires, ["one", "two"])
+
+ mod = """require ( ['two', 'numbers/three'], function(t, th) {});"""
+ requires = self.scan(mod)
+ self.failUnlessKeysAre(requires, ["two", "numbers/three"])
+
+ mod = """require (
+ ["bar", "fa" + 'ke' ], function (bar) {
+ var me = require("me");
+ // require("bad").doBad();
+ }
+ )"""
+ requires = self.scan(mod)
+ self.failUnlessKeysAre(requires, ["bar", "me"])
+
+def scan2(text, fn="fake.js"):
+ stderr = StringIO()
+ lines = StringIO(text).readlines()
+ requires, problems, locations = scan_module(fn, lines, stderr)
+ stderr.seek(0)
+ return requires, problems, stderr.readlines()
+
+class Chrome(unittest.TestCase, Extra):
+
+ def test_ignore_loader(self):
+ # we specifically ignore the loader itself
+ mod = """let {Cc,Ci} = require('chrome');"""
+ requires, problems, err = scan2(mod, "blah/cuddlefish.js")
+ self.failUnlessKeysAre(requires, ["chrome"])
+ self.failUnlessEqual(problems, False)
+ self.failUnlessEqual(err, [])
+
+ def test_chrome(self):
+ mod = """let {Cc,Ci} = require('chrome');"""
+ requires, problems, err = scan2(mod)
+ self.failUnlessKeysAre(requires, ["chrome"])
+ self.failUnlessEqual(problems, False)
+ self.failUnlessEqual(err, [])
+
+ mod = """var foo = require('foo');
+ let {Cc,Ci} = require('chrome');"""
+ requires, problems, err = scan2(mod)
+ self.failUnlessKeysAre(requires, ["foo", "chrome"])
+ self.failUnlessEqual(problems, False)
+ self.failUnlessEqual(err, [])
+
+ mod = """let c = require('chrome');"""
+ requires, problems, err = scan2(mod)
+ self.failUnlessKeysAre(requires, ["chrome"])
+ self.failUnlessEqual(problems, False)
+ self.failUnlessEqual(err, [])
+
+ mod = """var foo = require('foo');
+ let c = require('chrome');"""
+ requires, problems, err = scan2(mod)
+ self.failUnlessKeysAre(requires, ["foo", "chrome"])
+ self.failUnlessEqual(problems, False)
+ self.failUnlessEqual(err, [])
+
+ def test_not_chrome(self):
+ # from bug 596595
+ mod = r'soughtLines: new RegExp("^\\s*(\\[[0-9 .]*\\])?\\s*\\(\\((EE|WW)\\)|.* [Cc]hipsets?: \\)|\\s*Backtrace")'
+ requires, problems, err = scan2(mod)
+ self.failUnlessKeysAre(requires, [])
+ self.failUnlessEqual((problems,err), (False, []))
+
+ def test_not_chrome2(self):
+ # from bug 655788
+ mod = r"var foo = 'some stuff Cr';"
+ requires, problems, err = scan2(mod)
+ self.failUnlessKeysAre(requires, [])
+ self.failUnlessEqual((problems,err), (False, []))
+
+class BadChrome(unittest.TestCase, Extra):
+ def test_bad_alias(self):
+ # using Components.* gets you an error, with a message that teaches
+ # you the correct approach.
+ mod = """let Cc = Components.classes;
+ let Cu = Components.utils;
+ """
+ requires, problems, err = scan2(mod)
+ self.failUnlessKeysAre(requires, [])
+ self.failUnlessEqual(problems, True)
+ self.failUnlessEqual(err[1], "The following lines from file fake.js:\n")
+ self.failUnlessEqual(err[2], " 1: let Cc = Components.classes;\n")
+ self.failUnlessEqual(err[3], " 2: let Cu = Components.utils;\n")
+ self.failUnlessEqual(err[4], "use 'Components' to access chrome authority. To do so, you need to add a\n")
+ self.failUnlessEqual(err[5], "line somewhat like the following:\n")
+ self.failUnlessEqual(err[7], ' const {Cc,Cu} = require("chrome");\n')
+ self.failUnlessEqual(err[9], "Then you can use any shortcuts to its properties that you import from the\n")
+
+ def test_bad_misc(self):
+ # If it looks like you're using something that doesn't have an alias,
+ # the warning also suggests a better way.
+ mod = """if (Components.isSuccessCode(foo))
+ """
+ requires, problems, err = scan2(mod)
+ self.failUnlessKeysAre(requires, [])
+ self.failUnlessEqual(problems, True)
+ self.failUnlessEqual(err[1], "The following lines from file fake.js:\n")
+ self.failUnlessEqual(err[2], " 1: if (Components.isSuccessCode(foo))\n")
+ self.failUnlessEqual(err[3], "use 'Components' to access chrome authority. To do so, you need to add a\n")
+ self.failUnlessEqual(err[4], "line somewhat like the following:\n")
+ self.failUnlessEqual(err[6], ' const {components} = require("chrome");\n')
+ self.failUnlessEqual(err[8], "Then you can use any shortcuts to its properties that you import from the\n")
+
+ def test_chrome_components(self):
+ # Bug 636145/774636: We no longer tolerate usages of "Components",
+ # even when adding `require("chrome")` to your module.
+ mod = """require("chrome");
+ var ios = Components.classes['@mozilla.org/network/io-service;1'];"""
+ requires, problems, err = scan2(mod)
+ self.failUnlessKeysAre(requires, ["chrome"])
+ self.failUnlessEqual(problems, True)
+ self.failUnlessEqual(err[1], "The following lines from file fake.js:\n")
+ self.failUnlessEqual(err[2], " 2: var ios = Components.classes['@mozilla.org/network/io-service;1'];\n")
+ self.failUnlessEqual(err[3], "use 'Components' to access chrome authority. To do so, you need to add a\n")
+ self.failUnlessEqual(err[4], "line somewhat like the following:\n")
+ self.failUnlessEqual(err[6], ' const {Cc} = require("chrome");\n')
+ self.failUnlessEqual(err[8], "Then you can use any shortcuts to its properties that you import from the\n")
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/test_packaging.py b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/test_packaging.py
new file mode 100644
index 0000000..ccd2812
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/test_packaging.py
@@ -0,0 +1,116 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import os
+import unittest
+
+from cuddlefish import packaging
+from cuddlefish.bunch import Bunch
+
+tests_path = os.path.abspath(os.path.dirname(__file__))
+static_files_path = os.path.join(tests_path, 'static-files')
+
+def get_configs(pkg_name, dirname='static-files'):
+ root_path = os.path.join(tests_path, dirname)
+ pkg_path = os.path.join(root_path, 'packages', pkg_name)
+ if not (os.path.exists(pkg_path) and os.path.isdir(pkg_path)):
+ raise Exception('path does not exist: %s' % pkg_path)
+ target_cfg = packaging.get_config_in_dir(pkg_path)
+ pkg_cfg = packaging.build_config(root_path, target_cfg)
+ deps = packaging.get_deps_for_targets(pkg_cfg, [pkg_name])
+ build = packaging.generate_build_for_target(
+ pkg_cfg=pkg_cfg,
+ target=pkg_name,
+ deps=deps
+ )
+ return Bunch(target_cfg=target_cfg, pkg_cfg=pkg_cfg, build=build)
+
+class PackagingTests(unittest.TestCase):
+ def test_bug_588661(self):
+ configs = get_configs('foo', 'bug-588661-files')
+ self.assertEqual(configs.build.loader,
+ 'foo/lib/foo-loader.js')
+
+ def test_bug_614712(self):
+ configs = get_configs('commonjs-naming', 'bug-614712-files')
+ packages = configs.pkg_cfg.packages
+ base = os.path.join(tests_path, 'bug-614712-files', 'packages')
+ self.assertEqual(packages['original-naming'].tests,
+ [os.path.join(base, 'original-naming', 'tests')])
+ self.assertEqual(packages['commonjs-naming'].tests,
+ [os.path.join(base, 'commonjs-naming', 'test')])
+
+ def test_basic(self):
+ configs = get_configs('aardvark')
+ packages = configs.pkg_cfg.packages
+
+ self.assertTrue('addon-sdk' in packages)
+ self.assertTrue('aardvark' in packages)
+ self.assertTrue('addon-sdk' in packages.aardvark.dependencies)
+ self.assertEqual(packages['addon-sdk'].loader, 'lib/sdk/loader/cuddlefish.js')
+ self.assertTrue(packages.aardvark.main == 'main')
+ self.assertTrue(packages.aardvark.version == "1.0")
+
+class PackagePath(unittest.TestCase):
+ def test_packagepath(self):
+ root_path = os.path.join(tests_path, 'static-files')
+ pkg_path = os.path.join(root_path, 'packages', 'minimal')
+ target_cfg = packaging.get_config_in_dir(pkg_path)
+ pkg_cfg = packaging.build_config(root_path, target_cfg)
+ base_packages = set(pkg_cfg.packages.keys())
+ ppath = [os.path.join(tests_path, 'bug-611495-files')]
+ pkg_cfg2 = packaging.build_config(root_path, target_cfg, packagepath=ppath)
+ all_packages = set(pkg_cfg2.packages.keys())
+ self.assertEqual(sorted(["jspath-one"]),
+ sorted(all_packages - base_packages))
+
+class Directories(unittest.TestCase):
+ # for bug 652227
+ packages_path = os.path.join(tests_path, "bug-652227-files", "packages")
+ def get_config(self, pkg_name):
+ pkg_path = os.path.join(tests_path, "bug-652227-files", "packages",
+ pkg_name)
+ return packaging.get_config_in_dir(pkg_path)
+
+ def test_explicit_lib(self):
+ # package.json provides .lib
+ p = self.get_config('explicit-lib')
+ self.assertEqual(os.path.abspath(p.lib[0]),
+ os.path.abspath(os.path.join(self.packages_path,
+ "explicit-lib",
+ "alt2-lib")))
+
+ def test_directories_lib(self):
+ # package.json provides .directories.lib
+ p = self.get_config('explicit-dir-lib')
+ self.assertEqual(os.path.abspath(p.lib[0]),
+ os.path.abspath(os.path.join(self.packages_path,
+ "explicit-dir-lib",
+ "alt-lib")))
+
+ def test_lib(self):
+ # package.json is empty, but lib/ exists
+ p = self.get_config("default-lib")
+ self.assertEqual(os.path.abspath(p.lib[0]),
+ os.path.abspath(os.path.join(self.packages_path,
+ "default-lib",
+ "lib")))
+
+ def test_root(self):
+ # package.json is empty, no lib/, so files are in root
+ p = self.get_config('default-root')
+ self.assertEqual(os.path.abspath(p.lib[0]),
+ os.path.abspath(os.path.join(self.packages_path,
+ "default-root")))
+
+ def test_locale(self):
+ # package.json is empty, but locale/ exists and should be used
+ p = self.get_config("default-locale")
+ self.assertEqual(os.path.abspath(p.locale),
+ os.path.abspath(os.path.join(self.packages_path,
+ "default-locale",
+ "locale")))
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/test_preflight.py b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/test_preflight.py
new file mode 100644
index 0000000..571b791
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/test_preflight.py
@@ -0,0 +1,147 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+import os, shutil
+import simplejson as json
+import unittest
+import hashlib
+import base64
+from cuddlefish import preflight
+from StringIO import StringIO
+
+class Util(unittest.TestCase):
+ def get_basedir(self):
+ return os.path.join(".test_tmp", self.id())
+ def make_basedir(self):
+ basedir = self.get_basedir()
+ if os.path.isdir(basedir):
+ here = os.path.abspath(os.getcwd())
+ assert os.path.abspath(basedir).startswith(here) # safety
+ shutil.rmtree(basedir)
+ os.makedirs(basedir)
+ return basedir
+
+ def test_base62(self):
+ for i in range(1000):
+ h = hashlib.sha1(str(i)).digest()
+ s1 = base64.b64encode(h, "AB").strip("=")
+ s2 = base64.b64encode(h).strip("=").replace("+","A").replace("/","B")
+ self.failUnlessEqual(s1, s2)
+
+ def write(self, config):
+ basedir = self.get_basedir()
+ fn = os.path.join(basedir, "package.json")
+ open(fn,"w").write(config)
+ def read(self):
+ basedir = self.get_basedir()
+ fn = os.path.join(basedir, "package.json")
+ return open(fn,"r").read()
+
+ def get_cfg(self):
+ cfg = json.loads(self.read())
+ if "name" not in cfg:
+ # the cfx parser always provides a name, even if package.json
+ # doesn't contain one
+ cfg["name"] = "pretend name"
+ return cfg
+
+ def parse(self, keydata):
+ fields = {}
+ fieldnames = []
+ for line in keydata.split("\n"):
+ if line.strip():
+ k,v = line.split(":", 1)
+ k = k.strip() ; v = v.strip()
+ fields[k] = v
+ fieldnames.append(k)
+ return fields, fieldnames
+
+ def test_preflight(self):
+ basedir = self.make_basedir()
+ fn = os.path.join(basedir, "package.json")
+
+ # empty config is not ok: need id (name is automatically supplied)
+ config_orig = "{}"
+ self.write(config_orig)
+ out = StringIO()
+ cfg = self.get_cfg()
+ config_was_ok, modified = preflight.preflight_config(cfg, fn,
+ stderr=out)
+ self.failUnlessEqual(config_was_ok, False)
+ self.failUnlessEqual(modified, True)
+ backup_fn = os.path.join(basedir, "package.json.backup")
+ config_backup = open(backup_fn,"r").read()
+ self.failUnlessEqual(config_backup, config_orig)
+ config = json.loads(self.read())
+ self.failIf("name" in config)
+ self.failUnless("id" in config)
+ self.failUnless(config["id"].startswith("jid1-"), config["id"])
+ self.failUnlessEqual(out.getvalue().strip(),
+ "No 'id' in package.json: creating a new ID for you.")
+ os.unlink(backup_fn)
+
+ # just a name? we add the id
+ config_orig = '{"name": "my-awesome-package"}'
+ self.write(config_orig)
+ out = StringIO()
+ cfg = self.get_cfg()
+ config_was_ok, modified = preflight.preflight_config(cfg, fn,
+ stderr=out)
+ self.failUnlessEqual(config_was_ok, False)
+ self.failUnlessEqual(modified, True)
+ backup_fn = os.path.join(basedir, "package.json.backup")
+ config_backup = open(backup_fn,"r").read()
+ self.failUnlessEqual(config_backup, config_orig)
+ config = json.loads(self.read())
+ self.failUnlessEqual(config["name"], "my-awesome-package")
+ self.failUnless("id" in config)
+ self.failUnless(config["id"].startswith("jid1-"), config["id"])
+ jid = str(config["id"])
+ self.failUnlessEqual(out.getvalue().strip(),
+ "No 'id' in package.json: creating a new ID for you.")
+ os.unlink(backup_fn)
+
+ # name and valid id? great! ship it!
+ config2 = '{"name": "my-awesome-package", "id": "%s"}' % jid
+ self.write(config2)
+ out = StringIO()
+ cfg = self.get_cfg()
+ config_was_ok, modified = preflight.preflight_config(cfg, fn,
+ stderr=out)
+ self.failUnlessEqual(config_was_ok, True)
+ self.failUnlessEqual(modified, False)
+ config2a = self.read()
+ self.failUnlessEqual(config2a, config2)
+ self.failUnlessEqual(out.getvalue().strip(), "")
+
+ # name and anonymous ID? without asking to see its papers, ship it
+ config3 = '{"name": "my-old-skool-package", "id": "anonid0-deadbeef"}'
+ self.write(config3)
+ out = StringIO()
+ cfg = self.get_cfg()
+ config_was_ok, modified = preflight.preflight_config(cfg, fn,
+ stderr=out)
+ self.failUnlessEqual(config_was_ok, True)
+ self.failUnlessEqual(modified, False)
+ config3a = self.read()
+ self.failUnlessEqual(config3a, config3)
+ self.failUnlessEqual(out.getvalue().strip(), "")
+
+ # name and old-style ID? with nostalgic trepidation, ship it
+ config4 = '{"name": "my-old-skool-package", "id": "foo@bar.baz"}'
+ self.write(config4)
+ out = StringIO()
+ cfg = self.get_cfg()
+ config_was_ok, modified = preflight.preflight_config(cfg, fn,
+ stderr=out)
+ self.failUnlessEqual(config_was_ok, True)
+ self.failUnlessEqual(modified, False)
+ config4a = self.read()
+ self.failUnlessEqual(config4a, config4)
+ self.failUnlessEqual(out.getvalue().strip(), "")
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/test_property_parser.py b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/test_property_parser.py
new file mode 100644
index 0000000..4988f8e
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/test_property_parser.py
@@ -0,0 +1,93 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import unittest
+
+from cuddlefish.property_parser import parse, MalformedLocaleFileError
+
+class TestParser(unittest.TestCase):
+
+ def test_parse(self):
+ lines = [
+ # Comments are striped only if `#` is the first non-space character
+ "sharp=#can be in value",
+ "# comment",
+ "#key=value",
+ " # comment2",
+
+ "keyWithNoValue=",
+ "valueWithSpaces= ",
+ "valueWithMultilineSpaces= \\",
+ " \\",
+ " ",
+
+ # All spaces before/after are striped
+ " key = value ",
+ "key2=value2",
+ # Keys can contain '%'
+ "%s key=%s value",
+
+ # Accept empty lines
+ "",
+ " ",
+
+ # Multiline string must use backslash at end of lines
+ "multi=line\\", "value",
+ # With multiline string, left spaces are stripped ...
+ "some= spaces\\", " are\\ ", " stripped ",
+ # ... but not right spaces, except the last line!
+ "but=not \\", "all of \\", " them ",
+
+ # Explicit [other] plural definition
+ "explicitPlural[one] = one",
+ "explicitPlural[other] = other",
+
+ # Implicit [other] plural definition
+ "implicitPlural[one] = one",
+ "implicitPlural = other", # This key is the [other] one
+ ]
+ # Ensure that all lines end with a `\n`
+ # And that strings are unicode ones (parser code relies on it)
+ lines = [unicode(l + "\n") for l in lines]
+ pairs = parse(lines)
+ expected = {
+ "sharp": "#can be in value",
+
+ "key": "value",
+ "key2": "value2",
+ "%s key": "%s value",
+
+ "keyWithNoValue": "",
+ "valueWithSpaces": "",
+ "valueWithMultilineSpaces": "",
+
+ "multi": "linevalue",
+ "some": "spacesarestripped",
+ "but": "not all of them",
+
+ "implicitPlural": {
+ "one": "one",
+ "other": "other"
+ },
+ "explicitPlural": {
+ "one": "one",
+ "other": "other"
+ },
+ }
+ self.assertEqual(pairs, expected)
+
+ def test_exceptions(self):
+ self.failUnlessRaises(MalformedLocaleFileError, parse,
+ ["invalid line with no key value"])
+ self.failUnlessRaises(MalformedLocaleFileError, parse,
+ ["plural[one]=plural with no [other] value"])
+ self.failUnlessRaises(MalformedLocaleFileError, parse,
+ ["multiline with no last empty line=\\"])
+ self.failUnlessRaises(MalformedLocaleFileError, parse,
+ ["=no key"])
+ self.failUnlessRaises(MalformedLocaleFileError, parse,
+ [" =only spaces in key"])
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/test_rdf.py b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/test_rdf.py
new file mode 100644
index 0000000..67b486c
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/test_rdf.py
@@ -0,0 +1,45 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import unittest
+import xml.dom.minidom
+import os.path
+
+from cuddlefish import rdf, packaging
+
+parent = os.path.dirname
+test_dir = parent(os.path.abspath(__file__))
+template_dir = os.path.join(parent(test_dir), "../../app-extension")
+
+class RDFTests(unittest.TestCase):
+ def testBug567660(self):
+ obj = rdf.RDF()
+ data = u'\u2026'.encode('utf-8')
+ x = '<?xml version="1.0" encoding="utf-8"?><blah>%s</blah>' % data
+ obj.dom = xml.dom.minidom.parseString(x)
+ self.assertEqual(obj.dom.documentElement.firstChild.nodeValue,
+ u'\u2026')
+ self.assertEqual(str(obj).replace("\n",""), x.replace("\n",""))
+
+ def failUnlessIn(self, substring, s, msg=""):
+ if substring not in s:
+ self.fail("(%s) substring '%s' not in string '%s'"
+ % (msg, substring, s))
+
+ def testUnpack(self):
+ basedir = os.path.join(test_dir, "bug-715340-files")
+ for n in ["pkg-1-pack", "pkg-2-unpack", "pkg-3-pack"]:
+ cfg = packaging.get_config_in_dir(os.path.join(basedir, n))
+ m = rdf.gen_manifest(template_dir, cfg, jid="JID")
+ if n.endswith("-pack"):
+ # these ones should remain packed
+ self.failUnlessEqual(m.get("em:unpack"), "false")
+ self.failUnlessIn("<em:unpack>false</em:unpack>", str(m), n)
+ else:
+ # and these should be unpacked
+ self.failUnlessEqual(m.get("em:unpack"), "true")
+ self.failUnlessIn("<em:unpack>true</em:unpack>", str(m), n)
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/test_runner.py b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/test_runner.py
new file mode 100644
index 0000000..26583ab
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/test_runner.py
@@ -0,0 +1,27 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+def xulrunner_app_runner_doctests():
+ """
+ >>> import sys
+ >>> from cuddlefish import runner
+ >>> runner.XulrunnerAppRunner(binary='foo')
+ Traceback (most recent call last):
+ ...
+ Exception: Binary path does not exist foo
+
+ >>> runner.XulrunnerAppRunner(binary=sys.executable)
+ Traceback (most recent call last):
+ ...
+ ValueError: application.ini not found in cmdargs
+
+ >>> runner.XulrunnerAppRunner(binary=sys.executable,
+ ... cmdargs=['application.ini'])
+ Traceback (most recent call last):
+ ...
+ ValueError: file does not exist: 'application.ini'
+ """
+
+ pass
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/test_util.py b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/test_util.py
new file mode 100644
index 0000000..aa636a4
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/test_util.py
@@ -0,0 +1,22 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+import unittest
+from cuddlefish.manifest import filter_filenames, filter_dirnames
+
+class Filter(unittest.TestCase):
+ def test_filter_filenames(self):
+ names = ["foo", "bar.js", "image.png",
+ ".hidden", "foo~", ".foo.swp", "bar.js.swp"]
+ self.failUnlessEqual(sorted(filter_filenames(names)),
+ sorted(["foo", "bar.js", "image.png"]))
+
+ def test_filter_dirnames(self):
+ names = ["subdir", "data", ".git", ".hg", ".svn", "defaults"]
+ self.failUnlessEqual(sorted(filter_dirnames(names)),
+ sorted(["subdir", "data", "defaults"]))
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/test_version.py b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/test_version.py
new file mode 100644
index 0000000..814c57c
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/test_version.py
@@ -0,0 +1,28 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import os
+import unittest
+import shutil
+
+from cuddlefish._version import get_versions
+
+class Version(unittest.TestCase):
+ def get_basedir(self):
+ return os.path.join(".test_tmp", self.id())
+ def make_basedir(self):
+ basedir = self.get_basedir()
+ if os.path.isdir(basedir):
+ here = os.path.abspath(os.getcwd())
+ assert os.path.abspath(basedir).startswith(here) # safety
+ shutil.rmtree(basedir)
+ os.makedirs(basedir)
+ return basedir
+
+ def test_current_version(self):
+ # the SDK should be able to determine its own version. We don't care
+ # what it is, merely that it can be computed.
+ version = get_versions()["version"]
+ self.failUnless(isinstance(version, str), (version, type(version)))
+ self.failUnless(len(version) > 0, version)
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/test_webdocs.py b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/test_webdocs.py
new file mode 100644
index 0000000..7653bea
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/test_webdocs.py
@@ -0,0 +1,78 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import os
+import unittest
+
+from cuddlefish.docs import webdocs
+
+class WebDocTests(unittest.TestCase):
+
+ def test_create_guide1_doc(self):
+ root = os.path.join(os.getcwd() + \
+ '/python-lib/cuddlefish/tests/static-files')
+ web_docs = webdocs.WebDocs(root)
+ guide = web_docs.create_guide_page(os.path.join(\
+ root + '/doc/dev-guide-source/index.md'))
+ self._test_common_contents(guide)
+ self.assertTrue(\
+ '<title>An Imposing Title - Add-on SDK Documentation</title>'\
+ in guide)
+ self.assertTrue('<p><em>Some words!</em></p>'\
+ in guide)
+ self.assertTrue('<div id="version">Version '\
+ in guide)
+
+ def test_create_guide2_doc(self):
+ root = os.path.join(os.getcwd() + \
+ '/python-lib/cuddlefish/tests/static-files')
+ web_docs = webdocs.WebDocs(root)
+ guide = web_docs.create_guide_page(os.path.join(\
+ root + '/doc/dev-guide-source/no_h1.md'))
+ self._test_common_contents(guide)
+ self.assertTrue('<title>Add-on SDK Documentation</title>'\
+ in guide)
+ self.assertTrue('<h2>A heading</h2>'\
+ in guide)
+
+ def test_create_module_doc(self):
+ root = os.path.join(os.getcwd() + \
+ '/python-lib/cuddlefish/tests/static-files')
+ web_docs = webdocs.WebDocs(root)
+ module = web_docs.create_module_page(os.path.join(\
+ root + '/doc/module-source/sdk/aardvark-feeder.md'))
+ self._test_common_contents(module)
+ self.assertTrue(\
+ '<title>aardvark-feeder - Add-on SDK Documentation</title>'\
+ in module)
+ self.assertTrue(\
+ '<h1>aardvark-feeder</h1>'\
+ in module)
+ self.assertTrue(\
+ '<div class="module_description">'\
+ in module)
+ self.assertTrue(\
+ '<p>The <code>aardvark-feeder</code> module simplifies feeding aardvarks.</p>'\
+ in module)
+ self.assertTrue(\
+ '<h2 class="api_header">API Reference</h2>'\
+ in module)
+ self.assertTrue(\
+ '<h3 class="api_header">Functions</h3>'\
+ in module)
+ self.assertTrue(\
+ '<h4 class="api_name">feed(food)</h4>'\
+ in module)
+ self.assertTrue(
+ '<p>Feed the aardvark.</p>'\
+ in module)
+
+ def _test_common_contents(self, doc):
+ self.assertTrue(\
+ '<a href="modules/sdk/anteater/anteater.html">anteater/anteater</a>' in doc)
+ self.assertTrue(\
+ '<a href="modules/sdk/aardvark-feeder.html">aardvark-feeder</a>' in doc)
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/test_xpi.py b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/test_xpi.py
new file mode 100644
index 0000000..ec5eaa3
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/tests/test_xpi.py
@@ -0,0 +1,469 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import os
+import unittest
+import zipfile
+import pprint
+import shutil
+
+import simplejson as json
+from cuddlefish import xpi, packaging, manifest, buildJID
+from cuddlefish.tests import test_packaging
+from test_linker import up
+
+import xml.etree.ElementTree as ElementTree
+
+xpi_template_path = os.path.join(test_packaging.static_files_path,
+ 'xpi-template')
+
+fake_manifest = '<RDF><!-- Extension metadata is here. --></RDF>'
+
+class PrefsTests(unittest.TestCase):
+ def makexpi(self, pkg_name):
+ self.xpiname = "%s.xpi" % pkg_name
+ create_xpi(self.xpiname, pkg_name, 'preferences-files')
+ self.xpi = zipfile.ZipFile(self.xpiname, 'r')
+ options = self.xpi.read('harness-options.json')
+ self.xpi_harness_options = json.loads(options)
+
+ def setUp(self):
+ self.xpiname = None
+ self.xpi = None
+
+ def tearDown(self):
+ if self.xpi:
+ self.xpi.close()
+ if self.xpiname and os.path.exists(self.xpiname):
+ os.remove(self.xpiname)
+
+ def testPackageWithSimplePrefs(self):
+ self.makexpi('simple-prefs')
+ self.failUnless('options.xul' in self.xpi.namelist())
+ optsxul = self.xpi.read('options.xul').decode("utf-8")
+ self.failUnlessEqual(self.xpi_harness_options["jetpackID"],
+ "jid1-fZHqN9JfrDBa8A@jetpack")
+
+ root = ElementTree.XML(optsxul.encode('utf-8'))
+
+ xulNamespacePrefix = \
+ "{http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul}"
+
+ settings = root.findall(xulNamespacePrefix + 'setting')
+
+ def assertPref(setting, name, prefType, title):
+ packageName = 'jid1-fZHqN9JfrDBa8A@jetpack'
+ self.failUnlessEqual(setting.get('data-jetpack-id'), packageName)
+ self.failUnlessEqual(setting.get('pref'),
+ 'extensions.' + packageName + '.' + name)
+ self.failUnlessEqual(setting.get('pref-name'), name)
+ self.failUnlessEqual(setting.get('type'), prefType)
+ self.failUnlessEqual(setting.get('title'), title)
+
+ assertPref(settings[0], 'test', 'bool', u't\u00EBst')
+ assertPref(settings[1], 'test2', 'string', u't\u00EBst')
+ assertPref(settings[2], 'test3', 'menulist', '"><test')
+ assertPref(settings[3], 'test4', 'radio', u't\u00EBst')
+
+ menuItems = settings[2].findall(
+ '%(0)smenulist/%(0)smenupopup/%(0)smenuitem' % { "0": xulNamespacePrefix })
+ radios = settings[3].findall(
+ '%(0)sradiogroup/%(0)sradio' % { "0": xulNamespacePrefix })
+
+ def assertOption(option, value, label):
+ self.failUnlessEqual(option.get('value'), value)
+ self.failUnlessEqual(option.get('label'), label)
+
+ assertOption(menuItems[0], "0", "label1")
+ assertOption(menuItems[1], "1", "label2")
+ assertOption(radios[0], "red", "rouge")
+ assertOption(radios[1], "blue", "bleu")
+
+ prefsjs = self.xpi.read('defaults/preferences/prefs.js').decode("utf-8")
+ exp = [u'pref("extensions.jid1-fZHqN9JfrDBa8A@jetpack.test", false);',
+ u'pref("extensions.jid1-fZHqN9JfrDBa8A@jetpack.test2", "\u00FCnic\u00F8d\u00E9");',
+ u'pref("extensions.jid1-fZHqN9JfrDBa8A@jetpack.test3", "1");',
+ u'pref("extensions.jid1-fZHqN9JfrDBa8A@jetpack.test4", "red");',
+ ]
+ self.failUnlessEqual(prefsjs, "\n".join(exp)+"\n")
+
+ def testPackageWithNoPrefs(self):
+ self.makexpi('no-prefs')
+ self.failIf('options.xul' in self.xpi.namelist())
+ self.failUnlessEqual(self.xpi_harness_options["jetpackID"],
+ "jid1-fZHqN9JfrDBa8A@jetpack")
+ prefsjs = self.xpi.read('defaults/preferences/prefs.js').decode("utf-8")
+ self.failUnlessEqual(prefsjs, "")
+
+
+class Bug588119Tests(unittest.TestCase):
+ def makexpi(self, pkg_name):
+ self.xpiname = "%s.xpi" % pkg_name
+ create_xpi(self.xpiname, pkg_name, 'bug-588119-files')
+ self.xpi = zipfile.ZipFile(self.xpiname, 'r')
+ options = self.xpi.read('harness-options.json')
+ self.xpi_harness_options = json.loads(options)
+
+ def setUp(self):
+ self.xpiname = None
+ self.xpi = None
+
+ def tearDown(self):
+ if self.xpi:
+ self.xpi.close()
+ if self.xpiname and os.path.exists(self.xpiname):
+ os.remove(self.xpiname)
+
+ def testPackageWithImplicitIcon(self):
+ self.makexpi('implicit-icon')
+ assert 'icon.png' in self.xpi.namelist()
+
+ def testPackageWithImplicitIcon64(self):
+ self.makexpi('implicit-icon')
+ assert 'icon64.png' in self.xpi.namelist()
+
+ def testPackageWithExplicitIcon(self):
+ self.makexpi('explicit-icon')
+ assert 'icon.png' in self.xpi.namelist()
+
+ def testPackageWithExplicitIcon64(self):
+ self.makexpi('explicit-icon')
+ assert 'icon64.png' in self.xpi.namelist()
+
+ def testPackageWithNoIcon(self):
+ self.makexpi('no-icon')
+ assert 'icon.png' not in self.xpi.namelist()
+
+ def testIconPathNotInHarnessOptions(self):
+ self.makexpi('implicit-icon')
+ assert 'icon' not in self.xpi_harness_options
+
+ def testIcon64PathNotInHarnessOptions(self):
+ self.makexpi('implicit-icon')
+ assert 'icon64' not in self.xpi_harness_options
+
+class ExtraHarnessOptions(unittest.TestCase):
+ def setUp(self):
+ self.xpiname = None
+ self.xpi = None
+
+ def tearDown(self):
+ if self.xpi:
+ self.xpi.close()
+ if self.xpiname and os.path.exists(self.xpiname):
+ os.remove(self.xpiname)
+
+ def testOptions(self):
+ pkg_name = "extra-options"
+ self.xpiname = "%s.xpi" % pkg_name
+ create_xpi(self.xpiname, pkg_name, "bug-669274-files",
+ extra_harness_options={"builderVersion": "futuristic"})
+ self.xpi = zipfile.ZipFile(self.xpiname, 'r')
+ options = self.xpi.read('harness-options.json')
+ hopts = json.loads(options)
+ self.failUnless("builderVersion" in hopts)
+ self.failUnlessEqual(hopts["builderVersion"], "futuristic")
+
+ def testBadOptionName(self):
+ pkg_name = "extra-options"
+ self.xpiname = "%s.xpi" % pkg_name
+ self.failUnlessRaises(xpi.HarnessOptionAlreadyDefinedError,
+ create_xpi,
+ self.xpiname, pkg_name, "bug-669274-files",
+ extra_harness_options={"main": "already in use"})
+
+class SmallXPI(unittest.TestCase):
+ def setUp(self):
+ self.root = up(os.path.abspath(__file__), 4)
+ def get_linker_files_dir(self, name):
+ return os.path.join(up(os.path.abspath(__file__)), "linker-files", name)
+ def get_pkg(self, name):
+ d = self.get_linker_files_dir(name)
+ return packaging.get_config_in_dir(d)
+
+ def get_basedir(self):
+ return os.path.join(".test_tmp", self.id())
+ def make_basedir(self):
+ basedir = self.get_basedir()
+ if os.path.isdir(basedir):
+ here = os.path.abspath(os.getcwd())
+ assert os.path.abspath(basedir).startswith(here) # safety
+ shutil.rmtree(basedir)
+ os.makedirs(basedir)
+ return basedir
+
+ def test_contents(self):
+ target_cfg = self.get_pkg("three")
+ package_path = [self.get_linker_files_dir("three-deps")]
+ pkg_cfg = packaging.build_config(self.root, target_cfg,
+ packagepath=package_path)
+ deps = packaging.get_deps_for_targets(pkg_cfg,
+ [target_cfg.name, "addon-sdk"])
+ addon_sdk_dir = pkg_cfg.packages["addon-sdk"].lib[0]
+ m = manifest.build_manifest(target_cfg, pkg_cfg, deps, scan_tests=False)
+ used_files = list(m.get_used_files())
+ here = up(os.path.abspath(__file__))
+ def absify(*parts):
+ fn = os.path.join(here, "linker-files", *parts)
+ return os.path.abspath(fn)
+ expected = [absify(*parts) for parts in
+ [("three", "lib", "main.js"),
+ ("three-deps", "three-a", "lib", "main.js"),
+ ("three-deps", "three-a", "lib", "subdir", "subfile.js"),
+ ("three", "data", "msg.txt"),
+ ("three", "data", "subdir", "submsg.txt"),
+ ("three-deps", "three-b", "lib", "main.js"),
+ ("three-deps", "three-c", "lib", "main.js"),
+ ("three-deps", "three-c", "lib", "sub", "foo.js")
+ ]]
+
+ add_addon_sdk= lambda path: os.path.join(addon_sdk_dir, path)
+ expected.extend([add_addon_sdk(module) for module in [
+ os.path.join("sdk", "self.js"),
+ os.path.join("sdk", "core", "promise.js"),
+ os.path.join("sdk", "net", "url.js"),
+ os.path.join("sdk", "util", "object.js")
+ ]])
+
+ missing = set(expected) - set(used_files)
+ extra = set(used_files) - set(expected)
+
+ self.failUnlessEqual(list(missing), [])
+ self.failUnlessEqual(list(extra), [])
+ used_deps = m.get_used_packages()
+
+ build = packaging.generate_build_for_target(pkg_cfg, target_cfg.name,
+ used_deps,
+ include_tests=False)
+ options = {'main': target_cfg.main}
+ options.update(build)
+ basedir = self.make_basedir()
+ xpi_name = os.path.join(basedir, "contents.xpi")
+ xpi.build_xpi(template_root_dir=xpi_template_path,
+ manifest=fake_manifest,
+ xpi_path=xpi_name,
+ harness_options=options,
+ limit_to=used_files)
+ x = zipfile.ZipFile(xpi_name, "r")
+ names = x.namelist()
+ expected = ["components/",
+ "components/harness.js",
+ # the real template also has 'bootstrap.js', but the fake
+ # one in tests/static-files/xpi-template doesn't
+ "harness-options.json",
+ "install.rdf",
+ "defaults/preferences/prefs.js",
+ "resources/",
+ "resources/addon-sdk/",
+ "resources/addon-sdk/data/",
+ "resources/addon-sdk/lib/",
+ "resources/addon-sdk/lib/sdk/",
+ "resources/addon-sdk/lib/sdk/self.js",
+ "resources/addon-sdk/lib/sdk/core/",
+ "resources/addon-sdk/lib/sdk/util/",
+ "resources/addon-sdk/lib/sdk/net/",
+ "resources/addon-sdk/lib/sdk/core/promise.js",
+ "resources/addon-sdk/lib/sdk/util/object.js",
+ "resources/addon-sdk/lib/sdk/net/url.js",
+ "resources/three/",
+ "resources/three/lib/",
+ "resources/three/lib/main.js",
+ "resources/three/data/",
+ "resources/three/data/msg.txt",
+ "resources/three/data/subdir/",
+ "resources/three/data/subdir/submsg.txt",
+ "resources/three-a/",
+ "resources/three-a/lib/",
+ "resources/three-a/lib/main.js",
+ "resources/three-a/lib/subdir/",
+ "resources/three-a/lib/subdir/subfile.js",
+ "resources/three-b/",
+ "resources/three-b/lib/",
+ "resources/three-b/lib/main.js",
+ "resources/three-c/",
+ "resources/three-c/lib/",
+ "resources/three-c/lib/main.js",
+ "resources/three-c/lib/sub/",
+ "resources/three-c/lib/sub/foo.js",
+ # notably absent: three-a/lib/unused.js
+ "locale/",
+ "locale/fr-FR.json",
+ "locales.json",
+ ]
+ # showing deltas makes failures easier to investigate
+ missing = set(expected) - set(names)
+ extra = set(names) - set(expected)
+ self.failUnlessEqual((list(missing), list(extra)), ([], []))
+ self.failUnlessEqual(sorted(names), sorted(expected))
+
+ # check locale files
+ localedata = json.loads(x.read("locales.json"))
+ self.failUnlessEqual(sorted(localedata["locales"]), sorted(["fr-FR"]))
+ content = x.read("locale/fr-FR.json")
+ locales = json.loads(content)
+ # Locale files are merged into one.
+ # Conflicts are silently resolved by taking last package translation,
+ # so that we get "No" translation from three-c instead of three-b one.
+ self.failUnlessEqual(locales, json.loads(u'''
+ {
+ "No": "Nein",
+ "one": "un",
+ "What?": "Quoi?",
+ "Yes": "Oui",
+ "plural": {
+ "other": "other",
+ "one": "one"
+ },
+ "uft8_value": "\u00e9"
+ }'''))
+
+ def test_scantests(self):
+ target_cfg = self.get_pkg("three")
+ package_path = [self.get_linker_files_dir("three-deps")]
+ pkg_cfg = packaging.build_config(self.root, target_cfg,
+ packagepath=package_path)
+
+ deps = packaging.get_deps_for_targets(pkg_cfg,
+ [target_cfg.name, "addon-sdk"])
+ m = manifest.build_manifest(target_cfg, pkg_cfg, deps, scan_tests=True)
+ self.failUnlessEqual(sorted(m.get_all_test_modules()),
+ sorted(["test-one", "test-two"]))
+ # the current __init__.py code omits limit_to=used_files for 'cfx
+ # test', so all test files are included in the XPI. But the test
+ # runner will only execute the tests that m.get_all_test_modules()
+ # tells us about (which are put into the .allTestModules property of
+ # harness-options.json).
+ used_deps = m.get_used_packages()
+
+ build = packaging.generate_build_for_target(pkg_cfg, target_cfg.name,
+ used_deps,
+ include_tests=True)
+ options = {'main': target_cfg.main}
+ options.update(build)
+ basedir = self.make_basedir()
+ xpi_name = os.path.join(basedir, "contents.xpi")
+ xpi.build_xpi(template_root_dir=xpi_template_path,
+ manifest=fake_manifest,
+ xpi_path=xpi_name,
+ harness_options=options,
+ limit_to=None)
+ x = zipfile.ZipFile(xpi_name, "r")
+ names = x.namelist()
+ self.failUnless("resources/addon-sdk/lib/sdk/deprecated/unit-test.js" in names, names)
+ self.failUnless("resources/addon-sdk/lib/sdk/deprecated/unit-test-finder.js" in names, names)
+ self.failUnless("resources/addon-sdk/lib/sdk/test/harness.js" in names, names)
+ self.failUnless("resources/addon-sdk/lib/sdk/test/runner.js" in names, names)
+ # all files are copied into the XPI, even the things that don't look
+ # like tests.
+ self.failUnless("resources/three/tests/test-one.js" in names, names)
+ self.failUnless("resources/three/tests/test-two.js" in names, names)
+ self.failUnless("resources/three/tests/nontest.js" in names, names)
+
+ def test_scantests_filter(self):
+ target_cfg = self.get_pkg("three")
+ package_path = [self.get_linker_files_dir("three-deps")]
+ pkg_cfg = packaging.build_config(self.root, target_cfg,
+ packagepath=package_path)
+ deps = packaging.get_deps_for_targets(pkg_cfg,
+ [target_cfg.name, "addon-sdk"])
+ FILTER = ".*one.*"
+ m = manifest.build_manifest(target_cfg, pkg_cfg, deps, scan_tests=True,
+ test_filter_re=FILTER)
+ self.failUnlessEqual(sorted(m.get_all_test_modules()),
+ sorted(["test-one"]))
+ # the current __init__.py code omits limit_to=used_files for 'cfx
+ # test', so all test files are included in the XPI. But the test
+ # runner will only execute the tests that m.get_all_test_modules()
+ # tells us about (which are put into the .allTestModules property of
+ # harness-options.json).
+ used_deps = m.get_used_packages()
+
+ build = packaging.generate_build_for_target(pkg_cfg, target_cfg.name,
+ used_deps,
+ include_tests=True)
+ options = {'main': target_cfg.main}
+ options.update(build)
+ basedir = self.make_basedir()
+ xpi_name = os.path.join(basedir, "contents.xpi")
+ xpi.build_xpi(template_root_dir=xpi_template_path,
+ manifest=fake_manifest,
+ xpi_path=xpi_name,
+ harness_options=options,
+ limit_to=None)
+ x = zipfile.ZipFile(xpi_name, "r")
+ names = x.namelist()
+ self.failUnless("resources/addon-sdk/lib/sdk/deprecated/unit-test.js" in names, names)
+ self.failUnless("resources/addon-sdk/lib/sdk/deprecated/unit-test-finder.js" in names, names)
+ self.failUnless("resources/addon-sdk/lib/sdk/test/harness.js" in names, names)
+ self.failUnless("resources/addon-sdk/lib/sdk/test/runner.js" in names, names)
+ # get_all_test_modules() respects the filter. But all files are still
+ # copied into the XPI.
+ self.failUnless("resources/three/tests/test-one.js" in names, names)
+ self.failUnless("resources/three/tests/test-two.js" in names, names)
+ self.failUnless("resources/three/tests/nontest.js" in names, names)
+
+
+def document_dir(name):
+ if name in ['packages', 'xpi-template']:
+ dirname = os.path.join(test_packaging.static_files_path, name)
+ document_dir_files(dirname)
+ elif name == 'xpi-output':
+ create_xpi('test-xpi.xpi')
+ document_zip_file('test-xpi.xpi')
+ os.remove('test-xpi.xpi')
+ else:
+ raise Exception('unknown dir: %s' % name)
+
+def normpath(path):
+ """
+ Make a platform-specific relative path use '/' as a separator.
+ """
+
+ return path.replace(os.path.sep, '/')
+
+def document_zip_file(path):
+ zip = zipfile.ZipFile(path, 'r')
+ for name in sorted(zip.namelist()):
+ contents = zip.read(name)
+ lines = contents.splitlines()
+ if len(lines) == 1 and name.endswith('.json') and len(lines[0]) > 75:
+ # Ideally we would json-decode this, but it results
+ # in an annoying 'u' before every string literal,
+ # since json decoding makes all strings unicode.
+ contents = eval(contents)
+ contents = pprint.pformat(contents)
+ lines = contents.splitlines()
+ contents = "\n ".join(lines)
+ print "%s:\n %s" % (normpath(name), contents)
+ zip.close()
+
+def document_dir_files(path):
+ filename_contents_tuples = []
+ for dirpath, dirnames, filenames in os.walk(path):
+ relpath = dirpath[len(path)+1:]
+ for filename in filenames:
+ abspath = os.path.join(dirpath, filename)
+ contents = open(abspath, 'r').read()
+ contents = "\n ".join(contents.splitlines())
+ relfilename = os.path.join(relpath, filename)
+ filename_contents_tuples.append((normpath(relfilename), contents))
+ filename_contents_tuples.sort()
+ for filename, contents in filename_contents_tuples:
+ print "%s:" % filename
+ print " %s" % contents
+
+def create_xpi(xpiname, pkg_name='aardvark', dirname='static-files',
+ extra_harness_options={}):
+ configs = test_packaging.get_configs(pkg_name, dirname)
+ options = {'main': configs.target_cfg.main,
+ 'jetpackID': buildJID(configs.target_cfg), }
+ options.update(configs.build)
+ xpi.build_xpi(template_root_dir=xpi_template_path,
+ manifest=fake_manifest,
+ xpi_path=xpiname,
+ harness_options=options,
+ extra_harness_options=extra_harness_options)
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/util.py b/tools/addon-sdk-1.12/python-lib/cuddlefish/util.py
new file mode 100644
index 0000000..513495a
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/util.py
@@ -0,0 +1,23 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+IGNORED_FILE_PREFIXES = ["."]
+IGNORED_FILE_SUFFIXES = ["~", ".swp"]
+IGNORED_DIRS = [".git", ".svn", ".hg"]
+
+def filter_filenames(filenames, ignored_files=[".hgignore"]):
+ for filename in filenames:
+ if filename in ignored_files:
+ continue
+ if any([filename.startswith(suffix)
+ for suffix in IGNORED_FILE_PREFIXES]):
+ continue
+ if any([filename.endswith(suffix)
+ for suffix in IGNORED_FILE_SUFFIXES]):
+ continue
+ yield filename
+
+def filter_dirnames(dirnames):
+ return [dirname for dirname in dirnames if dirname not in IGNORED_DIRS]
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/version_comparator.py b/tools/addon-sdk-1.12/python-lib/cuddlefish/version_comparator.py
new file mode 100644
index 0000000..3999e71
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/version_comparator.py
@@ -0,0 +1,206 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+'''
+ This is a really crummy, slow Python implementation of the Mozilla
+ platform's nsIVersionComparator interface:
+
+ https://developer.mozilla.org/En/NsIVersionComparator
+
+ For more information, also see:
+
+ http://mxr.mozilla.org/mozilla/source/xpcom/glue/nsVersionComparator.cpp
+'''
+
+import re
+import sys
+
+class VersionPart(object):
+ '''
+ Examples:
+
+ >>> VersionPart('1')
+ (1, None, 0, None)
+
+ >>> VersionPart('1pre')
+ (1, 'pre', 0, None)
+
+ >>> VersionPart('1pre10')
+ (1, 'pre', 10, None)
+
+ >>> VersionPart('1pre10a')
+ (1, 'pre', 10, 'a')
+
+ >>> VersionPart('1+')
+ (2, 'pre', 0, None)
+
+ >>> VersionPart('*').numA == sys.maxint
+ True
+
+ >>> VersionPart('1') < VersionPart('2')
+ True
+
+ >>> VersionPart('2') > VersionPart('1')
+ True
+
+ >>> VersionPart('1') == VersionPart('1')
+ True
+
+ >>> VersionPart('1pre') > VersionPart('1')
+ False
+
+ >>> VersionPart('1') < VersionPart('1pre')
+ False
+
+ >>> VersionPart('1pre1') < VersionPart('1pre2')
+ True
+
+ >>> VersionPart('1pre10b') > VersionPart('1pre10a')
+ True
+
+ >>> VersionPart('1pre10b') == VersionPart('1pre10b')
+ True
+
+ >>> VersionPart('1pre10a') < VersionPart('1pre10b')
+ True
+
+ >>> VersionPart('1') > VersionPart('')
+ True
+ '''
+
+ _int_part = re.compile('[+-]?(\d*)(.*)')
+ _num_chars = '0123456789+-'
+
+ def __init__(self, part):
+ self.numA = 0
+ self.strB = None
+ self.numC = 0
+ self.extraD = None
+
+ if not part:
+ return
+
+ if part == '*':
+ self.numA = sys.maxint
+ else:
+ match = self._int_part.match(part)
+ self.numA = int(match.group(1))
+ self.strB = match.group(2) or None
+ if self.strB == '+':
+ self.strB = 'pre'
+ self.numA += 1
+ elif self.strB:
+ i = 0
+ num_found = -1
+ for char in self.strB:
+ if char in self._num_chars:
+ num_found = i
+ break
+ i += 1
+ if num_found != -1:
+ match = self._int_part.match(self.strB[num_found:])
+ self.numC = int(match.group(1))
+ self.extraD = match.group(2) or None
+ self.strB = self.strB[:num_found]
+
+ def _strcmp(self, str1, str2):
+ # Any string is *before* no string.
+ if str1 is None:
+ if str2 is None:
+ return 0
+ else:
+ return 1
+
+ if str2 is None:
+ return -1
+
+ return cmp(str1, str2)
+
+ def __cmp__(self, other):
+ r = cmp(self.numA, other.numA)
+ if r:
+ return r
+
+ r = self._strcmp(self.strB, other.strB)
+ if r:
+ return r
+
+ r = cmp(self.numC, other.numC)
+ if r:
+ return r
+
+ return self._strcmp(self.extraD, other.extraD)
+
+ def __repr__(self):
+ return repr((self.numA, self.strB, self.numC, self.extraD))
+
+def compare(a, b):
+ '''
+ Examples:
+
+ >>> compare('1', '2')
+ -1
+
+ >>> compare('1', '1')
+ 0
+
+ >>> compare('2', '1')
+ 1
+
+ >>> compare('1.0pre1', '1.0pre2')
+ -1
+
+ >>> compare('1.0pre2', '1.0')
+ -1
+
+ >>> compare('1.0', '1.0.0')
+ 0
+
+ >>> compare('1.0.0', '1.0.0.0')
+ 0
+
+ >>> compare('1.0.0.0', '1.1pre')
+ -1
+
+ >>> compare('1.1pre', '1.1pre0')
+ 0
+
+ >>> compare('1.1pre0', '1.0+')
+ 0
+
+ >>> compare('1.0+', '1.1pre1a')
+ -1
+
+ >>> compare('1.1pre1a', '1.1pre1')
+ -1
+
+ >>> compare('1.1pre1', '1.1pre10a')
+ -1
+
+ >>> compare('1.1pre10a', '1.1pre10')
+ -1
+
+ >>> compare('1.1pre10a', '1.*')
+ -1
+ '''
+
+ a_parts = a.split('.')
+ b_parts = b.split('.')
+
+ if len(a_parts) < len(b_parts):
+ a_parts.extend([''] * (len(b_parts) - len(a_parts)))
+ else:
+ b_parts.extend([''] * (len(a_parts) - len(b_parts)))
+
+ for a_part, b_part in zip(a_parts, b_parts):
+ r = cmp(VersionPart(a_part), VersionPart(b_part))
+ if r:
+ return r
+
+ return 0
+
+if __name__ == '__main__':
+ import doctest
+
+ doctest.testmod(verbose=True)
diff --git a/tools/addon-sdk-1.12/python-lib/cuddlefish/xpi.py b/tools/addon-sdk-1.12/python-lib/cuddlefish/xpi.py
new file mode 100644
index 0000000..3943e56
--- /dev/null
+++ b/tools/addon-sdk-1.12/python-lib/cuddlefish/xpi.py
@@ -0,0 +1,155 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import os
+import zipfile
+import simplejson as json
+from cuddlefish.util import filter_filenames, filter_dirnames
+
+class HarnessOptionAlreadyDefinedError(Exception):
+ """You cannot use --harness-option on keys that already exist in
+ harness-options.json"""
+
+ZIPSEP = "/" # always use "/" in zipfiles
+
+def make_zipfile_path(localroot, localpath):
+ return ZIPSEP.join(localpath[len(localroot)+1:].split(os.sep))
+
+def mkzipdir(zf, path):
+ dirinfo = zipfile.ZipInfo(path)
+ dirinfo.external_attr = int("040755", 8) << 16L
+ zf.writestr(dirinfo, "")
+
+def build_xpi(template_root_dir, manifest, xpi_path,
+ harness_options, limit_to=None, extra_harness_options={}):
+ zf = zipfile.ZipFile(xpi_path, "w", zipfile.ZIP_DEFLATED)
+
+ open('.install.rdf', 'w').write(str(manifest))
+ zf.write('.install.rdf', 'install.rdf')
+ os.remove('.install.rdf')
+
+ if 'icon' in harness_options:
+ zf.write(str(harness_options['icon']), 'icon.png')
+ del harness_options['icon']
+
+ if 'icon64' in harness_options:
+ zf.write(str(harness_options['icon64']), 'icon64.png')
+ del harness_options['icon64']
+
+ if 'preferences' in harness_options:
+ from options_xul import parse_options, validate_prefs
+
+ validate_prefs(harness_options["preferences"])
+
+ opts_xul = parse_options(harness_options["preferences"],
+ harness_options["jetpackID"])
+ open('.options.xul', 'wb').write(opts_xul.encode("utf-8"))
+ zf.write('.options.xul', 'options.xul')
+ os.remove('.options.xul')
+
+ from options_defaults import parse_options_defaults
+ prefs_js = parse_options_defaults(harness_options["preferences"],
+ harness_options["jetpackID"])
+ open('.prefs.js', 'wb').write(prefs_js.encode("utf-8"))
+
+ else:
+ open('.prefs.js', 'wb').write("")
+
+ zf.write('.prefs.js', 'defaults/preferences/prefs.js')
+ os.remove('.prefs.js')
+
+
+ IGNORED_FILES = [".hgignore", ".DS_Store", "install.rdf",
+ "application.ini", xpi_path]
+
+ files_to_copy = {} # maps zipfile path to local-disk abspath
+ dirs_to_create = set() # zipfile paths, no trailing slash
+
+ for dirpath, dirnames, filenames in os.walk(template_root_dir):
+ filenames = list(filter_filenames(filenames, IGNORED_FILES))
+ dirnames[:] = filter_dirnames(dirnames)
+ for dirname in dirnames:
+ arcpath = make_zipfile_path(template_root_dir,
+ os.path.join(dirpath, dirname))
+ dirs_to_create.add(arcpath)
+ for filename in filenames:
+ abspath = os.path.join(dirpath, filename)
+ arcpath = make_zipfile_path(template_root_dir, abspath)
+ files_to_copy[arcpath] = abspath
+
+ # `packages` attribute contains a dictionnary of dictionnary
+ # of all packages sections directories
+ for packageName in harness_options['packages']:
+ base_arcpath = ZIPSEP.join(['resources', packageName])
+ # Always write the top directory, even if it contains no files, since
+ # the harness will try to access it.
+ dirs_to_create.add(base_arcpath)
+ for sectionName in harness_options['packages'][packageName]:
+ abs_dirname = harness_options['packages'][packageName][sectionName]
+ base_arcpath = ZIPSEP.join(['resources', packageName, sectionName])
+ # Always write the top directory, even if it contains no files, since
+ # the harness will try to access it.
+ dirs_to_create.add(base_arcpath)
+ # cp -r stuff from abs_dirname/ into ZIP/resources/RESOURCEBASE/
+ for dirpath, dirnames, filenames in os.walk(abs_dirname):
+ goodfiles = list(filter_filenames(filenames, IGNORED_FILES))
+ dirnames[:] = filter_dirnames(dirnames)
+ for filename in goodfiles:
+ abspath = os.path.join(dirpath, filename)
+ if limit_to is not None and abspath not in limit_to:
+ continue # strip unused files
+ arcpath = ZIPSEP.join(
+ ['resources',
+ packageName,
+ sectionName,
+ make_zipfile_path(abs_dirname,
+ os.path.join(dirpath, filename)),
+ ])
+ files_to_copy[str(arcpath)] = str(abspath)
+ del harness_options['packages']
+
+ locales_json_data = {"locales": []}
+ mkzipdir(zf, "locale/")
+ for language in sorted(harness_options['locale']):
+ locales_json_data["locales"].append(language)
+ locale = harness_options['locale'][language]
+ # Be carefull about strings, we need to always ensure working with UTF-8
+ jsonStr = json.dumps(locale, indent=1, sort_keys=True, ensure_ascii=False)
+ info = zipfile.ZipInfo('locale/' + language + '.json')
+ info.external_attr = 0644 << 16L
+ zf.writestr(info, jsonStr.encode( "utf-8" ))
+ del harness_options['locale']
+
+ jsonStr = json.dumps(locales_json_data, ensure_ascii=True) +"\n"
+ info = zipfile.ZipInfo('locales.json')
+ info.external_attr = 0644 << 16L
+ zf.writestr(info, jsonStr.encode("utf-8"))
+
+ # now figure out which directories we need: all retained files parents
+ for arcpath in files_to_copy:
+ bits = arcpath.split("/")
+ for i in range(1,len(bits)):
+ parentpath = ZIPSEP.join(bits[0:i])
+ dirs_to_create.add(parentpath)
+
+ # create zipfile in alphabetical order, with each directory before its
+ # files
+ for name in sorted(dirs_to_create.union(set(files_to_copy))):
+ if name in dirs_to_create:
+ mkzipdir(zf, name+"/")
+ if name in files_to_copy:
+ zf.write(files_to_copy[name], name)
+
+ harness_options = harness_options.copy()
+ for key,value in extra_harness_options.items():
+ if key in harness_options:
+ msg = "Can't use --harness-option for existing key '%s'" % key
+ raise HarnessOptionAlreadyDefinedError(msg)
+ harness_options[key] = value
+ open('.options.json', 'w').write(json.dumps(harness_options, indent=1,
+ sort_keys=True))
+ zf.write('.options.json', 'harness-options.json')
+ os.remove('.options.json')
+
+ zf.close()