clickhouse-test 45.6 KB
Newer Older
A
Azat Khuzhin 已提交
1 2
#!/usr/bin/env python3

3
import shutil
4
import sys
5 6 7
import os
import os.path
import re
A
alesapin 已提交
8
import json
9
import traceback
10 11

from argparse import ArgumentParser
12
import shlex
13
import subprocess
14 15 16
from subprocess import Popen
from subprocess import PIPE
from subprocess import CalledProcessError
17
from subprocess import TimeoutExpired
18
from datetime import datetime
A
Alexey Milovidov 已提交
19
from time import time, sleep
20
from errno import ESRCH
21 22 23 24
try:
    import termcolor
except ImportError:
    termcolor = None
25 26
import random
import string
P
proller 已提交
27
import multiprocessing
P
proller 已提交
28
from contextlib import closing
29

30

A
alesapin 已提交
31
MESSAGES_TO_RETRY = [
32
    "DB::Exception: ZooKeeper session has been expired",
A
alesapin 已提交
33
    "Coordination::Exception: Connection loss",
A
Alexey Milovidov 已提交
34
    "Operation timed out",
A
Alexey Milovidov 已提交
35
    "ConnectionPoolWithFailover: Connection failed at try",
A
alesapin 已提交
36 37
]

38

39 40 41
def json_minify(string):
    """
    Removes all js-style comments from json string. Allows to have comments in skip_list.json.
A
alexey-milovidov 已提交
42
    The code taken from https://github.com/getify/JSON.minify/tree/python under the MIT license.
43 44
    """

45
    tokenizer = re.compile(r'"|(/\*)|(\*/)|(//)|\n|\r')
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
    end_slashes_re = re.compile(r'(\\)*$')

    in_string = False
    in_multi = False
    in_single = False

    new_str = []
    index = 0

    for match in re.finditer(tokenizer, string):
        if not (in_multi or in_single):
            tmp = string[index:match.start()]
            new_str.append(tmp)
        else:
            # Replace comments with white space so that the JSON parser reports
            # the correct column numbers on parsing errors.
            new_str.append(' ' * (match.start() - index))

        index = match.end()
        val = match.group()

        if val == '"' and not (in_multi or in_single):
            escaped = end_slashes_re.search(string, 0, match.start())

            # start of string or unescaped quote character to end string
            if not in_string or (escaped is None or len(escaped.group()) % 2 == 0):  # noqa
                in_string = not in_string
            index -= 1  # include " character in next catch
        elif not (in_string or in_multi or in_single):
            if val == '/*':
                in_multi = True
            elif val == '//':
                in_single = True
        elif val == '*/' and in_multi and not (in_string or in_single):
            in_multi = False
            new_str.append(' ' * len(val))
        elif val in '\r\n' and not (in_multi or in_string) and in_single:
            in_single = False
        elif not in_multi or in_single:  # noqa
            new_str.append(val)

        if val in '\r\n':
            new_str.append(val)
        elif in_multi or in_single:
            new_str.append(' ' * len(val))

    new_str.append(string[index:])
    return ''.join(new_str)


96 97 98 99 100 101
def remove_control_characters(s):
    """
    https://github.com/html5lib/html5lib-python/issues/96#issuecomment-43438438
    """
    def str_to_int(s, default, base=10):
        if int(s, base) < 0x10000:
A
Azat Khuzhin 已提交
102
            return chr(int(s, base))
103
        return default
104 105 106
    s = re.sub(r"&#(\d+);?", lambda c: str_to_int(c.group(1), c.group(0)), s)
    s = re.sub(r"&#[xX]([0-9a-fA-F]+);?", lambda c: str_to_int(c.group(1), c.group(0), base=16), s)
    s = re.sub(r"[\x00-\x08\x0b\x0e-\x1f\x7f]", "", s)
107 108
    return s

A
Alexander Tokmakov 已提交
109 110
def get_db_engine(args, database_name):
    if args.replicated_database:
A
fix  
Alexander Tokmakov 已提交
111
        return " ON CLUSTER test_cluster_database_replicated ENGINE=Replicated('/test/clickhouse/db/{}', '{{shard}}', '{{replica}}')".format(database_name)
112 113 114
    if args.db_engine:
        return " ENGINE=" + args.db_engine
    return ""   # Will use default engine
115

116
def run_single_test(args, ext, server_logs_level, client_options, case_file, stdout_file, stderr_file, suite_tmp_dir):
117 118
    # print(client_options)

119
    start_time = datetime.now()
120 121 122
    if args.database:
        database = args.database
        os.environ.setdefault("CLICKHOUSE_DATABASE", database)
123
        os.environ.setdefault("CLICKHOUSE_TMP", suite_tmp_dir)
124 125 126 127 128 129 130 131 132

    else:
        # If --database is not specified, we will create temporary database with unique name
        # And we will recreate and drop it for each test
        def random_str(length=6):
            alphabet = string.ascii_lowercase + string.digits
            return ''.join(random.choice(alphabet) for _ in range(length))
        database = 'test_{suffix}'.format(suffix=random_str())

A
Azat Khuzhin 已提交
133
        clickhouse_proc_create = Popen(shlex.split(args.client), stdin=PIPE, stdout=PIPE, stderr=PIPE, universal_newlines=True)
134
        try:
A
Alexander Tokmakov 已提交
135
            clickhouse_proc_create.communicate(("CREATE DATABASE " + database + get_db_engine(args, database)), timeout=args.timeout)
136 137 138
        except TimeoutExpired:
            total_time = (datetime.now() - start_time).total_seconds()
            return clickhouse_proc_create, "", "Timeout creating database {} before test".format(database), total_time
139 140

        os.environ["CLICKHOUSE_DATABASE"] = database
141 142 143 144 145 146
        # Set temporary directory to match the randomly generated database,
        # because .sh tests also use it for temporary files and we want to avoid
        # collisions.
        test_tmp_dir = os.path.join(suite_tmp_dir, database)
        os.mkdir(test_tmp_dir)
        os.environ.setdefault("CLICKHOUSE_TMP", test_tmp_dir)
147

148
    # This is for .sh tests
149
    os.environ["CLICKHOUSE_LOG_COMMENT"] = case_file
150

151
    params = {
152
        'client': args.client + ' --database=' + database,
153 154 155 156 157 158 159
        'logs_level': server_logs_level,
        'options': client_options,
        'test': case_file,
        'stdout': stdout_file,
        'stderr': stderr_file,
    }

160 161
    pattern = '{test} > {stdout} 2> {stderr}'

A
alesapin 已提交
162
    if ext == '.sql':
163
        pattern = "{client} --send_logs_level={logs_level} --testmode --multiquery {options} --log_comment='{test}' < " + pattern
164 165

    command = pattern.format(**params)
M
typo  
myrrc 已提交
166

M
myrrc 已提交
167
    # print(command)
A
alesapin 已提交
168

169
    proc = Popen(command, shell=True, env=os.environ)
M
typo  
myrrc 已提交
170

A
alesapin 已提交
171 172 173
    while (datetime.now() - start_time).total_seconds() < args.timeout and proc.poll() is None:
        sleep(0.01)

