Skip to content

Commit

Permalink
Add tests for non-interactive Dockerfile, and fix failing/stalling te…
Browse files Browse the repository at this point in the history
…sts.
  • Loading branch information
epaganon authored and Prabhakar Kumar committed Apr 15, 2024
1 parent 2ae11d7 commit dd7b3ef
Show file tree
Hide file tree
Showing 14 changed files with 272 additions and 24 deletions.
9 changes: 2 additions & 7 deletions .github/workflows/build-test-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ jobs:
if [ -f tests/requirements.txt ]; then pip install -r tests/requirements.txt; fi
- name: Generate license file
run: echo '${{ secrets.MATLAB_LICENSE_SECRET }}' > ${{ env.LICENSE_FILE_PATH }}
run: echo '${{ secrets.MATLAB_LICENSE_FILE_R2024A }}' > ${{ env.LICENSE_FILE_PATH }}

- name: Test container
env:
Expand All @@ -72,9 +72,4 @@ jobs:
run: python -m unittest

- name: Push the image to ghcr.io
uses: docker/build-push-action@v4
with:
platforms: linux/amd64
push: true
tags: |
${{ env.IMAGE_BASE_NAME }}:${{ matrix.matlab-release }}
run: docker push ${{ env.IMAGE_BASE_NAME }}:${{ matrix.matlab-release }}
4 changes: 2 additions & 2 deletions .github/workflows/from-matlab-docker-build-test.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright 2023-2024 The MathWorks, Inc.
# Copyright 2023 The MathWorks, Inc.

name: Build and Test the "Building on MATLAB Docker Image" Dockerfile

Expand Down Expand Up @@ -58,7 +58,7 @@ jobs:
if [ -f tests/requirements.txt ]; then pip install -r tests/requirements.txt; fi
- name: Generate license file
run: echo '${{ secrets.MATLAB_LICENSE_SECRET }}' > ${{ env.LICENSE_FILE_PATH }}
run: echo '${{ secrets.MATLAB_LICENSE_FILE_R2024A }}' > ${{ env.LICENSE_FILE_PATH }}

- name: Test container
working-directory: tests
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/matlab-installer-build-test.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright 2023-2024 The MathWorks, Inc.
# Copyright 2023 The MathWorks, Inc.

name: Build and Test the "MATLAB installer" Dockerfile

Expand Down
65 changes: 65 additions & 0 deletions .github/workflows/non-interactive-build-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Copyright 2024 The MathWorks, Inc.

name: Build and Test the "Non-Interactive" Dockerfile

# Trigger this workflow either manually or when a new change is pushed to the
# repo (except .md files)
on:
workflow_dispatch:
push:
# Trigger the workflow when the Dockerfile or any file under tests/ is modified
paths:
- "alternates/non-interactive/Dockerfile"
- "tests/**"
- "!tests/**.md"
schedule:
# Run at 00:00 on every Monday (1st Day of the Week)
- cron: "0 0 * * 1"

env:
IMAGE_BASE_NAME: matlab-non-interactive
ALT_PATH: alternates/non-interactive

jobs:
build-test-image:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
matlab-release: [r2024a, r2023b, r2023a, r2022b, r2022a, r2021b, r2021a, r2020b]

steps:
- name: Checkout repo
uses: actions/checkout@v3

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2

- name: Build image locally
uses: docker/build-push-action@v4
with:
platforms: linux/amd64
context: ${{ env.ALT_PATH }}
load: true
build-args: |
MATLAB_RELEASE=${{ matrix.matlab-release }}
tags: |
${{ env.IMAGE_BASE_NAME }}:${{ matrix.matlab-release }}
- name: Set up Python 3
uses: actions/setup-python@v4
with:
python-version: "3.10"

- name: Install test dependencies
run: |
python -m pip install --upgrade pip
if [ -f tests/requirements.txt ]; then pip install -r tests/requirements.txt; fi
- name: Test container
working-directory: tests
env:
IMAGE_NAME: ${{ env.IMAGE_BASE_NAME }}:${{ matrix.matlab-release }}
BATCH_TOKEN: ${{ secrets.MATLAB_BATCH_TOKEN_EXPIRES_03_2025 }}
run: |
python -m unittest ${{ env.ALT_PATH }}/test_matlabbatch.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ def test_packages_present(self):
with self.subTest(package=pkg):
self.assertTrue(
self.host.package(pkg).is_installed,
f"package {pkg} is not installed",
f"Package {pkg} is not installed",
)


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ def test_vnc_option(self):
self.assertRegex(
table_lines[-1],
expected_port,
f"command {vnc_list_cmd} returned:\n{vnc_list_output}",
f"Command {vnc_list_cmd} returned:\n{vnc_list_output}",
)

def test_browser_option(self):
Expand Down
2 changes: 1 addition & 1 deletion tests/alternates/matlab-installer/test_failing_build.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ def test_mismatching_releases_displays_err_msg(self):

self.assertTrue(
any([expected_fail_msg in l.get("stream", "") for l in build_log]),
f"expected error message '{expected_fail_msg}' not found in build log\n{build_log}",
f"Expected error message '{expected_fail_msg}' not found in build log\n{build_log}",
)

def test_install_error_message(self):
Expand Down
74 changes: 74 additions & 0 deletions tests/alternates/non-interactive/test_matlabbatch.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# Copyright 2024 The MathWorks, Inc.

"""
Test class to validate the "non-interactive" Dockerfile.
This test suite require a valid batch licensing token.
"""

from utils import base, helpers
import docker
import os
import unittest

################################################################################


class TestMatlabBatch(base.TestCase):
"""Extend the test methods of the base TestCase class."""

