Environment_Tests#

These tests are grouped into a single notebook called all_tests.ipynb which you can run using the ope test command. Running this command outputs the results of the tests, separating them into two groups of PASSED TESTS and FAILED TESTS. We keep track of these groups using the global variables ERRORS and PASSES which are initialized below.

# global var to keep track of test results we have
ERRORS = []
PASSES = []

Write Permission to Home Directory Test#

import subprocess
import os
def shelltest(CMD):
    global ERRORS
    try:
        result = subprocess.check_output(CMD, shell=True, stderr=subprocess.STDOUT)
        return 0, result.decode('utf-8')
    except subprocess.CalledProcessError as e:
        return e.returncode, e.output.decode('utf-8')
    except Exception as e:  # new add: handle exceptions 
        return -1, str(e)

# Test to check write permissions to home directory
TEST = "WRITE PERMISSION TO HOME DIRECTORY"
CMD = f"touch {os.path.expanduser('~')}/test_write_permissions.tmp && echo 'Write Permission: Yes' && rm {os.path.expanduser('~')}/test_write_permissions.tmp || echo 'Write Permission: No'"

# Execute Test
e, output = shelltest(CMD)
if e == 0:
    PASSES.append("Write Permission to Home Directory test")
else:
    ERRORS.append(output)

Environmental Variables test#

This test confirms that the environment variables set from Dockerfile still exist and maintain the same values within the Jupyter Notebook. It also ensures that the user has a valid UID and GID within a customizable range

import os

#Verify environment variables are correct

NB_UID = int(os.environ['NB_UID'])
NB_GID = int(os.environ['NB_GID'])
NB_GROUP = os.environ['NB_GROUP']

XDG_CACHE_HOME = f"/home/{NB_USER}/.cache/"

UID_LOWER_BOUND = 2000  
UID_UPPER_BOUND = 60000 

GID_LOWER_BOUND = 2000  
GID_UPPER_BOUND = 60000 

EXPECTED_NB_GROUP = 'root'

err = []

def check_environment_test():
    if not (UID_LOWER_BOUND <= NB_UID <= UID_UPPER_BOUND):
         err.append(f"NB_UID {NB_UID} is not within the acceptable range: {UID_LOWER_BOUND}-{UID_UPPER_BOUND}.")


    if not (GID_LOWER_BOUND <= NB_GID <= GID_UPPER_BOUND):
         err.append(f"NB_GID {NB_GID} is not within the acceptable range: {GID_LOWER_BOUND}-{GID_UPPER_BOUND}.")


    if  NB_GROUP != EXPECTED_NB_GROUP:
         err.append("NB_GROUP does not match " + "'" + EXPECTED_NB_GROUP + "'")


    if NB_USER != 'jovyan':
         err.append("NB_USER does not match 'jovyan'.")


    if XDG_CACHE_HOME != '/home/jovyan/.cache/':
         err.append("XDD_CACHE_HOME does not match expected path: /home/jovyan/.cache/")


    if len(err) != 0:
         err.append("Environmental Variables test")

    else:
        s = '; '.join(err)
        print("Environmental Variables test ERROR: " + s)
    
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
Cell In[3], line 5
      1 import os
      3 #Verify environment variables are correct
----> 5 NB_UID = int(os.environ['NB_UID'])
      6 NB_GID = int(os.environ['NB_GID'])
      7 NB_GROUP = os.environ['NB_GROUP']

File <frozen os>:679, in __getitem__(self, key)

KeyError: 'NB_UID'

Network Test#

This test ensures that the container has a connection to the internet. We use both ping and curl because some systems may not have one of these utilities by default—in our case, our cluster configuration did not include ping by default. If either ping or curl works properly, then the container is connected to the internet and the test passes, otherwise it fails.

# Curl test to check internet connectivity
e1, output = shelltest("curl google.com")
e2, output = shelltest("ping google.com")
if e1 == 0 or e2 == 0:
    PASSES.append("Network test")
else:
    ERRORS.append("Network test ERROR: " + output)

Pip-Conda Test#

TEST = "PIP PACKAGE INSTALLATION"
CMD = "pip install --user pytest"
e, output = shelltest(CMD)

if e == 0:
    PASSES.append("Pip-Conda test")
else:
    ERRORS.append("Pip-Conda test ERROR: " + output)

Git and SSH Test#

e, output = shelltest("readlink -f ~/.gitconfig")

if e == 0:
    PASSES.append("Git config test")
else:
    ERRORS.append("Git config test ERROR" + output)
e, output = shelltest("readlink -f /etc/ssh/ssh_config")

if e == 0:
    PASSES.append("ssh config test")
else:
    ERRORS.append("ssh config test ERROR" + output)

Conda Directory Test#

def check_permissions(dir_path):
    """Check if a directory is readable and writable."""
    global ERRORS
    try:
        readable = os.access(dir_path, os.R_OK)
        writable = os.access(dir_path, os.W_OK)
        return readable, writable
    except Exception as e:
        ERRORS.append("Conda directory r/w test" + f"checking permissions for {dir_path}.")
        return False, False

# Identify the conda directories
conda_base_dir = os.path.abspath(os.path.join(os.path.dirname(os.sys.executable), ".."))
conda_env_dir = os.environ.get('CONDA_PREFIX', '')

unaccessible_dirs = 0

for dir_name, dir_path in [('Conda Base Directory', conda_base_dir), ('Conda Environment Directory', conda_env_dir)]:
    readable, writable = check_permissions(dir_path)

    if not (readable and writable):
        unaccessible_dirs += 1
if unaccessible_dirs == 0:
    PASSES.append("Conda directory r/w test")
if len(ERRORS) > 0:
    print("FAILED TESTS:")
    print('-' + '\n-'.join(ERRORS))
    print('\n')
    print("PASSED TESTS:")
    print('-' + '\n-'.join(PASSES))
else:
    print("ALL TESTS PASS")
    print('-' + '\n-'.join(PASSES))
FAILED TESTS:
-ASLR test ERROR: ASLR status is enabled with status 2 (0 is disabled, 1 is partial and 2 is full)
-Network test ERROR: /bin/sh: 1: curl: not found



PASSED TESTS:
-Write Permission to Home Directory test
-Pip-Conda test
-Git config test
-ssh config test

Additional tests can be added to suit the compatability needs of users and textbooks. They may be written in this format:

# Define the test and conditions

test_command = "<INSERT TEST COMMAND HERE>"
expected_result = "<INSERT EXPECTED RESULT HERE>"
environment_variables = "<INSERT REQUIRED VARIABLES IF APPLICABLE>"

def perform_test(command, expected_result):

    execution_result, output = shelltest(command)

    if output == expected_result:
        # If test passes, add to the PASSES array 
        PASSES.append("<TEST NAME> PASSED")
    else:
        # If test fails, add to the ERRORS array with error message
        ERRORS.append("<TEST NAME> FAILED: " + output)

# Run test to verify success
perform_test(test_command, expected_result)

Test Authors: Ross Mikulskis, Jonathan Mikalov, Yuxie Ge, Yiqin Zhang