mirror of
https://github.com/tahoe-lafs/tahoe-lafs.git
synced 2025-03-11 06:43:54 +00:00
Merge pull request #1327 from exarkun/4057.circleci-windows-unittests
Run the unit test suite on Windows on CircleCI Fixes: ticket:4057
This commit is contained in:
commit
c2b4cebcdd
@ -24,6 +24,11 @@ version: 2.1
|
|||||||
dockerhub-context-template: &DOCKERHUB_CONTEXT
|
dockerhub-context-template: &DOCKERHUB_CONTEXT
|
||||||
context: "dockerhub-auth"
|
context: "dockerhub-auth"
|
||||||
|
|
||||||
|
# Required environment for using the coveralls tool to upload partial coverage
|
||||||
|
# reports and then finish the process.
|
||||||
|
coveralls-environment: &COVERALLS_ENVIRONMENT
|
||||||
|
COVERALLS_REPO_TOKEN: "JPf16rLB7T2yjgATIxFzTsEgMdN1UNq6o"
|
||||||
|
|
||||||
# Next is a Docker executor template that gets the credentials from the
|
# Next is a Docker executor template that gets the credentials from the
|
||||||
# environment and supplies them to the executor.
|
# environment and supplies them to the executor.
|
||||||
dockerhub-auth-template: &DOCKERHUB_AUTH
|
dockerhub-auth-template: &DOCKERHUB_AUTH
|
||||||
@ -112,6 +117,21 @@ workflows:
|
|||||||
- "another-locale":
|
- "another-locale":
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
- "windows-server-2022":
|
||||||
|
name: "Windows Server 2022, CPython <<matrix.pythonVersion>>"
|
||||||
|
matrix:
|
||||||
|
parameters:
|
||||||
|
# Run the job for a number of CPython versions. These are the
|
||||||
|
# two versions installed on the version of the Windows VM image
|
||||||
|
# we specify (in the executor). This is handy since it means we
|
||||||
|
# don't have to do any Python installation work. We pin the
|
||||||
|
# Windows VM image so these shouldn't shuffle around beneath us
|
||||||
|
# but if we want to update that image or get different versions
|
||||||
|
# of Python, we probably have to do something here.
|
||||||
|
pythonVersion:
|
||||||
|
- "3.9"
|
||||||
|
- "3.11"
|
||||||
|
|
||||||
- "integration":
|
- "integration":
|
||||||
# Run even the slow integration tests here. We need the `--` to
|
# Run even the slow integration tests here. We need the `--` to
|
||||||
# sneak past tox and get to pytest.
|
# sneak past tox and get to pytest.
|
||||||
@ -126,6 +146,15 @@ workflows:
|
|||||||
- "docs":
|
- "docs":
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
- "finish-coverage-report":
|
||||||
|
requires:
|
||||||
|
# Referencing the job by "alias" (as CircleCI calls the mapping
|
||||||
|
# key) instead of the value of its "name" property causes us to
|
||||||
|
# require every instance of the job from its matrix expansion. So
|
||||||
|
# this requirement is enough to require every Windows Server 2022
|
||||||
|
# job.
|
||||||
|
- "windows-server-2022"
|
||||||
|
|
||||||
images:
|
images:
|
||||||
<<: *IMAGES
|
<<: *IMAGES
|
||||||
|
|
||||||
@ -133,6 +162,20 @@ workflows:
|
|||||||
when: "<< pipeline.parameters.build-images >>"
|
when: "<< pipeline.parameters.build-images >>"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
finish-coverage-report:
|
||||||
|
docker:
|
||||||
|
- <<: *DOCKERHUB_AUTH
|
||||||
|
image: "python:3-slim"
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- run:
|
||||||
|
name: "Indicate completion to coveralls.io"
|
||||||
|
environment:
|
||||||
|
<<: *COVERALLS_ENVIRONMENT
|
||||||
|
command: |
|
||||||
|
pip install coveralls==3.3.1
|
||||||
|
python -m coveralls --finish
|
||||||
|
|
||||||
codechecks:
|
codechecks:
|
||||||
docker:
|
docker:
|
||||||
- <<: *DOCKERHUB_AUTH
|
- <<: *DOCKERHUB_AUTH
|
||||||
@ -151,6 +194,161 @@ jobs:
|
|||||||
command: |
|
command: |
|
||||||
~/.local/bin/tox -e codechecks
|
~/.local/bin/tox -e codechecks
|
||||||
|
|
||||||
|
windows-server-2022:
|
||||||
|
parameters:
|
||||||
|
pythonVersion:
|
||||||
|
description: >-
|
||||||
|
An argument to pass to the `py` launcher to choose a Python version.
|
||||||
|
type: "string"
|
||||||
|
default: ""
|
||||||
|
|
||||||
|
executor: "windows"
|
||||||
|
environment:
|
||||||
|
# Tweak Hypothesis to make its behavior more suitable for the CI
|
||||||
|
# environment. This should improve reproducibility and lessen the
|
||||||
|
# effects of variable compute resources.
|
||||||
|
TAHOE_LAFS_HYPOTHESIS_PROFILE: "ci"
|
||||||
|
|
||||||
|
# Tell pip where its download cache lives. This must agree with the
|
||||||
|
# "save_cache" step below or caching won't really work right.
|
||||||
|
PIP_CACHE_DIR: "pip-cache"
|
||||||
|
|
||||||
|
# And tell pip where it can find out cached wheelhouse for fast wheel
|
||||||
|
# installation, even for projects that don't distribute wheels. This
|
||||||
|
# must also agree with the "save_cache" step below.
|
||||||
|
PIP_FIND_LINKS: "wheelhouse"
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- "checkout"
|
||||||
|
|
||||||
|
# If possible, restore a pip download cache to save us from having to
|
||||||
|
# download all our Python dependencies from PyPI.
|
||||||
|
- "restore_cache":
|
||||||
|
keys:
|
||||||
|
# The download cache and/or the wheelhouse may contain Python
|
||||||
|
# version-specific binary packages so include the Python version
|
||||||
|
# in this key, as well as the canonical source of our
|
||||||
|
# dependencies.
|
||||||
|
- &CACHE_KEY "pip-packages-v1-<< parameters.pythonVersion >>-{{ checksum \"setup.py\" }}"
|
||||||
|
|
||||||
|
- "run":
|
||||||
|
name: "Fix $env:PATH"
|
||||||
|
command: |
|
||||||
|
# The Python this job is parameterized is not necessarily the one
|
||||||
|
# at the front of $env:PATH. Modify $env:PATH so that it is so we
|
||||||
|
# can just say "python" in the rest of the steps. Also get the
|
||||||
|
# related Scripts directory so tools from packages we install are
|
||||||
|
# also available.
|
||||||
|
$p = py -<<parameters.pythonVersion>> -c "import sys; print(sys.prefix)"
|
||||||
|
$q = py -<<parameters.pythonVersion>> -c "import sysconfig; print(sysconfig.get_path('scripts'))"
|
||||||
|
|
||||||
|
New-Item $Profile.CurrentUserAllHosts -Force
|
||||||
|
# $p gets "python" on PATH and $q gets tools from packages we
|
||||||
|
# install. Note we carefully construct the string so that
|
||||||
|
# $env:PATH is not substituted now but $p and $q are. ` is the
|
||||||
|
# PowerShell string escape character.
|
||||||
|
Add-Content -Path $Profile.CurrentUserAllHosts -Value "`$env:PATH = `"$p;$q;`$env:PATH`""
|
||||||
|
|
||||||
|
- "run":
|
||||||
|
name: "Display tool versions"
|
||||||
|
command: |
|
||||||
|
python misc/build_helpers/show-tool-versions.py
|
||||||
|
|
||||||
|
- "run":
|
||||||
|
# It's faster to install a wheel than a source package. If we don't
|
||||||
|
# have a cached wheelhouse then build all of the wheels and dump
|
||||||
|
# them into a directory where they can become a cached wheelhouse.
|
||||||
|
# We would have built these wheels during installation anyway so it
|
||||||
|
# doesn't cost us anything extra and saves us effort next time.
|
||||||
|
name: "(Maybe) Build Wheels"
|
||||||
|
command: |
|
||||||
|
if ((Test-Path .\wheelhouse) -and (Test-Path .\wheelhouse\*)) {
|
||||||
|
echo "Found populated wheelhouse, skipping wheel building."
|
||||||
|
} else {
|
||||||
|
python -m pip install wheel
|
||||||
|
python -m pip wheel --wheel-dir $env:PIP_FIND_LINKS .[testenv] .[test]
|
||||||
|
}
|
||||||
|
|
||||||
|
- "save_cache":
|
||||||
|
paths:
|
||||||
|
# Make sure this agrees with PIP_CACHE_DIR in the environment.
|
||||||
|
- "pip-cache"
|
||||||
|
- "wheelhouse"
|
||||||
|
key: *CACHE_KEY
|
||||||
|
|
||||||
|
- "run":
|
||||||
|
name: "Install Dependencies"
|
||||||
|
environment:
|
||||||
|
# By this point we should no longer need an index.
|
||||||
|
PIP_NO_INDEX: "1"
|
||||||
|
command: |
|
||||||
|
python -m pip install .[testenv] .[test]
|
||||||
|
|
||||||
|
- "run":
|
||||||
|
name: "Run Unit Tests"
|
||||||
|
environment:
|
||||||
|
# Configure the results location for the subunitv2-file reporter
|
||||||
|
# from subunitreporter
|
||||||
|
SUBUNITREPORTER_OUTPUT_PATH: "test-results.subunit2"
|
||||||
|
|
||||||
|
# Try to get prompt output from the reporter to avoid no-output
|
||||||
|
# timeouts.
|
||||||
|
PYTHONUNBUFFERED: "1"
|
||||||
|
|
||||||
|
command: |
|
||||||
|
# Run the test suite under coverage measurement using the
|
||||||
|
# parameterized version of Python, writing subunitv2-format
|
||||||
|
# results to the file given in the environment.
|
||||||
|
python -b -m coverage run -m twisted.trial --reporter=subunitv2-file --rterrors allmydata
|
||||||
|
|
||||||
|
- "run":
|
||||||
|
name: "Upload Coverage"
|
||||||
|
environment:
|
||||||
|
<<: *COVERALLS_ENVIRONMENT
|
||||||
|
# Mark the data as just one piece of many because we have more
|
||||||
|
# than one instance of this job (two on Windows now, some on other
|
||||||
|
# platforms later) which collects and reports coverage. This is
|
||||||
|
# necessary to cause Coveralls to merge multiple coverage results
|
||||||
|
# into a single report. Note the merge only happens when we
|
||||||
|
# "finish" a particular build, as identified by its "build_num"
|
||||||
|
# (aka "service_number").
|
||||||
|
COVERALLS_PARALLEL: "true"
|
||||||
|
command: |
|
||||||
|
python -m pip install coveralls==3.3.1
|
||||||
|
|
||||||
|
# .coveragerc sets parallel = True so we don't have a `.coverage`
|
||||||
|
# file but a `.coverage.<unique stuff>` file (or maybe more than
|
||||||
|
# one, but probably not). coveralls can't work with these so
|
||||||
|
# merge them before invoking it.
|
||||||
|
python -m coverage combine
|
||||||
|
|
||||||
|
# Now coveralls will be able to find the data, so have it do the
|
||||||
|
# upload. Also, have it strip the system config-specific prefix
|
||||||
|
# from all of the source paths.
|
||||||
|
$prefix = python -c "import sysconfig; print(sysconfig.get_path('purelib'))"
|
||||||
|
python -m coveralls --basedir $prefix
|
||||||
|
|
||||||
|
- "run":
|
||||||
|
name: "Convert Result Log"
|
||||||
|
command: |
|
||||||
|
# subunit2junitxml exits with error if the result stream it is
|
||||||
|
# converting has test failures in it! So this step might fail.
|
||||||
|
# Since the step in which we actually _ran_ the tests won't fail
|
||||||
|
# even if there are test failures, this is a good thing for now.
|
||||||
|
subunit2junitxml.exe --output-to=test-results.xml test-results.subunit2
|
||||||
|
|
||||||
|
- "store_test_results":
|
||||||
|
path: "test-results.xml"
|
||||||
|
|
||||||
|
- "store_artifacts":
|
||||||
|
path: "_trial_temp/test.log"
|
||||||
|
|
||||||
|
- "store_artifacts":
|
||||||
|
path: "eliot.log"
|
||||||
|
|
||||||
|
- "store_artifacts":
|
||||||
|
path: ".coverage"
|
||||||
|
|
||||||
pyinstaller:
|
pyinstaller:
|
||||||
docker:
|
docker:
|
||||||
- <<: *DOCKERHUB_AUTH
|
- <<: *DOCKERHUB_AUTH
|
||||||
@ -527,6 +725,15 @@ jobs:
|
|||||||
# PYTHON_VERSION: "2"
|
# PYTHON_VERSION: "2"
|
||||||
|
|
||||||
executors:
|
executors:
|
||||||
|
windows:
|
||||||
|
# Choose a Windows environment that closest matches our testing
|
||||||
|
# requirements and goals.
|
||||||
|
# https://circleci.com/developer/orbs/orb/circleci/windows#executors-server-2022
|
||||||
|
machine:
|
||||||
|
image: "windows-server-2022-gui:2023.06.1"
|
||||||
|
shell: "powershell.exe -ExecutionPolicy Bypass"
|
||||||
|
resource_class: "windows.large"
|
||||||
|
|
||||||
nix:
|
nix:
|
||||||
docker:
|
docker:
|
||||||
# Run in a highly Nix-capable environment.
|
# Run in a highly Nix-capable environment.
|
||||||
|
@ -19,7 +19,7 @@ skip_covered = True
|
|||||||
source =
|
source =
|
||||||
# It looks like this in the checkout
|
# It looks like this in the checkout
|
||||||
src/
|
src/
|
||||||
# It looks like this in the Windows build environment
|
# It looks like this in the GitHub Actions Windows build environment
|
||||||
D:/a/tahoe-lafs/tahoe-lafs/.tox/py*-coverage/Lib/site-packages/
|
D:/a/tahoe-lafs/tahoe-lafs/.tox/py*-coverage/Lib/site-packages/
|
||||||
# Although sometimes it looks like this instead. Also it looks like this on macOS.
|
# Although sometimes it looks like this instead. Also it looks like this on macOS.
|
||||||
.tox/py*-coverage/lib/python*/site-packages/
|
.tox/py*-coverage/lib/python*/site-packages/
|
||||||
|
7
.github/workflows/ci.yml
vendored
7
.github/workflows/ci.yml
vendored
@ -44,13 +44,6 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os:
|
|
||||||
- windows-latest
|
|
||||||
python-version:
|
|
||||||
- "3.8"
|
|
||||||
- "3.9"
|
|
||||||
- "3.10"
|
|
||||||
- "3.11"
|
|
||||||
include:
|
include:
|
||||||
# On macOS don't bother with 3.8, just to get faster builds.
|
# On macOS don't bother with 3.8, just to get faster builds.
|
||||||
- os: macos-12
|
- os: macos-12
|
||||||
|
@ -23,6 +23,9 @@ import click.testing
|
|||||||
from ..common_util import (
|
from ..common_util import (
|
||||||
run_cli,
|
run_cli,
|
||||||
)
|
)
|
||||||
|
from ..common import (
|
||||||
|
superuser,
|
||||||
|
)
|
||||||
from twisted.internet.defer import (
|
from twisted.internet.defer import (
|
||||||
inlineCallbacks,
|
inlineCallbacks,
|
||||||
)
|
)
|
||||||
@ -34,7 +37,6 @@ from twisted.python.runtime import (
|
|||||||
)
|
)
|
||||||
from allmydata.util import jsonbytes as json
|
from allmydata.util import jsonbytes as json
|
||||||
|
|
||||||
|
|
||||||
class GridManagerCommandLine(TestCase):
|
class GridManagerCommandLine(TestCase):
|
||||||
"""
|
"""
|
||||||
Test the mechanics of the `grid-manager` command
|
Test the mechanics of the `grid-manager` command
|
||||||
@ -223,7 +225,7 @@ class GridManagerCommandLine(TestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
@skipIf(platform.isWindows(), "We don't know how to set permissions on Windows.")
|
@skipIf(platform.isWindows(), "We don't know how to set permissions on Windows.")
|
||||||
@skipIf(os.getuid() == 0, "cannot test as superuser with all permissions")
|
@skipIf(superuser, "cannot test as superuser with all permissions")
|
||||||
def test_sign_bad_perms(self):
|
def test_sign_bad_perms(self):
|
||||||
"""
|
"""
|
||||||
Error reported if we can't create certificate file
|
Error reported if we can't create certificate file
|
||||||
|
@ -117,6 +117,10 @@ from subprocess import (
|
|||||||
PIPE,
|
PIPE,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Is the process running as an OS user with elevated privileges (ie, root)?
|
||||||
|
# We only know how to determine this for POSIX systems.
|
||||||
|
superuser = getattr(os, "getuid", lambda: -1)() == 0
|
||||||
|
|
||||||
EMPTY_CLIENT_CONFIG = config_from_string(
|
EMPTY_CLIENT_CONFIG = config_from_string(
|
||||||
"/dev/null",
|
"/dev/null",
|
||||||
"tub.port",
|
"tub.port",
|
||||||
|
@ -77,6 +77,7 @@ from allmydata.scripts.common import (
|
|||||||
from foolscap.api import flushEventualQueue
|
from foolscap.api import flushEventualQueue
|
||||||
import allmydata.test.common_util as testutil
|
import allmydata.test.common_util as testutil
|
||||||
from .common import (
|
from .common import (
|
||||||
|
superuser,
|
||||||
EMPTY_CLIENT_CONFIG,
|
EMPTY_CLIENT_CONFIG,
|
||||||
SyncTestCase,
|
SyncTestCase,
|
||||||
AsyncBrokenTestCase,
|
AsyncBrokenTestCase,
|
||||||
@ -151,7 +152,7 @@ class Basic(testutil.ReallyEqualMixin, unittest.TestCase):
|
|||||||
# EnvironmentError when reading a file that really exists), on
|
# EnvironmentError when reading a file that really exists), on
|
||||||
# windows, please fix this
|
# windows, please fix this
|
||||||
@skipIf(platform.isWindows(), "We don't know how to set permissions on Windows.")
|
@skipIf(platform.isWindows(), "We don't know how to set permissions on Windows.")
|
||||||
@skipIf(os.getuid() == 0, "cannot test as superuser with all permissions")
|
@skipIf(superuser, "cannot test as superuser with all permissions")
|
||||||
def test_unreadable_config(self):
|
def test_unreadable_config(self):
|
||||||
basedir = "test_client.Basic.test_unreadable_config"
|
basedir = "test_client.Basic.test_unreadable_config"
|
||||||
os.mkdir(basedir)
|
os.mkdir(basedir)
|
||||||
|
@ -62,6 +62,7 @@ from .common import (
|
|||||||
ConstantAddresses,
|
ConstantAddresses,
|
||||||
SameProcessStreamEndpointAssigner,
|
SameProcessStreamEndpointAssigner,
|
||||||
UseNode,
|
UseNode,
|
||||||
|
superuser,
|
||||||
)
|
)
|
||||||
|
|
||||||
def port_numbers():
|
def port_numbers():
|
||||||
@ -325,7 +326,7 @@ class TestCase(testutil.SignalMixin, unittest.TestCase):
|
|||||||
self.assertEqual(config.items("nosuch", default), default)
|
self.assertEqual(config.items("nosuch", default), default)
|
||||||
|
|
||||||
@skipIf(platform.isWindows(), "We don't know how to set permissions on Windows.")
|
@skipIf(platform.isWindows(), "We don't know how to set permissions on Windows.")
|
||||||
@skipIf(os.getuid() == 0, "cannot test as superuser with all permissions")
|
@skipIf(superuser, "cannot test as superuser with all permissions")
|
||||||
def test_private_config_unreadable(self):
|
def test_private_config_unreadable(self):
|
||||||
"""
|
"""
|
||||||
Asking for inaccessible private config is an error
|
Asking for inaccessible private config is an error
|
||||||
@ -341,7 +342,7 @@ class TestCase(testutil.SignalMixin, unittest.TestCase):
|
|||||||
config.get_or_create_private_config("foo")
|
config.get_or_create_private_config("foo")
|
||||||
|
|
||||||
@skipIf(platform.isWindows(), "We don't know how to set permissions on Windows.")
|
@skipIf(platform.isWindows(), "We don't know how to set permissions on Windows.")
|
||||||
@skipIf(os.getuid() == 0, "cannot test as superuser with all permissions")
|
@skipIf(superuser, "cannot test as superuser with all permissions")
|
||||||
def test_private_config_unreadable_preexisting(self):
|
def test_private_config_unreadable_preexisting(self):
|
||||||
"""
|
"""
|
||||||
error if reading private config data fails
|
error if reading private config data fails
|
||||||
@ -398,7 +399,7 @@ class TestCase(testutil.SignalMixin, unittest.TestCase):
|
|||||||
self.assertEqual(len(counter), 1) # don't call unless necessary
|
self.assertEqual(len(counter), 1) # don't call unless necessary
|
||||||
self.assertEqual(value, "newer")
|
self.assertEqual(value, "newer")
|
||||||
|
|
||||||
@skipIf(os.getuid() == 0, "cannot test as superuser with all permissions")
|
@skipIf(superuser, "cannot test as superuser with all permissions")
|
||||||
def test_write_config_unwritable_file(self):
|
def test_write_config_unwritable_file(self):
|
||||||
"""
|
"""
|
||||||
Existing behavior merely logs any errors upon writing
|
Existing behavior merely logs any errors upon writing
|
||||||
|
Loading…
x
Reference in New Issue
Block a user