Skip to content

Commit e7d6a08

Browse files
committed
Start working on testgres framework for pg_probackup.
1 parent 6642f27 commit e7d6a08

File tree

8 files changed

+384
-3
lines changed

8 files changed

+384
-3
lines changed

pgut/pgut.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -827,7 +827,7 @@ parse_pair(const char buffer[], char key[], char value[])
827827
if (end - start <= 0)
828828
{
829829
if (*start == '=')
830-
elog(WARNING, "syntax error in \"%s\"", buffer);
830+
elog(ERROR, "syntax error in \"%s\"", buffer);
831831
return false;
832832
}
833833

@@ -841,7 +841,7 @@ parse_pair(const char buffer[], char key[], char value[])
841841

842842
if (*start != '=')
843843
{
844-
elog(WARNING, "syntax error in \"%s\"", buffer);
844+
elog(ERROR, "syntax error in \"%s\"", buffer);
845845
return false;
846846
}
847847

@@ -858,7 +858,7 @@ parse_pair(const char buffer[], char key[], char value[])
858858

859859
if (*start != '\0' && *start != '#')
860860
{
861-
elog(WARNING, "syntax error in \"%s\"", buffer);
861+
elog(ERROR, "syntax error in \"%s\"", buffer);
862862
return false;
863863
}
864864

tests/__init__.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import unittest
2+
3+
from . import init_test, option_test, show_test
4+
5+
6+
def load_tests(loader, tests, pattern):
7+
suite = unittest.TestSuite()
8+
suite.addTests(loader.loadTestsFromModule(init_test))
9+
suite.addTests(loader.loadTestsFromModule(option_test))
10+
suite.addTests(loader.loadTestsFromModule(show_test))
11+
12+
return suite

tests/expected/option_help.out

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
pg_probackup manage backup/recovery of PostgreSQL database.
2+
3+
Usage:
4+
pg_probackup [option...] init
5+
pg_probackup [option...] backup
6+
pg_probackup [option...] restore
7+
pg_probackup [option...] show [backup-ID]
8+
pg_probackup [option...] validate backup-ID
9+
pg_probackup [option...] delete backup-ID
10+
pg_probackup [option...] delwal [backup-ID]
11+
12+
Common Options:
13+
-B, --backup-path=PATH location of the backup storage area
14+
-D, --pgdata=PATH location of the database storage area
15+
16+
Backup options:
17+
-b, --backup-mode=MODE backup mode (full, page, ptrack)
18+
-C, --smooth-checkpoint do smooth checkpoint before backup
19+
--stream stream the transaction log and include it in the backup
20+
-S, --slot=SLOTNAME replication slot to use
21+
--backup-pg-log backup of pg_log directory
22+
-j, --threads=NUM number of parallel threads
23+
--progress show progress
24+
25+
Restore options:
26+
--time time stamp up to which recovery will proceed
27+
--xid transaction ID up to which recovery will proceed
28+
--inclusive whether we stop just after the recovery target
29+
--timeline recovering into a particular timeline
30+
-j, --threads=NUM number of parallel threads
31+
--progress show progress
32+
33+
Delete options:
34+
--wal remove unnecessary wal files
35+
36+
Connection options:
37+
-d, --dbname=DBNAME database to connect
38+
-h, --host=HOSTNAME database server host or socket directory
39+
-p, --port=PORT database server port
40+
-U, --username=USERNAME user name to connect as
41+
-w, --no-password never prompt for password
42+
-W, --password force password prompt
43+
44+
Generic options:
45+
-q, --quiet don't write any messages
46+
-v, --verbose verbose mode
47+
--help show this help, then exit
48+
--version output version information and exit
49+
50+
Read the website for details. <https://github.com/postgrespro/pg_probackup>
51+
Report bugs to <https://github.com/postgrespro/pg_probackup/issues>.

tests/expected/option_version.out

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pg_probackup 1.0