A
fix  
Alexander Tokmakov 已提交
174 175 176 177 178 179
    need_drop_database = not args.database
    if need_drop_database and args.no_drop_if_fail:
        maybe_passed = (proc.returncode == 0) and (proc.stderr is None) and (proc.stdout is None or 'Exception' not in proc.stdout)
        need_drop_database = not maybe_passed

    if need_drop_database:
A
Azat Khuzhin 已提交
180
        clickhouse_proc_create = Popen(shlex.split(args.client), stdin=PIPE, stdout=PIPE, stderr=PIPE, universal_newlines=True)
181
        seconds_left = max(args.timeout - (datetime.now() - start_time).total_seconds(), 20)
182
        try:
183 184 185 186
            drop_database_query = "DROP DATABASE " + database
            if args.replicated_database:
                drop_database_query += " ON CLUSTER test_cluster_database_replicated"
            clickhouse_proc_create.communicate((drop_database_query), timeout=seconds_left)
187
        except TimeoutExpired:
A
alesapin 已提交
188 189 190 191 192 193 194
            # kill test process because it can also hung
            if proc.returncode is None:
                try:
                    proc.kill()
                except OSError as e:
                    if e.errno != ESRCH:
                        raise
A
alesapin 已提交
195 196

            total_time = (datetime.now() - start_time).total_seconds()
197
            return clickhouse_proc_create, "", "Timeout dropping database {} after test".format(database), total_time
198

199 200
        shutil.rmtree(test_tmp_dir)

201 202
    total_time = (datetime.now() - start_time).total_seconds()

A
alexey-milovidov 已提交
203
    # Normalize randomized database names in stdout, stderr files.
204
    os.system("LC_ALL=C sed -i -e 's/{test_db}/default/g' {file}".format(test_db=database, file=stdout_file))
A
fix  
Alexander Tokmakov 已提交
205 206
    if not args.show_db_name:
        os.system("LC_ALL=C sed -i -e 's/{test_db}/default/g' {file}".format(test_db=database, file=stderr_file))
A
Alexander Tokmakov 已提交
207 208 209
    if args.replicated_database:
        os.system("LC_ALL=C sed -i -e 's|/auto_{{shard}}||g' {file}".format(file=stdout_file))
        os.system("LC_ALL=C sed -i -e 's|auto_{{replica}}||g' {file}".format(file=stdout_file))
210

A
Azat Khuzhin 已提交
211 212 213 214
    stdout = open(stdout_file, 'rb').read() if os.path.exists(stdout_file) else b''
    stdout = str(stdout, errors='replace', encoding='utf-8')
    stderr = open(stderr_file, 'rb').read() if os.path.exists(stderr_file) else b''
    stderr = str(stderr, errors='replace', encoding='utf-8')
A
alesapin 已提交
215

216
    return proc, stdout, stderr, total_time
A
alesapin 已提交
217

218

A
alesapin 已提交
219 220 221
def need_retry(stderr):
    return any(msg in stderr for msg in MESSAGES_TO_RETRY)

222

A
alesapin 已提交
223
def get_processlist(args):
224
    try:
A
Alexander Tokmakov 已提交
225 226 227 228
        query = b"SHOW PROCESSLIST FORMAT Vertical"
        if args.replicated_database:
            query = b"SELECT materialize((hostName(), tcpPort())) as host, * " \
                    b"FROM clusterAllReplicas('r', system.processes) WHERE query NOT LIKE '%system.processes%' FORMAT Vertical"
A
alesapin 已提交
229
        clickhouse_proc = Popen(shlex.split(args.client), stdin=PIPE, stdout=PIPE, stderr=PIPE)
T
tavplubix 已提交
230
        (stdout, _) = clickhouse_proc.communicate((query), timeout=20)
A
alesapin 已提交
231 232 233 234
        return False, stdout.decode('utf-8')
    except Exception as ex:
        print("Exception", ex)
        return True, ""
235

236

A
alesapin 已提交
237
# collect server stacktraces using gdb
238
def get_stacktraces_from_gdb(server_pid):
239
    try:
240
        cmd = "gdb -batch -ex 'thread apply all backtrace' -p {}".format(server_pid)
T
tavplubix 已提交
241
        return subprocess.check_output(cmd, shell=True).decode('utf-8')
242
    except Exception as ex:
A
fix  
Alexander Tokmakov 已提交
243 244
        print("Error occured while receiving stack traces from gdb: {}".format(str(ex)))
        return None
245 246


A
alesapin 已提交
247
# collect server stacktraces from system.stack_trace table
248
# it does not work in Sandbox
249 250
def get_stacktraces_from_clickhouse(client):
    try:
T
tavplubix 已提交
251 252 253
        return subprocess.check_output("{} --allow_introspection_functions=1 --query "
               "\"SELECT arrayStringConcat(arrayMap(x, y -> concat(x, ': ', y), arrayMap(x -> addressToLine(x), trace), "
               "arrayMap(x -> demangle(addressToSymbol(x)), trace)), '\n') as trace "
A
fix  
Alexander Tokmakov 已提交
254
               "FROM system.stack_trace format Vertical\"".format(client), shell=True, stderr=subprocess.STDOUT).decode('utf-8')
255
    except Exception as ex:
A
fix  
Alexander Tokmakov 已提交
256 257
        print("Error occured while receiving stack traces from client: {}".format(str(ex)))
        return None
258

259

260
def get_server_pid(server_tcp_port):
A
fix  
Alexander Tokmakov 已提交
261 262 263 264
    # lsof does not work in stress tests for some reason
    cmd_lsof = "lsof -i tcp:{port} -s tcp:LISTEN -Fp | awk '/^p[0-9]+$/{{print substr($0, 2)}}'".format(port=server_tcp_port)
    cmd_pidof = "pidof -s clickhouse-server"
    commands = [cmd_lsof, cmd_pidof]
A
fix  
Alexander Tokmakov 已提交
265
    output = None
A
fix  
Alexander Tokmakov 已提交
266 267
    for cmd in commands:
        try:
268
            output = subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT, universal_newlines=True)
A
fix  
Alexander Tokmakov 已提交
269 270 271
            if output:
                return int(output)
        except Exception as e:
272
            print("Cannot get server pid with {}, got {}: {}".format(cmd, output, e))
A
fix  
Alexander Tokmakov 已提交
273
    return None # most likely server dead
274

275

P
proller 已提交
276
def colored(text, args, color=None, on_color=None, attrs=None):
277 278 279 280
    if termcolor and (sys.stdout.isatty() or args.force_color):
        return termcolor.colored(text, color, on_color, attrs)
    else:
        return text
P
proller 已提交
281

282

P
proller 已提交
283 284
SERVER_DIED = False
exit_code = 0
A
Alexey Milovidov 已提交
285
stop_time = None
P
proller 已提交
286

287

288
# def run_tests_array(all_tests, suite, suite_dir, suite_tmp_dir, run_total):
289
def run_tests_array(all_tests_with_params):
A
alesapin 已提交
290
    all_tests, suite, suite_dir, suite_tmp_dir = all_tests_with_params
