Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use Pytest for test execution #1043

Merged
merged 54 commits into from
Aug 6, 2024
Merged
Show file tree
Hide file tree
Changes from 51 commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
8294d5f
Create parallel testsuite using pytest
schroeding May 9, 2024
898cf57
Exclude pytest_* files from ruff wildcard import warning
schroeding May 9, 2024
a4cc9cb
Duplicated test_result.py
schroeding May 9, 2024
f72b39d
Transitioned test_result to pytest
schroeding May 9, 2024
9dd880e
Duplicated test_analyze_run_result.py
schroeding May 9, 2024
569b6d8
Transitioned test_analyze_run_result to pytest
schroeding May 9, 2024
469ac3c
Duplicated pytest_benchmark_definition.py
schroeding May 9, 2024
38daff1
Add dependency for pytest-mock
schroeding May 10, 2024
c70b08e
Transitioned test_benchmark_definition to pytest
schroeding May 10, 2024
6ede849
Duplicated test_core_assignment.py
schroeding May 10, 2024
715f777
Transitioned test_core_assignment to pytest
schroeding May 14, 2024
68396cc
Duplicated test_cgroups.py
schroeding May 15, 2024
22a7998
Transitioned test_cgroups to pytest
schroeding May 16, 2024
de9f2a1
Add pytest testsuite to Gitlab CI
schroeding May 17, 2024
e5f2f3c
... and actually execute it with different Python versions
schroeding May 17, 2024
890fa5d
Duplicated test_pqos.py
schroeding May 21, 2024
34abf3b
Transitioned test_pqos to pytest
schroeding May 21, 2024
d6eec07
Duplicated test_runexecutor.py
schroeding May 23, 2024
e3f291b
Reverting all changes on branch
schroeding Jun 4, 2024
5952499
Add correct globs to pytest
schroeding Jun 9, 2024
57f8852
Move nose to separate testsuite
schroeding Jun 9, 2024
2a20063
Ported test_result, test_runexecutor and test_util to pytest
schroeding Jun 10, 2024
e2c614d
Ported test_analyze_run_result, test_benchmark_definition, test_core_…
schroeding Jun 16, 2024
f417ae5
Ported test_cgroups, working for 3.9
schroeding Jun 16, 2024
5ed6c2e
reformatted test
schroeding Jun 16, 2024
51ae6a3
reverted test_cgroups
schroeding Jun 16, 2024
3ce2480
ported test_integration
schroeding Jun 24, 2024
72647bf
ported tablegenerator integration test
schroeding Jun 24, 2024
c78717f
changed code under test: added additional assertions, copying context…
schroeding Jun 24, 2024
f6bec7d
ported test_cgroups
schroeding Jun 30, 2024
b9e0086
Removed nose dependencies
schroeding Jun 30, 2024
2307f4a
removed unnecessary imports
schroeding Jul 13, 2024
373b379
... all redundant imports
schroeding Jul 13, 2024
bcd7c96
only test the table generator on appveyor
schroeding Jul 13, 2024
795d824
Merge remote-tracking branch 'origin/main' into pytest-testsuite
schroeding Jul 13, 2024
89a86b1
ported tools/test.py
schroeding Jul 21, 2024
54ec0d3
removed deprecated option for setup.py tests
schroeding Jul 21, 2024
67eb186
fixed formatting
schroeding Jul 21, 2024
674f7e0
removed tautological assertion, clarified comment
schroeding Jul 27, 2024
203014f
fixed wrong globs for pytest
schroeding Jul 27, 2024
f33c3bd
fixed issue 991, using local context instead of modifying the default…
schroeding Jul 27, 2024
e99ecc9
moved config for pytest to pyproject.toml
schroeding Jul 28, 2024
49f9f92
added build directory, made docker path more precise
schroeding Aug 2, 2024
cb3ce39
modified additional test
schroeding Aug 2, 2024
7b94907
fixed comment, use new context instead of setting the thread context,…
schroeding Aug 3, 2024
64a4e51
added local context to method which also depends of correct rounding …
schroeding Aug 5, 2024
97a5085
Merge remote-tracking branch 'origin/main' into pytest-testsuite
schroeding Aug 5, 2024
a77a2e8
add trailing space
schroeding Aug 5, 2024
36c605b
purge nose from all files (except release.sh)
schroeding Aug 5, 2024
1842b29
removed redundant import
schroeding Aug 5, 2024
4eb5da3
removed nose from release.sh, replaced it w/ pytest
schroeding Aug 5, 2024
4b11194
Update build dependency of Debian package
PhilippWendler Aug 6, 2024
d4ee4a6
Add some documentation on how to run our tests
PhilippWendler Aug 6, 2024
959f89d
Add pytest to our container images
PhilippWendler Aug 6, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ install:
- python -m pip install --user ".[dev]"

test_script:
- python -m nose --tests benchexec.tablegenerator
- python -m pytest benchexec/tablegenerator/
2 changes: 1 addition & 1 deletion .gitlab-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ stages:
script:
- sudo -u $PRIMARY_USER
COVERAGE_PROCESS_START=.coveragerc
coverage run -m nose
coverage run -m pytest
after_script:
- sudo -u $PRIMARY_USER coverage combine
- sudo -u $PRIMARY_USER coverage report
Expand Down
664 changes: 346 additions & 318 deletions benchexec/tablegenerator/columns.py
PhilippWendler marked this conversation as resolved.
Show resolved Hide resolved