@classmethod
def setUpClass(cls):
"""Run the container"""
cls.client = docker.from_env()
image_name = helpers.get_image_name()
cls.container = cls.client.containers.run(
image=image_name,
detach=True,
stdin_open=True,
environment = {"MLM_LICENSE_TOKEN": os.getenv("BATCH_TOKEN")},
)
cls.expected_ddux_force_enable = "true"
cls.expected_ddux_tags = [
"MATLAB:BATCHLICENSING:DOCKERFILE:V1",
]
cls.install_dirname = "mpm"
cls.release_tag = helpers.get_release_tag(image_name)
super().setUpClass()

@classmethod
def tearDownClass(cls):
"""Stop and remove the container"""
cls.container.stop()
cls.container.remove()
cls.client.close()

############################################################################

def test_matlabbatch_runs(self):
"""Test that matlab-batch runs successfully and that the matlab release is the correct one."""
matlabbatch_cmd = 'matlab-batch "disp(version(\'-release\'))"'
cmd_output = self.host.run(matlabbatch_cmd)
self.assertTrue(
cmd_output.succeeded,
f"Unable to run matlab-batch correctly: {cmd_output.stdout}",
)
expectedRelease=self.release_tag.strip().lstrip("Rr")
self.assertRegex(cmd_output.stdout, expectedRelease)

def test_matlabbatch_version(self):
"""Test the version of matlab-batch installed in the container"""
readme_filepath = "../alternates/non-interactive/MATLAB-BATCH.md"
expected_version = helpers.get_changelog_mb_version(readme_filepath)
expected_output = f"matlab-batch {expected_version} (glnxa64)"
version_cmd = f"matlab-batch -version"
self.assertEqual(
expected_output,
self.host.check_output(version_cmd),
"Mismatching versions of matlab-batch in changelog",
)


################################################################################

if __name__ == "__main__":
unittest.main()
7 changes: 4 additions & 3 deletions tests/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# Copyright 2022-2023 The MathWorks, Inc.
# Copyright 2022-2024 The MathWorks, Inc.

docker>=6.1.2
pytest-testinfra>=8.1.0
docker
markdown-it-py
pytest-testinfra
2 changes: 1 addition & 1 deletion tests/test_running_matlab.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ def test_matlab_runs(self):
def test_matlab_version(self):
"""Test that the release number written to VER_OUTPUT_FILE and
the one from the image name coincide."""
helpers.wait_for_file(self.host, self.diaryfullpath, timeout=10)
helpers.wait_for_file(self.host, self.diaryfullpath, timeout=30)
ver_from_mat = self.host.file(self.diaryfullpath).content_string.rstrip("\n")
self.assertEqual(
helpers.get_release_from_string(self.image_name).upper().lstrip("R"),
Expand Down
1 change: 0 additions & 1 deletion tests/utils/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import testinfra
import unittest


class TestCase(unittest.TestCase):
"""Base test class for Docker tests"""

Expand Down
2 changes: 1 addition & 1 deletion tests/utils/dockertool.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ def run(self, timeout=DOCKER_RUN_TIMEOUT):
"""run the tests in the docker container and report the logs"""
client = docker.from_env()
logging.info(
f"running command:\n\t{self.command}\nin a '{self.image_name}' container"
f"Running command:\n\t{self.command}\nin a '{self.image_name}' container"
)
container = client.containers.run(
image=self.image_name,
Expand Down
39 changes: 34 additions & 5 deletions tests/utils/helpers.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
# Copyright 2023 The MathWorks, Inc.
# Copyright 2023-2024 The MathWorks, Inc.

"""
Set of helpers functions for Docker tests.
"""

from . import mdparser
import re
import os
import time
Expand All @@ -13,7 +14,7 @@ def _getenv_(key):
try:
return os.environ.get(key)
except:
raise ValueError(f"environment variable {key} not defined")
raise ValueError(f"Environment variable {key} not defined")


def get_image_name():
Expand All @@ -23,11 +24,11 @@ def get_image_name():
def get_license_filepath():
filepath = _getenv_("LICENSE_FILE_PATH")
if filepath == "":
raise ValueError("environment variable 'LICENSE_FILE_PATH' is empty")
raise ValueError("Environment variable 'LICENSE_FILE_PATH' is empty")
if not os.path.exists(filepath):
raise ValueError(f"license file {filepath} does not exist")
raise ValueError(f"License file {filepath} does not exist")
if os.stat(filepath).st_size <= 1:
raise ValueError(f"license file {filepath} is empty")
raise ValueError(f"License file {filepath} is empty")
return filepath


Expand Down Expand Up @@ -59,6 +60,34 @@ def remove_file(filepath):
except OSError:
pass

def get_changelog_mb_version(filepath):
"""Get the latest version of matlab-batch from a .md file"""
## look for the vYYYY.MM.N pattern, where
# YYYY is the year
# MM is the month
# N is the patch number
pattern = "v(20[2-9][0-9]\.[0-9]{1,2}\.[0-9]+)"
ver_regexp = re.compile(pattern)

headings_tree=mdparser.get_headings_tree(filepath)

target_heading = "Changelog"
path_to_target = mdparser.find_element(headings_tree, target_heading)
if not path_to_target:
raise ValueError(f"Unable to find '{target_heading}' in the heading tree of {filepath}")

version_list = mdparser.get_children(headings_tree, path_to_target)
if not version_list:
raise ValueError(f"The heading '{target_heading}' does not have sub-headings")

latest_version=version_list[0]
match = ver_regexp.search(latest_version)
if match is None:
raise ValueError(
f"The pattern '{pattern}' cannot be found in the content of {filepath}"
)
return match.group(1)


## Wait functions ##

Expand Down
Loading

0 comments on commit dd7b3ef

Please sign in to comment.