P
proller 已提交
291
    global exit_code
P
proller 已提交
292
    global SERVER_DIED
A
Alexey Milovidov 已提交
293
    global stop_time
294

P
proller 已提交
295 296 297 298 299 300 301 302 303 304 305 306 307
    OP_SQUARE_BRACKET = colored("[", args, attrs=['bold'])
    CL_SQUARE_BRACKET = colored("]", args, attrs=['bold'])

    MSG_FAIL = OP_SQUARE_BRACKET + colored(" FAIL ", args, "red", attrs=['bold']) + CL_SQUARE_BRACKET
    MSG_UNKNOWN = OP_SQUARE_BRACKET + colored(" UNKNOWN ", args, "yellow", attrs=['bold']) + CL_SQUARE_BRACKET
    MSG_OK = OP_SQUARE_BRACKET + colored(" OK ", args, "green", attrs=['bold']) + CL_SQUARE_BRACKET
    MSG_SKIPPED = OP_SQUARE_BRACKET + colored(" SKIPPED ", args, "cyan", attrs=['bold']) + CL_SQUARE_BRACKET

    passed_total = 0
    skipped_total = 0
    failures_total = 0
    failures = 0
    failures_chain = 0
308
    start_time = datetime.now()
309

A
Alexander Kuzmenkov 已提交
310 311
    is_concurrent = multiprocessing.current_process().name != "MainProcess"

312 313
    client_options = get_additional_client_options(args)

314 315
    def print_test_time(test_time):
        if args.print_time:
316 317 318
            return " {0:.2f} sec.".format(test_time)
        else:
            return ''
319

320
    if all_tests:
A
Alexander Kuzmenkov 已提交
321
        print(f"\nRunning {len(all_tests)} {suite} tests ({multiprocessing.current_process().name}).\n")
A
alesapin 已提交
322

323
    for case in all_tests:
P
proller 已提交
324 325
        if SERVER_DIED:
            break
A
alesapin 已提交
326

A
Alexey Milovidov 已提交
327 328 329 330
        if stop_time and time() > stop_time:
            print("\nStop tests run because global time limit is exceeded.\n")
            break

P
proller 已提交
331 332 333 334
        case_file = os.path.join(suite_dir, case)
        (name, ext) = os.path.splitext(case)

        try:
335 336 337 338 339 340 341 342 343
            status = ''
            if not is_concurrent:
                sys.stdout.flush()
                sys.stdout.write("{0:72}".format(name + ": "))
                # This flush is needed so you can see the test name of the long
                # running test before it will finish. But don't do it in parallel
                # mode, so that the lines don't mix.
                sys.stdout.flush()
            else:
344
                status = "{0:72}".format(name + ": ")
P
proller 已提交
345 346

            if args.skip and any(s in name for s in args.skip):
347
                status += MSG_SKIPPED + " - skip\n"
P
proller 已提交
348
                skipped_total += 1
349 350
            elif not args.zookeeper and ('zookeeper' in name
                    or 'replica' in name):
351
                status += MSG_SKIPPED + " - no zookeeper\n"
P
proller 已提交
352
                skipped_total += 1
A
akuzm 已提交
353 354 355
            elif not args.shard and ('shard' in name
                    or 'distributed' in name
                    or 'global' in name):
356
                status += MSG_SKIPPED + " - no shard\n"
P
proller 已提交
357
                skipped_total += 1
C
CurtizJ 已提交
358 359 360 361 362
            elif not args.no_long and ('long' in name
                    # Tests for races and deadlocks usually are runned in loop
                    #  for significant amount of time
                    or 'deadlock' in name
                    or 'race' in name):
363
                status += MSG_SKIPPED + " - no long\n"
P
proller 已提交
364 365 366 367 368 369
                skipped_total += 1
            else:
                disabled_file = os.path.join(suite_dir, name) + '.disabled'

                if os.path.exists(disabled_file) and not args.disabled:
                    message = open(disabled_file, 'r').read()
370
                    status += MSG_SKIPPED + " - " + message + "\n"
P
proller 已提交
371 372 373
                else:

                    if args.testname:
A
Azat Khuzhin 已提交
374
                        clickhouse_proc = Popen(shlex.split(args.client), stdin=PIPE, stdout=PIPE, stderr=PIPE, universal_newlines=True)
A
alesapin 已提交
375 376
                        failed_to_check = False
                        try:
377
                            clickhouse_proc.communicate(("SELECT 'Running test {suite}/{case} from pid={pid}';".format(pid = os.getpid(), case = case, suite = suite)), timeout=20)
A
alesapin 已提交
378 379
                        except:
                            failed_to_check = True
P
proller 已提交
380

A
alesapin 已提交
381
                        if failed_to_check or clickhouse_proc.returncode != 0:
382
                            failures += 1
A
alexey-milovidov 已提交
383
                            print("Server does not respond to health check")
384 385 386
                            SERVER_DIED = True
                            break

387
                    file_suffix = ('.' + str(os.getpid())) if is_concurrent and args.test_runs > 1 else ''
P
proller 已提交
388
                    reference_file = os.path.join(suite_dir, name) + '.reference'
389 390
                    stdout_file = os.path.join(suite_tmp_dir, name) + file_suffix + '.stdout'
                    stderr_file = os.path.join(suite_tmp_dir, name) + file_suffix + '.stderr'
P
proller 已提交
391

392
                    proc, stdout, stderr, total_time = run_single_test(args, ext, server_logs_level, client_options, case_file, stdout_file, stderr_file, suite_tmp_dir)
M
typo  
myrrc 已提交
393

P
proller 已提交
394 395 396 397 398 399 400 401
                    if proc.returncode is None:
                        try:
                            proc.kill()
                        except OSError as e:
                            if e.errno != ESRCH:
                                raise

                        failures += 1
402 403 404
                        status += MSG_FAIL
                        status += print_test_time(total_time)
                        status += " - Timeout!\n"
405
                        if stderr:
406
                            status += stderr
P
proller 已提交
407 408 409
                    else:
                        counter = 1
                        while proc.returncode != 0 and need_retry(stderr):
410
                            proc, stdout, stderr, total_time = run_single_test(args, ext, server_logs_level, client_options, case_file, stdout_file, stderr_file, suite_tmp_dir)
P
proller 已提交
411 412 413 414 415 416 417 418
                            sleep(2**counter)
                            counter += 1
                            if counter > 6:
                                break

                        if proc.returncode != 0:
                            failures += 1
                            failures_chain += 1
419 420
                            status += MSG_FAIL
                            status += print_test_time(total_time)
421
                            status += ' - return code {}\n'.format(proc.returncode)
P
proller 已提交
422 423

                            if stderr:
424
                                status += stderr
P
proller 已提交
425

M
typo  
myrrc 已提交
426
                            # Stop on fatal errors like segmentation fault. They are sent to client via logs.
A
Alexey Milovidov 已提交
427 428 429
                            if ' <Fatal> ' in stderr:
                                SERVER_DIED = True

