summaryrefslogtreecommitdiffstats
path: root/dom/system/gonk/tests/marionette/test_ril_code_quality.py
diff options
context:
space:
mode:
Diffstat (limited to 'dom/system/gonk/tests/marionette/test_ril_code_quality.py')
-rw-r--r--dom/system/gonk/tests/marionette/test_ril_code_quality.py371
1 files changed, 0 insertions, 371 deletions
diff --git a/dom/system/gonk/tests/marionette/test_ril_code_quality.py b/dom/system/gonk/tests/marionette/test_ril_code_quality.py
deleted file mode 100644
index d741d8a2e..000000000
--- a/dom/system/gonk/tests/marionette/test_ril_code_quality.py
+++ /dev/null
@@ -1,371 +0,0 @@
-"""
-The test performs the static code analysis check by JSHint.
-
-Target js files:
-- RadioInterfaceLayer.js
-- ril_worker.js
-- ril_consts.js
-
-If the js file contains the line of 'importScript()' (Ex: ril_worker.js), the
-test will perform a special merge step before excuting JSHint.
-
-Ex: Script A
---------------------------------
-importScripts('Script B')
-...
---------------------------------
-
-We merge these two scripts into one by the following way.
-
---------------------------------
-[Script B (ex: ril_consts.js)]
-(function(){ [Script A (ex: ril_worker.js)]
-})();
---------------------------------
-
-Script A (ril_worker.js) runs global strict mode.
-Script B (ril_consts.js) not.
-
-The above merge way ensures the correct scope of 'strict mode.'
-"""
-
-import bisect
-import inspect
-import os
-import os.path
-import re
-import unicodedata
-
-from marionette_harness import MarionetteTestCase
-
-
-class StringUtility:
-
- """A collection of some string utilities."""
-
- @staticmethod
- def find_match_lines(lines, pattern):
- """Return a list of lines that contains given pattern."""
- return [line for line in lines if pattern in line]
-
- @staticmethod
- def remove_non_ascii(data):
- """Remove non ascii characters in data and return it as new string."""
- if type(data).__name__ == 'unicode':
- data = unicodedata.normalize(
- 'NFKD', data).encode('ascii', 'ignore')
- return data
-
- @staticmethod
- def auto_close(lines):
- """Ensure every line ends with '\n'."""
- if lines and not lines[-1].endswith('\n'):
- lines[-1] += '\n'
- return lines
-
- @staticmethod
- def auto_wrap_strict_mode(lines):
- """Wrap by function scope if lines contain 'use strict'."""
- if StringUtility.find_match_lines(lines, 'use strict'):
- lines[0] = '(function(){' + lines[0]
- lines.append('})();\n')
- return lines
-
- @staticmethod
- def get_imported_list(lines):
- """Get a list of imported items."""
- return [item
- for line in StringUtility.find_match_lines(lines, 'importScripts')
- for item in StringUtility._get_imported_list_from_line(line)]
-
- @staticmethod
- def _get_imported_list_from_line(line):
- """Extract all items from 'importScripts(...)'.
-
- importScripts("ril_consts.js", "systemlibs.js")
- => ['ril_consts', 'systemlibs.js']
-
- """
- pattern = re.compile(r'\s*importScripts\((.*)\)')
- m = pattern.match(line)
- if not m:
- raise Exception('Parse importScripts error.')
- return [name.translate(None, '\' "') for name in m.group(1).split(',')]
-
-
-class ResourceUriFileReader:
-
- """Handle the process of reading the source code from system."""
-
- URI_PREFIX = 'resource://gre/'
- URI_PATH = {
- 'RadioInterfaceLayer.js': 'components/RadioInterfaceLayer.js',
- 'ril_worker.js': 'modules/ril_worker.js',
- 'ril_consts.js': 'modules/ril_consts.js',
- 'systemlibs.js': 'modules/systemlibs.js',
- 'worker_buf.js': 'modules/workers/worker_buf.js',
- }
-
- CODE_OPEN_CHANNEL_BY_URI = '''
- var Cc = Components.classes;
- var Ci = Components.interfaces;
- var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
- var secMan = Cc["@mozilla.org/scriptsecuritymanager;1"].getService(Ci.nsIScriptSecurityManager);
- global.uri = '%(uri)s';
- global.channel = ios.newChannel2(global.uri,
- null,
- null,
- null, // aLoadingNode
- secMan.getSystemPrincipal(),
- null, // aTriggeringPrincipal
- Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
- Ci.nsIContentPolicy.TYPE_OTHER);
- '''
-
- CODE_GET_SPEC = '''
- return global.channel.URI.spec;
- '''
-
- CODE_READ_CONTENT = '''
- var Cc = Components.classes;
- var Ci = Components.interfaces;
-
- var zipReader = Cc["@mozilla.org/libjar/zip-reader;1"].createInstance(Ci.nsIZipReader);
- var inputStream = Cc["@mozilla.org/scriptableinputstream;1"].createInstance(Ci.nsIScriptableInputStream);
-
- var jaruri = global.channel.URI.QueryInterface(Ci.nsIJARURI);
- var file = jaruri.JARFile.QueryInterface(Ci.nsIFileURL).file;
- var entry = jaruri.JAREntry;
- zipReader.open(file);
- inputStream.init(zipReader.getInputStream(entry));
- var content = inputStream.read(inputStream.available());
- inputStream.close();
- zipReader.close();
- return content;
- '''
-
- @classmethod
- def get_uri(cls, filename):
- """Convert filename to URI in system."""
- if filename.startswith(cls.URI_PREFIX):
- return filename
- else:
- return cls.URI_PREFIX + cls.URI_PATH[filename]
-
- def __init__(self, marionette):
- self.runjs = lambda x: marionette.execute_script(x,
- new_sandbox=False,
- sandbox='system')
-
- def read_file(self, filename):
- """Read file and return the contents as string."""
- content = self._read_uri(self.get_uri(filename))
- content = content.replace('"use strict";', '')
- return StringUtility.remove_non_ascii(content)
-
- def _read_uri(self, uri):
- """Read URI in system and return the contents as string."""
- # Open the uri as a channel.
- self.runjs(self.CODE_OPEN_CHANNEL_BY_URI % {'uri': uri})
-
- # Make sure spec is a jar uri, and not recursive.
- # Ex: 'jar:file:///system/b2g/omni.ja!/modules/ril_worker.js'
- #
- # For simplicity, we don't handle other special cases in this test.
- # If B2G build system changes in the future, such as put the jar in
- # another jar, the test case will fail.
- spec = self.runjs(self.CODE_GET_SPEC)
- if (not spec.startswith('jar:file://')) or (spec.count('jar:') != 1):
- raise Exception('URI resolve error')
-
- # Read the content from channel.
- content = self.runjs(self.CODE_READ_CONTENT)
- return content
-
-
-class JSHintEngine:
-
- """Invoke jshint script on system."""
-
- CODE_INIT_JSHINT = '''
- %(script)s;
- global.JSHINT = JSHINT;
- global.options = JSON.parse(%(config_string)s);
- global.globals = global.options.globals;
- delete global.options.globals;
- '''
-
- CODE_RUN_JSHINT = '''
- global.script = %(code)s;
- return global.JSHINT(global.script, global.options, global.globals);
- '''
-
- CODE_GET_JSHINT_ERROR = '''
- return global.JSHINT.errors;
- '''
-
- def __init__(self, marionette, script, config):
- # Remove single line comment in config.
- config = '\n'.join([line.partition('//')[0]
- for line in config.splitlines()])
-
- # Set global (JSHINT, options, global) in js environment.
- self.runjs = lambda x: marionette.execute_script(x,
- new_sandbox=False,
- sandbox='system')
- self.runjs(self.CODE_INIT_JSHINT %
- {'script': script, 'config_string': repr(config)})
-
- def run(self, code, filename=''):
- """Excute JShint check for the given code."""
- check_pass = self.runjs(self.CODE_RUN_JSHINT % {'code': repr(code)})
- errors = self.runjs(self.CODE_GET_JSHINT_ERROR)
- return check_pass, self._get_error_messages(errors, filename)
-
- def _get_error_messages(self, errors, filename=''):
- """
- Convert an error object to a list of readable string.
-
- [{"a": null, "c": null, "code": "W033", "d": null, "character": 6,
- "evidence": "var a", "raw": "Missing semicolon.",
- "reason": "Missing semicolon.", "b": null, "scope": "(main)", "line": 1,
- "id": "(error)"}]
- => line 1, col 6, Missing semicolon.
-
- """
- LINE, COL, REASON = u'line', u'character', u'reason'
- return ["%s: line %s, col %s, %s" %
- (filename, error[LINE], error[COL], error[REASON])
- for error in errors if error]
-
-
-class Linter:
-
- """Handle the linting related process."""
-
- def __init__(self, code_reader, jshint, reporter=None):
- """Set the linter with code_reader, jshint engine, and reporter.
-
- Should have following functionality.
- - code_reader.read_file(filename)
- - jshint.run(code, filename)
- - reporter([...])
-
- """
- self.code_reader = code_reader
- self.jshint = jshint
- if reporter is None:
- self.reporter = lambda x: '\n'.join(x)
- else:
- self.reporter = reporter
-
- def lint_file(self, filename):
- """Lint the file and return (pass, error_message)."""
- # Get code contents.
- code = self.code_reader.read_file(filename)
- lines = code.splitlines()
- import_list = StringUtility.get_imported_list(lines)
- if not import_list:
- check_pass, error_message = self.jshint.run(code, filename)
- else:
- newlines, info = self._merge_multiple_codes(filename, import_list)
- # Each line of |newlines| contains '\n'.
- check_pass, error_message = self.jshint.run(''.join(newlines))
- error_message = self._convert_merged_result(error_message, info)
- # Only keep errors for this file.
- error_message = [line for line in error_message
- if line.startswith(filename)]
- check_pass = (len(error_message) == 0)
- return check_pass, self.reporter(error_message)
-
- def _merge_multiple_codes(self, filename, import_list):
- """Merge multiple codes from filename and import_list."""
- dirname, filename = os.path.split(filename)
- dst_line = 1
- dst_results = []
- info = []
-
- # Put the imported script first, and then the original script.
- for f in import_list + [filename]:
- filepath = os.path.join(dirname, f)
-
- # Maintain a mapping table.
- # New line number after merge => original file and line number.
- info.append((dst_line, filepath, 1))
- try:
- code = self.code_reader.read_file(filepath)
- lines = code.splitlines(True) # Keep '\n'.
- src_results = StringUtility.auto_wrap_strict_mode(
- StringUtility.auto_close(lines))
- dst_results.extend(src_results)
- dst_line += len(src_results)
- except:
- info.pop()
- return dst_results, info
-
- def _convert_merged_result(self, error_lines, line_info):
- pattern = re.compile(r'(.*): line (\d+),(.*)')
- start_line = [info[0] for info in line_info]
- new_result_lines = []
- for line in error_lines:
- m = pattern.match(line)
- if not m:
- continue
-
- line_number, remain = int(m.group(2)), m.group(3)
-
- # [1, 2, 7, 8]
- # ^ for 7, pos = 3
- # ^ for 6, pos = 2
- pos = bisect.bisect_right(start_line, line_number)
- dst_line, name, src_line = line_info[pos - 1]
- real_line_number = line_number - dst_line + src_line
- new_result_lines.append(
- "%s: line %s,%s" % (name, real_line_number, remain))
- return new_result_lines
-
-
-class TestRILCodeQuality(MarionetteTestCase):
-
- JSHINT_PATH = 'ril_jshint/jshint.js'
- JSHINTRC_PATH = 'ril_jshint/jshintrc'
-
- def _read_local_file(self, filepath):
- """Read file content from local (folder of this test case)."""
- test_dir = os.path.dirname(inspect.getfile(TestRILCodeQuality))
- return open(os.path.join(test_dir, filepath)).read()
-
- def _get_extended_error_message(self, error_message):
- return '\n'.join(['See errors below and more information in Bug 880643',
- '\n'.join(error_message),
- 'See errors above and more information in Bug 880643'])
-
- def _check(self, filename):
- check_pass, error_message = self.linter.lint_file(filename)
- self.assertTrue(check_pass, error_message)
-
- def setUp(self):
- MarionetteTestCase.setUp(self)
- self.linter = Linter(
- ResourceUriFileReader(self.marionette),
- JSHintEngine(self.marionette,
- self._read_local_file(self.JSHINT_PATH),
- self._read_local_file(self.JSHINTRC_PATH)),
- self._get_extended_error_message)
-
- def tearDown(self):
- MarionetteTestCase.tearDown(self)
-
- def test_RadioInterfaceLayer(self):
- self._check('RadioInterfaceLayer.js')
-
- # Bug 936504. Disable the test for 'ril_worker.js'. It sometimes runs very
- # slow and causes the timeout fail on try server.
- #def test_ril_worker(self):
- # self._check('ril_worker.js')
-
- def test_ril_consts(self):
- self._check('ril_consts.js')
-
- def test_worker_buf(self):
- self._check('worker_buf.js')