Large diffs are not rendered by default.

102 changes: 53 additions & 49 deletions benchexec/tablegenerator/statistics.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
from benchexec.tablegenerator import util
from benchexec.tablegenerator.columns import ColumnType

# It's important to make sure on *all* entry points / methods which perform arithmetics that the correct
# rounding / context is used.
DECIMAL_CONTEXT = decimal.Context(rounding=decimal.ROUND_HALF_UP)

nan = Decimal("nan")
inf = Decimal("inf")
Expand Down Expand Up @@ -65,55 +68,56 @@ def __str__(self):

@classmethod
def from_list(cls, values):
if any(v is not None and v.is_nan() for v in values):
return StatValue(nan, nan, nan, nan, nan, nan)

values = sorted(v for v in values if v is not None)
if not values:
return None

values_len = len(values)
min_value = values[0]
max_value = values[-1]

if min_value == -inf and max_value == +inf:
values_sum = nan
mean = nan
stdev = nan
elif max_value == inf:
values_sum = inf
mean = inf
stdev = inf
elif min_value == -inf:
values_sum = -inf
mean = -inf
stdev = inf
else:
values_sum = sum(values)
mean = values_sum / values_len

# The scaling is just to avoid having too few decimal digits when printing,
# the value is still just 0.
stdev = Decimal(0).scaleb(-decimal.getcontext().prec)
for v in values:
diff = v - mean
stdev += diff * diff
stdev = (stdev / values_len).sqrt()

half, len_is_odd = divmod(values_len, 2)
if len_is_odd:
median = values[half]
else:
median = (values[half - 1] + values[half]) / Decimal(2)

return StatValue(
values_sum,
min=min_value,
max=max_value,
avg=mean,
median=median,
stdev=stdev,
)
with decimal.localcontext(DECIMAL_CONTEXT):
if any(v is not None and v.is_nan() for v in values):
return StatValue(nan, nan, nan, nan, nan, nan)

values = sorted(v for v in values if v is not None)
if not values:
return None

values_len = len(values)
min_value = values[0]
max_value = values[-1]

if min_value == -inf and max_value == +inf:
values_sum = nan
mean = nan
stdev = nan
elif max_value == inf:
values_sum = inf
mean = inf
stdev = inf
elif min_value == -inf:
values_sum = -inf
mean = -inf
stdev = inf
else:
values_sum = sum(values)
mean = values_sum / values_len

# The scaling is just to avoid having too few decimal digits when printing,
# the value is still just 0.
stdev = Decimal(0).scaleb(-decimal.getcontext().prec)
for v in values:
diff = v - mean
stdev += diff * diff
stdev = (stdev / values_len).sqrt()

half, len_is_odd = divmod(values_len, 2)
if len_is_odd:
median = values[half]
else:
median = (values[half - 1] + values[half]) / Decimal(2)

return StatValue(
values_sum,
min=min_value,
max=max_value,
avg=mean,
median=median,
stdev=stdev,
)


def get_stats_of_run_set(runResults, correct_only):
Expand Down
9 changes: 9 additions & 0 deletions benchexec/tablegenerator/test_columns.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,15 @@ def test_format_value_small_value(self):
formatted_value_aligned = small_value_column.format_value("2", "html_cell")
self.assertEqual(formatted_value_aligned, ".0000000002  ")

def test_invalid_rounding_mode(self):
import decimal

decimal.getcontext().rounding = decimal.ROUND_HALF_DOWN
formatted_value_no_align_zeros_cut = self.measure_column.format_value(
"5.7715", *self.default_optionals
)
self.assertEqual(formatted_value_no_align_zeros_cut, "5.772")

