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:
Jean-Paul Calderone 2023-08-09 18:38:32 -04:00 committed by GitHub
commit c2b4cebcdd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 222 additions and 14 deletions

View File

@ -24,6 +24,11 @@ version: 2.1
dockerhub-context-template: &DOCKERHUB_CONTEXT
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
# environment and supplies them to the executor.
dockerhub-auth-template: &DOCKERHUB_AUTH
@ -112,6 +117,21 @@ workflows:
- "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":
# Run even the slow integration tests here. We need the `--` to
# sneak past tox and get to pytest.
@ -126,6 +146,15 @@ workflows:
- "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
@ -133,6 +162,20 @@ workflows:
when: "<< pipeline.parameters.build-images >>"
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:
docker:
- <<: *DOCKERHUB_AUTH
@ -151,6 +194,161 @@ jobs:
command: |
~/.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:
docker:
- <<: *DOCKERHUB_AUTH
@ -527,6 +725,15 @@ jobs:
# PYTHON_VERSION: "2"
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:
docker:
# Run in a highly Nix-capable environment.

View File

@ -19,7 +19,7 @@ skip_covered = True
source =
# It looks like this in the checkout
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/
# Although sometimes it looks like this instead. Also it looks like this on macOS.
.tox/py*-coverage/lib/python*/site-packages/

View File

@ -44,13 +44,6 @@ jobs:
strategy:
fail-fast: false
matrix:
os:
- windows-latest
python-version:
- "3.8"
- "3.9"
- "3.10"
- "3.11"
include:
# On macOS don't bother with 3.8, just to get faster builds.
- os: macos-12

View File

@ -23,6 +23,9 @@ import click.testing
from ..common_util import (
run_cli,
)
from ..common import (
superuser,
)
from twisted.internet.defer import (
inlineCallbacks,
)
@ -34,7 +37,6 @@ from twisted.python.runtime import (
)
from allmydata.util import jsonbytes as json
class GridManagerCommandLine(TestCase):
"""
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(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):
"""
Error reported if we can't create certificate file

View File

@ -117,6 +117,10 @@ from subprocess import (
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(
"/dev/null",
"tub.port",

View File

@ -77,6 +77,7 @@ from allmydata.scripts.common import (
from foolscap.api import flushEventualQueue
import allmydata.test.common_util as testutil
from .common import (
superuser,
EMPTY_CLIENT_CONFIG,
SyncTestCase,
AsyncBrokenTestCase,
@ -151,7 +152,7 @@ class Basic(testutil.ReallyEqualMixin, unittest.TestCase):
# EnvironmentError when reading a file that really exists), on
# windows, please fix this
@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):
basedir = "test_client.Basic.test_unreadable_config"
os.mkdir(basedir)

View File

@ -62,6 +62,7 @@ from .common import (
ConstantAddresses,
SameProcessStreamEndpointAssigner,
UseNode,
superuser,
)
def port_numbers():
@ -325,7 +326,7 @@ class TestCase(testutil.SignalMixin, unittest.TestCase):
self.assertEqual(config.items("nosuch", default), default)
@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):
"""
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")
@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):
"""
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(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):
"""
Existing behavior merely logs any errors upon writing