comparison tests/e2e/run_e2e.py @ 3429:d4558f3cbf13

tests, docker(e2e): added e2e tests for Libervia: - moved jp tests to `e2e/jp` - new fixtures - adapted docker-compose - improved `run_e2e` with several flags + report on failure - doc to come
author Goffi <goffi@goffi.org>
date Fri, 27 Nov 2020 16:39:40 +0100
parents 814e118d9ef3
children be6d91572633
comparison
equal deleted inserted replaced
3428:a6ea53248c14 3429:d4558f3cbf13
20 20
21 import sys 21 import sys
22 from pathlib import Path 22 from pathlib import Path
23 import tempfile 23 import tempfile
24 from textwrap import dedent 24 from textwrap import dedent
25 from datetime import datetime
25 import sh 26 import sh
27 import io
28 import re
29 import sat_templates
30 import libervia
26 from sat.core import exceptions 31 from sat.core import exceptions
27 32 import yaml
28 KEEP_OPT = "--keep" 33 try:
34 from yaml import CLoader as Loader, CDumper as Dumper
35 except ImportError:
36 from yaml import Loader, Dumper
37
38
39 OPT_KEEP_CONTAINERS = "--keep-containers"
40 OPT_KEEP_PROFILES = "--keep-profiles"
41 OPT_KEEP_VNC = "--keep-vnc"
42 OPT_KEEP_BROWSER = "--keep-browser"
43 OPT_VISUAL = "--visual"
44 OPT_DEV_MODE = "--dev-mode"
45
46 dev_mode_inst = dedent("""\
47 Here is a short script to start working with a logged account:
48
49 from helium import *
50 start_firefox()
51 go_to("https://libervia.test:8443/login")
52 write("account1", "login")
53 write("test", "password")
54 click("log in")
55 """)
56 report_buffer = io.StringIO()
29 57
30 58
31 def live_out(data): 59 def live_out(data):
32 sys.stdout.write(data) 60 sys.stdout.write(data)
33 sys.stdout.flush() 61 sys.stdout.flush()
62 report_buffer.write(data)
34 63
35 64
36 def live_err(data): 65 def live_err(data):
37 sys.stderr.write(data) 66 sys.stderr.write(data)
38 sys.stderr.flush() 67 sys.stderr.flush()
68 report_buffer.write(data)
69
70
71 def get_opt(opt_name):
72 """Check is an option flag is set, and remove it for sys.argv
73
74 This allow to have simple flags without interfering with pytest options
75 """
76 if opt_name in sys.argv:
77 sys.argv.remove(opt_name)
78 return True
79 else:
80 return False
81
82
83 def set_env(override, name, value="1"):
84 """Set environement variable"""
85 environment = override["services"]["sat"].setdefault("environment", {})
86 environment[name] = value
39 87
40 88
41 def use_e2e_env(): 89 def use_e2e_env():
42 if KEEP_OPT in sys.argv: 90 visual = get_opt(OPT_VISUAL)
91 keep_containers = get_opt(OPT_KEEP_CONTAINERS)
92 keep_profiles = get_opt(OPT_KEEP_PROFILES)
93 keep_vnc = get_opt(OPT_KEEP_VNC)
94 keep_browser = get_opt(OPT_KEEP_BROWSER)
95 if keep_browser:
43 keep_containers = True 96 keep_containers = True
44 sys.argv.remove(KEEP_OPT) 97 keep_vnc = True
45 else: 98 if keep_vnc:
46 keep_containers = False 99 visual = True
100 dev_mode = get_opt(OPT_DEV_MODE)
101 if dev_mode:
102 keep_containers = keep_profiles = keep_vnc = visual = True
103
47 for p in Path.cwd().parents: 104 for p in Path.cwd().parents:
48 package_path = p / "sat" 105 package_path = p / "sat"
49 docker_path = p / "docker" 106 docker_path = p / "docker"
50 if package_path.is_dir() and docker_path.is_dir(): 107 if package_path.is_dir() and docker_path.is_dir():
51 sat_root_path = p 108 sat_root_path = p
54 raise exceptions.NotFound( 111 raise exceptions.NotFound(
55 "Can't find root of SàT code, are you sure that you are running the test " 112 "Can't find root of SàT code, are you sure that you are running the test "
56 "from the backend repository?" 113 "from the backend repository?"
57 ) 114 )
58 115
116 libervia_path = Path(libervia.__file__).parent.resolve()
117 libervia_root_path = libervia_path.parent
118 if (libervia_root_path / ".hg").is_dir():
119 libervia_source = libervia_root_path
120 libervia_target = "/src/libervia"
121 else:
122 libervia_source = libervia_path
123 libervia_target = "/src/libervia/libervia"
124
125 sat_templates_path = Path(sat_templates.__file__).parent.resolve()
126 sat_templates_root_path = sat_templates_path.parent
127 if (sat_templates_root_path / ".hg").is_dir():
128 sat_templates_source = sat_templates_root_path
129 sat_templates_target = "/src/sat_templates"
130 else:
131 sat_templates_source = sat_templates_path
132 sat_templates_target = "/src/sat_templates/sat_templates"
133
59 compose_e2e_path = docker_path / "docker-compose_e2e.yml" 134 compose_e2e_path = docker_path / "docker-compose_e2e.yml"
60 if not compose_e2e_path.is_file(): 135 if not compose_e2e_path.is_file():
61 raise exceptions.NotFound('"docker-compose_e2e.yml" file can\'t be found') 136 raise exceptions.NotFound('"docker-compose_e2e.yml" file can\'t be found')
62 137
63 with tempfile.TemporaryDirectory() as temp_dir: 138 with tempfile.TemporaryDirectory(prefix="sat_test_e2e_") as temp_dir:
64 override_path = Path(temp_dir) / "test_override.yml" 139 override_path = Path(temp_dir) / "test_override.yml"
65 with override_path.open("w") as f: 140 override = yaml.load(
66 f.write(dedent(f"""\ 141 dedent(f"""\
67 version: "3.6" 142 version: "3.6"
68 services: 143 services:
69 sat: 144 sat:
70 volumes: 145 volumes:
71 - type: bind 146 - type: bind
72 source: {sat_root_path} 147 source: {sat_root_path}
73 target: /src/sat 148 target: /src/sat
74 read_only: true 149 read_only: true
75 """)) 150 libervia:
151 volumes:
152 - type: bind
153 source: {sat_root_path}
154 target: /src/sat
155 read_only: true
156 - type: bind
157 source: {libervia_source}
158 target: {libervia_target}
159 read_only: true
160 - type: bind
161 source: {sat_templates_source}
162 target: {sat_templates_target}
163 read_only: true
164 """
165 ),
166 Loader=Loader
167 )
168
169 if keep_profiles:
170 set_env(override, "SAT_TEST_E2E_KEEP_PROFILES")
171
172 if visual:
173 set_env(override, "SAT_TEST_E2E_LIBERVIA_NO_HEADLESS")
174
175 if keep_browser:
176 set_env(override, "SAT_TEST_E2E_LIBERVIA_KEEP_BROWSER")
177
178 with override_path.open("w") as f:
179 yaml.dump(override, f, Dumper=Dumper)
76 180
77 docker_compose = sh.docker_compose.bake( 181 docker_compose = sh.docker_compose.bake(
78 "-f", compose_e2e_path, "-f", override_path) 182 "-f", compose_e2e_path, "-f", override_path)
79 docker_compose.up("-d") 183 docker_compose.up("-d")
80 184
81 try: 185 p = docker_compose.exec(
82 docker_compose.exec(
83 "-T", "--workdir", "/src/sat/tests", "sat", 186 "-T", "--workdir", "/src/sat/tests", "sat",
84 "pytest", "-o", "cache_dir=/tmp", *sys.argv[1:], color="yes", 187 "pytest", "-o", "cache_dir=/tmp", *sys.argv[1:], color="yes",
85 _in=sys.stdin, _out=live_out, _out_bufsize=0, _err=live_err, _err_bufsize=0 188 _in=sys.stdin, _out=live_out, _out_bufsize=0, _err=live_err, _err_bufsize=0,
189 _bg=True
86 ) 190 )
191 if visual:
192 vnc_port = docker_compose.port("sat", "5900").split(':', 1)[1].strip()
193 p_vnc = sh.vncviewer(
194 f"localhost:{vnc_port}",
195 _bg=True,
196 # vncviewer exits with 1 when we send an SIGTERM to it, and it's printed
197 # before we can catch it (it's happening in a thread). Thus we set exit
198 # code 1 as OK to avoid the backtrace.
199 _ok_code=[0, 1]
200 )
201 else:
202 p_vnc = None
203
204 try:
205 p.wait()
87 except sh.ErrorReturnCode as e: 206 except sh.ErrorReturnCode as e:
207 sat_cont_id = docker_compose.ps("-q", "sat").strip()
208 report_dest = Path(f"reports_{datetime.now().isoformat()}/")
209 # we need to make `report_dest` explicitely local with "./", otherwise
210 # docker parse takes it as a container path due to the presence of ":"
211 # with `isoformat()`.
212 sh.docker.cp(f"{sat_cont_id}:/reports", f"./{report_dest}")
213 # we save 2 versions: one with ANSI escape codes
214 report_ansi = report_dest / "report.ansi"
215 with report_ansi.open('w') as f:
216 f.write(report_buffer.getvalue())
217 # and one without (cf. https://stackoverflow.com/a/14693789)
218 ansi_escape = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])')
219 report_log = report_dest / "report.log"
220 with report_log.open('w') as f:
221 f.write(ansi_escape.sub('', report_buffer.getvalue()))
222
223 print(f"report saved to {report_dest}")
88 sys.exit(e.exit_code) 224 sys.exit(e.exit_code)
89 finally: 225 finally:
226 if p_vnc is not None and p_vnc.is_alive() and not keep_vnc:
227 p_vnc.terminate()
90 if not keep_containers: 228 if not keep_containers:
91 docker_compose.down(volumes=True) 229 docker_compose.down(volumes=True)
230 if dev_mode:
231 print(dev_mode_inst)
232
92 233
93 if __name__ == "__main__": 234 if __name__ == "__main__":
94 use_e2e_env() 235 use_e2e_env()