def test_format_value_align_int(self):
formatted_value_int_aligned = self.measure_column.format_value(
"20", "html_cell"
Expand Down
7 changes: 0 additions & 7 deletions benchexec/tablegenerator/test_integration/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@
import benchexec.util
import benchexec.tablegenerator.util

sys.dont_write_bytecode = True # prevent creation of .pyc files

here = os.path.relpath(os.path.dirname(__file__))
base_dir = os.path.join(here, "..", "..", "..")
bin_dir = os.path.join(base_dir, "bin")
Expand All @@ -38,11 +36,6 @@ class TableGeneratorIntegrationTests(unittest.TestCase):
# Tests compare the generated CSV files and ignore the HTML files
# because we assume the HTML files change more often on purpose.

@classmethod
def setUpClass(cls):
cls.longMessage = True
cls.maxDiff = None

def setUp(self):
# We use a temporary directory inside the source tree to avoid mismatching
# path names inside HTML tables.
Expand Down
8 changes: 0 additions & 8 deletions benchexec/tablegenerator/test_statvalue.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,12 @@
# SPDX-License-Identifier: Apache-2.0

from decimal import Decimal
import sys
import unittest

from benchexec.tablegenerator.statistics import StatValue

sys.dont_write_bytecode = True # prevent creation of .pyc files


class TestStatValue(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.longMessage = True
cls.maxDiff = None

def test_empty(self):
self.assertIsNone(StatValue.from_list([]))

Expand Down
7 changes: 0 additions & 7 deletions benchexec/tablegenerator/test_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,12 @@
# SPDX-License-Identifier: Apache-2.0

from decimal import Decimal
import sys
import unittest

from benchexec.tablegenerator import util

sys.dont_write_bytecode = True # prevent creation of .pyc files


class TestUnit(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.longMessage = True
cls.maxDiff = None

def assertEqualNumberAndUnit(self, value, number, unit):
self.assertEqual(util.split_number_and_unit(value), (number, unit))
Expand Down
9 changes: 0 additions & 9 deletions benchexec/test_analyze_run_result.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
#
# SPDX-License-Identifier: Apache-2.0

import logging
import sys
import unittest
import types

Expand All @@ -20,17 +18,10 @@
)
from benchexec.tools.template import BaseTool

sys.dont_write_bytecode = True # prevent creation of .pyc files

normal_result = ProcessExitCode(raw=0, value=0, signal=None)


class TestResult(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.longMessage = True
logging.disable(logging.CRITICAL)

def create_run(self, info_result=RESULT_UNKNOWN):
runSet = types.SimpleNamespace()
runSet.log_folder = "."
Expand Down
4 changes: 0 additions & 4 deletions benchexec/test_benchmark_definition.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,6 @@ class TestBenchmarkDefinition(unittest.TestCase):
testing mostly the classes from benchexec.model.
"""

@classmethod
def setUpClass(cls):
cls.longMessage = True

@patch("benchexec.model.load_task_definition_file", new=mock_load_task_def_file)
@patch("benchexec.result.Property.create", new=mock_property_create)
@patch("benchexec.util.expand_filename_pattern", new=mock_expand_filename_pattern)
Expand Down
10 changes: 0 additions & 10 deletions benchexec/test_cgroups.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,13 @@
#
# SPDX-License-Identifier: Apache-2.0

import logging
import subprocess
import sys
import unittest

from benchexec import check_cgroups

sys.dont_write_bytecode = True # prevent creation of .pyc files


class TestCheckCgroups(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.longMessage = True
cls.maxDiff = None
logging.disable(logging.CRITICAL)

def execute_run_extern(self, *args, **kwargs):
try:
return subprocess.check_output(
Expand Down
8 changes: 0 additions & 8 deletions benchexec/test_core_assignment.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,17 @@
# SPDX-License-Identifier: Apache-2.0

import itertools
import logging
import sys
import unittest
import math

from benchexec.resources import _get_cpu_cores_per_run0

sys.dont_write_bytecode = True # prevent creation of .pyc files


def lrange(start, end):
return list(range(start, end))


class TestCpuCoresPerRun(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.longMessage = True
logging.disable(logging.CRITICAL)

def assertValid(self, coreLimit, num_of_threads, expectedResult=None):
result = _get_cpu_cores_per_run0(
Expand Down
7 changes: 0 additions & 7 deletions benchexec/test_integration/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,12 @@
import os
import shutil
import subprocess
import sys
import tempfile
import unittest
import zipfile

from xml.etree import ElementTree

sys.dont_write_bytecode = True # prevent creation of .pyc files

here = os.path.dirname(__file__)
base_dir = os.path.join(here, "..", "..")
bin_dir = os.path.join(base_dir, "bin")
Expand All @@ -45,10 +42,6 @@


class BenchExecIntegrationTests(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.longMessage = True
cls.maxDiff = None

def _build_tmp_dir(self):
"""
Expand Down
6 changes: 0 additions & 6 deletions benchexec/test_pqos.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,11 @@
"""
import json
import copy
import logging
import unittest
from subprocess import CalledProcessError
from unittest.mock import patch, MagicMock
from benchexec.pqos import Pqos


mock_pqos_wrapper_output = {
"load_pqos": {
"function_output": {},
Expand Down Expand Up @@ -147,10 +145,6 @@ class TestPqos(unittest.TestCase):
Unit tests for pqos module
"""

@classmethod
def setUpClass(cls):
logging.disable(logging.CRITICAL)

@patch("benchexec.pqos.find_executable2", return_value="/path/to/pqos_wrapper/lib")
def test_pqos_init(self, mock_find_executable):
"""
Expand Down
8 changes: 0 additions & 8 deletions benchexec/test_result.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
#
# SPDX-License-Identifier: Apache-2.0

import logging
import sys
import tempfile
import unittest

Expand All @@ -18,8 +16,6 @@
_SCORE_WRONG_FALSE,
)

sys.dont_write_bytecode = True # prevent creation of .pyc files


class TestExpectedResult(unittest.TestCase):
def test_via_string(self):
Expand Down Expand Up @@ -56,10 +52,6 @@ def test(s):


class TestResult(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.longMessage = True
logging.disable(logging.CRITICAL)

def expected_result(self, result, subcategory=None):
return {"dummy.prp": ExpectedResult(result, subcategory)}
Expand Down
Loading