tests/init_test.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import unittest
2+
import os
3+
from os import path
4+
import six
5+
from .pb_lib import dir_files, ProbackupTest
6+
7+
8+
class InitTest(ProbackupTest, unittest.TestCase):
9+
10+
def __init__(self, *args, **kwargs):
11+
super(InitTest, self).__init__(*args, **kwargs)
12+
13+
def test_success_1(self):
14+
"""Success normal init"""
15+
node = self.make_bnode('test_success_1', base_dir="tmp_dirs/init/success_1")
16+
self.assertEqual(self.init_pb(node), six.b(""))
17+
self.assertEqual(
18+
dir_files(self.backup_dir(node)),
19+
['backups', 'pg_probackup.conf', 'wal']
20+
)
21+
22+
def test_already_exist_2(self):
23+
"""Failure with backup catalog already existed"""
24+
node = self.make_bnode('test_already_exist_2', base_dir="tmp_dirs/init/already_exist_2")
25+
self.init_pb(node)
26+
self.assertEqual(
27+
self.init_pb(node),
28+
six.b("ERROR: backup catalog already exist and it's not empty\n")
29+
)
30+
31+
def test_abs_path_3(self):
32+
"""failure with backup catalog should be given as absolute path"""
33+
node = self.make_bnode('test_abs_path_3', base_dir="tmp_dirs/init/abs_path_3")
34+
self.assertEqual(
35+
self.run_pb(["init", "-B", path.relpath("%s/backup" % node.base_dir, self.dir_path)]),
36+
six.b("ERROR: -B, --backup-path must be an absolute path\n")
37+
)
38+
39+
40+
if __name__ == '__main__':
41+
unittest.main()

tests/option_test.py

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
import unittest
2+
from os import path
3+
import six
4+
from .pb_lib import ProbackupTest
5+
from testgres import stop_all
6+
7+
8+
class OptionTest(ProbackupTest, unittest.TestCase):
9+
10+
def __init__(self, *args, **kwargs):
11+
super(OptionTest, self).__init__(*args, **kwargs)
12+
13+
@classmethod
14+
def tearDownClass(cls):
15+
stop_all()
16+
17+
def test_help_1(self):
18+
"""help options"""
19+
with open(path.join(self.dir_path, "expected/option_help.out"), "rb") as help_out:
20+
self.assertEqual(
21+
self.run_pb(["--help"]),
22+
help_out.read()
23+
)
24+
25+
def test_version_2(self):
26+
"""help options"""
27+
with open(path.join(self.dir_path, "expected/option_version.out"), "rb") as version_out:
28+
self.assertEqual(
29+
self.run_pb(["--version"]),
30+
version_out.read()
31+
)
32+
33+
def test_without_backup_path_3(self):
34+
"""backup command failure without backup mode option"""
35+
self.assertEqual(
36+
self.run_pb(["backup", "-b", "full"]),
37+
six.b("ERROR: required parameter not specified: BACKUP_PATH (-B, --backup-path)\n")
38+
)
39+
40+
def test_options_4(self):
41+
node = self.make_bnode('test_options_4', base_dir="tmp_dirs/option/option_common")
42+
try:
43+
node.stop()
44+
except:
45+
pass
46+
self.assertEqual(self.init_pb(node), six.b(""))
47+
48+
# backup command failure without backup mode option
49+
self.assertEqual(
50+
self.run_pb(["backup", "-B", self.backup_dir(node), "-D", node.data_dir]),
51+
six.b("ERROR: Required parameter not specified: BACKUP_MODE (-b, --backup-mode)\n")
52+
)
53+
54+
# backup command failure with invalid backup mode option
55+
self.assertEqual(
56+
self.run_pb(["backup", "-b", "bad", "-B", self.backup_dir(node)]),
57+
six.b('ERROR: invalid backup-mode "bad"\n')
58+
)
59+
60+
# delete failure without ID
61+
self.assertEqual(
62+
self.run_pb(["delete", "-B", self.backup_dir(node)]),
63+
six.b("ERROR: required backup ID not specified\n")
64+
)
65+
66+
node.start()
67+
68+
# syntax error in pg_probackup.conf
69+
with open(path.join(self.backup_dir(node), "pg_probackup.conf"), "a") as conf:
70+
conf.write(" = INFINITE\n")
71+
72+
self.assertEqual(
73+
self.backup_pb(node),
74+
six.b('ERROR: syntax error in " = INFINITE"\n')
75+
)
76+
77+
self.clean_pb(node)
78+
self.assertEqual(self.init_pb(node), six.b(""))
79+
80+
# invalid value in pg_probackup.conf
81+
with open(path.join(self.backup_dir(node), "pg_probackup.conf"), "a") as conf:
82+
conf.write("BACKUP_MODE=\n")
83+
84+
self.assertEqual(
85+
self.backup_pb(node, backup_type=None),
86+
six.b('ERROR: invalid backup-mode ""\n')
87+
)
88+
89+
self.clean_pb(node)
90+
self.assertEqual(self.init_pb(node), six.b(""))
91+
92+
# TODO: keep data generations
93+
94+
# invalid value in pg_probackup.conf
95+
with open(path.join(self.backup_dir(node), "pg_probackup.conf"), "a") as conf:
96+
conf.write("SMOOTH_CHECKPOINT=FOO\n")
97+
98+
self.assertEqual(
99+
self.backup_pb(node),
100+
six.b("ERROR: option -C, --smooth-checkpoint should be a boolean: 'FOO'\n")
101+
)
102+
103+
self.clean_pb(node)
104+
self.assertEqual(self.init_pb(node), six.b(""))
105+
106+
# invalid option in pg_probackup.conf
107+
with open(path.join(self.backup_dir(node), "pg_probackup.conf"), "a") as conf:
108+
conf.write("TIMELINEID=1\n")
109+
110+
self.assertEqual(
111+
self.backup_pb(node),
112+
six.b('ERROR: invalid option "TIMELINEID"\n')
113+
)
114+
115+
self.clean_pb(node)
116+
self.assertEqual(self.init_pb(node), six.b(""))
117+
118+
node.stop()

