Repository: oppia/oppia

Issue:
Code climate inconsistent coverage
There is currently a possibility where the Code Climate total diff coverage checks can be at 99% but yet also show up as a green check, this leads to developer confusion and should be resolved soon.

![image](https://user-images.githubusercontent.com/5758256/69004101-80743b00-08c2-11ea-987d-80da89edc65c.png)

Code climate inconsistent coverage
There is currently a possibility where the Code Climate total diff coverage checks can be at 99% but yet also show up as a green check, this leads to developer confusion and should be resolved soon.

![image](https://user-images.githubusercontent.com/5758256/69004101-80743b00-08c2-11ea-987d-80da89edc65c.png)



Discussion:

Resolving patch:
diff --git a/scripts/common.py b/scripts/common.py
--- a/scripts/common.py
+++ b/scripts/common.py
@@ -29,10 +29,11 @@
 
 NODE_VERSION = '10.15.3'
 YARN_VERSION = 'v1.17.3'
+COVERAGE_VERSION = '4.5.4'
 
 RELEASE_BRANCH_NAME_PREFIX = 'release-'
 CURR_DIR = os.path.abspath(os.getcwd())
-OPPIA_TOOLS_DIR = os.path.join(CURR_DIR, '..', 'oppia_tools')
+OPPIA_TOOLS_DIR = os.path.join(CURR_DIR, os.pardir, 'oppia_tools')
 THIRD_PARTY_DIR = os.path.join(CURR_DIR, 'third_party')
 GOOGLE_APP_ENGINE_HOME = os.path.join(
     OPPIA_TOOLS_DIR, 'google_appengine_1.9.67', 'google_appengine')
@@ -105,7 +106,7 @@ def require_cwd_to_be_oppia(allow_deploy_dir=False):
     current_dirname = os.path.basename(os.path.normpath(os.getcwd()))
     is_deploy_dir = (
         current_dirname.startswith('deploy-') and
-        os.path.isdir(os.path.join(os.getcwd(), '..', 'oppia')))
+        os.path.isdir(os.path.join(os.getcwd(), os.pardir, 'oppia')))
 
     if is_oppia_dir or (allow_deploy_dir and is_deploy_dir):
         return
diff --git a/scripts/install_third_party_libs.py b/scripts/install_third_party_libs.py
--- a/scripts/install_third_party_libs.py
+++ b/scripts/install_third_party_libs.py
@@ -25,7 +25,7 @@
 import sys
 
 # These libraries need to be installed before running or importing any script.
-TOOLS_DIR = os.path.join('..', 'oppia_tools')
+TOOLS_DIR = os.path.join(os.pardir, 'oppia_tools')
 # Download and install pyyaml.
 if not os.path.exists(os.path.join(TOOLS_DIR, 'pyyaml-5.1.2')):
     subprocess.check_call([
@@ -123,6 +123,8 @@ def pip_install(package, version, install_path):
                 'Windows%29')
         raise Exception
 
+    # The call to python -m is used to ensure that Python and Pip versions are
+    # compatible.
     subprocess.check_call([
         sys.executable, '-m', 'pip', 'install', '%s==%s' % (package, version),
         '--target', install_path])
@@ -201,7 +203,7 @@ def install_skulpt(parsed_args):
 
             # NB: Check call cannot be used because the commands above make the
             # git tree for skulpt dirty.
-            subprocess.call(['python', skulpt_filepath, 'dist'])
+            subprocess.call([sys.executable, skulpt_filepath, 'dist'])
 
             # Return to the Oppia root folder.
             os.chdir(common.CURR_DIR)
@@ -237,6 +239,7 @@ def main(args=None):
     setup.main(args=[])
     setup_gae.main(args=[])
     pip_dependencies = [
+        ('coverage', common.COVERAGE_VERSION, common.OPPIA_TOOLS_DIR),
         ('pylint', '1.9.4', common.OPPIA_TOOLS_DIR),
         ('Pillow', '6.0.0', common.OPPIA_TOOLS_DIR),
         ('pylint-quotes', '0.1.8', common.OPPIA_TOOLS_DIR),
diff --git a/scripts/setup_gae.py b/scripts/setup_gae.py
--- a/scripts/setup_gae.py
+++ b/scripts/setup_gae.py
@@ -37,7 +37,8 @@
 def main(args=None):
     """Runs the script to setup GAE."""
     unused_parsed_args = _PARSER.parse_args(args=args)
-    coverage_home = os.path.join(common.OPPIA_TOOLS_DIR, 'coverage-4.5.4')
+    coverage_home = os.path.join(
+        common.OPPIA_TOOLS_DIR, 'coverage-%s' % common.COVERAGE_VERSION)
 
     # Note that if the following line is changed so that it uses webob_1_1_1,
     # PUT requests from the frontend fail.


Test patch:
diff --git a/scripts/run_backend_tests.py b/scripts/run_backend_tests.py
--- a/scripts/run_backend_tests.py
+++ b/scripts/run_backend_tests.py
@@ -58,7 +58,6 @@
 import python_utils
 
 from . import common
-from . import install_third_party_libs
 from . import setup
 from . import setup_gae
 
@@ -91,8 +90,13 @@
     os.path.join(common.THIRD_PARTY_DIR, 'webencodings-0.5.1'),
 ]
 
-COVERAGE_PATH = os.path.join(
-    os.getcwd(), '..', 'oppia_tools', 'coverage-4.5.4', 'coverage')
+COVERAGE_DIR = os.path.join(
+    os.getcwd(), os.pardir, 'oppia_tools',
+    'coverage-%s' % common.COVERAGE_VERSION)
+COVERAGE_MODULE_PATH = os.path.join(
+    os.getcwd(), os.pardir, 'oppia_tools',
+    'coverage-%s' % common.COVERAGE_VERSION, 'coverage')
+
 TEST_RUNNER_PATH = os.path.join(os.getcwd(), 'core', 'tests', 'gae_suite.py')
 LOG_LOCK = threading.Lock()
 ALL_ERRORS = []
@@ -218,10 +222,10 @@ def run(self):
         test_target_flag = '--test_target=%s' % self.test_target
         if self.generate_coverage_report:
             exc_list = [
-                'python', COVERAGE_PATH, 'run', '-p', TEST_RUNNER_PATH,
-                test_target_flag]
+                sys.executable, COVERAGE_MODULE_PATH, 'run', '-p',
+                TEST_RUNNER_PATH, test_target_flag]
         else:
-            exc_list = ['python', TEST_RUNNER_PATH, test_target_flag]
+            exc_list = [sys.executable, TEST_RUNNER_PATH, test_target_flag]
 
         return run_shell_cmd(exc_list)
 
@@ -344,7 +348,11 @@ def main(args=None):
     for directory in DIRS_TO_ADD_TO_SYS_PATH:
         if not os.path.exists(os.path.dirname(directory)):
             raise Exception('Directory %s does not exist.' % directory)
-        sys.path.insert(0, directory)
+
+        # The directories should only be inserted starting at index 1. See
+        # https://stackoverflow.com/a/10095099 and
+        # https://stackoverflow.com/q/10095037 for more details.
+        sys.path.insert(1, directory)
 
     import dev_appserver
     dev_appserver.fix_sys_path()
@@ -354,11 +362,17 @@ def main(args=None):
             'Checking whether coverage is installed in %s'
             % common.OPPIA_TOOLS_DIR)
         if not os.path.exists(
-                os.path.join(common.OPPIA_TOOLS_DIR, 'coverage-4.5.4')):
-            python_utils.PRINT('Installing coverage')
-            install_third_party_libs.pip_install(
-                'coverage', '4.5.4',
-                os.path.join(common.OPPIA_TOOLS_DIR, 'coverage-4.5.4'))
+                os.path.join(
+                    common.OPPIA_TOOLS_DIR,
+                    'coverage-%s' % common.COVERAGE_VERSION)):
+            raise Exception('Coverage is not installed, please run the start '
+                            'script.')
+
+        pythonpath_components = [COVERAGE_DIR]
+        if os.environ.get('PYTHONPATH'):
+            pythonpath_components.append(os.environ.get('PYTHONPATH'))
+
+        os.environ['PYTHONPATH'] = os.pathsep.join(pythonpath_components)
 
     if parsed_args.test_target and parsed_args.test_path:
         raise Exception('At most one of test_path and test_target '
@@ -504,14 +518,23 @@ def main(args=None):
             '%s errors, %s failures' % (total_errors, total_failures))
 
     if parsed_args.generate_coverage_report:
-        subprocess.check_call(['python', COVERAGE_PATH, 'combine'])
-        subprocess.check_call([
-            'python', COVERAGE_PATH, 'report',
-            '--omit="%s*","third_party/*","/usr/share/*"'
-            % common.OPPIA_TOOLS_DIR, '--show-missing'])
+        subprocess.check_call([sys.executable, COVERAGE_MODULE_PATH, 'combine'])
+        process = subprocess.Popen(
+            [sys.executable, COVERAGE_MODULE_PATH, 'report',
+             '--omit="%s*","third_party/*","/usr/share/*"'
+             % common.OPPIA_TOOLS_DIR, '--show-missing'],
+            stdout=subprocess.PIPE)
+
+        report_stdout, _ = process.communicate()
+        python_utils.PRINT(report_stdout)
 
         python_utils.PRINT('Generating xml coverage report...')
-        subprocess.check_call(['python', COVERAGE_PATH, 'xml'])
+        subprocess.check_call([sys.executable, COVERAGE_MODULE_PATH, 'xml'])
+
+        coverage_result = re.search(
+            r'TOTAL\s+(\d+)\s+(\d+)\s+(?P<total>\d+)%\s+', report_stdout)
+        if coverage_result.group('total') != '100':
+            raise Exception('Backend test coverage is not 100%')
 
     python_utils.PRINT('')
     python_utils.PRINT('Done!')
diff --git a/scripts/script_import_test.py b/scripts/script_import_test.py
--- a/scripts/script_import_test.py
+++ b/scripts/script_import_test.py
@@ -66,6 +66,6 @@ def mock_exists(unused_path):
         self.assertEqual(
             self.commands, [
                 sys.executable, '-m', 'pip', 'install', 'pyyaml==5.1.2',
-                '--target', '../oppia_tools/pyyaml-5.1.2', sys.executable,
-                '-m', 'pip', 'install', 'future==0.17.1', '--target',
-                'third_party/future-0.17.1'])
+                '--target', '../oppia_tools/pyyaml-5.1.2',
+                sys.executable, '-m', 'pip', 'install',
+                'future==0.17.1', '--target', 'third_party/future-0.17.1'])