P
proller 已提交
430 431 432
                            if args.stop and ('Connection refused' in stderr or 'Attempt to read after eof' in stderr) and not 'Received exception from server' in stderr:
                                SERVER_DIED = True

433
                            if os.path.isfile(stdout_file):
434 435 436
                                status += ", result:\n\n"
                                status += '\n'.join(
                                    open(stdout_file).read().split('\n')[:100])
437
                                status += '\n'
438

P
proller 已提交
439 440 441
                        elif stderr:
                            failures += 1
                            failures_chain += 1
442 443 444 445
                            status += MSG_FAIL
                            status += print_test_time(total_time)
                            status += " - having stderror:\n{}\n".format(
                                '\n'.join(stderr.split('\n')[:100]))
P
proller 已提交
446 447 448
                        elif 'Exception' in stdout:
                            failures += 1
                            failures_chain += 1
449 450 451 452
                            status += MSG_FAIL
                            status += print_test_time(total_time)
                            status += " - having exception:\n{}\n".format(
                                '\n'.join(stdout.split('\n')[:100]))
P
proller 已提交
453
                        elif not os.path.isfile(reference_file):
454 455 456
                            status += MSG_UNKNOWN
                            status += print_test_time(total_time)
                            status += " - no reference file\n"
P
proller 已提交
457
                        else:
A
Azat Khuzhin 已提交
458
                            result_is_different = subprocess.call(['diff', '-q', reference_file, stdout_file], stdout=PIPE)
P
proller 已提交
459 460

                            if result_is_different:
A
Azat Khuzhin 已提交
461
                                diff = Popen(['diff', '-U', str(args.unified), reference_file, stdout_file], stdout=PIPE, universal_newlines=True).communicate()[0]
P
proller 已提交
462
                                failures += 1
463 464 465
                                status += MSG_FAIL
                                status += print_test_time(total_time)
                                status += " - result differs with reference:\n{}\n".format(diff)
P
proller 已提交
466
                            else:
467 468 469 470 471 472
                                if args.test_runs > 1 and total_time > 30 and 'long' not in name:
                                    # We're in Flaky Check mode, check the run time as well while we're at it.
                                    failures += 1
                                    failures_chain += 1
                                    status += MSG_FAIL
                                    status += print_test_time(total_time)
A
faster  
Alexander Kuzmenkov 已提交
473
                                    status += " - Test runs too long (> 30s). Make it faster."
474 475 476 477 478 479 480 481 482 483
                                else:
                                    passed_total += 1
                                    failures_chain = 0
                                    status += MSG_OK
                                    status += print_test_time(total_time)
                                    status += "\n"
                                    if os.path.exists(stdout_file):
                                        os.remove(stdout_file)
                                    if os.path.exists(stderr_file):
                                        os.remove(stderr_file)
484

A
alesapin 已提交
485 486 487
            if status and not status.endswith('\n'):
                status += '\n'

488 489
            sys.stdout.write(status)
            sys.stdout.flush()
490
        except KeyboardInterrupt as e:
P
proller 已提交
491
            print(colored("Break tests execution", args, "red"))
492
            raise e
P
proller 已提交
493 494 495
        except:
            exc_type, exc_value, tb = sys.exc_info()
            failures += 1
496
            print("{0} - Test internal error: {1}\n{2}\n{3}".format(MSG_FAIL, exc_type.__name__, exc_value, "\n".join(traceback.format_tb(tb, 10))))
P
proller 已提交
497 498 499 500 501 502 503

        if failures_chain >= 20:
            break

    failures_total = failures_total + failures

    if failures_total > 0:
504
        print(colored(f"\nHaving {failures_total} errors! {passed_total} tests passed."
A
Alexander Kuzmenkov 已提交
505 506
            f" {skipped_total} tests skipped. {(datetime.now() - start_time).total_seconds():.2f} s elapsed"
            f' ({multiprocessing.current_process().name}).',
507
            args, "red", attrs=["bold"]))
P
proller 已提交
508 509
        exit_code = 1
    else:
510
        print(colored(f"\n{passed_total} tests passed. {skipped_total} tests skipped."
A
Alexander Kuzmenkov 已提交
511 512
            f" {(datetime.now() - start_time).total_seconds():.2f} s elapsed"
            f' ({multiprocessing.current_process().name}).',
513
            args, "green", attrs=["bold"]))
A
alesapin 已提交
514

A
flush  
Alexander Kuzmenkov 已提交
515 516
    sys.stdout.flush()

517

P
proller 已提交
518 519
server_logs_level = "warning"

520

521
def check_server_started(client, retry_count):
522 523
    print("Connecting to ClickHouse server...", end='')
    sys.stdout.flush()
524 525
    while retry_count > 0:
        clickhouse_proc = Popen(shlex.split(client), stdin=PIPE, stdout=PIPE, stderr=PIPE)
A
Azat Khuzhin 已提交
526
        (stdout, stderr) = clickhouse_proc.communicate(b"SELECT 1")
527

A
Azat Khuzhin 已提交
528
        if clickhouse_proc.returncode == 0 and stdout.startswith(b"1"):
529 530 531 532 533 534 535 536
            print(" OK")
            sys.stdout.flush()
            return True

        if clickhouse_proc.returncode == 210:
            # Connection refused, retry
            print('.', end = '')
            sys.stdout.flush()
537 538
            retry_count -= 1
            sleep(0.5)
539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556
            continue

        # Other kind of error, fail.
        print('')
        print("Client invocation failed with code ", clickhouse_proc.returncode, ": ")
        # We can't print this, because for some reason this is python 2,
        # and args appeared in 3.3. To hell with it.
        # print(''.join(clickhouse_proc.args))
        print("stdout: ")
        print(stdout)
        print("stderr: ")
        print(stderr)
        sys.stdout.flush()
        return False

    print('')
    print('All connection tries failed')
    sys.stdout.flush()
557 558 559 560

    return False


561
class BuildFlags():
A
alesapin 已提交
562 563 564 565 566 567 568
    THREAD = 'thread-sanitizer'
    ADDRESS = 'address-sanitizer'
    UNDEFINED = 'ub-sanitizer'
    MEMORY = 'memory-sanitizer'
    DEBUG = 'debug-build'
    UNBUNDLED = 'unbundled-build'
    RELEASE = 'release-build'
569
    DATABASE_ORDINARY = 'database-ordinary'
A
alesapin 已提交
570
    POLYMORPHIC_PARTS = 'polymorphic-parts'
A
Alexander Tokmakov 已提交
571 572
    ANTLR = 'antlr'
    DATABASE_REPLICATED = 'database-replicated'
A
alesapin 已提交
573 574 575 576


def collect_build_flags(client):
    clickhouse_proc = Popen(shlex.split(client), stdin=PIPE, stdout=PIPE, stderr=PIPE)
A
Azat Khuzhin 已提交
577
    (stdout, stderr) = clickhouse_proc.communicate(b"SELECT value FROM system.build_options WHERE name = 'CXX_FLAGS'")
A
alesapin 已提交
578 579 580
    result = []

    if clickhouse_proc.returncode == 0:
