diff -Nru click-reviewers-tools-0.19/bin/click-check-lint click-reviewers-tools-0.19/bin/click-check-lint --- click-reviewers-tools-0.19/bin/click-check-lint 2014-10-15 09:06:00.000000000 +0000 +++ click-reviewers-tools-0.19/bin/click-check-lint 2014-11-21 15:31:17.000000000 +0000 @@ -16,6 +16,7 @@ # along with this program. If not, see . from __future__ import print_function +import json import sys import clickreviews.cr_common as cr_common @@ -25,7 +26,14 @@ if len(sys.argv) < 2: cr_common.error("Must give path to click package") - review = cr_lint.ClickReviewLint(sys.argv[1]) + # extract args + fn = sys.argv[1] + if len(sys.argv) > 2: + overrides = json.loads(sys.argv[2]) + else: + overrides = None + + review = cr_lint.ClickReviewLint(fn, overrides=overrides) review.do_checks() rc = review.do_report() sys.exit(rc) diff -Nru click-reviewers-tools-0.19/bin/click-check-security click-reviewers-tools-0.19/bin/click-check-security --- click-reviewers-tools-0.19/bin/click-check-security 2014-10-15 09:06:00.000000000 +0000 +++ click-reviewers-tools-0.19/bin/click-check-security 2014-11-21 15:31:17.000000000 +0000 @@ -16,6 +16,7 @@ # along with this program. If not, see . from __future__ import print_function +import json import sys import clickreviews.cr_common as cr_common @@ -25,7 +26,14 @@ if len(sys.argv) < 2: cr_common.error("Must give path to click package") - review = cr_security.ClickReviewSecurity(sys.argv[1]) + # extract args + fn = sys.argv[1] + if len(sys.argv) > 2: + overrides = json.loads(sys.argv[2]) + else: + overrides = None + + review = cr_security.ClickReviewSecurity(fn, overrides=overrides) review.do_checks() rc = review.do_report() sys.exit(rc) diff -Nru click-reviewers-tools-0.19/bin/clickreviews/cr_desktop.py click-reviewers-tools-0.19/bin/clickreviews/cr_desktop.py --- click-reviewers-tools-0.19/bin/clickreviews/cr_desktop.py 2014-10-15 09:06:00.000000000 +0000 +++ click-reviewers-tools-0.19/bin/clickreviews/cr_desktop.py 2014-11-21 15:31:17.000000000 +0000 @@ -54,6 +54,15 @@ # msg("Skipped missing desktop hook for account-qml-plugin" # " '%s'" % app) continue + elif 'systemd' in self.manifest['hooks'][app]: + # msg("Skipped missing desktop hook for systemd" + # " '%s'" % app) + continue + elif 'framework' in self.manifest['hooks'][app]: + # TODO: verify this is the long term hook name + # msg("Skipped missing desktop hook for framework" + # " '%s'" % app) + continue else: error("could not find desktop hook for '%s'" % app) if not isinstance(self.manifest['hooks'][app]['desktop'], str): @@ -327,19 +336,39 @@ s = 'OK' found_url_patterns = False found_model_search_path = False + found_named_webapp = False + urls = [] for i in de.getExec().split(): if i.startswith('--webappUrlPatterns'): found_url_patterns = True if i.startswith('--webappModelSearchPath'): found_model_search_path = True - if found_url_patterns and found_model_search_path: - t = 'error' - s = "should not specify --webappUrlPatterns when using " + \ - "--webappModelSearchPath" - elif not found_url_patterns and not found_model_search_path: - t = 'error' - s = "must specify one of --webappUrlPatterns or " + \ - "--webappModelSearchPath" + if i.startswith('--webapp='): + found_model_search_path = True + if not i.startswith('--'): + urls.append(i) + is_launching_local_app = True + for url in urls: + parts = urlsplit(url) + if parts.scheme in ['http', 'https']: + is_launching_local_app = False + break + if is_launching_local_app and \ + (found_url_patterns or found_model_search_path + or found_named_webapp): + t = 'error' + s = "should not specify --webappUrlPatterns, " + \ + "--webappModelSearchPath or --webapp= when " + \ + "running local application" + elif not is_launching_local_app: + if found_url_patterns and found_model_search_path: + t = 'error' + s = "should not specify --webappUrlPatterns when using " + \ + "--webappModelSearchPath" + elif not found_url_patterns and not found_model_search_path: + t = 'error' + s = "must specify one of --webappUrlPatterns or " + \ + "--webappModelSearchPath" self._add_result(t, n, s) def _check_patterns(self, app, patterns, args): diff -Nru click-reviewers-tools-0.19/bin/clickreviews/cr_lint.py click-reviewers-tools-0.19/bin/clickreviews/cr_lint.py --- click-reviewers-tools-0.19/bin/clickreviews/cr_lint.py 2014-10-15 09:06:00.000000000 +0000 +++ click-reviewers-tools-0.19/bin/clickreviews/cr_lint.py 2014-11-21 15:31:17.000000000 +0000 @@ -31,7 +31,7 @@ class ClickReviewLint(ClickReview): '''This class represents click lint reviews''' - def __init__(self, fn): + def __init__(self, fn, overrides=None): '''Set up the class.''' ClickReview.__init__(self, fn, "lint") self.control_files = dict() @@ -67,10 +67,17 @@ self.is_core_scope = (self.click_pkgname.startswith('com.ubuntu.scopes.') and self.email == 'ubuntu-devel-discuss@lists.ubuntu.com') + # "core snappy" is not necessarily a word we use right now, but + # we want to special case scopes which are written through our + # vetted development process. + self.is_core_snappy = (self.click_pkgname.startswith('com.ubuntu.snappy.') + and self.email == + 'ubuntu-devel-discuss@lists.ubuntu.com') else: self.email = None self.is_core_app = False self.is_core_scope = False + self.is_core_snappy = False self._list_all_compiled_binaries() @@ -87,6 +94,9 @@ 'urls'] self.redflagged_hooks = ['pay-ui'] + if overrides is None: + overrides = {} + self.overrides = overrides def _list_control_files(self): '''List all control files with their full path.''' @@ -589,6 +599,9 @@ elif self.is_core_scope: t = 'info' s = "OK ('com.ubuntu.scopes' uses '%s' as email)" % self.email + elif self.is_core_snappy: + t = 'info' + s = "OK ('com.ubuntu.snappy' uses '%s' as email)" % self.email else: t = 'error' s = "email=%s does not match package domain=%s " \ @@ -641,9 +654,8 @@ '''Check framework()''' n = 'framework' l = "http://askubuntu.com/questions/460512/what-framework-should-i-use-in-my-manifest-file" - local_copy = os.path.join(os.path.dirname(__file__), - '../data/frameworks.json') - frameworks = Frameworks(local_copy) + framework_overrides = self.overrides.get('framework', {}) + frameworks = Frameworks(overrides=framework_overrides) if self.manifest['framework'] in frameworks.AVAILABLE_FRAMEWORKS: t = 'info' s = 'OK' diff -Nru click-reviewers-tools-0.19/bin/clickreviews/cr_scope.py click-reviewers-tools-0.19/bin/clickreviews/cr_scope.py --- click-reviewers-tools-0.19/bin/clickreviews/cr_scope.py 2014-10-15 09:06:00.000000000 +0000 +++ click-reviewers-tools-0.19/bin/clickreviews/cr_scope.py 2014-11-21 15:31:17.000000000 +0000 @@ -17,8 +17,10 @@ from __future__ import print_function from clickreviews.cr_common import ClickReview, error, msg +import codecs import configparser import os +import re KNOWN_SECTIONS = set(["ScopeConfig", "Appearance"]) @@ -57,8 +59,8 @@ error("Could not find scope INI file '%s'" % ini_fn_bn) try: d["scope_config"] = configparser.ConfigParser() - d["scope_config"].read(ini_fn) - except Exception: + d["scope_config"].read_file(codecs.open(ini_fn, "r", "utf8")) + except Exception as e: error("scope config unparseable: %s (%s)" % (ini_fn_bn, str(e))) d["dir"] = fn @@ -104,6 +106,9 @@ 'resultsttltype', 'scoperunner', 'searchhint'] + translated = ['description', + 'displayname', + 'searchhint'] missing = [] t = 'info' @@ -128,9 +133,12 @@ n = 'ini_%s_scope_unknown_fields' % (app) s = 'OK' unknown = [] - for f in self.scopes[app]["scope_config"]['ScopeConfig'].keys(): - if f.lower() not in required and f.lower() not in optional: - unknown.append(f.lower()) + for i in self.scopes[app]["scope_config"]['ScopeConfig'].keys(): + f = i.lower() + if f not in required and f not in optional and \ + (f.split("[")[0] not in translated or not + re.search(r'.*\[[a-z][a-z](_[a-z][a-z])?\]$', f)): + unknown.append(f) if len(unknown) == 1: t = 'warn' diff -Nru click-reviewers-tools-0.19/bin/clickreviews/cr_security.py click-reviewers-tools-0.19/bin/clickreviews/cr_security.py --- click-reviewers-tools-0.19/bin/clickreviews/cr_security.py 2014-10-15 09:06:00.000000000 +0000 +++ click-reviewers-tools-0.19/bin/clickreviews/cr_security.py 2014-11-21 15:31:17.000000000 +0000 @@ -25,7 +25,7 @@ class ClickReviewSecurity(ClickReview): '''This class represents click lint reviews''' - def __init__(self, fn): + def __init__(self, fn, overrides=None): ClickReview.__init__(self, fn, "security") local_copy = os.path.join(os.path.dirname(__file__), @@ -78,10 +78,21 @@ # framework policy is based on major framework version. In 13.10, there # was only 'ubuntu-sdk-13.10', but in 14.04, there will be several, # like 'ubuntu-sdk-14.04-html5', 'ubuntu-sdk-14.04-platform', etc - self.major_framework_policy = {'ubuntu-sdk-13.10': 1.0, - 'ubuntu-sdk-14.04': 1.1, - 'ubuntu-sdk-14.10': 1.2, - } + self.major_framework_policy = { + 'ubuntu-sdk-13.10': { + 'policy_version': 1.0, + }, + 'ubuntu-sdk-14.04': { + 'policy_version': 1.1, + }, + 'ubuntu-sdk-14.10': { + 'policy_version': 1.2, + }, + } + if overrides is None: + overrides = {} + framework_overrides = overrides.get('framework', {}) + self._override_framework_policies(framework_overrides) self.security_manifests = dict() self.security_apps = [] @@ -102,6 +113,23 @@ self._extract_security_manifest(app) self.security_apps.append(app) + def _override_framework_policies(self, overrides): + # override major framework policies + self.major_framework_policy.update(overrides) + + # override apparmor policies + for name, data in overrides.items(): + vendor = data.get('policy_vendor') + version = str(data.get('policy_version')) + + if vendor not in self.aa_policy: + self.aa_policy[vendor] = {} + + if version not in self.aa_policy[vendor]: + # just ensure the version is defined + # TODO: add support to override templates and policy groups + self.aa_policy[vendor][version] = {} + def _extract_security_manifest(self, app): '''Extract security manifest and verify it has the expected structure''' @@ -269,15 +297,15 @@ n = 'policy_version_matches_framework (%s)' % (f) s = "OK" found_major = False - for k in self.major_framework_policy.keys(): + for name, data in self.major_framework_policy.items(): # TODO: use libclick when it is available - if not self.manifest['framework'].startswith(k): + if not self.manifest['framework'].startswith(name): continue found_major = True - if m['policy_version'] != self.major_framework_policy[k]: + if m['policy_version'] != data['policy_version']: t = 'error' s = '%s != %s (%s)' % (str(m['policy_version']), - self.major_framework_policy[k], + data['policy_version'], self.manifest['framework']) if not found_major: t = 'error' diff -Nru click-reviewers-tools-0.19/bin/clickreviews/frameworks.py click-reviewers-tools-0.19/bin/clickreviews/frameworks.py --- click-reviewers-tools-0.19/bin/clickreviews/frameworks.py 2014-10-15 09:06:00.000000000 +0000 +++ click-reviewers-tools-0.19/bin/clickreviews/frameworks.py 2014-11-21 15:31:17.000000000 +0000 @@ -32,15 +32,21 @@ OBSOLETE_FRAMEWORKS = [] AVAILABLE_FRAMEWORKS = [] - def __init__(self, local_copy_fn=None): + def __init__(self, overrides=None): self.FRAMEWORKS = clickreviews.remote.read_cr_file(USER_DATA_FILE, - FRAMEWORKS_DATA_URL, - local_copy_fn) + FRAMEWORKS_DATA_URL) + if overrides is not None: + self.FRAMEWORKS.update(overrides) - for k, v in self.FRAMEWORKS.items(): - if v == 'deprecated': - self.DEPRECATED_FRAMEWORKS.append(k) - elif v == 'obsolete': - self.OBSOLETE_FRAMEWORKS.append(k) - elif v == 'available': - self.AVAILABLE_FRAMEWORKS.append(k) + for name, data in self.FRAMEWORKS.items(): + if type(data) is dict: + state = data.get('state') + else: + state = data + + if state == 'deprecated': + self.DEPRECATED_FRAMEWORKS.append(name) + elif state == 'obsolete': + self.OBSOLETE_FRAMEWORKS.append(name) + elif state == 'available': + self.AVAILABLE_FRAMEWORKS.append(name) diff -Nru click-reviewers-tools-0.19/bin/clickreviews/remote.py click-reviewers-tools-0.19/bin/clickreviews/remote.py --- click-reviewers-tools-0.19/bin/clickreviews/remote.py 2014-10-15 09:06:00.000000000 +0000 +++ click-reviewers-tools-0.19/bin/clickreviews/remote.py 2014-11-21 15:31:17.000000000 +0000 @@ -28,7 +28,7 @@ def _update_is_necessary(fn): return (not os.path.exists(fn)) or \ - (time.time() - os.path.getctime(fn) >= UPDATE_INTERVAL) + (time.time() - os.path.getmtime(fn) >= UPDATE_INTERVAL) def _update_is_possible(url): diff -Nru click-reviewers-tools-0.19/bin/clickreviews/tests/test_cr_desktop.py click-reviewers-tools-0.19/bin/clickreviews/tests/test_cr_desktop.py --- click-reviewers-tools-0.19/bin/clickreviews/tests/test_cr_desktop.py 2014-10-15 09:06:00.000000000 +0000 +++ click-reviewers-tools-0.19/bin/clickreviews/tests/test_cr_desktop.py 2014-11-21 15:31:17.000000000 +0000 @@ -713,3 +713,57 @@ r = c.click_report expected_counts = {'info': None, 'warn': 1, 'error': 0} self.check_results(r, expected_counts) + + def test_check_desktop_exec_webbrowser_local_app(self): + '''Test test_check_desktop_exec_webbrowser_local_app() local app''' + c = ClickReviewDesktop(self.test_name) + ex = "webapp-container ./www/index.html" + self.set_test_desktop(self.default_appname, + "Exec", + ex) + c.check_desktop_exec_webapp_args() + r = c.click_report + expected_counts = {'info': None, 'warn': 0, 'error': 0} + self.check_results(r, expected_counts) + + def test_check_desktop_exec_webbrowser_local_pattern(self): + '''Test test_check_desktop_exec_webbrowser_local_pattern() invalid pattern''' + c = ClickReviewDesktop(self.test_name) + ex = "webapp-container " + \ + "--webappUrlPatterns=https?://mobile.twitter.com/* " + \ + "./www/index.html" + self.set_test_desktop(self.default_appname, + "Exec", + ex) + c.check_desktop_exec_webapp_args() + r = c.click_report + expected_counts = {'info': None, 'warn': 0, 'error': 1} + self.check_results(r, expected_counts) + + def test_check_desktop_exec_webbrowser_local_webapp(self): + '''Test test_check_desktop_exec_webbrowser_local_webapp() invalid webapp cli''' + c = ClickReviewDesktop(self.test_name) + ex = "webapp-container " + \ + "--webapp=DEADBEEF " + \ + "./www/index.html" + self.set_test_desktop(self.default_appname, + "Exec", + ex) + c.check_desktop_exec_webapp_args() + r = c.click_report + expected_counts = {'info': None, 'warn': 0, 'error': 1} + self.check_results(r, expected_counts) + + def test_check_desktop_exec_webbrowser_local_model(self): + '''Test test_check_desktop_exec_webbrowser_local_model() invalid model''' + c = ClickReviewDesktop(self.test_name) + ex = "webapp-container " + \ + "--webappModelSearchPath=. " + \ + "./www/index.html" + self.set_test_desktop(self.default_appname, + "Exec", + ex) + c.check_desktop_exec_webapp_args() + r = c.click_report + expected_counts = {'info': None, 'warn': 0, 'error': 1} + self.check_results(r, expected_counts) diff -Nru click-reviewers-tools-0.19/bin/clickreviews/tests/test_cr_lint.py click-reviewers-tools-0.19/bin/clickreviews/tests/test_cr_lint.py --- click-reviewers-tools-0.19/bin/clickreviews/tests/test_cr_lint.py 2014-10-15 09:06:00.000000000 +0000 +++ click-reviewers-tools-0.19/bin/clickreviews/tests/test_cr_lint.py 2014-11-21 15:31:17.000000000 +0000 @@ -18,6 +18,7 @@ from clickreviews.cr_lint import ClickReviewLint from clickreviews.cr_lint import MINIMUM_CLICK_FRAMEWORK_VERSION +from clickreviews.frameworks import FRAMEWORKS_DATA_URL, USER_DATA_FILE import clickreviews.cr_tests as cr_tests @@ -29,6 +30,21 @@ cr_tests.mock_patch() super() + def patch_frameworks(self): + def _mock_frameworks(self, overrides=None): + self.FRAMEWORKS = { + 'ubuntu-sdk-14.10-qml-dev2': 'available', + 'ubuntu-sdk-13.10': 'deprecated', + 'ubuntu-sdk-14.10-qml-dev1': 'obsolete', + } + self.AVAILABLE_FRAMEWORKS = ['ubuntu-sdk-14.10-qml-dev2'] + self.OBSOLETE_FRAMEWORKS = ['ubuntu-sdk-14.10-qml-dev1'] + self.DEPRECATED_FRAMEWORKS = ['ubuntu-sdk-13.10'] + p = patch('clickreviews.frameworks.Frameworks.__init__', + _mock_frameworks) + p.start() + self.addCleanup(p.stop) + def test_check_architecture(self): '''Test check_architecture()''' c = ClickReviewLint(self.test_name) @@ -554,7 +570,7 @@ self.check_results(r, expected_counts) def test_check_maintainer_email_special(self): - '''Test check_maintainer() - ubuntu-devel-discuss@lists.ubuntu.com''' + '''Test check_maintainer() - ubuntu-devel-discuss@ - app''' self.set_test_control("Package", "com.canonical.app") self.set_test_manifest("maintainer", "Ubuntu Core Developers " @@ -575,7 +591,7 @@ self.check_results(r, expected=expected) def test_check_maintainer_email_special2(self): - '''Test check_maintainer() - ubuntu-devel-discuss@lists.ubuntu.com''' + '''Test check_maintainer() - ubuntu-devel-discuss@ - scope''' self.set_test_control("Package", "com.ubuntu.scopes.youtube") self.set_test_manifest("maintainer", "Ubuntu Core Developers " @@ -595,6 +611,27 @@ "'ubuntu-devel-discuss@lists.ubuntu.com' as email)"} self.check_results(r, expected=expected) + def test_check_maintainer_email_special3(self): + '''Test check_maintainer() - ubuntu-devel-discuss@ - snappy''' + self.set_test_control("Package", "com.ubuntu.snappy.foo") + self.set_test_manifest("maintainer", + "Ubuntu Core Developers " + "") + c = ClickReviewLint(self.test_name) + c.check_maintainer() + r = c.click_report + expected_counts = {'info': None, 'warn': 0, 'error': 0} + self.check_results(r, expected_counts) + + expected = dict() + expected['info'] = dict() + expected['warn'] = dict() + expected['error'] = dict() + expected['info']['lint_maintainer_domain'] = \ + {"text": "OK ('com.ubuntu.snappy' uses " + "'ubuntu-devel-discuss@lists.ubuntu.com' as email)"} + self.check_results(r, expected=expected) + def test_check_icon(self): '''Test check_icon()''' self.set_test_manifest("icon", "someicon") @@ -682,15 +719,33 @@ def test_check_framework(self): '''Test check_framework()''' + self.patch_frameworks() + self.set_test_manifest("framework", "ubuntu-sdk-14.10-qml-dev2") + c = ClickReviewLint(self.test_name) + c.check_framework() + r = c.click_report + expected_counts = {'info': 1, 'warn': 0, 'error': 0} + self.check_results(r, expected_counts) + + @patch('clickreviews.remote.read_cr_file') + def test_check_framework_fetches_remote_data(self, mock_read_cr_file): + '''Test check_framework()''' + mock_read_cr_file.return_value = { + 'ubuntu-sdk-14.10-qml-dev2': 'available', + } self.set_test_manifest("framework", "ubuntu-sdk-14.10-qml-dev2") c = ClickReviewLint(self.test_name) c.check_framework() r = c.click_report expected_counts = {'info': 1, 'warn': 0, 'error': 0} self.check_results(r, expected_counts) + # ensure no local fn is provided when reading frameworks + mock_read_cr_file.assert_called_once_with( + USER_DATA_FILE, FRAMEWORKS_DATA_URL) def test_check_framework_bad(self): '''Test check_framework() - bad''' + self.patch_frameworks() self.set_test_manifest("framework", "nonexistent") c = ClickReviewLint(self.test_name) c.check_framework() @@ -700,6 +755,7 @@ def test_check_framework_deprecated(self): '''Test check_framework() - deprecated''' + self.patch_frameworks() self.set_test_manifest("framework", "ubuntu-sdk-13.10") c = ClickReviewLint(self.test_name) c.check_framework() @@ -709,6 +765,7 @@ def test_check_framework_obsolete(self): '''Test check_framework() - obsolete''' + self.patch_frameworks() self.set_test_manifest("framework", "ubuntu-sdk-14.10-qml-dev1") c = ClickReviewLint(self.test_name) c.check_framework() @@ -716,6 +773,34 @@ expected_counts = {'info': None, 'warn': 0, 'error': 1} self.check_results(r, expected_counts) + @patch('clickreviews.remote.read_cr_file') + def test_check_framework_with_overrides(self, mock_read_cr_file): + '''Test check_framework() - using overrides''' + mock_read_cr_file.return_value = { + 'ubuntu-sdk-14.10-qml-dev2': 'available', + } + self.set_test_manifest("framework", "nonexistent") + overrides = {'framework': {'nonexistent': {'state': 'available'}}} + c = ClickReviewLint(self.test_name, overrides=overrides) + c.check_framework() + r = c.click_report + expected_counts = {'info': 1, 'warn': 0, 'error': 0} + self.check_results(r, expected_counts) + + @patch('clickreviews.remote.read_cr_file') + def test_check_framework_with_malformed_overrides(self, mock_read_cr_file): + '''Test check_framework() - using overrides''' + mock_read_cr_file.return_value = { + 'ubuntu-sdk-14.10-qml-dev2': 'available', + } + self.set_test_manifest("framework", "nonexistent") + overrides = {'nonexistent': {'state': 'available'}} + c = ClickReviewLint(self.test_name, overrides=overrides) + c.check_framework() + r = c.click_report + expected_counts = {'info': 0, 'warn': 0, 'error': 1} + self.check_results(r, expected_counts) + def test_check_hooks(self): '''Test check_hooks()''' self.set_test_manifest("framework", "ubuntu-sdk-13.10") diff -Nru click-reviewers-tools-0.19/bin/clickreviews/tests/test_cr_scope.py click-reviewers-tools-0.19/bin/clickreviews/tests/test_cr_scope.py --- click-reviewers-tools-0.19/bin/clickreviews/tests/test_cr_scope.py 2014-10-15 09:06:00.000000000 +0000 +++ click-reviewers-tools-0.19/bin/clickreviews/tests/test_cr_scope.py 2014-11-21 15:31:17.000000000 +0000 @@ -123,6 +123,45 @@ expected_counts = {'info': None, 'warn': 0, 'error': 1} self.check_results(r, expected_counts) + def test_check_scope_ini_translated_field(self): + '''Test check_scope_ini() - translated field - es''' + config = self._stub_config() + config['searchhint[es]'] = "foo" + scope = self._create_scope(config) + + self.set_test_scope(self.default_appname, scope) + c = ClickReviewScope(self.test_name) + c.check_scope_ini() + r = c.click_report + expected_counts = {'info': None, 'warn': 0, 'error': 0} + self.check_results(r, expected_counts) + + def test_check_scope_ini_translated_field2(self): + '''Test check_scope_ini() - translated field - pt_br''' + config = self._stub_config() + config['searchhint[pt_br]'] = "foo" + scope = self._create_scope(config) + + self.set_test_scope(self.default_appname, scope) + c = ClickReviewScope(self.test_name) + c.check_scope_ini() + r = c.click_report + expected_counts = {'info': None, 'warn': 0, 'error': 0} + self.check_results(r, expected_counts) + + def test_check_scope_ini_bad_translated_field(self): + '''Test check_scope_ini() - bad translated field''' + config = self._stub_config() + config['searchhint[ba;r]'] = "foo" + scope = self._create_scope(config) + + self.set_test_scope(self.default_appname, scope) + c = ClickReviewScope(self.test_name) + c.check_scope_ini() + r = c.click_report + expected_counts = {'info': None, 'warn': 1, 'error': 0} + self.check_results(r, expected_counts) + def test_check_scope_ini_nonexistent_field(self): '''Test check_scope_ini() - non-existent field''' config = self._stub_config() diff -Nru click-reviewers-tools-0.19/bin/clickreviews/tests/test_cr_security.py click-reviewers-tools-0.19/bin/clickreviews/tests/test_cr_security.py --- click-reviewers-tools-0.19/bin/clickreviews/tests/test_cr_security.py 2014-10-15 09:06:00.000000000 +0000 +++ click-reviewers-tools-0.19/bin/clickreviews/tests/test_cr_security.py 2014-11-21 15:31:17.000000000 +0000 @@ -136,7 +136,7 @@ policy_version = 0 for k in tmp.major_framework_policy.keys(): if f.startswith(k): - policy_version = tmp.major_framework_policy[k] + policy_version = tmp.major_framework_policy[k]['policy_version'] self.set_test_security_manifest(self.default_appname, "policy_version", policy_version) @@ -209,6 +209,36 @@ {"text": "Invalid framework 'nonexistent'"} self.check_results(report, expected=expected) + def test_check_policy_version_framework_with_overrides(self): + '''Test check_policy_version() - override framework (nonexistent)''' + self.set_test_manifest("framework", "nonexistent") + self.set_test_security_manifest(self.default_appname, + "policy_version", 1.3) + overrides = {'framework': {'nonexistent': {'state': 'available', + 'policy_vendor': 'ubuntu', + 'policy_version': 1.3}}} + c = ClickReviewSecurity(self.test_name, overrides=overrides) + c.check_policy_version() + report = c.click_report + + expected_counts = {'info': 3, 'warn': 0, 'error': 0} + self.check_results(report, expected_counts) + + def test_check_policy_version_framework_with_malformed_overrides(self): + '''Test check_policy_version() - incorrectly override framework''' + self.set_test_manifest("framework", "nonexistent") + self.set_test_security_manifest(self.default_appname, + "policy_version", 1.3) + overrides = {'nonexistent': {'state': 'available', + 'policy_vendor': 'ubuntu', + 'policy_version': 1.3}} + c = ClickReviewSecurity(self.test_name, overrides=overrides) + c.check_policy_version() + report = c.click_report + + expected_counts = {'info': 1, 'warn': 0, 'error': 2} + self.check_results(report, expected_counts) + def test_check_policy_vendor_unspecified(self): '''Test check_policy_vendor() - unspecified''' c = ClickReviewSecurity(self.test_name) diff -Nru click-reviewers-tools-0.19/bin/clickreviews/tests/test_remote.py click-reviewers-tools-0.19/bin/clickreviews/tests/test_remote.py --- click-reviewers-tools-0.19/bin/clickreviews/tests/test_remote.py 1970-01-01 00:00:00.000000000 +0000 +++ click-reviewers-tools-0.19/bin/clickreviews/tests/test_remote.py 2014-11-21 15:31:17.000000000 +0000 @@ -0,0 +1,38 @@ +import time +from unittest import TestCase +from unittest.mock import patch + +from clickreviews.remote import UPDATE_INTERVAL, _update_is_necessary + + +class RemoteTestCase(TestCase): + def patch_path(self): + p = patch('clickreviews.remote.os.path') + self.mock_path = p.start() + self.addCleanup(p.stop) + + def patch_time(self, now): + p = patch('clickreviews.remote.time.time') + self.mock_time = p.start() + self.mock_time.return_value = now + self.addCleanup(p.stop) + + def test_no_update_needed(self): + now = time.time() + self.patch_time(now) + self.patch_path() + + # last update was 10 seconds ago + self.mock_path.getmtime.return_value = now - 10 + + self.assertFalse(_update_is_necessary('some-file')) + + def test_update_needed(self): + now = time.time() + self.patch_time(now) + self.patch_path() + + # last update was UPDATE_INTERVAL + 10 seconds ago + self.mock_path.getmtime.return_value = now - UPDATE_INTERVAL - 10 + + self.assertTrue(_update_is_necessary('some-file')) diff -Nru click-reviewers-tools-0.19/clickreviews/cr_desktop.py click-reviewers-tools-0.19/clickreviews/cr_desktop.py --- click-reviewers-tools-0.19/clickreviews/cr_desktop.py 2014-10-15 09:06:00.000000000 +0000 +++ click-reviewers-tools-0.19/clickreviews/cr_desktop.py 2014-11-21 15:31:17.000000000 +0000 @@ -54,6 +54,15 @@ # msg("Skipped missing desktop hook for account-qml-plugin" # " '%s'" % app) continue + elif 'systemd' in self.manifest['hooks'][app]: + # msg("Skipped missing desktop hook for systemd" + # " '%s'" % app) + continue + elif 'framework' in self.manifest['hooks'][app]: + # TODO: verify this is the long term hook name + # msg("Skipped missing desktop hook for framework" + # " '%s'" % app) + continue else: error("could not find desktop hook for '%s'" % app) if not isinstance(self.manifest['hooks'][app]['desktop'], str): @@ -327,19 +336,39 @@ s = 'OK' found_url_patterns = False found_model_search_path = False + found_named_webapp = False + urls = [] for i in de.getExec().split(): if i.startswith('--webappUrlPatterns'): found_url_patterns = True if i.startswith('--webappModelSearchPath'): found_model_search_path = True - if found_url_patterns and found_model_search_path: - t = 'error' - s = "should not specify --webappUrlPatterns when using " + \ - "--webappModelSearchPath" - elif not found_url_patterns and not found_model_search_path: - t = 'error' - s = "must specify one of --webappUrlPatterns or " + \ - "--webappModelSearchPath" + if i.startswith('--webapp='): + found_model_search_path = True + if not i.startswith('--'): + urls.append(i) + is_launching_local_app = True + for url in urls: + parts = urlsplit(url) + if parts.scheme in ['http', 'https']: + is_launching_local_app = False + break + if is_launching_local_app and \ + (found_url_patterns or found_model_search_path + or found_named_webapp): + t = 'error' + s = "should not specify --webappUrlPatterns, " + \ + "--webappModelSearchPath or --webapp= when " + \ + "running local application" + elif not is_launching_local_app: + if found_url_patterns and found_model_search_path: + t = 'error' + s = "should not specify --webappUrlPatterns when using " + \ + "--webappModelSearchPath" + elif not found_url_patterns and not found_model_search_path: + t = 'error' + s = "must specify one of --webappUrlPatterns or " + \ + "--webappModelSearchPath" self._add_result(t, n, s) def _check_patterns(self, app, patterns, args): diff -Nru click-reviewers-tools-0.19/clickreviews/cr_lint.py click-reviewers-tools-0.19/clickreviews/cr_lint.py --- click-reviewers-tools-0.19/clickreviews/cr_lint.py 2014-10-15 09:06:00.000000000 +0000 +++ click-reviewers-tools-0.19/clickreviews/cr_lint.py 2014-11-21 15:31:17.000000000 +0000 @@ -31,7 +31,7 @@ class ClickReviewLint(ClickReview): '''This class represents click lint reviews''' - def __init__(self, fn): + def __init__(self, fn, overrides=None): '''Set up the class.''' ClickReview.__init__(self, fn, "lint") self.control_files = dict() @@ -67,10 +67,17 @@ self.is_core_scope = (self.click_pkgname.startswith('com.ubuntu.scopes.') and self.email == 'ubuntu-devel-discuss@lists.ubuntu.com') + # "core snappy" is not necessarily a word we use right now, but + # we want to special case scopes which are written through our + # vetted development process. + self.is_core_snappy = (self.click_pkgname.startswith('com.ubuntu.snappy.') + and self.email == + 'ubuntu-devel-discuss@lists.ubuntu.com') else: self.email = None self.is_core_app = False self.is_core_scope = False + self.is_core_snappy = False self._list_all_compiled_binaries() @@ -87,6 +94,9 @@ 'urls'] self.redflagged_hooks = ['pay-ui'] + if overrides is None: + overrides = {} + self.overrides = overrides def _list_control_files(self): '''List all control files with their full path.''' @@ -589,6 +599,9 @@ elif self.is_core_scope: t = 'info' s = "OK ('com.ubuntu.scopes' uses '%s' as email)" % self.email + elif self.is_core_snappy: + t = 'info' + s = "OK ('com.ubuntu.snappy' uses '%s' as email)" % self.email else: t = 'error' s = "email=%s does not match package domain=%s " \ @@ -641,9 +654,8 @@ '''Check framework()''' n = 'framework' l = "http://askubuntu.com/questions/460512/what-framework-should-i-use-in-my-manifest-file" - local_copy = os.path.join(os.path.dirname(__file__), - '../data/frameworks.json') - frameworks = Frameworks(local_copy) + framework_overrides = self.overrides.get('framework', {}) + frameworks = Frameworks(overrides=framework_overrides) if self.manifest['framework'] in frameworks.AVAILABLE_FRAMEWORKS: t = 'info' s = 'OK' diff -Nru click-reviewers-tools-0.19/clickreviews/cr_scope.py click-reviewers-tools-0.19/clickreviews/cr_scope.py --- click-reviewers-tools-0.19/clickreviews/cr_scope.py 2014-10-15 09:06:00.000000000 +0000 +++ click-reviewers-tools-0.19/clickreviews/cr_scope.py 2014-11-21 15:31:17.000000000 +0000 @@ -17,8 +17,10 @@ from __future__ import print_function from clickreviews.cr_common import ClickReview, error, msg +import codecs import configparser import os +import re KNOWN_SECTIONS = set(["ScopeConfig", "Appearance"]) @@ -57,8 +59,8 @@ error("Could not find scope INI file '%s'" % ini_fn_bn) try: d["scope_config"] = configparser.ConfigParser() - d["scope_config"].read(ini_fn) - except Exception: + d["scope_config"].read_file(codecs.open(ini_fn, "r", "utf8")) + except Exception as e: error("scope config unparseable: %s (%s)" % (ini_fn_bn, str(e))) d["dir"] = fn @@ -104,6 +106,9 @@ 'resultsttltype', 'scoperunner', 'searchhint'] + translated = ['description', + 'displayname', + 'searchhint'] missing = [] t = 'info' @@ -128,9 +133,12 @@ n = 'ini_%s_scope_unknown_fields' % (app) s = 'OK' unknown = [] - for f in self.scopes[app]["scope_config"]['ScopeConfig'].keys(): - if f.lower() not in required and f.lower() not in optional: - unknown.append(f.lower()) + for i in self.scopes[app]["scope_config"]['ScopeConfig'].keys(): + f = i.lower() + if f not in required and f not in optional and \ + (f.split("[")[0] not in translated or not + re.search(r'.*\[[a-z][a-z](_[a-z][a-z])?\]$', f)): + unknown.append(f) if len(unknown) == 1: t = 'warn' diff -Nru click-reviewers-tools-0.19/clickreviews/cr_security.py click-reviewers-tools-0.19/clickreviews/cr_security.py --- click-reviewers-tools-0.19/clickreviews/cr_security.py 2014-10-15 09:06:00.000000000 +0000 +++ click-reviewers-tools-0.19/clickreviews/cr_security.py 2014-11-21 15:31:17.000000000 +0000 @@ -25,7 +25,7 @@ class ClickReviewSecurity(ClickReview): '''This class represents click lint reviews''' - def __init__(self, fn): + def __init__(self, fn, overrides=None): ClickReview.__init__(self, fn, "security") local_copy = os.path.join(os.path.dirname(__file__), @@ -78,10 +78,21 @@ # framework policy is based on major framework version. In 13.10, there # was only 'ubuntu-sdk-13.10', but in 14.04, there will be several, # like 'ubuntu-sdk-14.04-html5', 'ubuntu-sdk-14.04-platform', etc - self.major_framework_policy = {'ubuntu-sdk-13.10': 1.0, - 'ubuntu-sdk-14.04': 1.1, - 'ubuntu-sdk-14.10': 1.2, - } + self.major_framework_policy = { + 'ubuntu-sdk-13.10': { + 'policy_version': 1.0, + }, + 'ubuntu-sdk-14.04': { + 'policy_version': 1.1, + }, + 'ubuntu-sdk-14.10': { + 'policy_version': 1.2, + }, + } + if overrides is None: + overrides = {} + framework_overrides = overrides.get('framework', {}) + self._override_framework_policies(framework_overrides) self.security_manifests = dict() self.security_apps = [] @@ -102,6 +113,23 @@ self._extract_security_manifest(app) self.security_apps.append(app) + def _override_framework_policies(self, overrides): + # override major framework policies + self.major_framework_policy.update(overrides) + + # override apparmor policies + for name, data in overrides.items(): + vendor = data.get('policy_vendor') + version = str(data.get('policy_version')) + + if vendor not in self.aa_policy: + self.aa_policy[vendor] = {} + + if version not in self.aa_policy[vendor]: + # just ensure the version is defined + # TODO: add support to override templates and policy groups + self.aa_policy[vendor][version] = {} + def _extract_security_manifest(self, app): '''Extract security manifest and verify it has the expected structure''' @@ -269,15 +297,15 @@ n = 'policy_version_matches_framework (%s)' % (f) s = "OK" found_major = False - for k in self.major_framework_policy.keys(): + for name, data in self.major_framework_policy.items(): # TODO: use libclick when it is available - if not self.manifest['framework'].startswith(k): + if not self.manifest['framework'].startswith(name): continue found_major = True - if m['policy_version'] != self.major_framework_policy[k]: + if m['policy_version'] != data['policy_version']: t = 'error' s = '%s != %s (%s)' % (str(m['policy_version']), - self.major_framework_policy[k], + data['policy_version'], self.manifest['framework']) if not found_major: t = 'error' diff -Nru click-reviewers-tools-0.19/clickreviews/frameworks.py click-reviewers-tools-0.19/clickreviews/frameworks.py --- click-reviewers-tools-0.19/clickreviews/frameworks.py 2014-10-15 09:06:00.000000000 +0000 +++ click-reviewers-tools-0.19/clickreviews/frameworks.py 2014-11-21 15:31:17.000000000 +0000 @@ -32,15 +32,21 @@ OBSOLETE_FRAMEWORKS = [] AVAILABLE_FRAMEWORKS = [] - def __init__(self, local_copy_fn=None): + def __init__(self, overrides=None): self.FRAMEWORKS = clickreviews.remote.read_cr_file(USER_DATA_FILE, - FRAMEWORKS_DATA_URL, - local_copy_fn) + FRAMEWORKS_DATA_URL) + if overrides is not None: + self.FRAMEWORKS.update(overrides) - for k, v in self.FRAMEWORKS.items(): - if v == 'deprecated': - self.DEPRECATED_FRAMEWORKS.append(k) - elif v == 'obsolete': - self.OBSOLETE_FRAMEWORKS.append(k) - elif v == 'available': - self.AVAILABLE_FRAMEWORKS.append(k) + for name, data in self.FRAMEWORKS.items(): + if type(data) is dict: + state = data.get('state') + else: + state = data + + if state == 'deprecated': + self.DEPRECATED_FRAMEWORKS.append(name) + elif state == 'obsolete': + self.OBSOLETE_FRAMEWORKS.append(name) + elif state == 'available': + self.AVAILABLE_FRAMEWORKS.append(name) diff -Nru click-reviewers-tools-0.19/clickreviews/remote.py click-reviewers-tools-0.19/clickreviews/remote.py --- click-reviewers-tools-0.19/clickreviews/remote.py 2014-10-15 09:06:00.000000000 +0000 +++ click-reviewers-tools-0.19/clickreviews/remote.py 2014-11-21 15:31:17.000000000 +0000 @@ -28,7 +28,7 @@ def _update_is_necessary(fn): return (not os.path.exists(fn)) or \ - (time.time() - os.path.getctime(fn) >= UPDATE_INTERVAL) + (time.time() - os.path.getmtime(fn) >= UPDATE_INTERVAL) def _update_is_possible(url): diff -Nru click-reviewers-tools-0.19/clickreviews/tests/test_cr_desktop.py click-reviewers-tools-0.19/clickreviews/tests/test_cr_desktop.py --- click-reviewers-tools-0.19/clickreviews/tests/test_cr_desktop.py 2014-10-15 09:06:00.000000000 +0000 +++ click-reviewers-tools-0.19/clickreviews/tests/test_cr_desktop.py 2014-11-21 15:31:17.000000000 +0000 @@ -713,3 +713,57 @@ r = c.click_report expected_counts = {'info': None, 'warn': 1, 'error': 0} self.check_results(r, expected_counts) + + def test_check_desktop_exec_webbrowser_local_app(self): + '''Test test_check_desktop_exec_webbrowser_local_app() local app''' + c = ClickReviewDesktop(self.test_name) + ex = "webapp-container ./www/index.html" + self.set_test_desktop(self.default_appname, + "Exec", + ex) + c.check_desktop_exec_webapp_args() + r = c.click_report + expected_counts = {'info': None, 'warn': 0, 'error': 0} + self.check_results(r, expected_counts) + + def test_check_desktop_exec_webbrowser_local_pattern(self): + '''Test test_check_desktop_exec_webbrowser_local_pattern() invalid pattern''' + c = ClickReviewDesktop(self.test_name) + ex = "webapp-container " + \ + "--webappUrlPatterns=https?://mobile.twitter.com/* " + \ + "./www/index.html" + self.set_test_desktop(self.default_appname, + "Exec", + ex) + c.check_desktop_exec_webapp_args() + r = c.click_report + expected_counts = {'info': None, 'warn': 0, 'error': 1} + self.check_results(r, expected_counts) + + def test_check_desktop_exec_webbrowser_local_webapp(self): + '''Test test_check_desktop_exec_webbrowser_local_webapp() invalid webapp cli''' + c = ClickReviewDesktop(self.test_name) + ex = "webapp-container " + \ + "--webapp=DEADBEEF " + \ + "./www/index.html" + self.set_test_desktop(self.default_appname, + "Exec", + ex) + c.check_desktop_exec_webapp_args() + r = c.click_report + expected_counts = {'info': None, 'warn': 0, 'error': 1} + self.check_results(r, expected_counts) + + def test_check_desktop_exec_webbrowser_local_model(self): + '''Test test_check_desktop_exec_webbrowser_local_model() invalid model''' + c = ClickReviewDesktop(self.test_name) + ex = "webapp-container " + \ + "--webappModelSearchPath=. " + \ + "./www/index.html" + self.set_test_desktop(self.default_appname, + "Exec", + ex) + c.check_desktop_exec_webapp_args() + r = c.click_report + expected_counts = {'info': None, 'warn': 0, 'error': 1} + self.check_results(r, expected_counts) diff -Nru click-reviewers-tools-0.19/clickreviews/tests/test_cr_lint.py click-reviewers-tools-0.19/clickreviews/tests/test_cr_lint.py --- click-reviewers-tools-0.19/clickreviews/tests/test_cr_lint.py 2014-10-15 09:06:00.000000000 +0000 +++ click-reviewers-tools-0.19/clickreviews/tests/test_cr_lint.py 2014-11-21 15:31:17.000000000 +0000 @@ -18,6 +18,7 @@ from clickreviews.cr_lint import ClickReviewLint from clickreviews.cr_lint import MINIMUM_CLICK_FRAMEWORK_VERSION +from clickreviews.frameworks import FRAMEWORKS_DATA_URL, USER_DATA_FILE import clickreviews.cr_tests as cr_tests @@ -29,6 +30,21 @@ cr_tests.mock_patch() super() + def patch_frameworks(self): + def _mock_frameworks(self, overrides=None): + self.FRAMEWORKS = { + 'ubuntu-sdk-14.10-qml-dev2': 'available', + 'ubuntu-sdk-13.10': 'deprecated', + 'ubuntu-sdk-14.10-qml-dev1': 'obsolete', + } + self.AVAILABLE_FRAMEWORKS = ['ubuntu-sdk-14.10-qml-dev2'] + self.OBSOLETE_FRAMEWORKS = ['ubuntu-sdk-14.10-qml-dev1'] + self.DEPRECATED_FRAMEWORKS = ['ubuntu-sdk-13.10'] + p = patch('clickreviews.frameworks.Frameworks.__init__', + _mock_frameworks) + p.start() + self.addCleanup(p.stop) + def test_check_architecture(self): '''Test check_architecture()''' c = ClickReviewLint(self.test_name) @@ -554,7 +570,7 @@ self.check_results(r, expected_counts) def test_check_maintainer_email_special(self): - '''Test check_maintainer() - ubuntu-devel-discuss@lists.ubuntu.com''' + '''Test check_maintainer() - ubuntu-devel-discuss@ - app''' self.set_test_control("Package", "com.canonical.app") self.set_test_manifest("maintainer", "Ubuntu Core Developers " @@ -575,7 +591,7 @@ self.check_results(r, expected=expected) def test_check_maintainer_email_special2(self): - '''Test check_maintainer() - ubuntu-devel-discuss@lists.ubuntu.com''' + '''Test check_maintainer() - ubuntu-devel-discuss@ - scope''' self.set_test_control("Package", "com.ubuntu.scopes.youtube") self.set_test_manifest("maintainer", "Ubuntu Core Developers " @@ -595,6 +611,27 @@ "'ubuntu-devel-discuss@lists.ubuntu.com' as email)"} self.check_results(r, expected=expected) + def test_check_maintainer_email_special3(self): + '''Test check_maintainer() - ubuntu-devel-discuss@ - snappy''' + self.set_test_control("Package", "com.ubuntu.snappy.foo") + self.set_test_manifest("maintainer", + "Ubuntu Core Developers " + "") + c = ClickReviewLint(self.test_name) + c.check_maintainer() + r = c.click_report + expected_counts = {'info': None, 'warn': 0, 'error': 0} + self.check_results(r, expected_counts) + + expected = dict() + expected['info'] = dict() + expected['warn'] = dict() + expected['error'] = dict() + expected['info']['lint_maintainer_domain'] = \ + {"text": "OK ('com.ubuntu.snappy' uses " + "'ubuntu-devel-discuss@lists.ubuntu.com' as email)"} + self.check_results(r, expected=expected) + def test_check_icon(self): '''Test check_icon()''' self.set_test_manifest("icon", "someicon") @@ -682,15 +719,33 @@ def test_check_framework(self): '''Test check_framework()''' + self.patch_frameworks() + self.set_test_manifest("framework", "ubuntu-sdk-14.10-qml-dev2") + c = ClickReviewLint(self.test_name) + c.check_framework() + r = c.click_report + expected_counts = {'info': 1, 'warn': 0, 'error': 0} + self.check_results(r, expected_counts) + + @patch('clickreviews.remote.read_cr_file') + def test_check_framework_fetches_remote_data(self, mock_read_cr_file): + '''Test check_framework()''' + mock_read_cr_file.return_value = { + 'ubuntu-sdk-14.10-qml-dev2': 'available', + } self.set_test_manifest("framework", "ubuntu-sdk-14.10-qml-dev2") c = ClickReviewLint(self.test_name) c.check_framework() r = c.click_report expected_counts = {'info': 1, 'warn': 0, 'error': 0} self.check_results(r, expected_counts) + # ensure no local fn is provided when reading frameworks + mock_read_cr_file.assert_called_once_with( + USER_DATA_FILE, FRAMEWORKS_DATA_URL) def test_check_framework_bad(self): '''Test check_framework() - bad''' + self.patch_frameworks() self.set_test_manifest("framework", "nonexistent") c = ClickReviewLint(self.test_name) c.check_framework() @@ -700,6 +755,7 @@ def test_check_framework_deprecated(self): '''Test check_framework() - deprecated''' + self.patch_frameworks() self.set_test_manifest("framework", "ubuntu-sdk-13.10") c = ClickReviewLint(self.test_name) c.check_framework() @@ -709,6 +765,7 @@ def test_check_framework_obsolete(self): '''Test check_framework() - obsolete''' + self.patch_frameworks() self.set_test_manifest("framework", "ubuntu-sdk-14.10-qml-dev1") c = ClickReviewLint(self.test_name) c.check_framework() @@ -716,6 +773,34 @@ expected_counts = {'info': None, 'warn': 0, 'error': 1} self.check_results(r, expected_counts) + @patch('clickreviews.remote.read_cr_file') + def test_check_framework_with_overrides(self, mock_read_cr_file): + '''Test check_framework() - using overrides''' + mock_read_cr_file.return_value = { + 'ubuntu-sdk-14.10-qml-dev2': 'available', + } + self.set_test_manifest("framework", "nonexistent") + overrides = {'framework': {'nonexistent': {'state': 'available'}}} + c = ClickReviewLint(self.test_name, overrides=overrides) + c.check_framework() + r = c.click_report + expected_counts = {'info': 1, 'warn': 0, 'error': 0} + self.check_results(r, expected_counts) + + @patch('clickreviews.remote.read_cr_file') + def test_check_framework_with_malformed_overrides(self, mock_read_cr_file): + '''Test check_framework() - using overrides''' + mock_read_cr_file.return_value = { + 'ubuntu-sdk-14.10-qml-dev2': 'available', + } + self.set_test_manifest("framework", "nonexistent") + overrides = {'nonexistent': {'state': 'available'}} + c = ClickReviewLint(self.test_name, overrides=overrides) + c.check_framework() + r = c.click_report + expected_counts = {'info': 0, 'warn': 0, 'error': 1} + self.check_results(r, expected_counts) + def test_check_hooks(self): '''Test check_hooks()''' self.set_test_manifest("framework", "ubuntu-sdk-13.10") diff -Nru click-reviewers-tools-0.19/clickreviews/tests/test_cr_scope.py click-reviewers-tools-0.19/clickreviews/tests/test_cr_scope.py --- click-reviewers-tools-0.19/clickreviews/tests/test_cr_scope.py 2014-10-15 09:06:00.000000000 +0000 +++ click-reviewers-tools-0.19/clickreviews/tests/test_cr_scope.py 2014-11-21 15:31:17.000000000 +0000 @@ -123,6 +123,45 @@ expected_counts = {'info': None, 'warn': 0, 'error': 1} self.check_results(r, expected_counts) + def test_check_scope_ini_translated_field(self): + '''Test check_scope_ini() - translated field - es''' + config = self._stub_config() + config['searchhint[es]'] = "foo" + scope = self._create_scope(config) + + self.set_test_scope(self.default_appname, scope) + c = ClickReviewScope(self.test_name) + c.check_scope_ini() + r = c.click_report + expected_counts = {'info': None, 'warn': 0, 'error': 0} + self.check_results(r, expected_counts) + + def test_check_scope_ini_translated_field2(self): + '''Test check_scope_ini() - translated field - pt_br''' + config = self._stub_config() + config['searchhint[pt_br]'] = "foo" + scope = self._create_scope(config) + + self.set_test_scope(self.default_appname, scope) + c = ClickReviewScope(self.test_name) + c.check_scope_ini() + r = c.click_report + expected_counts = {'info': None, 'warn': 0, 'error': 0} + self.check_results(r, expected_counts) + + def test_check_scope_ini_bad_translated_field(self): + '''Test check_scope_ini() - bad translated field''' + config = self._stub_config() + config['searchhint[ba;r]'] = "foo" + scope = self._create_scope(config) + + self.set_test_scope(self.default_appname, scope) + c = ClickReviewScope(self.test_name) + c.check_scope_ini() + r = c.click_report + expected_counts = {'info': None, 'warn': 1, 'error': 0} + self.check_results(r, expected_counts) + def test_check_scope_ini_nonexistent_field(self): '''Test check_scope_ini() - non-existent field''' config = self._stub_config() diff -Nru click-reviewers-tools-0.19/clickreviews/tests/test_cr_security.py click-reviewers-tools-0.19/clickreviews/tests/test_cr_security.py --- click-reviewers-tools-0.19/clickreviews/tests/test_cr_security.py 2014-10-15 09:06:00.000000000 +0000 +++ click-reviewers-tools-0.19/clickreviews/tests/test_cr_security.py 2014-11-21 15:31:17.000000000 +0000 @@ -136,7 +136,7 @@ policy_version = 0 for k in tmp.major_framework_policy.keys(): if f.startswith(k): - policy_version = tmp.major_framework_policy[k] + policy_version = tmp.major_framework_policy[k]['policy_version'] self.set_test_security_manifest(self.default_appname, "policy_version", policy_version) @@ -209,6 +209,36 @@ {"text": "Invalid framework 'nonexistent'"} self.check_results(report, expected=expected) + def test_check_policy_version_framework_with_overrides(self): + '''Test check_policy_version() - override framework (nonexistent)''' + self.set_test_manifest("framework", "nonexistent") + self.set_test_security_manifest(self.default_appname, + "policy_version", 1.3) + overrides = {'framework': {'nonexistent': {'state': 'available', + 'policy_vendor': 'ubuntu', + 'policy_version': 1.3}}} + c = ClickReviewSecurity(self.test_name, overrides=overrides) + c.check_policy_version() + report = c.click_report + + expected_counts = {'info': 3, 'warn': 0, 'error': 0} + self.check_results(report, expected_counts) + + def test_check_policy_version_framework_with_malformed_overrides(self): + '''Test check_policy_version() - incorrectly override framework''' + self.set_test_manifest("framework", "nonexistent") + self.set_test_security_manifest(self.default_appname, + "policy_version", 1.3) + overrides = {'nonexistent': {'state': 'available', + 'policy_vendor': 'ubuntu', + 'policy_version': 1.3}} + c = ClickReviewSecurity(self.test_name, overrides=overrides) + c.check_policy_version() + report = c.click_report + + expected_counts = {'info': 1, 'warn': 0, 'error': 2} + self.check_results(report, expected_counts) + def test_check_policy_vendor_unspecified(self): '''Test check_policy_vendor() - unspecified''' c = ClickReviewSecurity(self.test_name) diff -Nru click-reviewers-tools-0.19/clickreviews/tests/test_remote.py click-reviewers-tools-0.19/clickreviews/tests/test_remote.py --- click-reviewers-tools-0.19/clickreviews/tests/test_remote.py 1970-01-01 00:00:00.000000000 +0000 +++ click-reviewers-tools-0.19/clickreviews/tests/test_remote.py 2014-11-21 15:31:17.000000000 +0000 @@ -0,0 +1,38 @@ +import time +from unittest import TestCase +from unittest.mock import patch + +from clickreviews.remote import UPDATE_INTERVAL, _update_is_necessary + + +class RemoteTestCase(TestCase): + def patch_path(self): + p = patch('clickreviews.remote.os.path') + self.mock_path = p.start() + self.addCleanup(p.stop) + + def patch_time(self, now): + p = patch('clickreviews.remote.time.time') + self.mock_time = p.start() + self.mock_time.return_value = now + self.addCleanup(p.stop) + + def test_no_update_needed(self): + now = time.time() + self.patch_time(now) + self.patch_path() + + # last update was 10 seconds ago + self.mock_path.getmtime.return_value = now - 10 + + self.assertFalse(_update_is_necessary('some-file')) + + def test_update_needed(self): + now = time.time() + self.patch_time(now) + self.patch_path() + + # last update was UPDATE_INTERVAL + 10 seconds ago + self.mock_path.getmtime.return_value = now - UPDATE_INTERVAL - 10 + + self.assertTrue(_update_is_necessary('some-file')) diff -Nru click-reviewers-tools-0.19/data/frameworks.json click-reviewers-tools-0.19/data/frameworks.json --- click-reviewers-tools-0.19/data/frameworks.json 2014-10-15 09:06:00.000000000 +0000 +++ click-reviewers-tools-0.19/data/frameworks.json 1970-01-01 00:00:00.000000000 +0000 @@ -1,23 +0,0 @@ -{ - "ubuntu-sdk-13.10": "deprecated", - "ubuntu-sdk-14.04-html": "available", - "ubuntu-sdk-14.04-qml-dev1": "deprecated", - "ubuntu-sdk-14.10-qml-dev3": "available", - "ubuntu-sdk-14.10-papi-dev3": "available", - "ubuntu-sdk-14.04-html-dev1": "deprecated", - "ubuntu-sdk-14.10-papi-dev1": "obsolete", - "ubuntu-sdk-14.04-papi-dev1": "deprecated", - "ubuntu-sdk-14.10-dev2": "available", - "ubuntu-sdk-14.04": "available", - "ubuntu-sdk-14.10-dev3": "available", - "ubuntu-sdk-14.10-qml-dev1": "obsolete", - "ubuntu-sdk-14.04-dev1": "deprecated", - "ubuntu-sdk-14.10-dev1": "obsolete", - "ubuntu-sdk-14.04-papi": "available", - "ubuntu-sdk-14.04-qml": "available", - "ubuntu-sdk-14.10-papi-dev2": "available", - "ubuntu-sdk-14.10-html-dev3": "available", - "ubuntu-sdk-14.10-html-dev2": "available", - "ubuntu-sdk-14.10-html-dev1": "obsolete", - "ubuntu-sdk-14.10-qml-dev2": "available" -} diff -Nru click-reviewers-tools-0.19/debian/bzr-builder.manifest click-reviewers-tools-0.19/debian/bzr-builder.manifest --- click-reviewers-tools-0.19/debian/bzr-builder.manifest 2014-10-15 09:06:00.000000000 +0000 +++ click-reviewers-tools-0.19/debian/bzr-builder.manifest 2014-11-21 15:31:17.000000000 +0000 @@ -1,2 +1,2 @@ -# bzr-builder format 0.3 deb-version {debupstream}-0~281 -lp:click-reviewers-tools revid:daniel.holbach@canonical.com-20141015084712-ezies2zzr9klz234 +# bzr-builder format 0.3 deb-version {debupstream}-0~300 +lp:click-reviewers-tools revid:jamie@ubuntu.com-20141121140457-0n9802z9wrcv3pcm diff -Nru click-reviewers-tools-0.19/debian/changelog click-reviewers-tools-0.19/debian/changelog --- click-reviewers-tools-0.19/debian/changelog 2014-10-15 09:06:00.000000000 +0000 +++ click-reviewers-tools-0.19/debian/changelog 2014-11-21 15:31:17.000000000 +0000 @@ -1,14 +1,27 @@ -click-reviewers-tools (0.19-0~281~ubuntu14.04.1) trusty; urgency=low +click-reviewers-tools (0.19-0~300~ubuntu14.04.1) trusty; urgency=low * Auto build. - -- Daniel Holbach Wed, 15 Oct 2014 09:06:00 +0000 + -- Daniel Holbach Fri, 21 Nov 2014 15:31:17 +0000 click-reviewers-tools (0.19) UNRELEASED; urgency=medium - * + [ Ricardo Kirkner ] + * fetch framework data before running framework related checks + * use mtime instead of ctime to check remote file freshness + * allow specifying overrides for framework checks + * handle case when overrides data is malformed + + [ Alexandre Abreu ] + * add support for local html5 app launch mode for webapp-container + (LP: #1388988) + + [ Jamie Strandboge ] + * open scopes .ini file as utf8 (LP: #1371692) + * allow for translatable fields in the scopes .ini file (LP: #1392133) + * don't require desktop hook with systemd or framework - -- Daniel Holbach Wed, 15 Oct 2014 10:46:59 +0200 + -- Jamie Strandboge Thu, 13 Nov 2014 15:00:07 -0600 click-reviewers-tools (0.18) utopic; urgency=medium diff -Nru click-reviewers-tools-0.19/debian/rules click-reviewers-tools-0.19/debian/rules --- click-reviewers-tools-0.19/debian/rules 2014-10-15 09:06:00.000000000 +0000 +++ click-reviewers-tools-0.19/debian/rules 2014-11-21 15:31:17.000000000 +0000 @@ -18,7 +18,6 @@ rm -rf build *.egg-info .pybuild find -name \*.pyc -print0 | xargs -0r rm -f find -name __pycache__ -print0 | xargs -0r rm -rf - -$(shell python3 ./bin/update-frameworks ./data/frameworks.json) -$(shell python3 ./bin/update-apparmor-policy ./data/apparmor-easyprof-ubuntu.json) override_dh_auto_build: