clickhouse-test 27.8 KB
Newer Older
1
#!/usr/bin/env python2
2
from __future__ import print_function
3
import sys
4 5 6 7 8 9 10
import os
import os.path
import re

from argparse import ArgumentParser
from argparse import FileType
from pprint import pprint
11
import shlex
12
import subprocess
13 14 15 16
from subprocess import check_call
from subprocess import Popen
from subprocess import PIPE
from subprocess import CalledProcessError
17 18 19
from datetime import datetime
from time import sleep
from errno import ESRCH
20 21 22 23
try:
    import termcolor
except ImportError:
    termcolor = None
24
from random import random
P
proller 已提交
25
import commands
P
proller 已提交
26
import multiprocessing
P
proller 已提交
27
from contextlib import closing
28

29

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

37

38 39 40 41 42 43 44 45
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:
            return unichr(int(s, base))
        return default
46 47 48
    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)
49 50
    return s

51 52 53

def run_single_test(args, ext, server_logs_level, client_options, case_file, stdout_file, stderr_file):

54 55
    # print(client_options)

56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
    if args.database:
        database = args.database
        os.environ.setdefault("CLICKHOUSE_DATABASE", database)

    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):
            import random
            import string
            alphabet = string.ascii_lowercase + string.digits
            return ''.join(random.choice(alphabet) for _ in range(length))
        database = 'test_{suffix}'.format(suffix=random_str())

        clickhouse_proc_create = Popen(shlex.split(args.client), stdin=PIPE, stdout=PIPE, stderr=PIPE)
        clickhouse_proc_create.communicate("CREATE DATABASE " + database)

        os.environ["CLICKHOUSE_DATABASE"] = database

75
    params = {
76
        'client': args.client + ' --database=' + database,
77 78 79 80 81 82 83
        'logs_level': server_logs_level,
        'options': client_options,
        'test': case_file,
        'stdout': stdout_file,
        'stderr': stderr_file,
    }

84 85
    pattern = '{test} > {stdout} 2> {stderr}'

A
alesapin 已提交
86
    if ext == '.sql':
87 88 89 90
        pattern = "{client} --send_logs_level={logs_level} --testmode --multiquery {options} < " + pattern

    command = pattern.format(**params)
    #print(command)
A
alesapin 已提交
91

92
    proc = Popen(command, shell=True, env=os.environ)
A
alesapin 已提交
93 94 95 96
    start_time = datetime.now()
    while (datetime.now() - start_time).total_seconds() < args.timeout and proc.poll() is None:
        sleep(0.01)

97
    if not args.database:
98
        clickhouse_proc_create = Popen(shlex.split(args.client), stdin=PIPE, stdout=PIPE, stderr=PIPE)
99
        clickhouse_proc_create.communicate("DROP DATABASE " + database)
100

101 102
    total_time = (datetime.now() - start_time).total_seconds()

A
alexey-milovidov 已提交
103
    # Normalize randomized database names in stdout, stderr files.
104 105
    os.system("LC_ALL=C sed -i -e 's/{test_db}/default/g' {file}".format(test_db=database, file=stdout_file))
    os.system("LC_ALL=C sed -i -e 's/{test_db}/default/g' {file}".format(test_db=database, file=stderr_file))
106

A
alesapin 已提交
107 108 109 110 111
    stdout = open(stdout_file, 'r').read() if os.path.exists(stdout_file) else ''
    stdout = unicode(stdout, errors='replace', encoding='utf-8')
    stderr = open(stderr_file, 'r').read() if os.path.exists(stderr_file) else ''
    stderr = unicode(stderr, errors='replace', encoding='utf-8')

112
    return proc, stdout, stderr, total_time
A
alesapin 已提交
113

114

A
alesapin 已提交
115 116 117
def need_retry(stderr):
    return any(msg in stderr for msg in MESSAGES_TO_RETRY)

118

119 120 121 122 123 124
def get_processlist(client_cmd):
    try:
        return subprocess.check_output("{} --query 'SHOW PROCESSLIST FORMAT Vertical'".format(client_cmd), shell=True)
    except:
        return "" #  server seems dead

125

A
alesapin 已提交
126
# collect server stacktraces using gdb
127
def get_stacktraces_from_gdb(server_pid):
128
    cmd = "gdb -batch -ex 'thread apply all backtrace' -p {}".format(server_pid)
129 130 131
    try:
        return subprocess.check_output(cmd, shell=True)
    except Exception as ex:
132 133 134
        return "Error occured while receiving stack traces from gdb: {}".format(str(ex))


A
alesapin 已提交
135
# collect server stacktraces from system.stack_trace table
136
# it does not work in Sandbox
137 138
def get_stacktraces_from_clickhouse(client):
    try:
139
        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 FROM system.stack_trace format Vertical\"".format(client), shell=True)
140 141
    except Exception as ex:
        return "Error occured while receiving stack traces from client: {}".format(str(ex))
142

143

144
def get_server_pid(server_tcp_port):
145
    cmd = "lsof -i tcp:{port} -s tcp:LISTEN -Fp | awk '/^p[0-9]+$/{{print substr($0, 2)}}'".format(port=server_tcp_port)
146 147 148
    try:
        output = subprocess.check_output(cmd, shell=True)
        if output:
149
            return int(output)
150 151 152 153 154
        else:
            return None # server dead
    except Exception as ex:
        return None

155

P
proller 已提交
156
def colored(text, args, color=None, on_color=None, attrs=None):
157
       if termcolor and (sys.stdout.isatty() or args.force_color):
P
proller 已提交
158 159 160 161
           return termcolor.colored(text, color, on_color, attrs)
       else:
           return text

162

P
proller 已提交
163 164 165
SERVER_DIED = False
exit_code = 0

166

167
# def run_tests_array(all_tests, suite, suite_dir, suite_tmp_dir, run_total):
168 169
def run_tests_array(all_tests_with_params):
    all_tests, suite, suite_dir, suite_tmp_dir, run_total = all_tests_with_params
P
proller 已提交
170
    global exit_code
P
proller 已提交
171
    global SERVER_DIED
172

P
proller 已提交
173 174 175 176 177 178 179 180 181 182 183 184 185
    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
186

187 188
    client_options = get_additional_client_options(args)

189 190 191 192
    def print_test_time(test_time):
        if args.print_time:
            print(" {0:.2f} sec.".format(test_time), end='')

193 194
    if len(all_tests):
        print("\nRunning {} {} tests.".format(len(all_tests), suite) + "\n")
A
alesapin 已提交
195

196
    for case in all_tests:
P
proller 已提交
197 198
        if SERVER_DIED:
            break
A
alesapin 已提交
199

P
proller 已提交
200 201 202 203 204 205 206 207 208 209 210
        case_file = os.path.join(suite_dir, case)
        (name, ext) = os.path.splitext(case)

        try:
            sys.stdout.write("{0:72}".format(name + ": "))
            if run_total == 1:
                sys.stdout.flush()

            if args.skip and any(s in name for s in args.skip):
                print(MSG_SKIPPED + " - skip")
                skipped_total += 1
211 212
            elif not args.zookeeper and ('zookeeper' in name
                    or 'replica' in name):
P
proller 已提交
213 214
                print(MSG_SKIPPED + " - no zookeeper")
                skipped_total += 1
A
akuzm 已提交
215 216 217
            elif not args.shard and ('shard' in name
                    or 'distributed' in name
                    or 'global' in name):
P
proller 已提交
218 219
                print(MSG_SKIPPED + " - no shard")
                skipped_total += 1
C
CurtizJ 已提交
220 221 222 223 224
            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):
P
proller 已提交
225 226 227 228 229 230 231 232 233 234 235
                print(MSG_SKIPPED + " - no long")
                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()
                    print(MSG_SKIPPED + " - " + message)
                else:

                    if args.testname:
236
                        clickhouse_proc = Popen(shlex.split(args.client), stdin=PIPE, stdout=PIPE, stderr=PIPE)
P
proller 已提交
237 238 239 240 241 242
                        clickhouse_proc.communicate("SELECT 'Running test {suite}/{case} from pid={pid}';".format(pid = os.getpid(), case = case, suite = suite))

                    reference_file = os.path.join(suite_dir, name) + '.reference'
                    stdout_file = os.path.join(suite_tmp_dir, name) + '.stdout'
                    stderr_file = os.path.join(suite_tmp_dir, name) + '.stderr'

243
                    proc, stdout, stderr, total_time = run_single_test(args, ext, server_logs_level, client_options, case_file, stdout_file, stderr_file)
P
proller 已提交
244 245 246 247 248 249 250 251
                    if proc.returncode is None:
                        try:
                            proc.kill()
                        except OSError as e:
                            if e.errno != ESRCH:
                                raise

                        failures += 1
252 253 254
                        print(MSG_FAIL, end='')
                        print_test_time(total_time)
                        print(" - Timeout!")
P
proller 已提交
255 256 257
                    else:
                        counter = 1
                        while proc.returncode != 0 and need_retry(stderr):
258
                            proc, stdout, stderr, total_time = run_single_test(args, ext, server_logs_level, client_options, case_file, stdout_file, stderr_file)
P
proller 已提交
259 260 261 262 263 264 265 266
                            sleep(2**counter)
                            counter += 1
                            if counter > 6:
                                break

                        if proc.returncode != 0:
                            failures += 1
                            failures_chain += 1
267 268 269
                            print(MSG_FAIL, end='')
                            print_test_time(total_time)
                            print(" - return code {}".format(proc.returncode))
P
proller 已提交
270 271

                            if stderr:
272
                                print(stderr.encode('utf-8'))
P
proller 已提交
273 274 275 276 277 278 279

                            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

                        elif stderr:
                            failures += 1
                            failures_chain += 1
280 281 282
                            print(MSG_FAIL, end='')
                            print_test_time(total_time)
                            print(" - having stderror:\n{}".format(stderr.encode('utf-8')))
P
proller 已提交
283 284 285
                        elif 'Exception' in stdout:
                            failures += 1
                            failures_chain += 1
286 287 288
                            print(MSG_FAIL, end='')
                            print_test_time(total_time)
                            print(" - having exception:\n{}".format(stdout.encode('utf-8')))
P
proller 已提交
289
                        elif not os.path.isfile(reference_file):
290 291 292
                            print(MSG_UNKNOWN, end='')
                            print_test_time(total_time)
                            print(" - no reference file")
P
proller 已提交
293 294 295 296
                        else:
                            result_is_different = subprocess.call(['diff', '-q', reference_file, stdout_file], stdout = PIPE)

                            if result_is_different:
297
                                diff = Popen(['diff', '-U', str(args.unified), reference_file, stdout_file], stdout = PIPE).communicate()[0]
P
proller 已提交
298
                                failures += 1
299 300 301
                                print(MSG_FAIL, end='')
                                print_test_time(total_time)
                                print(" - result differs with reference:\n{}".format(diff))
P
proller 已提交
302 303 304
                            else:
                                passed_total += 1
                                failures_chain = 0
305 306 307
                                print(MSG_OK, end='')
                                print_test_time(total_time)
                                print()
P
proller 已提交
308 309 310 311
                                if os.path.exists(stdout_file):
                                    os.remove(stdout_file)
                                if os.path.exists(stderr_file):
                                    os.remove(stderr_file)
312
        except KeyboardInterrupt as e:
P
proller 已提交
313
            print(colored("Break tests execution", args, "red"))
314
            raise e
P
proller 已提交
315 316 317 318
        except:
            import traceback
            exc_type, exc_value, tb = sys.exc_info()
            failures += 1
319
            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 已提交
320 321 322 323 324 325 326

        if failures_chain >= 20:
            break

    failures_total = failures_total + failures

    if failures_total > 0:
327
        print(colored("\nHaving {failures_total} errors! {passed_total} tests passed. {skipped_total} tests skipped.".format(passed_total = passed_total, skipped_total = skipped_total, failures_total = failures_total), args, "red", attrs=["bold"]))
P
proller 已提交
328 329 330
        exit_code = 1
    else:
        print(colored("\n{passed_total} tests passed. {skipped_total} tests skipped.".format(passed_total = passed_total, skipped_total = skipped_total), args, "green", attrs=["bold"]))
A
alesapin 已提交
331

332

P
proller 已提交
333 334
server_logs_level = "warning"

335

336 337 338 339 340 341 342 343 344 345 346 347 348
def check_server_started(client, retry_count):
    while retry_count > 0:
        clickhouse_proc = Popen(shlex.split(client), stdin=PIPE, stdout=PIPE, stderr=PIPE)
        (stdout, stderr) = clickhouse_proc.communicate("SELECT 1")
        if clickhouse_proc.returncode != 0 or not stdout.startswith("1"):
            retry_count -= 1
            sleep(0.5)
        else:
            return True

    return False


P
proller 已提交
349 350 351 352
def main(args):
    global SERVER_DIED
    global exit_code
    global server_logs_level
A
alesapin 已提交
353

354
    def is_data_present():
P
proller 已提交
355 356
        clickhouse_proc = Popen(shlex.split(args.client), stdin=PIPE, stdout=PIPE, stderr=PIPE)
        (stdout, stderr) = clickhouse_proc.communicate("EXISTS TABLE test.hits")
P
proller 已提交
357
        if clickhouse_proc.returncode != 0:
P
proller 已提交
358
            raise CalledProcessError(clickhouse_proc.returncode, args.client, stderr)
359 360 361

        return stdout.startswith('1')

362 363 364
    if not check_server_started(args.client, args.server_check_retries):
        raise Exception("clickhouse-server is not responding. Cannot execute 'SELECT 1' query.")

P
proller 已提交
365 366 367
    base_dir = os.path.abspath(args.queries)
    tmp_dir = os.path.abspath(args.tmp)

368
    # Keep same default values as in queries/shell_config.sh
P
proller 已提交
369
    os.environ.setdefault("CLICKHOUSE_BINARY", args.binary)
P
proller 已提交
370
    #os.environ.setdefault("CLICKHOUSE_CLIENT", args.client)
371
    os.environ.setdefault("CLICKHOUSE_CONFIG", args.configserver)
P
proller 已提交
372 373
    if args.configclient:
        os.environ.setdefault("CLICKHOUSE_CONFIG_CLIENT", args.configclient)
P
proller 已提交
374
    os.environ.setdefault("CLICKHOUSE_TMP", tmp_dir)
375

376
    # Force to print server warnings in stderr
377
    # Shell scripts could change logging level
378 379
    os.environ.setdefault("CLICKHOUSE_CLIENT_SERVER_LOGS_LEVEL", server_logs_level)

380
    if args.zookeeper is None:
381
        code, out = commands.getstatusoutput(args.extract_from_config + " --try --config " + args.configserver + ' --key zookeeper | grep . | wc -l')
382 383 384 385 386 387
        try:
            if int(out) > 0:
                args.zookeeper = True
            else:
                args.zookeeper = False
        except ValueError:
388 389 390
            args.zookeeper = False

    if args.shard is None:
P
proller 已提交
391
        code, out = commands.getstatusoutput(args.extract_from_config + " --try --config " + args.configserver + ' --key listen_host | grep -E "127.0.0.2|::"')
P
proller 已提交
392
        if out:
P
proller 已提交
393
            args.shard = True
P
proller 已提交
394 395
        else:
            args.shard = False
396

397
    if args.database and args.database != "test":
398
        clickhouse_proc_create = Popen(shlex.split(args.client), stdin=PIPE, stdout=PIPE, stderr=PIPE)
399 400 401 402
        clickhouse_proc_create.communicate("CREATE DATABASE IF NOT EXISTS " + args.database)

    clickhouse_proc_create = Popen(shlex.split(args.client), stdin=PIPE, stdout=PIPE, stderr=PIPE)
    clickhouse_proc_create.communicate("CREATE DATABASE IF NOT EXISTS test")
403 404 405 406 407

    def is_test_from_dir(suite_dir, case):
        case_file = os.path.join(suite_dir, case)
        (name, ext) = os.path.splitext(case)
        return os.path.isfile(case_file) and (ext == '.sql' or ext == '.sh' or ext == '.py')
P
proller 已提交
408

409 410 411 412 413 414 415 416 417 418 419 420 421 422
    def sute_key_func(item):
       if args.order == 'random':
             return random()

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

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

       try:
           return int(prefix), suffix
       except ValueError:
           return 99997

423
    total_tests_run = 0
424
    for suite in sorted(os.listdir(base_dir), key=sute_key_func):
425 426 427 428 429 430 431
        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
432 433 434 435 436

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

437 438 439
        suite = suite_re_obj.group(1)
        if os.path.isdir(suite_dir):

P
proller 已提交
440
            if 'stateful' in suite and not args.no_stateful and not is_data_present():
441
                print("Won't run stateful tests because test data wasn't loaded.")
442
                continue
443 444 445
            if 'stateless' in suite and args.no_stateless:
                print("Won't run stateless tests because they were manually disabled.")
                continue
446 447 448
            if 'stateful' in suite and args.no_stateful:
                print("Won't run stateful tests because they were manually disabled.")
                continue
449

P
proller 已提交
450 451 452
            # Reverse sort order: we want run newest test first.
            # And not reverse subtests
            def key_func(item):
453
                if args.order == 'random':
454
                    return random()
455 456 457 458 459 460

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

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

P
proller 已提交
461
                prefix, suffix = item.split('_', 1)
462 463 464 465 466 467

                try:
                    return reverse * int(prefix), suffix
                except ValueError:
                    return 99997

468 469
            all_tests = os.listdir(suite_dir)
            all_tests = filter(lambda case: is_test_from_dir(suite_dir, case), all_tests)
470 471
            if args.test:
                all_tests = [t for t in all_tests if any([re.search(r, t) for r in args.test])]
472
            all_tests.sort(key=key_func)
473

474 475 476
            run_n, run_total = args.parallel.split('/')
            run_n = float(run_n)
            run_total = float(run_total)
P
proller 已提交
477 478 479 480 481
            tests_n = len(all_tests)
            if run_total > tests_n:
                run_total = tests_n
            if run_n > run_total:
                continue
482

P
proller 已提交
483
            jobs = args.jobs
P
proller 已提交
484 485
            if jobs > tests_n:
                jobs = tests_n
P
proller 已提交
486 487
            if jobs > run_total:
                run_total = jobs
P
proller 已提交
488

P
proller 已提交
489
            all_tests_array = []
490 491 492 493
            for n in range(1, 1 + int(run_total)):
                start = int(tests_n / run_total * (n - 1))
                end = int(tests_n / run_total * n)
                all_tests_array.append([all_tests[start : end], suite, suite_dir, suite_tmp_dir, run_total])
P
proller 已提交
494

P
proller 已提交
495
            if jobs > 1:
496 497 498
                with closing(multiprocessing.Pool(processes=jobs)) as pool:
                    pool.map(run_tests_array, all_tests_array)
                    pool.terminate()
P
proller 已提交
499
            else:
500
                run_tests_array(all_tests_array[int(run_n)-1])
501

502 503
            total_tests_run += tests_n

504
    if args.hung_check:
505 506 507 508 509 510 511 512

        # Some queries may execute in background for some time after test was finished. This is normal.
        for n in range(1, 60):
            processlist = get_processlist(args.client)
            if not processlist:
                break
            sleep(1)

513 514 515 516 517 518 519 520
        if processlist:
            print(colored("\nFound hung queries in processlist:", args, "red", attrs=["bold"]))
            print(processlist)

            clickhouse_tcp_port = os.getenv("CLICKHOUSE_PORT_TCP", '9000')
            server_pid = get_server_pid(clickhouse_tcp_port)
            if server_pid:
                print("\nLocated ClickHouse server process {} listening at TCP port {}".format(server_pid, clickhouse_tcp_port))
521 522 523 524 525

                # It does not work in Sandbox
                #print("\nCollecting stacktraces from system.stacktraces table:")
                #print(get_stacktraces_from_clickhouse(args.client))

526 527
                print("\nCollecting stacktraces from all running threads with gdb:")
                print(get_stacktraces_from_gdb(server_pid))
528
            else:
529 530 531 532 533 534 535
                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"]))

            exit_code = 1
536
        else:
537
            print(colored("\nNo queries hung.", args, "green", attrs=["bold"]))
538

539 540 541 542
    if total_tests_run == 0:
        print("No tests were run.")
        sys.exit(1)

543 544
    sys.exit(exit_code)

545

A
alesapin 已提交
546
def find_binary(name):
547 548
    if os.path.exists(name) and os.access(name, os.X_OK):
        return True
A
alesapin 已提交
549 550 551 552 553 554
    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 已提交
555 556 557 558 559 560
    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

561

562
def get_additional_client_options(args):
563 564 565 566 567 568 569 570 571 572 573
    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 ''