A
Azat Khuzhin 已提交
581
        if b'-fsanitize=thread' in stdout:
A
alesapin 已提交
582
            result.append(BuildFlags.THREAD)
A
Azat Khuzhin 已提交
583
        elif b'-fsanitize=address' in stdout:
A
alesapin 已提交
584
            result.append(BuildFlags.ADDRESS)
A
Azat Khuzhin 已提交
585
        elif b'-fsanitize=undefined' in stdout:
A
alesapin 已提交
586
            result.append(BuildFlags.UNDEFINED)
A
Azat Khuzhin 已提交
587
        elif b'-fsanitize=memory' in stdout:
A
alesapin 已提交
588 589
            result.append(BuildFlags.MEMORY)
    else:
M
typo  
myrrc 已提交
590
        raise Exception("Cannot get information about build from server errorcode {}, stderr {}".format(clickhouse_proc.returncode, stderr))
A
alesapin 已提交
591 592

    clickhouse_proc = Popen(shlex.split(client), stdin=PIPE, stdout=PIPE, stderr=PIPE)
A
Azat Khuzhin 已提交
593
    (stdout, stderr) = clickhouse_proc.communicate(b"SELECT value FROM system.build_options WHERE name = 'BUILD_TYPE'")
A
alesapin 已提交
594 595

    if clickhouse_proc.returncode == 0:
A
Azat Khuzhin 已提交
596
        if b'Debug' in stdout:
A
alesapin 已提交
597
            result.append(BuildFlags.DEBUG)
A
Azat Khuzhin 已提交
598
        elif b'RelWithDebInfo' in stdout or b'Release' in stdout:
A
alesapin 已提交
599 600
            result.append(BuildFlags.RELEASE)
    else:
M
typo  
myrrc 已提交
601
        raise Exception("Cannot get information about build from server errorcode {}, stderr {}".format(clickhouse_proc.returncode, stderr))
A
alesapin 已提交
602 603

    clickhouse_proc = Popen(shlex.split(client), stdin=PIPE, stdout=PIPE, stderr=PIPE)
A
Azat Khuzhin 已提交
604
    (stdout, stderr) = clickhouse_proc.communicate(b"SELECT value FROM system.build_options WHERE name = 'UNBUNDLED'")
A
alesapin 已提交
605 606

    if clickhouse_proc.returncode == 0:
A
Azat Khuzhin 已提交
607
        if b'ON' in stdout or b'1' in stdout:
A
alesapin 已提交
608 609
            result.append(BuildFlags.UNBUNDLED)
    else:
M
typo  
myrrc 已提交
610
        raise Exception("Cannot get information about build from server errorcode {}, stderr {}".format(clickhouse_proc.returncode, stderr))
A
alesapin 已提交
611 612

    clickhouse_proc = Popen(shlex.split(client), stdin=PIPE, stdout=PIPE, stderr=PIPE)
A
Azat Khuzhin 已提交
613
    (stdout, stderr) = clickhouse_proc.communicate(b"SELECT value FROM system.settings WHERE name = 'default_database_engine'")
A
alesapin 已提交
614 615

    if clickhouse_proc.returncode == 0:
A
Azat Khuzhin 已提交
616
        if b'Ordinary' in stdout:
617
            result.append(BuildFlags.DATABASE_ORDINARY)
A
alesapin 已提交
618
    else:
M
typo  
myrrc 已提交
619
        raise Exception("Cannot get information about build from server errorcode {}, stderr {}".format(clickhouse_proc.returncode, stderr))
A
alesapin 已提交
620

A
Anton Popov 已提交
621 622 623 624 625 626 627 628 629
    clickhouse_proc = Popen(shlex.split(client), stdin=PIPE, stdout=PIPE, stderr=PIPE)
    (stdout, stderr) = clickhouse_proc.communicate(b"SELECT value FROM system.merge_tree_settings WHERE name = 'min_bytes_for_wide_part'")

    if clickhouse_proc.returncode == 0:
        if stdout == b'0\n':
            result.append(BuildFlags.POLYMORPHIC_PARTS)
    else:
        raise Exception("Cannot get inforamtion about build from server errorcode {}, stderr {}".format(clickhouse_proc.returncode, stderr))

A
alesapin 已提交
630 631 632
    return result


P
proller 已提交
633 634
def main(args):
    global SERVER_DIED
A
Alexey Milovidov 已提交
635
    global stop_time
P
proller 已提交
636 637
    global exit_code
    global server_logs_level
A
alesapin 已提交
638

639
    def is_data_present():
P
proller 已提交
640
        clickhouse_proc = Popen(shlex.split(args.client), stdin=PIPE, stdout=PIPE, stderr=PIPE)
A
Azat Khuzhin 已提交
641
        (stdout, stderr) = clickhouse_proc.communicate(b"EXISTS TABLE test.hits")
P
proller 已提交
642
        if clickhouse_proc.returncode != 0:
P
proller 已提交
643
            raise CalledProcessError(clickhouse_proc.returncode, args.client, stderr)
644

A
Azat Khuzhin 已提交
645
        return stdout.startswith(b'1')
646

647
    if not check_server_started(args.client, args.server_check_retries):
M
typo  
myrrc 已提交
648 649
        raise Exception(
            "Server is not responding. Cannot execute 'SELECT 1' query. \
A
Alexander Kuzmenkov 已提交
650
            Note: if you are using split build, you may have to specify -c option.")
M
typo  
myrrc 已提交
651

A
alesapin 已提交
652
    build_flags = collect_build_flags(args.client)
653
    if args.antlr:
A
Alexander Tokmakov 已提交
654 655 656
        build_flags.append(BuildFlags.ANTLR)
    if args.replicated_database:
        build_flags.append(BuildFlags.DATABASE_REPLICATED)
M
typo  
myrrc 已提交
657

A
alesapin 已提交
658 659 660
    if args.use_skip_list:
        tests_to_skip_from_list = collect_tests_to_skip(args.skip_list_path, build_flags)
    else:
A
Fixes  
alesapin 已提交
661
        tests_to_skip_from_list = set([])
A
alesapin 已提交
662

A
alesapin 已提交
663 664 665 666
    if args.skip:
        args.skip = set(args.skip) | tests_to_skip_from_list
    else:
        args.skip = tests_to_skip_from_list
667

668 669 670
    if args.use_skip_list and not args.sequential:
        args.sequential = collect_sequential_list(args.skip_list_path)

P
proller 已提交
671 672 673
    base_dir = os.path.abspath(args.queries)
    tmp_dir = os.path.abspath(args.tmp)

674
    # Keep same default values as in queries/shell_config.sh
P
proller 已提交
675
    os.environ.setdefault("CLICKHOUSE_BINARY", args.binary)
676
    #os.environ.setdefault("CLICKHOUSE_CLIENT", args.client)
677
    os.environ.setdefault("CLICKHOUSE_CONFIG", args.configserver)
P
proller 已提交
678 679
    if args.configclient:
        os.environ.setdefault("CLICKHOUSE_CONFIG_CLIENT", args.configclient)
680

681
    # Force to print server warnings in stderr
682
    # Shell scripts could change logging level
683 684
    os.environ.setdefault("CLICKHOUSE_CLIENT_SERVER_LOGS_LEVEL", server_logs_level)

A
Alexey Milovidov 已提交
685 686 687 688
    # This code is bad as the time is not monotonic
    if args.global_time_limit:
        stop_time = time() + args.global_time_limit

689
    if args.zookeeper is None:
690
        _, out = subprocess.getstatusoutput(args.extract_from_config + " --try --config " + args.configserver + ' --key zookeeper | grep . | wc -l')
691 692 693 694 695 696
        try:
            if int(out) > 0:
                args.zookeeper = True
            else:
                args.zookeeper = False
        except ValueError:
697 698 699
            args.zookeeper = False

    if args.shard is None:
700
        _, out = subprocess.getstatusoutput(args.extract_from_config + " --try --config " + args.configserver + ' --key listen_host | grep -E "127.0.0.2|::"')
P
proller 已提交
701
        if out:
P
proller 已提交
702
            args.shard = True
P
proller 已提交
703 704
        else:
            args.shard = False
705

706
    if args.database and args.database != "test":
A
Azat Khuzhin 已提交
707
        clickhouse_proc_create = Popen(shlex.split(args.client), stdin=PIPE, stdout=PIPE, stderr=PIPE, universal_newlines=True)
A
Alexander Tokmakov 已提交
708
        clickhouse_proc_create.communicate(("CREATE DATABASE IF NOT EXISTS " + args.database + get_db_engine(args, args.database)))
709

A
Azat Khuzhin 已提交
710
    clickhouse_proc_create = Popen(shlex.split(args.client), stdin=PIPE, stdout=PIPE, stderr=PIPE, universal_newlines=True)
A
Alexander Tokmakov 已提交
711
    clickhouse_proc_create.communicate(("CREATE DATABASE IF NOT EXISTS test" + get_db_engine(args, 'test')))
712 713 714

    def is_test_from_dir(suite_dir, case):
        case_file = os.path.join(suite_dir, case)
715
        (_, ext) = os.path.splitext(case)
A
Alexander Kuzmenkov 已提交
716 717 718
        # We could also test for executable files (os.access(case_file, os.X_OK),
        # but it interferes with 01610_client_spawn_editor.editor, which is invoked
        # as a query editor in the test, and must be marked as executable.
719
        return os.path.isfile(case_file) and (ext in ['.sql', '.sh', '.py', '.expect'])
P
proller 已提交
720

721
    def sute_key_func(item):
722 723
        if args.order == 'random':
            return random.random()
724

725 726
        if -1 == item.find('_'):
            return 99998, ''
727

728
        prefix, suffix = item.split('_', 1)
729

730 731 732 733
        try:
            return int(prefix), suffix
        except ValueError:
            return 99997, ''
734

735
    total_tests_run = 0
736
    for suite in sorted(os.listdir(base_dir), key=sute_key_func):
737 738 739 740 741 742 743
        if SERVER_DIED:
            break

        suite_dir = os.path.join(base_dir, suite)
        suite_re_obj = re.search('^[0-9]+_(.*)$', suite)
        if not suite_re_obj: #skip .gitignore and so on
            continue
744 745 746 747 748

        suite_tmp_dir = os.path.join(tmp_dir, suite)
        if not os.path.exists(suite_tmp_dir):
            os.makedirs(suite_tmp_dir)

749 750 751
        suite = suite_re_obj.group(1)
        if os.path.isdir(suite_dir):

P
proller 已提交
752
            if 'stateful' in suite and not args.no_stateful and not is_data_present():
753
                print("Won't run stateful tests because test data wasn't loaded.")
754
                continue
755 756 757
            if 'stateless' in suite and args.no_stateless:
                print("Won't run stateless tests because they were manually disabled.")
                continue
758 759 760
            if 'stateful' in suite and args.no_stateful:
                print("Won't run stateful tests because they were manually disabled.")
                continue
761

P
proller 已提交
762 763 764
            # Reverse sort order: we want run newest test first.
            # And not reverse subtests
            def key_func(item):
765
                if args.order == 'random':
766
                    return random.random()
767 768 769 770 771 772

                reverse = 1 if args.order == 'asc' else -1

                if -1 == item.find('_'):
                    return 99998

773
                prefix, _ = item.split('_', 1)
774 775

                try:
A
Alexey Milovidov 已提交
776
                    return reverse * int(prefix)
777 778 779
                except ValueError:
                    return 99997

780
            all_tests = os.listdir(suite_dir)
A
Azat Khuzhin 已提交
781
            all_tests = [case for case in all_tests if is_test_from_dir(suite_dir, case)]
782 783
            if args.test:
                all_tests = [t for t in all_tests if any([re.search(r, t) for r in args.test])]
784
            all_tests = all_tests * args.test_runs
785
            all_tests.sort(key=key_func)
786

A
alesapin 已提交
787
            jobs = args.jobs
788 789 790 791 792 793 794 795
            parallel_tests = []
            sequential_tests = []
            for test in all_tests:
                if any(s in test for s in args.sequential):
                    sequential_tests.append(test)
                else:
                    parallel_tests.append(test)

A
alesapin 已提交
796 797 798 799 800 801 802 803 804 805 806 807 808 809
            if jobs > 1 and len(parallel_tests) > 0:
                print("Found", len(parallel_tests), "parallel tests and", len(sequential_tests), "sequential tests")
                run_n, run_total = args.parallel.split('/')
                run_n = float(run_n)
                run_total = float(run_total)
                tests_n = len(parallel_tests)
                if run_total > tests_n:
                    run_total = tests_n

                if jobs > tests_n:
                    jobs = tests_n
                if jobs > run_total:
                    run_total = jobs

810 811
                # Create two batches per process for more uniform execution time.
                batch_size = max(1, len(parallel_tests) // (jobs * 2))
A
alesapin 已提交
812 813 814
                parallel_tests_array = []
                for i in range(0, len(parallel_tests), batch_size):
                    parallel_tests_array.append((parallel_tests[i:i+batch_size], suite, suite_dir, suite_tmp_dir))
P
proller 已提交
815

816
                with closing(multiprocessing.Pool(processes=jobs)) as pool:
817
                    pool.map(run_tests_array, parallel_tests_array)
818

A
alesapin 已提交
819
                run_tests_array((sequential_tests, suite, suite_dir, suite_tmp_dir))
820 821
                total_tests_run += len(sequential_tests) + len(parallel_tests)
            else:
A
alesapin 已提交
822
                run_tests_array((all_tests, suite, suite_dir, suite_tmp_dir))
823
                total_tests_run += len(all_tests)
824

825
    if args.hung_check:
826 827

        # Some queries may execute in background for some time after test was finished. This is normal.
828
        for _ in range(1, 60):
A
alesapin 已提交
829 830
            timeout, processlist = get_processlist(args)
            if timeout or not processlist:
831 832 833
                break
            sleep(1)

A
alesapin 已提交
834 835 836 837 838
        if timeout or processlist:
            if processlist:
                print(colored("\nFound hung queries in processlist:", args, "red", attrs=["bold"]))
                print(processlist)
            else:
839
                print(colored("Seems like server hung and cannot respond to queries", args, "red", attrs=["bold"]))
840 841 842

            clickhouse_tcp_port = os.getenv("CLICKHOUSE_PORT_TCP", '9000')
            server_pid = get_server_pid(clickhouse_tcp_port)
A
fix  
Alexander Tokmakov 已提交
843
            bt = None
844 845
            if server_pid:
                print("\nLocated ClickHouse server process {} listening at TCP port {}".format(server_pid, clickhouse_tcp_port))
846
                print("\nCollecting stacktraces from all running threads with gdb:")
A
fix  
Alexander Tokmakov 已提交
847
                bt = get_stacktraces_from_gdb(server_pid)
848 849 850
                if len(bt) < 1000:
                    print("Got suspiciously small stacktraces: ", bt)
                    bt = None
A
fix  
Alexander Tokmakov 已提交
851 852 853 854
            if bt is None:
                print("\nCollecting stacktraces from system.stacktraces table:")
                bt = get_stacktraces_from_clickhouse(args.client)
            if bt is None:
855 856 857 858 859
                print(
                    colored(
                        "\nUnable to locate ClickHouse server process listening at TCP port {}. "
                        "It must have crashed or exited prematurely!".format(clickhouse_tcp_port),
                        args, "red", attrs=["bold"]))
A
fix  
Alexander Tokmakov 已提交
860 861 862
            else:
                print(bt)

863 864

            exit_code = 1
865
        else:
866
            print(colored("\nNo queries hung.", args, "green", attrs=["bold"]))
867

868 869 870
    if total_tests_run == 0:
        print("No tests were run.")
        sys.exit(1)
A
Alexander Tokmakov 已提交
871 872
    else:
        print("All tests have finished.")
873

874 875
    sys.exit(exit_code)

876

A
alesapin 已提交
877
def find_binary(name):
878 879
    if os.path.exists(name) and os.access(name, os.X_OK):
        return True
A
alesapin 已提交
880 881 882 883 884 885
    paths = os.environ.get("PATH").split(':')
    for path in paths:
        if os.access(os.path.join(path, name), os.X_OK):
            return True

    # maybe it wasn't in PATH
P
proller 已提交
886 887 888 889 890 891
    if os.access(os.path.join('/usr/local/bin', name), os.X_OK):
        return True
    if os.access(os.path.join('/usr/bin', name), os.X_OK):
        return True
    return False

892

893
def get_additional_client_options(args):
894 895 896 897 898 899 900 901 902 903 904
    if args.client_option:
        return ' '.join('--' + option for option in args.client_option)

    return ''


def get_additional_client_options_url(args):
    if args.client_option:
        return '&'.join(args.client_option)

    return ''
905 906


A
alesapin 已提交
907 908 909 910 911 912
def collect_tests_to_skip(skip_list_path, build_flags):
    result = set([])
    if not os.path.exists(skip_list_path):
        return result

    with open(skip_list_path, 'r') as skip_list_file:
913 914 915
        content = skip_list_file.read()
        # allows to have comments in skip_list.json
        skip_dict = json.loads(json_minify(content))
A
alesapin 已提交
916 917
        for build_flag in build_flags:
            result |= set(skip_dict[build_flag])
A
alesapin 已提交
918 919 920 921

    if len(result) > 0:
        print("Found file with skip-list {}, {} test will be skipped".format(skip_list_path, len(result)))

A
alesapin 已提交
922 923
    return result

924 925 926 927 928 929 930 931 932

def collect_sequential_list(skip_list_path):
    if not os.path.exists(skip_list_path):
        return set([])

    with open(skip_list_path, 'r') as skip_list_file:
        content = skip_list_file.read()
        # allows to have comments in skip_list.json
        skip_dict = json.loads(json_minify(content))
A
alesapin 已提交
933 934
        if 'parallel' in skip_dict:
            return skip_dict['parallel']
935 936 937
        return set([])


938
if __name__ == '__main__':
939 940 941
    parser=ArgumentParser(description='ClickHouse functional tests')
    parser.add_argument('-q', '--queries', help='Path to queries dir')
    parser.add_argument('--tmp', help='Path to tmp dir')
M
typo  
myrrc 已提交
942 943

    parser.add_argument('-b', '--binary', default='clickhouse',
A
Alexander Kuzmenkov 已提交
944
        help='Path to clickhouse (if monolithic build, clickhouse-server otherwise) binary or name of binary in PATH')
M
typo  
myrrc 已提交
945 946

    parser.add_argument('-c', '--client',
A
Alexander Kuzmenkov 已提交
947
        help='Path to clickhouse-client (if split build, useless otherwise) binary of name of binary in PATH')
M
typo  
myrrc 已提交
948

949 950 951 952 953
    parser.add_argument('--extract_from_config', help='extract-from-config program')
    parser.add_argument('--configclient', help='Client config (if you use not default ports)')
    parser.add_argument('--configserver', default= '/etc/clickhouse-server/config.xml', help='Preprocessed server config')
    parser.add_argument('-o', '--output', help='Output xUnit compliant test report directory')
    parser.add_argument('-t', '--timeout', type=int, default=600, help='Timeout for each test case in seconds')
A
Alexey Milovidov 已提交
954
    parser.add_argument('--global_time_limit', type=int, help='Stop if executing more than specified time (after current test finished)')
955
    parser.add_argument('test', nargs='*', help='Optional test case name regex')
956 957
    parser.add_argument('-d', '--disabled', action='store_true', default=False, help='Also run disabled tests')
    parser.add_argument('--stop', action='store_true', default=None, dest='stop', help='Stop on network errors')
958
    parser.add_argument('--order', default='desc', choices=['asc', 'desc', 'random'], help='Run order')
959 960
    parser.add_argument('--testname', action='store_true', default=None, dest='testname', help='Make query with test name before test run')
    parser.add_argument('--hung-check', action='store_true', default=False)
A
alesapin 已提交
961
    parser.add_argument('--force-color', action='store_true', default=False)
962
    parser.add_argument('--database', help='Database for tests (random name test_XXXXXX by default)')
A
fix  
Alexander Tokmakov 已提交
963 964
    parser.add_argument('--no-drop-if-fail', action='store_true', help='Do not drop database for test if test has failed')
    parser.add_argument('--show-db-name', action='store_true', help='Do not replace random database name with "default"')
P
proller 已提交
965
    parser.add_argument('--parallel', default='1/1', help='One parallel test run number/total')
P
proller 已提交
966
    parser.add_argument('-j', '--jobs', default=1, nargs='?', type=int, help='Run all tests in parallel')
967
    parser.add_argument('--test-runs', default=1, nargs='?', type=int, help='Run each test many times (useful for e.g. flaky check)')
968
    parser.add_argument('-U', '--unified', default=3, type=int, help='output NUM lines of unified context')
969
    parser.add_argument('-r', '--server-check-retries', default=30, type=int, help='Num of tries to execute SELECT 1 before tests started')
A
alesapin 已提交
970 971
    parser.add_argument('--skip-list-path', help="Path to skip-list file")
    parser.add_argument('--use-skip-list', action='store_true', default=False, help="Use skip list to skip tests if found")
972
    parser.add_argument('--db-engine', help='Database engine name')
A
Alexander Tokmakov 已提交
973
    parser.add_argument('--replicated-database', action='store_true', default=False, help='Run tests with Replicated database engine')
974

975
    parser.add_argument('--antlr', action='store_true', default=False, dest='antlr', help='Use new ANTLR parser in tests')
976
    parser.add_argument('--no-stateless', action='store_true', help='Disable all stateless tests')
977
    parser.add_argument('--no-stateful', action='store_true', help='Disable all stateful tests')
978
    parser.add_argument('--skip', nargs='+', help="Skip these tests")
979
    parser.add_argument('--sequential', nargs='+', help="Run these tests sequentially even if --parallel specified")
980
    parser.add_argument('--no-long', action='store_false', dest='no_long', help='Do not run long tests')
981
    parser.add_argument('--client-option', nargs='+', help='Specify additional client argument')
982
    parser.add_argument('--print-time', action='store_true', dest='print_time', help='Print test time')
983 984 985 986 987 988
    group=parser.add_mutually_exclusive_group(required=False)
    group.add_argument('--zookeeper', action='store_true', default=None, dest='zookeeper', help='Run zookeeper related tests')
    group.add_argument('--no-zookeeper', action='store_false', default=None, dest='zookeeper', help='Do not run zookeeper related tests')
    group=parser.add_mutually_exclusive_group(required=False)
    group.add_argument('--shard', action='store_true', default=None, dest='shard', help='Run sharding related tests (required to clickhouse-server listen 127.0.0.2 127.0.0.3)')
    group.add_argument('--no-shard', action='store_false', default=None, dest='shard', help='Do not run shard related tests')
989 990

    args = parser.parse_args()
991

A
Alexander Kuzmenkov 已提交
992
    if args.queries and not os.path.isdir(args.queries):
A
Alexander Kuzmenkov 已提交
993
        print("Cannot access the specified directory with queries (" + args.queries + ")", file=sys.stderr)
994
        sys.exit(1)
A
Alexander Kuzmenkov 已提交
995 996 997

    # Autodetect the directory with queries if not specified
    if args.queries is None:
998
        args.queries = 'queries'
A
Alexander Kuzmenkov 已提交
999 1000 1001 1002 1003 1004 1005

    if not os.path.isdir(args.queries):
        # If we're running from the repo
        args.queries = os.path.join(os.path.dirname(os.path.abspath( __file__ )), 'queries')

    if not os.path.isdir(args.queries):
        # Next we're going to try some system directories, don't write 'stdout' files into them.
1006 1007
        if args.tmp is None:
            args.tmp = '/tmp/clickhouse-test'
A
Alexander Kuzmenkov 已提交
1008 1009 1010 1011 1012 1013 1014

        args.queries = '/usr/local/share/clickhouse-test/queries'

    if not os.path.isdir(args.queries):
        args.queries = '/usr/share/clickhouse-test/queries'

    if not os.path.isdir(args.queries):
1015
        print("Failed to detect path to the queries directory. Please specify it with '--queries' option.", file=sys.stderr)
1016
        sys.exit(1)
A
alesapin 已提交
1017

A
Alexander Kuzmenkov 已提交
1018 1019
    print("Using queries from '" + args.queries + "' directory")

A
alesapin 已提交
1020 1021
    if args.skip_list_path is None:
        args.skip_list_path = os.path.join(args.queries, 'skip_list.json')
A
alesapin 已提交
1022

1023 1024 1025
    if args.sequential is None:
        args.sequential = set([])

P
proller 已提交
1026 1027
    if args.tmp is None:
        args.tmp = args.queries
1028
    if args.client is None:
A
alesapin 已提交
1029
        if find_binary(args.binary + '-client'):
P
proller 已提交
1030
            args.client = args.binary + '-client'
M
typo  
myrrc 已提交
1031

A
Alexander Kuzmenkov 已提交
1032
            print("Using " + args.client + " as client program (expecting split build)")
A
alesapin 已提交
1033
        elif find_binary(args.binary):
P
proller 已提交
1034
            args.client = args.binary + ' client'
M
typo  
myrrc 已提交
1035

A
Alexander Kuzmenkov 已提交
1036
            print("Using " + args.client + " as client program (expecting monolithic build)")
1037
        else:
M
typo  
myrrc 已提交
1038
            print("No 'clickhouse' or 'clickhouse-client' client binary found", file=sys.stderr)
A
alesapin 已提交
1039
            parser.print_help()
1040
            sys.exit(1)
A
alesapin 已提交
1041

P
proller 已提交
1042
        if args.configclient:
1043 1044 1045 1046 1047
            args.client += ' --config-file=' + args.configclient
        if os.getenv("CLICKHOUSE_HOST"):
            args.client += ' --host=' + os.getenv("CLICKHOUSE_HOST")
        if os.getenv("CLICKHOUSE_PORT_TCP"):
            args.client += ' --port=' + os.getenv("CLICKHOUSE_PORT_TCP")
1048 1049 1050
        if os.getenv("CLICKHOUSE_DATABASE"):
            args.client += ' --database=' + os.getenv("CLICKHOUSE_DATABASE")

1051
    if args.client_option:
1052 1053
        # Set options for client
        if 'CLICKHOUSE_CLIENT_OPT' in os.environ:
1054
            os.environ['CLICKHOUSE_CLIENT_OPT'] += ' '
1055
        else:
1056
            os.environ['CLICKHOUSE_CLIENT_OPT'] = ''
1057 1058 1059 1060 1061

        os.environ['CLICKHOUSE_CLIENT_OPT'] += get_additional_client_options(args)

        # Set options for curl
        if 'CLICKHOUSE_URL_PARAMS' in os.environ:
1062
            os.environ['CLICKHOUSE_URL_PARAMS'] += '&'
1063
        else:
1064
            os.environ['CLICKHOUSE_URL_PARAMS'] = ''
1065 1066 1067

        os.environ['CLICKHOUSE_URL_PARAMS'] += get_additional_client_options_url(args)

1068
    if args.antlr:
1069
        if 'CLICKHOUSE_CLIENT_OPT' in os.environ:
1070
            os.environ['CLICKHOUSE_CLIENT_OPT'] += ' --use_antlr_parser=1'
1071
        else:
1072
            os.environ['CLICKHOUSE_CLIENT_OPT'] = '--use_antlr_parser=1'
1073

P
proller 已提交
1074 1075 1076 1077 1078 1079
    if args.extract_from_config is None:
        if os.access(args.binary + '-extract-from-config', os.X_OK):
            args.extract_from_config = args.binary + '-extract-from-config'
        else:
            args.extract_from_config = args.binary + ' extract-from-config'

P
proller 已提交
1080
    if args.jobs is None:
1081
        args.jobs = multiprocessing.cpu_count()
P
proller 已提交
1082

1083
    main(args)