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

Run CI-testing on GHA/Windows #337

Draft
wants to merge 20 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
fef4ccf
Run CI-testing on GHA/Windows
amotl Nov 13, 2020
bd760d0
Adjust .gitignore file
amotl Nov 13, 2020
dd90112
CI: Add support for extracting zip archives
amotl Nov 13, 2020
6f1e981
CI: Mess with LANG or LC_ALL only on non-Windows systems
amotl Nov 13, 2020
ede5144
CI: Mitigate nasty "Unrecognized Windows Sockets error: 10106"
amotl Nov 13, 2020
bf79d43
CI: Don't invoke SourceBuildTest on Windows
amotl Nov 13, 2020
87f9e5b
CI: Try to invoke unit tests explicitly with bash to work around Windows
amotl Nov 13, 2020
a34b07e
Tests: Improve diff output at "test_geoshape_type_default"
amotl Nov 13, 2020
4d13194
CI: Skip "test_geoshape_type_default" test on Windows
amotl Nov 13, 2020
bc773b1
CI: Use LANG/LC_ALL/LANGUAGE also on Windows systems again
amotl Feb 10, 2024
4fb5e43
CI: Set environment variable `WINUTF8=1`
amotl Feb 10, 2024
ea1a42a
Windows: Attempt to fix Unicode encoding error using `chcp`
amotl Feb 10, 2024
7355ed3
Tests: Use single quotes in README's doctest commands to fix Windows
amotl Feb 10, 2024
4825bcb
README: Fix typo, an excessive line continuation character
amotl Feb 10, 2024
b70a69d
Testing: Generalize integration test setup/teardown boilerplate code
amotl Feb 10, 2024
793d55d
Testing: Add Python-based integration tests
amotl Feb 10, 2024
7e390d0
CI: Add MSYS64 to path, in order to use a different `echo`
amotl Feb 10, 2024
2028f44
CI: Quoting on `cmd` is different, even if it pretends to be `bash`?
amotl Feb 11, 2024
da92988
CI: Skip tests if incorrect quoting on Windows
amotl Feb 11, 2024
3bef462
Windows: Don't freeze on CrateDB shutdown?
amotl Feb 11, 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
20 changes: 18 additions & 2 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,15 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest]
os: [ubuntu-latest, windows-latest]
python-version: [3.7, 3.8, 3.9, '3.10', '3.11']

env:
LANG: en_US.UTF-8
LC_ALL: en_US.UTF-8
LANGUAGE: en_US.UTF-8
WINUTF8: 1

steps:
- uses: actions/checkout@master
- name: Set up Python ${{ matrix.python-version }}
Expand All @@ -33,8 +39,18 @@ jobs:
pip install pycodestyle unittest-xml-reporting
pip install .

- name: Adjust Windows PATH
if: startsWith(matrix.os, 'win')
shell: bash
run: |
echo "C:\msys64\usr\bin" >> $GITHUB_PATH
dir 'C:\msys64\usr\bin'

- name: Test
run: python -m xmlrunner discover -v -o test-results/test/
shell: bash
run: |
command -v chcp && chcp 65001 # Set code page to utf-8.
python -m xmlrunner discover -v -o test-results/test/

- name: Pycodestyle
run: pycodestyle cr8
Expand Down
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

# Created by https://www.gitignore.io/api/python

### Python ###
Expand Down Expand Up @@ -67,6 +66,9 @@ target/
bin/
venv/

# IDE
.idea

nose2-junit.xml

test-results/
6 changes: 3 additions & 3 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ timeit 🕐
A tool that can be used to measure the runtime of a given SQL statement on a
cluster::

>>> echo "select name from sys.cluster" | cr8 timeit --hosts localhost:4200
>>> echo 'select name from sys.cluster' | cr8 timeit --hosts localhost:4200
Runtime (in ms):
mean: ... ± ...
min/max: ... → ...
Expand Down Expand Up @@ -205,7 +205,7 @@ Copies data from one CrateDB cluster or PostgreSQL server to another.
... --src-uri "postgresql://crate@localhost:5432/doc" \
... --query "SELECT name FROM x.demo" \
... --hosts localhost:4200 \
... --table y.demo \
... --table y.demo
INSERT INTO y.demo ("name") VALUES ($1)
Runtime (in ms):
...
Expand Down Expand Up @@ -396,7 +396,7 @@ To use the ``postgres`` protocol, the ``asyncpg`` scheme must be used inside hos
::


>>> echo "select 1" | cr8 timeit --hosts asyncpg://localhost:5432
>>> echo 'select 1' | cr8 timeit --hosts asyncpg://localhost:5432
Runtime (in ms):
...

Expand Down
32 changes: 26 additions & 6 deletions cr8/run_crate.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import zipfile

import argh
import os
import json
Expand Down Expand Up @@ -270,16 +272,29 @@ def __init__(self,
else:
java_home = os.environ.get('JAVA_HOME', '')
self.env.setdefault('JAVA_HOME', java_home)

# Propagate charset encoding / code page information.
self.env.setdefault('LANG',
os.environ.get('LANG', os.environ.get('LC_ALL')))
if not self.env['LANG']:
raise SystemExit('Your locale are not configured correctly. '
'Please set LANG or alternatively LC_ALL.')

# Operating system specific configuration.
if sys.platform == "win32":
start_script = 'crate.bat'

# Mitigate errors like
# java.io.IOException: Unable to establish loopback connection
# java.net.SocketException: Unrecognized Windows Sockets error: 10106: socket
self.env.setdefault('SystemRoot', 'C:\\Windows')
else:
start_script = 'crate'

self.monitor = OutputMonitor()
self.process = None # type: Optional[subprocess.Popen]
self.http_url = None # type: Optional[str]
self.http_host = None # type: Optional[str]
start_script = 'crate.bat' if sys.platform == 'win32' else 'crate'

settings = _get_settings(settings)
if self.version < (1, 1, 0):
Expand Down Expand Up @@ -379,7 +394,8 @@ def _set_addr(self, protocol, addr):
def stop(self):
if self.process:
self.process.terminate()
self.process.communicate(timeout=120)
if not sys.platform.startswith("win"):
self.process.communicate(timeout=120)
self.addresses = DotDict({})
self.http_host = None
self.http_url = None
Expand Down Expand Up @@ -472,18 +488,22 @@ def _can_use_cache(uri, crate_dir):

def _download_and_extract(uri, crate_root):
filename = os.path.basename(uri)
crate_folder_name = re.sub(r'\.tar(\.gz)?$', '', filename)
crate_folder_name = re.sub(r'\.(tar|zip)(\.gz)?$', '', filename)
crate_dir = os.path.join(crate_root, crate_folder_name)

if _can_use_cache(uri, crate_dir):
log.info('Skipping download, tarball alrady extracted at %s', crate_dir)
log.info('Skipping download, archive already extracted at %s', crate_dir)
return crate_dir
elif os.path.exists(crate_dir):
shutil.rmtree(crate_dir, ignore_errors=True)
log.info('Downloading %s and extracting to %s', uri, crate_root)
with _openuri(uri) as tmpfile:
with tarfile.open(fileobj=tmpfile) as t:
t.extractall(crate_root)
if uri.endswith(".zip"):
with zipfile.ZipFile(file=tmpfile) as t:
t.extractall(crate_root)
else:
with tarfile.open(fileobj=tmpfile) as t:
t.extractall(crate_root)
tmpfile.seek(0)
checksum = sha1(tmpfile.read()).hexdigest()
with open(os.path.join(crate_dir, checksum), 'a'):
Expand Down
54 changes: 54 additions & 0 deletions tests/integration_util.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
from cr8.clients import client
from cr8 import aio
from cr8.run_crate import get_crate, CrateNode


crate_dir = get_crate('latest-testing')
node = CrateNode(
crate_dir=crate_dir,
settings={
'cluster.name': 'cr8-tests',
'http.port': '44200-44250',
'cluster.routing.allocation.disk.threshold_enabled': 'false',
})


def setup(*args):
with client(node.http_url) as c:
aio.run(
c.execute,
'create table x.demo (id int, name string, country string) \
with (number_of_replicas = 0)'
)
aio.run(c.execute, 'create table y.demo (name text) with (number_of_replicas = 0)')
aio.run(c.execute, 'create blob table blobtable with (number_of_replicas = 0)')


def teardown(*args):
with client(node.http_url) as c:
aio.run(c.execute, 'drop table x.demo')
aio.run(c.execute, 'drop table y.demo')
aio.run(c.execute, 'drop blob table blobtable')


def translate(s):
"""
Translate canonical database addresses to match the ones provided by the test layer.
"""
s = s.replace('localhost:4200', node.http_url)
s = s.replace(
'asyncpg://localhost:5432',
f'asyncpg://{node.addresses.psql.host}:{node.addresses.psql.port}')
s = s.replace(
'postgresql://crate@localhost:5432/doc',
f'postgresql://crate@{node.addresses.psql.host}:{node.addresses.psql.port}/doc')
return s


def transform(s):
"""
Transform all commands parsed from doctests.
"""
s = translate(s)
return (
r'print(sh("""%s""").stdout.decode("utf-8"))' % s) + '\n'
16 changes: 13 additions & 3 deletions tests/test_insert_fake_data.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import sys
import unittest

from cr8.insert_fake_data import DataFaker, Column
from cr8 import insert_fake_data
from unittest import TestCase, main
Expand Down Expand Up @@ -120,17 +123,24 @@ def test_geopoint_type_default(self):
provider = self.f.provider_for_column(Column('location', 'geo_point', None))
self.assertEqual(provider(), [50.19364744483815, -85.49806405991995])

@unittest.skipIf(sys.platform.startswith("win"), "Has anomaly on Windows")
def test_geoshape_type_default(self):
"""
FIXME
This test has an anomaly on Windows where the longitude
36.10457754779138 will be returned as 36.104577547791386,
i.e. with one more precision digit. Why is that?
"""
provider = self.f.provider_for_column(Column('area', 'geo_shape', None))
self.assertEqual(provider(),
'POLYGON (( '
self.assertEqual('POLYGON (( '
'-132.47048275355667 44.147296981090086, '
'-131.2679223792111 42.09369742374501, '
'-132.14018682738413 37.17894586552094, '
'-133.04540290479406 36.10457754779138, '
'-142.31051949147854 46.75961787621673, '
'-132.47048275355667 44.147296981090086 '
'))')
'))',
provider())

def test_invalid_provider_for_column(self):
msg = 'No fake provider found for column "x" with type "y"'
Expand Down
64 changes: 22 additions & 42 deletions tests/test_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,54 +2,20 @@
import doctest
import subprocess
import functools
from unittest import TestCase
import sys
import unittest

from cr8.run_crate import CrateNode, get_crate
from cr8.clients import client
from cr8 import aio
from cr8.run_crate import get_crate
from tests.integration_util import teardown, node, setup, transform


crate_dir = get_crate('latest-testing')
node = CrateNode(
crate_dir=crate_dir,
settings={
'cluster.name': 'cr8-tests',
'http.port': '44200-44250'
})


def setup(*args):
with client(node.http_url) as c:
aio.run(
c.execute,
'create table x.demo (id int, name string, country string) \
with (number_of_replicas = 0)'
)
aio.run(c.execute, 'create table y.demo (name text) with (number_of_replicas = 0)')
aio.run(c.execute, 'create blob table blobtable with (number_of_replicas = 0)')


def teardown(*args):
def final_teardown(*args):
try:
with client(node.http_url) as c:
aio.run(c.execute, 'drop table x.demo')
aio.run(c.execute, 'drop blob table blobtable')
teardown()
finally:
node.stop()


def transform(s):
s = s.replace('localhost:4200', node.http_url)
s = s.replace(
'asyncpg://localhost:5432',
f'asyncpg://{node.addresses.psql.host}:{node.addresses.psql.port}')
s = s.replace(
'postgresql://crate@localhost:5432/doc',
f'postgresql://crate@{node.addresses.psql.host}:{node.addresses.psql.port}/doc')
return (
r'print(sh("""%s""").stdout.decode("utf-8"))' % s) + '\n'


class Parser(doctest.DocTestParser):

def parse(self, string, name='<string>'):
Expand All @@ -60,17 +26,31 @@ def parse(self, string, name='<string>'):
return r


class SourceBuildTest(TestCase):
@unittest.skipIf(sys.platform.startswith("win"), "Not supported on Windows")
class SourceBuildTest(unittest.TestCase):

def test_build_from_branch(self):
self.assertIsNotNone(get_crate('4.1'))


def load_tests(loader, tests, ignore):
"""
Intercept test discovery, in order to add doctests from `README.rst`.
"""

# FIXME: doctests have errors on Windows.
if sys.platform.startswith("win"):
return tests

# Parsing doctests happens early, way before the test suite is invoked.
# However, the doctest translator needs to know about the TCP address
# of CrateDB, so it needs to be started right away.
env = os.environ.copy()
env['CR8_NO_TQDM'] = 'True'
node.start()
assert node.http_host, "http_url must be available"

# Add integration tests defined as doctests in README.rst.
tests.addTests(doctest.DocFileSuite(
os.path.join('..', 'README.rst'),
globs={
Expand All @@ -86,7 +66,7 @@ def load_tests(loader, tests, ignore):
},
optionflags=doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS,
setUp=setup,
tearDown=teardown,
tearDown=final_teardown,
parser=Parser()
))
return tests
Expand Down
Loading
Loading