574 575


576
if __name__ == '__main__':
577 578 579
    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')
580
    parser.add_argument('-b', '--binary', default='clickhouse', help='Path to clickhouse binary or name of binary in PATH')
581 582 583 584 585 586
    parser.add_argument('-c', '--client', help='Client program')
    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')
587
    parser.add_argument('test', nargs='*', help='Optional test case name regex')
588 589
    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')
590
    parser.add_argument('--order', default='desc', choices=['asc', 'desc', 'random'], help='Run order')
591 592
    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 已提交
593
    parser.add_argument('--force-color', action='store_true', default=False)
594
    parser.add_argument('--database', help='Database for tests (random name test_XXXXXX by default)')
P
proller 已提交
595
    parser.add_argument('--parallel', default='1/1', help='One parallel test run number/total')
P
proller 已提交
596
    parser.add_argument('-j', '--jobs', default=1, nargs='?', type=int, help='Run all tests in parallel')
597
    parser.add_argument('-U', '--unified', default=3, type=int, help='output NUM lines of unified context')
598
    parser.add_argument('-r', '--server-check-retries', default=30, type=int, help='Num of tries to execute SELECT 1 before tests started')
599 600

    parser.add_argument('--no-stateless', action='store_true', help='Disable all stateless tests')
601
    parser.add_argument('--no-stateful', action='store_true', help='Disable all stateful tests')
602 603
    parser.add_argument('--skip', nargs='+', help="Skip these tests")
    parser.add_argument('--no-long', action='store_false', dest='no_long', help='Do not run long tests')
604
    parser.add_argument('--client-option', nargs='+', help='Specify additional client argument')
605
    parser.add_argument('--print-time', action='store_true', dest='print_time', help='Print test time')
606 607 608 609 610 611
    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')
612 613

    args = parser.parse_args()
614 615 616

    if args.queries is None and os.path.isdir('queries'):
        args.queries = 'queries'
P
proller 已提交
617
    elif args.queries is None:
P
proller 已提交
618 619 620 621
        if (os.path.isdir('/usr/local/share/clickhouse-test/queries')):
            args.queries = '/usr/local/share/clickhouse-test/queries'
        if (args.queries is None and os.path.isdir('/usr/share/clickhouse-test/queries')):
            args.queries = '/usr/share/clickhouse-test/queries'
622 623
        if args.tmp is None:
            args.tmp = '/tmp/clickhouse-test'
A
akuzm 已提交
624
    if args.queries is None:
625
        print("Failed to detect path to the queries directory. Please specify it with '--queries' option.", file=sys.stderr)
A
akuzm 已提交
626
        exit(1)
P
proller 已提交
627 628
    if args.tmp is None:
        args.tmp = args.queries
629
    if args.client is None:
A
alesapin 已提交
630
        if find_binary(args.binary + '-client'):
P
proller 已提交
631
            args.client = args.binary + '-client'
A
alesapin 已提交
632
        elif find_binary(args.binary):
P
proller 已提交
633
            args.client = args.binary + ' client'
634
        else:
635
            print("No 'clickhouse' binary found in PATH", file=sys.stderr)
A
alesapin 已提交
636 637 638
            parser.print_help()
            exit(1)

P
proller 已提交
639
        if args.configclient:
640 641 642 643 644
            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")
645 646 647
        if os.getenv("CLICKHOUSE_DATABASE"):
            args.client += ' --database=' + os.getenv("CLICKHOUSE_DATABASE")

648
    if args.client_option:
649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664
        # Set options for client
        if 'CLICKHOUSE_CLIENT_OPT' in os.environ:
           os.environ['CLICKHOUSE_CLIENT_OPT'] += ' '
        else:
           os.environ['CLICKHOUSE_CLIENT_OPT'] = ''

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

        # Set options for curl
        if 'CLICKHOUSE_URL_PARAMS' in os.environ:
           os.environ['CLICKHOUSE_URL_PARAMS'] += '&'
        else:
           os.environ['CLICKHOUSE_URL_PARAMS'] = ''

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

P
proller 已提交
665 666 667 668 669 670
    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 已提交
671
    if args.jobs is None:
672
        args.jobs = multiprocessing.cpu_count()
P
proller 已提交
673

674
    main(args)