tests/pb_lib.py

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import os
2+
from os import path
3+
import subprocess
4+
import shutil
5+
import six
6+
from testgres import get_new_node
7+
8+
9+
def dir_files(base_dir):
10+
out_list = []
11+
for dir_name, subdir_list, file_list in os.walk(base_dir):
12+
if dir_name != base_dir:
13+
out_list.append(path.relpath(dir_name, base_dir))
14+
for fname in file_list:
15+
out_list.append(path.relpath(path.join(dir_name, fname), base_dir))
16+
out_list.sort()
17+
return out_list
18+
19+
20+
class ProbackupTest(object):
21+
def __init__(self, *args, **kwargs):
22+
super(ProbackupTest, self).__init__(*args, **kwargs)
23+
self.dir_path = path.dirname(os.path.realpath(__file__))
24+
try:
25+
os.makedirs(path.join(self.dir_path, "tmp_dirs"))
26+
except:
27+
pass
28+
self.probackup_path = os.path.abspath(path.join(
29+
self.dir_path,
30+
"../pg_probackup"
31+
))
32+
33+
def arcwal_dir(self, node):
34+
return "%s/backup/wal" % node.base_dir
35+
36+
def backup_dir(self, node):
37+
return os.path.abspath("%s/backup" % node.base_dir)
38+
39+
def make_bnode(self, name, base_dir=None):
40+
node = get_new_node('test', base_dir=path.join(self.dir_path, base_dir))
41+
try:
42+
node.cleanup()
43+
except:
44+
pass
45+
shutil.rmtree(self.backup_dir(node), ignore_errors=True)
46+
node.init()
47+
48+
node.append_conf("postgresql.conf", "wal_level = hot_standby")
49+
node.append_conf("postgresql.conf", "archive_mode = on")
50+
node.append_conf(
51+
"postgresql.conf",
52+
"""archive_command = 'cp "%%p" "%s/%%f"'""" % os.path.abspath(self.arcwal_dir(node))
53+
)
54+
return node
55+
56+
def run_pb(self, command):
57+
try:
58+
return subprocess.check_output(
59+
[self.probackup_path] + command,
60+
stderr=subprocess.STDOUT
61+
)
62+
except subprocess.CalledProcessError as err:
63+
return err.output
64+
65+
def init_pb(self, node):
66+
return self.run_pb([
67+
"init",
68+
"-B", self.backup_dir(node),
69+
"-D", node.data_dir
70+
])
71+
72+
def clean_pb(self, node):
73+
shutil.rmtree(self.backup_dir(node), ignore_errors=True)
74+
75+
def backup_pb(self, node, backup_type="full", options=[]):
76+
cmd_list = [
77+
"backup",
78+
"-D", node.data_dir,
79+
"-B", self.backup_dir(node),
80+
"-p", "%i" % node.port,
81+
"-d", "postgres"
82+
]
83+
if backup_type:
84+
cmd_list += ["-b", backup_type]
85+
86+
# print(cmd_list)
87+
return self.run_pb(cmd_list + options)
88+
89+
def show_pb(self, node, id=None, options=[]):
90+
cmd_list = [
91+
"-B", self.backup_dir(node),
92+
"show",
93+
]
94+
if id:
95+
cmd_list += [id]
96+
97+
# print(cmd_list)
98+
return self.run_pb(options + cmd_list)
99+
100+
def validate_pb(self, node, id=None, options=[]):
101+
cmd_list = [
102+
"-B", self.backup_dir(node),
103+
"validate",
104+
]
105+
if id:
106+
cmd_list += [id]
107+
108+
# print(cmd_list)
109+
return self.run_pb(options + cmd_list)

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy