mirror of
https://github.com/neondatabase/neon.git
synced 2026-05-26 17:40:37 +00:00
Merge pull request #219 from zenithdb/tidy-up-tests
Tidy up pytest-based tests
This commit is contained in:
@@ -8,6 +8,8 @@ pytest = ">=6.0.0"
|
||||
psycopg2 = "*"
|
||||
|
||||
[dev-packages]
|
||||
yapf = "*"
|
||||
flake8 = "*"
|
||||
|
||||
[requires]
|
||||
# we need at least 3.6, but pipenv doesn't allow to say this directly
|
||||
|
||||
50
test_runner/Pipfile.lock
generated
50
test_runner/Pipfile.lock
generated
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "79fd1b57f1f9eb92a446948e6658880cd0a0f64ab40dd6b38986e72db3007325"
|
||||
"sha256": "c0de1f00310b584a272dda58386939d53098e49d14db518804a9e56269bfec1d"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {
|
||||
@@ -21,6 +21,7 @@
|
||||
"sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1",
|
||||
"sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
|
||||
"version": "==21.2.0"
|
||||
},
|
||||
"iniconfig": {
|
||||
@@ -35,6 +36,7 @@
|
||||
"sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5",
|
||||
"sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==20.9"
|
||||
},
|
||||
"pluggy": {
|
||||
@@ -42,6 +44,7 @@
|
||||
"sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0",
|
||||
"sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==0.13.1"
|
||||
},
|
||||
"psycopg2": {
|
||||
@@ -70,6 +73,7 @@
|
||||
"sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3",
|
||||
"sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==1.10.0"
|
||||
},
|
||||
"pyparsing": {
|
||||
@@ -77,6 +81,7 @@
|
||||
"sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1",
|
||||
"sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"
|
||||
],
|
||||
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==2.4.7"
|
||||
},
|
||||
"pytest": {
|
||||
@@ -92,8 +97,49 @@
|
||||
"sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b",
|
||||
"sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"
|
||||
],
|
||||
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==0.10.2"
|
||||
}
|
||||
},
|
||||
"develop": {}
|
||||
"develop": {
|
||||
"flake8": {
|
||||
"hashes": [
|
||||
"sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b",
|
||||
"sha256:bf8fd333346d844f616e8d47905ef3a3384edae6b4e9beb0c5101e25e3110907"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==3.9.2"
|
||||
},
|
||||
"mccabe": {
|
||||
"hashes": [
|
||||
"sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42",
|
||||
"sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"
|
||||
],
|
||||
"version": "==0.6.1"
|
||||
},
|
||||
"pycodestyle": {
|
||||
"hashes": [
|
||||
"sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068",
|
||||
"sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==2.7.0"
|
||||
},
|
||||
"pyflakes": {
|
||||
"hashes": [
|
||||
"sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3",
|
||||
"sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db"
|
||||
],
|
||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
|
||||
"version": "==2.3.1"
|
||||
},
|
||||
"yapf": {
|
||||
"hashes": [
|
||||
"sha256:408fb9a2b254c302f49db83c59f9aa0b4b0fd0ec25be3a5c51181327922ff63d",
|
||||
"sha256:e3a234ba8455fe201eaa649cdac872d590089a18b661e39bbac7020978dd9c2e"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.31.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,7 +69,8 @@ The tests make heavy use of pytest fixtures. You can read about how they work he
|
||||
Essentially, this means that each time you see a fixture named as an input parameter, the function with that name will be run and passed as a parameter to the function.
|
||||
|
||||
So this code:
|
||||
```
|
||||
|
||||
```python
|
||||
def test_something(zenith_cli, pg_bin):
|
||||
pass
|
||||
```
|
||||
@@ -77,9 +78,11 @@ def test_something(zenith_cli, pg_bin):
|
||||
... will run the fixtures called `zenith_cli` and `pg_bin` and deliver those results to the test function.
|
||||
|
||||
Fixtures can't be imported using the normal python syntax. Instead, use this:
|
||||
```
|
||||
|
||||
```python
|
||||
pytest_plugins = ("fixtures.something")
|
||||
```
|
||||
|
||||
That will make all the fixtures in the `fixtures/something.py` file available.
|
||||
|
||||
Anything that's likely to be used in multiple tests should be built into a fixture.
|
||||
@@ -87,3 +90,14 @@ Anything that's likely to be used in multiple tests should be built into a fixtu
|
||||
Note that fixtures can clean up after themselves if they use the `yield` syntax.
|
||||
Cleanup will happen even if the test fails (raises an unhandled exception).
|
||||
Python destructors, e.g. `__del__()` aren't recommended for cleanup.
|
||||
|
||||
|
||||
### Code quality
|
||||
|
||||
Before submitting a patch, please consider:
|
||||
|
||||
* Writing a couple of docstrings to clarify the reasoning behind a new test.
|
||||
* Running `flake8` (or a linter of your choice, e.g. `pycodestyle`) and fixing possible defects, if any.
|
||||
* Formatting the code with `yapf -r -i .` (TODO: implement an opt-in pre-commit hook for that).
|
||||
|
||||
The tools can be installed with `pipenv install --dev`.
|
||||
|
||||
@@ -1,49 +1,60 @@
|
||||
import pytest
|
||||
import getpass
|
||||
import psycopg2
|
||||
|
||||
pytest_plugins = ("fixtures.zenith_fixtures")
|
||||
|
||||
|
||||
#
|
||||
# Create a couple of branches off the main branch, at a historical point in time.
|
||||
#
|
||||
def test_branch_behind(zenith_cli, pageserver, postgres, pg_bin):
|
||||
# Branch at the point where only 100 rows were inserted
|
||||
zenith_cli.run(["branch", "test_branch_behind", "empty"]);
|
||||
zenith_cli.run(["branch", "test_branch_behind", "empty"])
|
||||
|
||||
pgmain = postgres.create_start('test_branch_behind')
|
||||
print("postgres is running on 'test_branch_behind' branch")
|
||||
|
||||
main_pg_conn = psycopg2.connect(pgmain.connstr());
|
||||
main_pg_conn = psycopg2.connect(pgmain.connstr())
|
||||
main_pg_conn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT)
|
||||
main_cur = main_pg_conn.cursor()
|
||||
|
||||
# Create table, and insert the first 100 rows
|
||||
main_cur.execute('CREATE TABLE foo (t text)');
|
||||
main_cur.execute("INSERT INTO foo SELECT 'long string to consume some space' || g FROM generate_series(1, 100) g");
|
||||
main_cur.execute('SELECT pg_current_wal_insert_lsn()');
|
||||
main_cur.execute('CREATE TABLE foo (t text)')
|
||||
main_cur.execute('''
|
||||
INSERT INTO foo
|
||||
SELECT 'long string to consume some space' || g
|
||||
FROM generate_series(1, 100) g
|
||||
''')
|
||||
main_cur.execute('SELECT pg_current_wal_insert_lsn()')
|
||||
lsn_a = main_cur.fetchone()[0]
|
||||
print('LSN after 100 rows: ' + lsn_a)
|
||||
|
||||
# Insert some more rows. (This generates enough WAL to fill a few segments.)
|
||||
main_cur.execute("INSERT INTO foo SELECT 'long string to consume some space' || g FROM generate_series(1, 100000) g");
|
||||
main_cur.execute('SELECT pg_current_wal_insert_lsn()');
|
||||
main_cur.execute('''
|
||||
INSERT INTO foo
|
||||
SELECT 'long string to consume some space' || g
|
||||
FROM generate_series(1, 100000) g
|
||||
''')
|
||||
main_cur.execute('SELECT pg_current_wal_insert_lsn()')
|
||||
lsn_b = main_cur.fetchone()[0]
|
||||
print('LSN after 100100 rows: ' + lsn_b)
|
||||
|
||||
# Branch at the point where only 100 rows were inserted
|
||||
zenith_cli.run(["branch", "test_branch_behind_hundred", "test_branch_behind@"+lsn_a]);
|
||||
zenith_cli.run(["branch", "test_branch_behind_hundred", "test_branch_behind@" + lsn_a])
|
||||
|
||||
# Insert many more rows. This generates enough WAL to fill a few segments.
|
||||
main_cur.execute("INSERT INTO foo SELECT 'long string to consume some space' || g FROM generate_series(1, 100000) g");
|
||||
main_cur.execute('SELECT pg_current_wal_insert_lsn()');
|
||||
main_cur.execute('''
|
||||
INSERT INTO foo
|
||||
SELECT 'long string to consume some space' || g
|
||||
FROM generate_series(1, 100000) g
|
||||
''')
|
||||
main_cur.execute('SELECT pg_current_wal_insert_lsn()')
|
||||
|
||||
main_cur.execute('SELECT pg_current_wal_insert_lsn()');
|
||||
main_cur.execute('SELECT pg_current_wal_insert_lsn()')
|
||||
lsn_c = main_cur.fetchone()[0]
|
||||
print('LSN after 200100 rows: ' + lsn_c)
|
||||
|
||||
# Branch at the point where only 200 rows were inserted
|
||||
zenith_cli.run(["branch", "test_branch_behind_more", "test_branch_behind@"+lsn_b]);
|
||||
zenith_cli.run(["branch", "test_branch_behind_more", "test_branch_behind@" + lsn_b])
|
||||
|
||||
pg_hundred = postgres.create_start("test_branch_behind_hundred")
|
||||
pg_more = postgres.create_start("test_branch_behind_more")
|
||||
@@ -52,16 +63,16 @@ def test_branch_behind(zenith_cli, pageserver, postgres, pg_bin):
|
||||
hundred_pg_conn = psycopg2.connect(pg_hundred.connstr())
|
||||
hundred_pg_conn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT)
|
||||
hundred_cur = hundred_pg_conn.cursor()
|
||||
hundred_cur.execute('SELECT count(*) FROM foo');
|
||||
assert(hundred_cur.fetchone()[0] == 100);
|
||||
hundred_cur.execute('SELECT count(*) FROM foo')
|
||||
assert hundred_cur.fetchone() == (100, )
|
||||
|
||||
# On the 'more' branch, we should see 100200 rows
|
||||
more_pg_conn = psycopg2.connect(pg_more.connstr())
|
||||
more_pg_conn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT)
|
||||
more_cur = more_pg_conn.cursor()
|
||||
more_cur.execute('SELECT count(*) FROM foo');
|
||||
assert(more_cur.fetchone()[0] == 100100);
|
||||
more_cur.execute('SELECT count(*) FROM foo')
|
||||
assert more_cur.fetchone() == (100100, )
|
||||
|
||||
# All the rows are visible on the main branch
|
||||
main_cur.execute('SELECT count(*) FROM foo');
|
||||
assert(main_cur.fetchone()[0] == 200100);
|
||||
main_cur.execute('SELECT count(*) FROM foo')
|
||||
assert main_cur.fetchone() == (200100, )
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
import pytest
|
||||
import os
|
||||
import getpass
|
||||
import psycopg2
|
||||
|
||||
pytest_plugins = ("fixtures.zenith_fixtures")
|
||||
@@ -11,20 +8,24 @@ pytest_plugins = ("fixtures.zenith_fixtures")
|
||||
#
|
||||
def test_config(zenith_cli, pageserver, postgres, pg_bin):
|
||||
# Create a branch for us
|
||||
zenith_cli.run(["branch", "test_config", "empty"]);
|
||||
zenith_cli.run(["branch", "test_config", "empty"])
|
||||
|
||||
# change config
|
||||
pg = postgres.create_start('test_config', ['log_min_messages=debug1'])
|
||||
print('postgres is running on test_config branch')
|
||||
|
||||
pg_conn = psycopg2.connect(pg.connstr())
|
||||
pg_conn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT)
|
||||
cur = pg_conn.cursor()
|
||||
with psycopg2.connect(pg.connstr()) as conn:
|
||||
conn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT)
|
||||
|
||||
#check that config change was applied
|
||||
cur.execute('SELECT name, setting from pg_settings WHERE source!=%s and source!=%s', ("default","override",))
|
||||
for record in cur:
|
||||
if record[0] == 'log_min_messages':
|
||||
assert(record[1] == 'debug1')
|
||||
with conn.cursor() as cur:
|
||||
cur.execute('''
|
||||
SELECT setting
|
||||
FROM pg_settings
|
||||
WHERE
|
||||
source != 'default'
|
||||
AND source != 'override'
|
||||
AND name = 'log_min_messages'
|
||||
''')
|
||||
|
||||
pg_conn.close()
|
||||
# check that config change was applied
|
||||
assert cur.fetchone() == ('debug1', )
|
||||
|
||||
@@ -1,37 +1,34 @@
|
||||
import pytest
|
||||
import getpass
|
||||
import psycopg2
|
||||
|
||||
pytest_plugins = ("fixtures.zenith_fixtures")
|
||||
|
||||
|
||||
#
|
||||
# Test CREATE DATABASE when there have been relmapper changes
|
||||
#
|
||||
def test_createdb(zenith_cli, pageserver, postgres, pg_bin):
|
||||
zenith_cli.run(["branch", "test_createdb", "empty"]);
|
||||
zenith_cli.run(["branch", "test_createdb", "empty"])
|
||||
|
||||
pg = postgres.create_start('test_createdb')
|
||||
print("postgres is running on 'test_createdb' branch")
|
||||
|
||||
conn = psycopg2.connect(pg.connstr());
|
||||
conn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT)
|
||||
cur = conn.cursor()
|
||||
with psycopg2.connect(pg.connstr()) as conn:
|
||||
conn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT)
|
||||
|
||||
# Cause a 'relmapper' change in the original branch
|
||||
cur.execute('VACUUM FULL pg_class');
|
||||
with conn.cursor() as cur:
|
||||
# Cause a 'relmapper' change in the original branch
|
||||
cur.execute('VACUUM FULL pg_class')
|
||||
|
||||
cur.execute('CREATE DATABASE foodb');
|
||||
cur.execute('CREATE DATABASE foodb')
|
||||
|
||||
cur.execute('SELECT pg_current_wal_insert_lsn()');
|
||||
lsn = cur.fetchone()[0]
|
||||
|
||||
conn.close();
|
||||
cur.execute('SELECT pg_current_wal_insert_lsn()')
|
||||
lsn = cur.fetchone()[0]
|
||||
|
||||
# Create a branch
|
||||
zenith_cli.run(["branch", "test_createdb2", "test_createdb@"+lsn]);
|
||||
zenith_cli.run(["branch", "test_createdb2", "test_createdb@" + lsn])
|
||||
|
||||
pg2 = postgres.create_start('test_createdb2')
|
||||
|
||||
# Test that you can connect to the new database on both branches
|
||||
conn = psycopg2.connect(pg.connstr('foodb'));
|
||||
conn2 = psycopg2.connect(pg2.connstr('foodb'));
|
||||
for db in (pg, pg2):
|
||||
psycopg2.connect(db.connstr('foodb')).close()
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import pytest
|
||||
import os
|
||||
import psycopg2
|
||||
|
||||
pytest_plugins = ("fixtures.zenith_fixtures")
|
||||
|
||||
|
||||
#
|
||||
# Test multixact state after branching
|
||||
# Now this test is very minimalistic -
|
||||
@@ -11,7 +10,6 @@ pytest_plugins = ("fixtures.zenith_fixtures")
|
||||
# since we don't have functions to check multixact internals.
|
||||
#
|
||||
def test_multixact(pageserver, postgres, pg_bin, zenith_cli, base_dir):
|
||||
|
||||
# Create a branch for us
|
||||
zenith_cli.run(["branch", "test_multixact", "empty"])
|
||||
pg = postgres.create_start('test_multixact')
|
||||
@@ -21,10 +19,12 @@ def test_multixact(pageserver, postgres, pg_bin, zenith_cli, base_dir):
|
||||
pg_conn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT)
|
||||
cur = pg_conn.cursor()
|
||||
|
||||
cur.execute('CREATE TABLE t1(i int primary key);'
|
||||
'INSERT INTO t1 select * from generate_series(1,100);')
|
||||
cur.execute('''
|
||||
CREATE TABLE t1(i int primary key);
|
||||
INSERT INTO t1 select * from generate_series(1, 100);
|
||||
''')
|
||||
|
||||
cur.execute('SELECT next_multixact_id FROM pg_control_checkpoint();')
|
||||
cur.execute('SELECT next_multixact_id FROM pg_control_checkpoint()')
|
||||
next_multixact_id_old = cur.fetchone()[0]
|
||||
|
||||
# Lock entries in parallel connections to set multixact
|
||||
@@ -33,7 +33,7 @@ def test_multixact(pageserver, postgres, pg_bin, zenith_cli, base_dir):
|
||||
for i in range(nclients):
|
||||
con = psycopg2.connect(pg.connstr())
|
||||
# Do not turn on autocommit. We want to hold the key-share locks.
|
||||
con.cursor().execute('select * from t1 for key share;')
|
||||
con.cursor().execute('select * from t1 for key share')
|
||||
connections.append(con)
|
||||
|
||||
# We should have a multixact now. We can close the connections.
|
||||
@@ -43,16 +43,16 @@ def test_multixact(pageserver, postgres, pg_bin, zenith_cli, base_dir):
|
||||
# force wal flush
|
||||
cur.execute('checkpoint')
|
||||
|
||||
cur.execute('SELECT next_multixact_id, pg_current_wal_flush_lsn() FROM pg_control_checkpoint();')
|
||||
cur.execute('SELECT next_multixact_id, pg_current_wal_flush_lsn() FROM pg_control_checkpoint()')
|
||||
res = cur.fetchone()
|
||||
next_multixact_id = res[0]
|
||||
lsn = res[1]
|
||||
|
||||
# Ensure that we did lock some tuples
|
||||
assert(int(next_multixact_id) > int(next_multixact_id_old))
|
||||
assert int(next_multixact_id) > int(next_multixact_id_old)
|
||||
|
||||
# Branch at this point
|
||||
zenith_cli.run(["branch", "test_multixact_new", "test_multixact@"+lsn]);
|
||||
zenith_cli.run(["branch", "test_multixact_new", "test_multixact@" + lsn])
|
||||
pg_new = postgres.create_start('test_multixact_new')
|
||||
|
||||
print("postgres is running on 'test_multixact_new' branch")
|
||||
@@ -60,8 +60,8 @@ def test_multixact(pageserver, postgres, pg_bin, zenith_cli, base_dir):
|
||||
pg_new_conn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT)
|
||||
cur_new = pg_new_conn.cursor()
|
||||
|
||||
cur_new.execute('SELECT next_multixact_id FROM pg_control_checkpoint();')
|
||||
cur_new.execute('SELECT next_multixact_id FROM pg_control_checkpoint()')
|
||||
next_multixact_id_new = cur_new.fetchone()[0]
|
||||
|
||||
# Check that we restored pg_controlfile correctly
|
||||
assert(next_multixact_id_new == next_multixact_id)
|
||||
assert next_multixact_id_new == next_multixact_id
|
||||
|
||||
@@ -1,28 +1,27 @@
|
||||
import pytest
|
||||
import psycopg2
|
||||
import getpass
|
||||
import json
|
||||
|
||||
pytest_plugins = ("fixtures.zenith_fixtures")
|
||||
|
||||
|
||||
def test_status(pageserver):
|
||||
pg_conn = psycopg2.connect(pageserver.connstr())
|
||||
pg_conn.autocommit = True
|
||||
cur = pg_conn.cursor()
|
||||
cur.execute('status;')
|
||||
assert cur.fetchone() == ('hello world',)
|
||||
cur.execute('status')
|
||||
assert cur.fetchone() == ('hello world', )
|
||||
pg_conn.close()
|
||||
|
||||
def test_branch_list(pageserver, zenith_cli):
|
||||
|
||||
def test_branch_list(pageserver, zenith_cli):
|
||||
# Create a branch for us
|
||||
zenith_cli.run(["branch", "test_branch_list_main", "empty"]);
|
||||
zenith_cli.run(["branch", "test_branch_list_main", "empty"])
|
||||
|
||||
page_server_conn = psycopg2.connect(pageserver.connstr())
|
||||
page_server_conn.autocommit = True
|
||||
page_server_cur = page_server_conn.cursor()
|
||||
|
||||
page_server_cur.execute('branch_list;')
|
||||
page_server_cur.execute('branch_list')
|
||||
branches = json.loads(page_server_cur.fetchone()[0])
|
||||
# Filter out branches created by other tests
|
||||
branches = [x for x in branches if x['name'].startswith('test_branch_list')]
|
||||
@@ -38,7 +37,7 @@ def test_branch_list(pageserver, zenith_cli):
|
||||
zenith_cli.run(['branch', 'test_branch_list_experimental', 'test_branch_list_main'])
|
||||
zenith_cli.run(['pg', 'create', 'test_branch_list_experimental'])
|
||||
|
||||
page_server_cur.execute('branch_list;')
|
||||
page_server_cur.execute('branch_list')
|
||||
new_branches = json.loads(page_server_cur.fetchone()[0])
|
||||
# Filter out branches created by other tests
|
||||
new_branches = [x for x in new_branches if x['name'].startswith('test_branch_list')]
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
import pytest
|
||||
|
||||
pytest_plugins = ("fixtures.zenith_fixtures")
|
||||
|
||||
|
||||
def test_pgbench(pageserver, postgres, pg_bin, zenith_cli):
|
||||
|
||||
# Create a branch for us
|
||||
zenith_cli.run(["branch", "test_pgbench", "empty"]);
|
||||
zenith_cli.run(["branch", "test_pgbench", "empty"])
|
||||
|
||||
pg = postgres.create_start('test_pgbench')
|
||||
print("postgres is running on 'test_pgbench' branch")
|
||||
|
||||
connstr = pg.connstr();
|
||||
connstr = pg.connstr()
|
||||
|
||||
pg_bin.run_capture(['pgbench', '-i', connstr])
|
||||
pg_bin.run_capture(['pgbench'] + '-c 10 -T 5 -P 1 -M prepared'.split() + [connstr])
|
||||
|
||||
@@ -1,43 +1,42 @@
|
||||
import pytest
|
||||
import getpass
|
||||
import psycopg2
|
||||
import time
|
||||
|
||||
pytest_plugins = ("fixtures.zenith_fixtures")
|
||||
|
||||
|
||||
#
|
||||
# Test restarting and recreating a postgres instance
|
||||
#
|
||||
def test_restart_compute(zenith_cli, pageserver, postgres, pg_bin):
|
||||
zenith_cli.run(["branch", "test_restart_compute", "empty"]);
|
||||
zenith_cli.run(["branch", "test_restart_compute", "empty"])
|
||||
|
||||
pg = postgres.create_start('test_restart_compute')
|
||||
print("postgres is running on 'test_restart_compute' branch")
|
||||
|
||||
pg_conn = psycopg2.connect(pg.connstr());
|
||||
pg_conn = psycopg2.connect(pg.connstr())
|
||||
pg_conn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT)
|
||||
cur = pg_conn.cursor()
|
||||
|
||||
# Create table, and insert a row
|
||||
cur.execute('CREATE TABLE foo (t text)');
|
||||
cur.execute("INSERT INTO foo VALUES ('bar')");
|
||||
cur.execute('CREATE TABLE foo (t text)')
|
||||
cur.execute("INSERT INTO foo VALUES ('bar')")
|
||||
|
||||
# Stop and restart the Postgres instance
|
||||
pg_conn.close();
|
||||
pg.stop();
|
||||
pg.start();
|
||||
pg_conn = psycopg2.connect(pg.connstr());
|
||||
pg_conn.close()
|
||||
pg.stop()
|
||||
pg.start()
|
||||
pg_conn = psycopg2.connect(pg.connstr())
|
||||
pg_conn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT)
|
||||
cur = pg_conn.cursor()
|
||||
|
||||
# We can still see the row
|
||||
cur.execute('SELECT count(*) FROM foo');
|
||||
assert(cur.fetchone()[0] == 1);
|
||||
cur.execute('SELECT count(*) FROM foo')
|
||||
assert cur.fetchone() == (1, )
|
||||
|
||||
# Insert another row
|
||||
cur.execute("INSERT INTO foo VALUES ('bar2')");
|
||||
cur.execute('SELECT count(*) FROM foo');
|
||||
assert(cur.fetchone()[0] == 2);
|
||||
cur.execute("INSERT INTO foo VALUES ('bar2')")
|
||||
cur.execute('SELECT count(*) FROM foo')
|
||||
assert cur.fetchone() == (2, )
|
||||
|
||||
# FIXME: Currently, there is no guarantee that by the time the INSERT commits, the WAL
|
||||
# has been streamed safely to the WAL safekeeper or page server. It is merely stored
|
||||
@@ -47,13 +46,13 @@ def test_restart_compute(zenith_cli, pageserver, postgres, pg_bin):
|
||||
time.sleep(5)
|
||||
|
||||
# Stop, and destroy the Postgres instance. Then recreate and restart it.
|
||||
pg_conn.close();
|
||||
pg.stop_and_destroy();
|
||||
pg.create_start('test_restart_compute');
|
||||
pg_conn = psycopg2.connect(pg.connstr());
|
||||
pg_conn.close()
|
||||
pg.stop_and_destroy()
|
||||
pg.create_start('test_restart_compute')
|
||||
pg_conn = psycopg2.connect(pg.connstr())
|
||||
pg_conn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT)
|
||||
cur = pg_conn.cursor()
|
||||
|
||||
# We can still see the rows
|
||||
cur.execute('SELECT count(*) FROM foo');
|
||||
assert(cur.fetchone()[0] == 2);
|
||||
cur.execute('SELECT count(*) FROM foo')
|
||||
assert cur.fetchone() == (2, )
|
||||
|
||||
@@ -1,50 +1,49 @@
|
||||
#
|
||||
# Test branching, when a transaction is in prepared state
|
||||
#
|
||||
import pytest
|
||||
import getpass
|
||||
import psycopg2
|
||||
|
||||
pytest_plugins = ("fixtures.zenith_fixtures")
|
||||
|
||||
|
||||
#
|
||||
# Test branching, when a transaction is in prepared state
|
||||
#
|
||||
def test_twophase(zenith_cli, pageserver, postgres, pg_bin):
|
||||
zenith_cli.run(["branch", "test_twophase", "empty"]);
|
||||
zenith_cli.run(["branch", "test_twophase", "empty"])
|
||||
|
||||
pg = postgres.create_start('test_twophase', ['max_prepared_transactions=5'])
|
||||
print("postgres is running on 'test_twophase' branch")
|
||||
|
||||
conn = psycopg2.connect(pg.connstr());
|
||||
conn = psycopg2.connect(pg.connstr())
|
||||
conn.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT)
|
||||
cur = conn.cursor()
|
||||
|
||||
cur.execute('CREATE TABLE foo (t text)');
|
||||
cur.execute('CREATE TABLE foo (t text)')
|
||||
|
||||
# Prepare a transaction that will insert a row
|
||||
cur.execute('BEGIN');
|
||||
cur.execute("INSERT INTO foo VALUES ('one')");
|
||||
cur.execute("PREPARE TRANSACTION 'insert_one'");
|
||||
cur.execute('BEGIN')
|
||||
cur.execute("INSERT INTO foo VALUES ('one')")
|
||||
cur.execute("PREPARE TRANSACTION 'insert_one'")
|
||||
|
||||
# Prepare another transaction that will insert a row
|
||||
cur.execute('BEGIN');
|
||||
cur.execute("INSERT INTO foo VALUES ('two')");
|
||||
cur.execute("PREPARE TRANSACTION 'insert_two'");
|
||||
cur.execute('BEGIN')
|
||||
cur.execute("INSERT INTO foo VALUES ('two')")
|
||||
cur.execute("PREPARE TRANSACTION 'insert_two'")
|
||||
|
||||
# Create a branch with the transaction in prepared state
|
||||
zenith_cli.run(["branch", "test_twophase_prepared", "test_twophase"]);
|
||||
zenith_cli.run(["branch", "test_twophase_prepared", "test_twophase"])
|
||||
|
||||
pg2 = postgres.create_start('test_twophase_prepared', ['max_prepared_transactions=5'])
|
||||
conn2 = psycopg2.connect(pg2.connstr());
|
||||
conn2 = psycopg2.connect(pg2.connstr())
|
||||
conn2.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT)
|
||||
cur2 = conn2.cursor()
|
||||
|
||||
# On the new branch, commit one of the prepared transactions, abort the other one.
|
||||
cur2.execute("COMMIT PREPARED 'insert_one'");
|
||||
cur2.execute("ROLLBACK PREPARED 'insert_two'");
|
||||
cur2.execute("COMMIT PREPARED 'insert_one'")
|
||||
cur2.execute("ROLLBACK PREPARED 'insert_two'")
|
||||
|
||||
cur2.execute('SELECT * FROM foo');
|
||||
assert(cur2.fetchall() == [('one',)]);
|
||||
cur2.execute('SELECT * FROM foo')
|
||||
assert cur2.fetchall() == [('one', )]
|
||||
|
||||
# Neither insert is visible on the original branch, the transactions are still
|
||||
# in prepared state there.
|
||||
cur.execute('SELECT * FROM foo');
|
||||
assert(cur.fetchall() == []);
|
||||
cur.execute('SELECT * FROM foo')
|
||||
assert cur.fetchall() == []
|
||||
|
||||
@@ -1,25 +1,26 @@
|
||||
import pytest
|
||||
import psycopg2
|
||||
import json
|
||||
|
||||
pytest_plugins = ("fixtures.zenith_fixtures")
|
||||
|
||||
|
||||
def helper_compare_branch_list(page_server_cur, zenith_cli):
|
||||
"""
|
||||
Compare branches list returned by CLI and directly via API.
|
||||
Filters out branches created by other tests.
|
||||
"""
|
||||
|
||||
page_server_cur.execute('branch_list;')
|
||||
page_server_cur.execute('branch_list')
|
||||
branches_api = sorted(map(lambda b: b['name'], json.loads(page_server_cur.fetchone()[0])))
|
||||
branches_api = [b for b in branches_api if b.startswith('test_cli_') or b in ('empty', 'main')]
|
||||
|
||||
res = zenith_cli.run(["branch"]);
|
||||
assert(res.stderr == '')
|
||||
res = zenith_cli.run(["branch"])
|
||||
assert res.stderr == ''
|
||||
branches_cli = sorted(map(lambda b: b.split(':')[-1].strip(), res.stdout.strip().split("\n")))
|
||||
branches_cli = [b for b in branches_cli if b.startswith('test_cli_') or b in ('empty', 'main')]
|
||||
|
||||
assert(branches_api == branches_cli)
|
||||
assert branches_api == branches_cli
|
||||
|
||||
|
||||
def test_cli_branch_list(pageserver, zenith_cli):
|
||||
|
||||
@@ -31,19 +32,19 @@ def test_cli_branch_list(pageserver, zenith_cli):
|
||||
helper_compare_branch_list(page_server_cur, zenith_cli)
|
||||
|
||||
# Create a branch for us
|
||||
res = zenith_cli.run(["branch", "test_cli_branch_list_main", "main"]);
|
||||
assert(res.stderr == '')
|
||||
res = zenith_cli.run(["branch", "test_cli_branch_list_main", "main"])
|
||||
assert res.stderr == ''
|
||||
helper_compare_branch_list(page_server_cur, zenith_cli)
|
||||
|
||||
# Create a nested branch
|
||||
res = zenith_cli.run(["branch", "test_cli_branch_list_nested", "test_cli_branch_list_main"]);
|
||||
assert(res.stderr == '')
|
||||
res = zenith_cli.run(["branch", "test_cli_branch_list_nested", "test_cli_branch_list_main"])
|
||||
assert res.stderr == ''
|
||||
helper_compare_branch_list(page_server_cur, zenith_cli)
|
||||
|
||||
# Check that all new branches are visible via CLI
|
||||
res = zenith_cli.run(["branch"]);
|
||||
assert(res.stderr == '')
|
||||
res = zenith_cli.run(["branch"])
|
||||
assert res.stderr == ''
|
||||
branches_cli = sorted(map(lambda b: b.split(':')[-1].strip(), res.stdout.strip().split("\n")))
|
||||
|
||||
assert('test_cli_branch_list_main' in branches_cli)
|
||||
assert('test_cli_branch_list_nested' in branches_cli)
|
||||
assert 'test_cli_branch_list_main' in branches_cli
|
||||
assert 'test_cli_branch_list_nested' in branches_cli
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import pytest
|
||||
from fixtures.utils import mkdir_if_needed
|
||||
import getpass
|
||||
import os
|
||||
import psycopg2
|
||||
|
||||
from fixtures.utils import mkdir_if_needed
|
||||
|
||||
pytest_plugins = ("fixtures.zenith_fixtures")
|
||||
|
||||
|
||||
def test_isolation(pageserver, postgres, pg_bin, zenith_cli, test_output_dir, pg_distrib_dir, base_dir, capsys):
|
||||
def test_isolation(pageserver, postgres, pg_bin, zenith_cli, test_output_dir, pg_distrib_dir,
|
||||
base_dir, capsys):
|
||||
|
||||
# Create a branch for us
|
||||
zenith_cli.run(["branch", "test_isolation", "empty"]);
|
||||
zenith_cli.run(["branch", "test_isolation", "empty"])
|
||||
|
||||
# Connect to postgres and create a database called "regression".
|
||||
# isolation tests use prepared transactions, so enable them
|
||||
@@ -27,10 +27,8 @@ def test_isolation(pageserver, postgres, pg_bin, zenith_cli, test_output_dir, pg
|
||||
mkdir_if_needed(os.path.join(runpath, 'testtablespace'))
|
||||
|
||||
# Compute all the file locations that pg_isolation_regress will need.
|
||||
build_path = os.path.join(
|
||||
pg_distrib_dir, 'build/src/test/isolation')
|
||||
src_path = os.path.join(
|
||||
base_dir, 'vendor/postgres/src/test/isolation')
|
||||
build_path = os.path.join(pg_distrib_dir, 'build/src/test/isolation')
|
||||
src_path = os.path.join(base_dir, 'vendor/postgres/src/test/isolation')
|
||||
bindir = os.path.join(pg_distrib_dir, 'bin')
|
||||
schedule = os.path.join(src_path, 'isolation_schedule')
|
||||
pg_isolation_regress = os.path.join(build_path, 'pg_isolation_regress')
|
||||
@@ -41,7 +39,7 @@ def test_isolation(pageserver, postgres, pg_bin, zenith_cli, test_output_dir, pg
|
||||
'--bindir={}'.format(bindir),
|
||||
'--dlpath={}'.format(build_path),
|
||||
'--inputdir={}'.format(src_path),
|
||||
'--schedule={}'.format(schedule)
|
||||
'--schedule={}'.format(schedule),
|
||||
]
|
||||
|
||||
env = {
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
import pytest
|
||||
from fixtures.utils import mkdir_if_needed
|
||||
import getpass
|
||||
import os
|
||||
import psycopg2
|
||||
|
||||
from fixtures.utils import mkdir_if_needed
|
||||
|
||||
pytest_plugins = ("fixtures.zenith_fixtures")
|
||||
|
||||
def test_pg_regress(pageserver, postgres, pg_bin, zenith_cli, test_output_dir, pg_distrib_dir, base_dir, capsys):
|
||||
|
||||
def test_pg_regress(pageserver, postgres, pg_bin, zenith_cli, test_output_dir, pg_distrib_dir,
|
||||
base_dir, capsys):
|
||||
|
||||
# Create a branch for us
|
||||
zenith_cli.run(["branch", "test_pg_regress", "empty"]);
|
||||
zenith_cli.run(["branch", "test_pg_regress", "empty"])
|
||||
|
||||
# Connect to postgres and create a database called "regression".
|
||||
pg = postgres.create_start('test_pg_regress')
|
||||
@@ -25,10 +26,8 @@ def test_pg_regress(pageserver, postgres, pg_bin, zenith_cli, test_output_dir, p
|
||||
mkdir_if_needed(os.path.join(runpath, 'testtablespace'))
|
||||
|
||||
# Compute all the file locations that pg_regress will need.
|
||||
build_path = os.path.join(
|
||||
pg_distrib_dir, 'build/src/test/regress')
|
||||
src_path = os.path.join(
|
||||
base_dir, 'vendor/postgres/src/test/regress')
|
||||
build_path = os.path.join(pg_distrib_dir, 'build/src/test/regress')
|
||||
src_path = os.path.join(base_dir, 'vendor/postgres/src/test/regress')
|
||||
bindir = os.path.join(pg_distrib_dir, 'bin')
|
||||
schedule = os.path.join(src_path, 'parallel_schedule')
|
||||
pg_regress = os.path.join(build_path, 'pg_regress')
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
import pytest
|
||||
from fixtures.utils import mkdir_if_needed
|
||||
import getpass
|
||||
import os
|
||||
import psycopg2
|
||||
|
||||
from fixtures.utils import mkdir_if_needed
|
||||
|
||||
pytest_plugins = ("fixtures.zenith_fixtures")
|
||||
|
||||
def test_zenith_regress(pageserver, postgres, pg_bin, zenith_cli, test_output_dir, pg_distrib_dir, base_dir, capsys):
|
||||
|
||||
def test_zenith_regress(pageserver, postgres, pg_bin, zenith_cli, test_output_dir, pg_distrib_dir,
|
||||
base_dir, capsys):
|
||||
|
||||
# Create a branch for us
|
||||
zenith_cli.run(["branch", "test_zenith_regress", "empty"]);
|
||||
zenith_cli.run(["branch", "test_zenith_regress", "empty"])
|
||||
|
||||
# Connect to postgres and create a database called "regression".
|
||||
pg = postgres.create_start('test_zenith_regress')
|
||||
@@ -26,10 +27,8 @@ def test_zenith_regress(pageserver, postgres, pg_bin, zenith_cli, test_output_di
|
||||
|
||||
# Compute all the file locations that pg_regress will need.
|
||||
# This test runs zenith specific tests
|
||||
build_path = os.path.join(
|
||||
pg_distrib_dir, 'build/src/test/regress')
|
||||
src_path = os.path.join(
|
||||
base_dir, 'test_runner/zenith_regress')
|
||||
build_path = os.path.join(pg_distrib_dir, 'build/src/test/regress')
|
||||
src_path = os.path.join(base_dir, 'test_runner/zenith_regress')
|
||||
bindir = os.path.join(pg_distrib_dir, 'bin')
|
||||
schedule = os.path.join(src_path, 'parallel_schedule')
|
||||
pg_regress = os.path.join(build_path, 'pg_regress')
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
|
||||
def get_self_dir():
|
||||
""" Get the path to the directory where this script lives. """
|
||||
return os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
import getpass
|
||||
import os
|
||||
import psycopg2
|
||||
import pytest
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
from .utils import (get_self_dir, mkdir_if_needed,
|
||||
subprocess_capture, global_counter)
|
||||
|
||||
from .utils import (get_self_dir, mkdir_if_needed, subprocess_capture)
|
||||
"""
|
||||
This file contains pytest fixtures. A fixture is a test resource that can be
|
||||
summoned by placing its name in the test's arguments.
|
||||
@@ -20,7 +17,7 @@ ZENITH_BIN, POSTGRES_DISTRIB_DIR, etc. See README.md for more information.
|
||||
|
||||
To use fixtures in a test file, add this line of code:
|
||||
|
||||
pytest_plugins = ("fixtures.zenith_fixtures")
|
||||
>>> pytest_plugins = ("fixtures.zenith_fixtures")
|
||||
|
||||
Don't import functions from this file, or pytest will emit warnings. Instead
|
||||
put directly-importable functions into utils.py or another separate file.
|
||||
@@ -35,7 +32,8 @@ def determine_scope(fixture_name, config):
|
||||
|
||||
|
||||
def zenfixture(func):
|
||||
""" This is a python decorator for fixtures with a flexible scope.
|
||||
"""
|
||||
This is a python decorator for fixtures with a flexible scope.
|
||||
|
||||
By default every test function will set up and tear down a new
|
||||
database. In pytest, this is called fixtures "function" scope.
|
||||
@@ -43,8 +41,8 @@ def zenfixture(func):
|
||||
If the environment variable TEST_SHARED_FIXTURES is set, then all
|
||||
tests will share the same database. State, logs, etc. will be
|
||||
stored in a directory called "shared".
|
||||
|
||||
"""
|
||||
|
||||
if os.environ.get('TEST_SHARED_FIXTURES') is None:
|
||||
scope = 'function'
|
||||
else:
|
||||
@@ -55,6 +53,7 @@ def zenfixture(func):
|
||||
@pytest.fixture(autouse=True, scope='session')
|
||||
def safety_check():
|
||||
""" Ensure that no unwanted daemons are running before we start testing. """
|
||||
|
||||
# does not use -c as it is not supported on macOS
|
||||
cmd = ['pgrep', 'pageserver|postgres|wal_acceptor']
|
||||
result = subprocess.run(cmd, stdout=subprocess.DEVNULL)
|
||||
@@ -66,12 +65,12 @@ def safety_check():
|
||||
|
||||
|
||||
class ZenithCli:
|
||||
""" An object representing the CLI binary named "zenith".
|
||||
"""
|
||||
An object representing the CLI binary named "zenith".
|
||||
|
||||
We also store an environment that will tell the CLI to operate
|
||||
on a particular ZENITH_REPO_DIR.
|
||||
"""
|
||||
|
||||
def __init__(self, binpath, repo_dir, pg_distrib_dir):
|
||||
assert os.path.isdir(binpath)
|
||||
self.binpath = binpath
|
||||
@@ -81,21 +80,27 @@ class ZenithCli:
|
||||
self.env['POSTGRES_DISTRIB_DIR'] = pg_distrib_dir
|
||||
|
||||
def run(self, arguments):
|
||||
""" Run "zenith" with the specified arguments.
|
||||
"""
|
||||
Run "zenith" with the specified arguments.
|
||||
|
||||
arguments must be in list form, e.g. ['pg', 'create']
|
||||
Arguments must be in list form, e.g. ['pg', 'create']
|
||||
|
||||
Return both stdout and stderr, which can be accessed as
|
||||
|
||||
result = zenith_cli.run(...)
|
||||
assert(result.stderr == "")
|
||||
print(result.stdout)
|
||||
|
||||
>>> result = zenith_cli.run(...)
|
||||
>>> assert result.stderr == ""
|
||||
>>> print(result.stdout)
|
||||
"""
|
||||
|
||||
assert type(arguments) == list
|
||||
args = [self.bin_zenith] + arguments
|
||||
print('Running command "{}"'.format(' '.join(args)))
|
||||
return subprocess.run(args, env=self.env, check=True, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
return subprocess.run(args,
|
||||
env=self.env,
|
||||
check=True,
|
||||
universal_newlines=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
|
||||
|
||||
@zenfixture
|
||||
@@ -105,7 +110,6 @@ def zenith_cli(zenith_binpath, repo_dir, pg_distrib_dir):
|
||||
|
||||
class ZenithPageserver:
|
||||
""" An object representing a running pageserver. """
|
||||
|
||||
def __init__(self, zenith_cli):
|
||||
self.zenith_cli = zenith_cli
|
||||
self.running = False
|
||||
@@ -129,34 +133,37 @@ class ZenithPageserver:
|
||||
# returns a libpq connection string for connecting to it.
|
||||
def connstr(self):
|
||||
username = getpass.getuser()
|
||||
conn_str = 'host={} port={} dbname=postgres user={}'.format(
|
||||
'localhost', 64000, username)
|
||||
conn_str = 'host={} port={} dbname=postgres user={}'.format('localhost', 64000, username)
|
||||
return conn_str
|
||||
|
||||
# The 'pageserver' fixture provides a Page Server that's up and running.
|
||||
#
|
||||
# If TEST_SHARED_FIXTURES is set, the Page Server instance is shared by all
|
||||
# the tests. To avoid clashing with other tests, don't use the 'main' branch in
|
||||
# the tests directly. Instead, create a branch off the 'empty' branch and use
|
||||
# that.
|
||||
#
|
||||
# By convention, the test branches are named after the tests. For example,
|
||||
# test called 'test_foo' would create and use branches with the 'test_foo' prefix.
|
||||
|
||||
@zenfixture
|
||||
def pageserver(zenith_cli):
|
||||
"""
|
||||
The 'pageserver' fixture provides a Page Server that's up and running.
|
||||
|
||||
If TEST_SHARED_FIXTURES is set, the Page Server instance is shared by all
|
||||
the tests. To avoid clashing with other tests, don't use the 'main' branch in
|
||||
the tests directly. Instead, create a branch off the 'empty' branch and use
|
||||
that.
|
||||
|
||||
By convention, the test branches are named after the tests. For example,
|
||||
test called 'test_foo' would create and use branches with the 'test_foo' prefix.
|
||||
"""
|
||||
|
||||
ps = ZenithPageserver(zenith_cli)
|
||||
ps.init()
|
||||
ps.start()
|
||||
# For convenience in tests, create a branch from the freshly-initialized cluster.
|
||||
zenith_cli.run(["branch", "empty", "main"]);
|
||||
zenith_cli.run(["branch", "empty", "main"])
|
||||
yield ps
|
||||
# After the yield comes any cleanup code we need.
|
||||
print('Starting pageserver cleanup')
|
||||
ps.stop()
|
||||
|
||||
|
||||
class Postgres:
|
||||
""" An object representing a running postgres daemon. """
|
||||
|
||||
def __init__(self, zenith_cli, repo_dir, instance_num):
|
||||
self.zenith_cli = zenith_cli
|
||||
self.instance_num = instance_num
|
||||
@@ -169,22 +176,37 @@ class Postgres:
|
||||
# path to conf is <repo_dir>/pgdatadirs/<branch_name>/postgresql.conf
|
||||
|
||||
def create(self, branch, config_lines=None):
|
||||
""" create the pg data directory """
|
||||
"""
|
||||
Create the pg data directory.
|
||||
Returns self.
|
||||
"""
|
||||
|
||||
self.zenith_cli.run(['pg', 'create', branch])
|
||||
self.branch = branch
|
||||
if config_lines is None:
|
||||
config_lines = []
|
||||
self.config(config_lines)
|
||||
return
|
||||
|
||||
return self
|
||||
|
||||
def start(self):
|
||||
""" start the server """
|
||||
"""
|
||||
Start the Postgres instance.
|
||||
Returns self.
|
||||
"""
|
||||
|
||||
self.zenith_cli.run(['pg', 'start', self.branch])
|
||||
self.running = True
|
||||
return
|
||||
|
||||
#lines should be an array of valid postgresql.conf rows
|
||||
return self
|
||||
|
||||
def config(self, lines):
|
||||
"""
|
||||
Add lines to postgresql.conf.
|
||||
Lines should be an array of valid postgresql.conf rows.
|
||||
Returns self.
|
||||
"""
|
||||
|
||||
filename = 'pgdatadirs/{}/postgresql.conf'.format(self.branch)
|
||||
config_name = os.path.join(self.repo_dir, filename)
|
||||
with open(config_name, 'a') as conf:
|
||||
@@ -192,25 +214,50 @@ class Postgres:
|
||||
conf.write(line)
|
||||
conf.write('\n')
|
||||
|
||||
return self
|
||||
|
||||
def stop(self):
|
||||
""" stop the server """
|
||||
"""
|
||||
Stop the Postgres instance if it's running.
|
||||
Returns self.
|
||||
"""
|
||||
|
||||
if self.running:
|
||||
self.zenith_cli.run(['pg', 'stop', self.branch])
|
||||
|
||||
return self
|
||||
|
||||
def stop_and_destroy(self):
|
||||
"""
|
||||
Stop the Postgres instance, then destroy it.
|
||||
Returns self.
|
||||
"""
|
||||
|
||||
self.zenith_cli.run(['pg', 'stop', '--destroy', self.branch])
|
||||
|
||||
def create_start(self, branch, config_lines=None):
|
||||
self.create(branch, config_lines);
|
||||
self.start();
|
||||
return
|
||||
return self
|
||||
|
||||
def create_start(self, branch, config_lines=None):
|
||||
"""
|
||||
Create a Postgres instance, then start it.
|
||||
Returns self.
|
||||
"""
|
||||
|
||||
self.create(branch, config_lines).start()
|
||||
|
||||
return self
|
||||
|
||||
# Return a libpq connection string to connect to the Postgres instance
|
||||
def connstr(self, dbname='postgres'):
|
||||
conn_str = 'host={} port={} dbname={} user={}'.format(
|
||||
self.host, self.port, dbname, self.username)
|
||||
"""
|
||||
Build a libpq connection string for the Postgres instance.
|
||||
"""
|
||||
|
||||
conn_str = 'host={} port={} dbname={} user={}'.format(self.host, self.port, dbname,
|
||||
self.username)
|
||||
|
||||
return conn_str
|
||||
|
||||
|
||||
class PostgresFactory:
|
||||
""" An object representing multiple running postgres daemons. """
|
||||
def __init__(self, zenith_cli, repo_dir):
|
||||
@@ -224,13 +271,13 @@ class PostgresFactory:
|
||||
pg = Postgres(self.zenith_cli, self.repo_dir, self.num_instances + 1)
|
||||
self.num_instances += 1
|
||||
self.instances.append(pg)
|
||||
pg.create_start(branch, config_lines)
|
||||
return pg
|
||||
return pg.create_start(branch, config_lines)
|
||||
|
||||
def stop_all(self):
|
||||
for pg in self.instances:
|
||||
pg.stop()
|
||||
|
||||
|
||||
@zenfixture
|
||||
def postgres(zenith_cli, repo_dir):
|
||||
pgfactory = PostgresFactory(zenith_cli, repo_dir)
|
||||
@@ -242,7 +289,6 @@ def postgres(zenith_cli, repo_dir):
|
||||
|
||||
class PgBin:
|
||||
""" A helper class for executing postgres binaries """
|
||||
|
||||
def __init__(self, log_dir, pg_distrib_dir):
|
||||
self.log_dir = log_dir
|
||||
self.pg_install_path = pg_distrib_dir
|
||||
@@ -251,7 +297,7 @@ class PgBin:
|
||||
self.env['LD_LIBRARY_PATH'] = os.path.join(self.pg_install_path, 'lib')
|
||||
|
||||
def _fixpath(self, command):
|
||||
if not '/' in command[0]:
|
||||
if '/' not in command[0]:
|
||||
command[0] = os.path.join(self.pg_bin_path, command[0])
|
||||
|
||||
def _build_env(self, env_add):
|
||||
@@ -262,7 +308,8 @@ class PgBin:
|
||||
return env
|
||||
|
||||
def run(self, command, env=None, cwd=None):
|
||||
""" Run one of the postgres binaries.
|
||||
"""
|
||||
Run one of the postgres binaries.
|
||||
|
||||
The command should be in list form, e.g. ['pgbench', '-p', '55432']
|
||||
|
||||
@@ -272,18 +319,20 @@ class PgBin:
|
||||
characters present), then it will be edited to include the correct path.
|
||||
|
||||
If you want stdout/stderr captured to files, use `run_capture` instead.
|
||||
|
||||
"""
|
||||
|
||||
self._fixpath(command)
|
||||
print('Running command "{}"'.format(' '.join(command)))
|
||||
env = self._build_env(env)
|
||||
subprocess.run(command, env=env, cwd=cwd, check=True)
|
||||
|
||||
def run_capture(self, command, env=None, cwd=None):
|
||||
""" Run one of the postgres binaries, with stderr and stdout redirected to a file.
|
||||
"""
|
||||
Run one of the postgres binaries, with stderr and stdout redirected to a file.
|
||||
|
||||
This is just like `run`, but for chatty programs.
|
||||
"""
|
||||
|
||||
self._fixpath(command)
|
||||
print('Running command "{}"'.format(' '.join(command)))
|
||||
env = self._build_env(env)
|
||||
@@ -298,6 +347,7 @@ def pg_bin(test_output_dir, pg_distrib_dir):
|
||||
@zenfixture
|
||||
def base_dir():
|
||||
""" find the base directory (currently this is the git root) """
|
||||
|
||||
base_dir = os.path.normpath(os.path.join(get_self_dir(), '../..'))
|
||||
print('base_dir is', base_dir)
|
||||
return base_dir
|
||||
@@ -306,6 +356,7 @@ def base_dir():
|
||||
@zenfixture
|
||||
def top_output_dir(base_dir):
|
||||
""" Compute the top-level directory for all tests. """
|
||||
|
||||
env_test_output = os.environ.get('TEST_OUTPUT')
|
||||
if env_test_output is not None:
|
||||
output_dir = env_test_output
|
||||
@@ -318,6 +369,7 @@ def top_output_dir(base_dir):
|
||||
@zenfixture
|
||||
def test_output_dir(request, top_output_dir):
|
||||
""" Compute the working directory for an individual test. """
|
||||
|
||||
if os.environ.get('TEST_SHARED_FIXTURES') is None:
|
||||
# one directory per test
|
||||
test_name = request.node.name
|
||||
@@ -334,18 +386,21 @@ def test_output_dir(request, top_output_dir):
|
||||
|
||||
@zenfixture
|
||||
def repo_dir(request, test_output_dir):
|
||||
""" Compute the test repo_dir
|
||||
"""
|
||||
Compute the test repo_dir.
|
||||
|
||||
"repo_dir" is the place where all of the pageserver files will go.
|
||||
It doesn't have anything to do with the git repo.
|
||||
"""
|
||||
|
||||
repo_dir = os.path.join(test_output_dir, 'repo')
|
||||
return repo_dir
|
||||
|
||||
|
||||
@zenfixture
|
||||
def zenith_binpath(base_dir):
|
||||
""" find the zenith binaries """
|
||||
""" Find the zenith binaries. """
|
||||
|
||||
env_zenith_bin = os.environ.get('ZENITH_BIN')
|
||||
if env_zenith_bin:
|
||||
zenith_dir = env_zenith_bin
|
||||
@@ -358,7 +413,8 @@ def zenith_binpath(base_dir):
|
||||
|
||||
@zenfixture
|
||||
def pg_distrib_dir(base_dir):
|
||||
""" find the postgress install """
|
||||
""" Find the postgres install. """
|
||||
|
||||
env_postgres_bin = os.environ.get('POSTGRES_DISTRIB_DIR')
|
||||
if env_postgres_bin:
|
||||
pg_dir = env_postgres_bin
|
||||
|
||||
12
test_runner/setup.cfg
Normal file
12
test_runner/setup.cfg
Normal file
@@ -0,0 +1,12 @@
|
||||
# Just trying to gather linter settings in one file.
|
||||
# I wonder if there's a way to de-duplicate them...
|
||||
|
||||
[flake8]
|
||||
max-line-length = 100
|
||||
|
||||
[pycodestyle]
|
||||
max-line-length = 100
|
||||
|
||||
[yapf]
|
||||
based_on_style = pep8
|
||||
column_limit = 100
|
||||
@@ -2,9 +2,7 @@ import pytest
|
||||
import os
|
||||
|
||||
pytest_plugins = ("fixtures.zenith_fixtures")
|
||||
|
||||
"""
|
||||
|
||||
Use this test to see what happens when tests fail.
|
||||
|
||||
We should be able to clean up after ourselves, including stopping any
|
||||
@@ -12,21 +10,18 @@ postgres or pageserver processes.
|
||||
|
||||
Set the environment variable RUN_BROKEN to see this test run (and fail,
|
||||
and hopefully not leave any server processes behind).
|
||||
|
||||
"""
|
||||
|
||||
run_broken = pytest.mark.skipif(os.environ.get('RUN_BROKEN') is None,
|
||||
reason="only used for testing the fixtures")
|
||||
|
||||
run_broken = pytest.mark.skipif(
|
||||
os.environ.get('RUN_BROKEN') == None,
|
||||
reason="only used for testing the fixtures"
|
||||
)
|
||||
|
||||
@run_broken
|
||||
def test_broken(zenith_cli, pageserver, postgres, pg_bin):
|
||||
# Create a branch for us
|
||||
zenith_cli.run(["branch", "test_broken", "empty"]);
|
||||
zenith_cli.run(["branch", "test_broken", "empty"])
|
||||
|
||||
pg = postgres.create_start("test_broken")
|
||||
postgres.create_start("test_broken")
|
||||
print('postgres is running')
|
||||
|
||||
print('THIS NEXT COMMAND WILL FAIL:')
|
||||
|
||||
Reference in New Issue
Block a user