mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-24 08:32:02 +00:00
Run .sql tests with standalone server
This commit is contained in:
parent
e56dea2f4f
commit
5a6d95ef3e
@ -1,14 +0,0 @@
|
||||
import pytest
|
||||
|
||||
|
||||
def pytest_addoption(parser):
|
||||
parser.addoption(
|
||||
"--builddir", action="store", default=None, help="Path to build directory to use binaries from",
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def cmdopts(request):
|
||||
return {
|
||||
'builddir': request.config.getoption("--builddir"),
|
||||
}
|
0
dbms/tests/queries/__init__.py
Normal file
0
dbms/tests/queries/__init__.py
Normal file
56
dbms/tests/queries/conftest.py
Normal file
56
dbms/tests/queries/conftest.py
Normal file
@ -0,0 +1,56 @@
|
||||
import pytest
|
||||
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
from server import ServerThread
|
||||
|
||||
|
||||
def pytest_addoption(parser):
|
||||
parser.addoption(
|
||||
"--builddir", action="store", default=None, help="Path to build directory to use binaries from",
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(scope='module')
|
||||
def cmdopts(request):
|
||||
return {
|
||||
'builddir': request.config.getoption("--builddir"),
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture(scope='module')
|
||||
def bin_prefix(cmdopts):
|
||||
prefix = 'clickhouse'
|
||||
if cmdopts['builddir'] is not None:
|
||||
prefix = os.path.join(cmdopts['builddir'], 'dbms', 'programs', prefix)
|
||||
return prefix
|
||||
|
||||
|
||||
# TODO: also support stateful queries.
|
||||
QUERIES_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), '0_stateless')
|
||||
|
||||
@pytest.fixture(scope='module', params=[f for f in os.listdir(QUERIES_PATH) if f.endswith('.sql')])
|
||||
def sql_query(request):
|
||||
return os.path.join(QUERIES_PATH, os.path.splitext(request.param)[0])
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def standalone_server(bin_prefix):
|
||||
tmp_dir = tempfile.mkdtemp(prefix='clickhouse.test..')
|
||||
|
||||
server = ServerThread(bin_prefix, tmp_dir)
|
||||
server.start()
|
||||
wait_result = server.wait()
|
||||
|
||||
if wait_result is not None:
|
||||
with open(os.path.join(server.log_dir, 'server', 'stdout.txt'), 'r') as f:
|
||||
print >> sys.stderr, f.read()
|
||||
with open(os.path.join(server.log_dir, 'server', 'stderr.txt'), 'r') as f:
|
||||
print >> sys.stderr, f.read()
|
||||
pytest.fail('Server died unexpectedly with code {code}'.format(code=server._proc.returncode), pytrace=False)
|
||||
|
||||
yield server
|
||||
|
||||
server.stop()
|
36
dbms/tests/queries/query_test.py
Normal file
36
dbms/tests/queries/query_test.py
Normal file
@ -0,0 +1,36 @@
|
||||
import pytest
|
||||
|
||||
import difflib
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
|
||||
@pytest.mark.timeout(timeout=10, method='signal')
|
||||
def test_query(bin_prefix, sql_query, standalone_server):
|
||||
tcp_port = standalone_server.tcp_port
|
||||
|
||||
query_path = sql_query + ".sql"
|
||||
reference_path = sql_query + ".reference"
|
||||
|
||||
if not os.path.exists(reference_path):
|
||||
pytest.skip('No .reference file found')
|
||||
|
||||
with open(query_path, 'r') as file:
|
||||
query = file.read()
|
||||
with open(reference_path, 'r') as file:
|
||||
reference = file.read()
|
||||
|
||||
client = subprocess.Popen([bin_prefix + '-client', '--port', str(tcp_port), '-m', '-n', '--testmode'],
|
||||
stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
result, error = client.communicate(query)
|
||||
assert client.returncode is not None, "Client should exit after processing all queries"
|
||||
|
||||
if client.returncode != 0:
|
||||
print >> sys.stderr, error
|
||||
pytest.fail('Client died unexpectedly with code {code}'.format(code=client.returncode), pytrace=False)
|
||||
elif result != reference:
|
||||
pytest.fail("Query output doesn't match reference:{eol}{diff}".format(
|
||||
eol=os.linesep,
|
||||
diff=os.linesep.join(l.strip() for l in difflib.unified_diff(reference.splitlines(), result.splitlines(), fromfile='expected', tofile='actual'))),
|
||||
pytrace=False)
|
224
dbms/tests/queries/server.py
Normal file
224
dbms/tests/queries/server.py
Normal file
@ -0,0 +1,224 @@
|
||||
import os
|
||||
import random
|
||||
import socket
|
||||
import subprocess
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
|
||||
|
||||
class ServerThread(threading.Thread):
|
||||
DEFAULT_RETRIES = 3
|
||||
DEFAULT_SERVER_DELAY = 0.5 # seconds
|
||||
DEFAULT_CONNECTION_TIMEOUT = 1.0 # seconds
|
||||
|
||||
def __init__(self, bin_prefix, tmp_dir):
|
||||
self._bin = bin_prefix + '-server'
|
||||
self._lock = threading.Lock()
|
||||
threading.Thread.__init__(self)
|
||||
self._lock.acquire()
|
||||
|
||||
self.tmp_dir = tmp_dir
|
||||
self.log_dir = os.path.join(tmp_dir, 'log')
|
||||
self.etc_dir = os.path.join(tmp_dir, 'etc')
|
||||
self.server_config = os.path.join(self.etc_dir, 'server-config.xml')
|
||||
self.users_config = os.path.join(self.etc_dir, 'users.xml')
|
||||
|
||||
os.makedirs(self.log_dir)
|
||||
os.makedirs(self.etc_dir)
|
||||
|
||||
def _choose_ports_and_args(self):
|
||||
port_base = random.SystemRandom().randrange(10000, 60000)
|
||||
self.tcp_port = port_base + 1
|
||||
self.http_port = port_base + 2
|
||||
self.inter_port = port_base + 3
|
||||
self.tcps_port = port_base + 4
|
||||
self.https_port = port_base + 5
|
||||
self.odbc_port = port_base + 6
|
||||
|
||||
self._args = [
|
||||
'--config-file={config_path}'.format(config_path=self.server_config),
|
||||
'--',
|
||||
'--tcp_port={tcp_port}'.format(tcp_port=self.tcp_port),
|
||||
'--http_port={http_port}'.format(http_port=self.http_port),
|
||||
'--interserver_http_port={inter_port}'.format(inter_port=self.inter_port),
|
||||
]
|
||||
|
||||
with open(self.server_config, 'w') as f:
|
||||
f.write(ServerThread.DEFAULT_SERVER_CONFIG.format(
|
||||
tmp_dir=self.tmp_dir, log_dir=self.log_dir, tcp_port=self.tcp_port))
|
||||
|
||||
with open(self.users_config, 'w') as f:
|
||||
f.write(ServerThread.DEFAULT_USERS_CONFIG)
|
||||
|
||||
def run(self):
|
||||
retries = ServerThread.DEFAULT_RETRIES
|
||||
|
||||
while retries:
|
||||
self._choose_ports_and_args()
|
||||
print 'Start clickhouse-server with args:', self._args
|
||||
self._proc = subprocess.Popen([self._bin] + self._args, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
|
||||
while self._proc.poll() is None:
|
||||
try:
|
||||
time.sleep(ServerThread.DEFAULT_SERVER_DELAY)
|
||||
s = socket.create_connection(('localhost', self.tcp_port), ServerThread.DEFAULT_CONNECTION_TIMEOUT)
|
||||
s.sendall('G') # trigger expected "bad" HELLO response
|
||||
print 'Successful server response:', s.recv(1024) # FIXME: read whole buffered response
|
||||
s.shutdown(socket.SHUT_RDWR)
|
||||
s.close()
|
||||
except Exception as e:
|
||||
print >> sys.stderr, 'Failed to connect to server:', e
|
||||
continue
|
||||
else:
|
||||
break
|
||||
|
||||
# If process has died then try to fetch output before releasing lock
|
||||
if self._proc.returncode is not None:
|
||||
self._proc.communicate()
|
||||
|
||||
if self._proc.returncode == 70: # Address already in use
|
||||
retries -= 1
|
||||
continue
|
||||
|
||||
break
|
||||
|
||||
self._lock.release()
|
||||
|
||||
while self._proc.returncode is None:
|
||||
self._proc.communicate()
|
||||
|
||||
def wait(self):
|
||||
self._lock.acquire()
|
||||
if self._proc.returncode is not None:
|
||||
self.join()
|
||||
self._lock.release()
|
||||
return self._proc.returncode
|
||||
|
||||
def stop(self):
|
||||
if self._proc.returncode is None:
|
||||
self._proc.terminate()
|
||||
self.join()
|
||||
print 'Stop clickhouse-server'
|
||||
|
||||
|
||||
ServerThread.DEFAULT_SERVER_CONFIG = \
|
||||
"""\
|
||||
<?xml version="1.0"?>
|
||||
<yandex>
|
||||
<logger>
|
||||
<level>trace</level>
|
||||
<log>{log_dir}/server/stdout.txt</log>
|
||||
<errorlog>{log_dir}/server/stderr.txt</errorlog>
|
||||
<size>never</size>
|
||||
<count>1</count>
|
||||
</logger>
|
||||
|
||||
<listen_host>::</listen_host>
|
||||
|
||||
<path>{tmp_dir}/data/</path>
|
||||
<tmp_path>{tmp_dir}/tmp/</tmp_path>
|
||||
<users_config>users.xml</users_config>
|
||||
<mark_cache_size>5368709120</mark_cache_size>
|
||||
|
||||
<timezone>UTC</timezone>
|
||||
|
||||
<remote_servers>
|
||||
<test_shard_localhost>
|
||||
<shard>
|
||||
<replica>
|
||||
<host>localhost</host>
|
||||
<port>{tcp_port}</port>
|
||||
</replica>
|
||||
</shard>
|
||||
</test_shard_localhost>
|
||||
|
||||
<test_cluster_two_shards_localhost>
|
||||
<shard>
|
||||
<replica>
|
||||
<host>localhost</host>
|
||||
<port>{tcp_port}</port>
|
||||
</replica>
|
||||
</shard>
|
||||
<shard>
|
||||
<replica>
|
||||
<host>localhost</host>
|
||||
<port>{tcp_port}</port>
|
||||
</replica>
|
||||
</shard>
|
||||
</test_cluster_two_shards_localhost>
|
||||
|
||||
<test_unavailable_shard>
|
||||
<shard>
|
||||
<replica>
|
||||
<host>localhost</host>
|
||||
<port>{tcp_port}</port>
|
||||
</replica>
|
||||
</shard>
|
||||
<shard>
|
||||
<replica>
|
||||
<host>localhost</host>
|
||||
<port>1</port>
|
||||
</replica>
|
||||
</shard>
|
||||
</test_unavailable_shard>
|
||||
</remote_servers>
|
||||
</yandex>
|
||||
"""
|
||||
|
||||
|
||||
ServerThread.DEFAULT_USERS_CONFIG = \
|
||||
"""\
|
||||
<?xml version="1.0"?>
|
||||
<yandex>
|
||||
<profiles>
|
||||
<default>
|
||||
<max_memory_usage>10000000000</max_memory_usage>
|
||||
<use_uncompressed_cache>0</use_uncompressed_cache>
|
||||
<load_balancing>random</load_balancing>
|
||||
</default>
|
||||
|
||||
<readonly>
|
||||
<readonly>1</readonly>
|
||||
</readonly>
|
||||
</profiles>
|
||||
|
||||
<users>
|
||||
<default>
|
||||
<password></password>
|
||||
<networks replace="replace">
|
||||
<ip>::/0</ip>
|
||||
</networks>
|
||||
|
||||
<profile>default</profile>
|
||||
|
||||
<quota>default</quota>
|
||||
</default>
|
||||
|
||||
<readonly>
|
||||
<password></password>
|
||||
<networks replace="replace">
|
||||
<ip>::1</ip>
|
||||
<ip>127.0.0.1</ip>
|
||||
</networks>
|
||||
|
||||
<profile>readonly</profile>
|
||||
|
||||
<quota>default</quota>
|
||||
</readonly>
|
||||
</users>
|
||||
|
||||
<quotas>
|
||||
<default>
|
||||
<interval>
|
||||
<duration>3600</duration>
|
||||
<queries>0</queries>
|
||||
<errors>0</errors>
|
||||
<result_rows>0</result_rows>
|
||||
<read_rows>0</read_rows>
|
||||
<execution_time>0</execution_time>
|
||||
</interval>
|
||||
</default>
|
||||
</quotas>
|
||||
</yandex>
|
||||
"""
|
@ -1,270 +0,0 @@
|
||||
import pytest
|
||||
|
||||
import os
|
||||
import random
|
||||
import signal
|
||||
import subprocess
|
||||
import tempfile
|
||||
import threading
|
||||
import time
|
||||
|
||||
|
||||
# TODO: also support stateful queries.
|
||||
QUERIES_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'queries', '0_stateless')
|
||||
|
||||
SERVER_CONFIG = \
|
||||
"""\
|
||||
<?xml version="1.0"?>
|
||||
<!-- Config for test server -->
|
||||
<yandex>
|
||||
<logger>
|
||||
<level>trace</level>
|
||||
<log>{log_dir}/clickhouse-server.log</log>
|
||||
<errorlog>{log_dir}/clickhouse-server.err.log</errorlog>
|
||||
<size>10M</size>
|
||||
<count>1</count>
|
||||
<compress>0</compress>
|
||||
</logger>
|
||||
<listen_host>::</listen_host>
|
||||
<listen_host>0.0.0.0</listen_host>
|
||||
<listen_try>1</listen_try>
|
||||
<openSSL>
|
||||
<server> <!-- Used for https server AND secure tcp port -->
|
||||
<certificateFile>{etc_dir}/server.crt</certificateFile>
|
||||
<privateKeyFile>{etc_dir}/server.key</privateKeyFile>
|
||||
<dhParamsFile>{etc_dir}/dhparam.pem</dhParamsFile>
|
||||
<verificationMode>none</verificationMode>
|
||||
<loadDefaultCAFile>true</loadDefaultCAFile>
|
||||
<cacheSessions>true</cacheSessions>
|
||||
<disableProtocols>sslv2,sslv3</disableProtocols>
|
||||
<preferServerCiphers>true</preferServerCiphers>
|
||||
</server>
|
||||
|
||||
<client> <!-- Used for connecting to https dictionary source -->
|
||||
<loadDefaultCAFile>true</loadDefaultCAFile>
|
||||
<cacheSessions>true</cacheSessions>
|
||||
<disableProtocols>sslv2,sslv3</disableProtocols>
|
||||
<preferServerCiphers>true</preferServerCiphers>
|
||||
<verificationMode>none</verificationMode>
|
||||
<invalidCertificateHandler>
|
||||
<name>AcceptCertificateHandler</name>
|
||||
</invalidCertificateHandler>
|
||||
</client>
|
||||
</openSSL>
|
||||
|
||||
<keep_alive_timeout>3</keep_alive_timeout>
|
||||
<path>{tmp_dir}/data/</path>
|
||||
<tmp_path>{tmp_dir}/tmp/</tmp_path>
|
||||
<users_config>users.xml</users_config>
|
||||
<mark_cache_size>5368709120</mark_cache_size>
|
||||
<default_profile>default</default_profile>
|
||||
<default_database>default</default_database>
|
||||
<timezone>Europe/Moscow</timezone>
|
||||
<remote_servers incl="clickhouse_remote_servers" >
|
||||
<!-- Test only shard config for testing distributed storage -->
|
||||
<test_shard_localhost>
|
||||
<shard>
|
||||
<replica>
|
||||
<host>localhost</host>
|
||||
<port>59000</port>
|
||||
</replica>
|
||||
</shard>
|
||||
</test_shard_localhost>
|
||||
<test_shard_localhost_secure>
|
||||
<shard>
|
||||
<replica>
|
||||
<host>localhost</host>
|
||||
<port>59440</port>
|
||||
<secure>1</secure>
|
||||
</replica>
|
||||
</shard>
|
||||
</test_shard_localhost_secure>
|
||||
</remote_servers>
|
||||
<include_from/>
|
||||
<zookeeper incl="zookeeper-servers" optional="true" />
|
||||
<macros incl="macros" optional="true" />
|
||||
<builtin_dictionaries_reload_interval>3600</builtin_dictionaries_reload_interval>
|
||||
<max_session_timeout>3600</max_session_timeout>
|
||||
<default_session_timeout>60</default_session_timeout>
|
||||
<query_log>
|
||||
<database>system</database>
|
||||
<table>query_log</table>
|
||||
<flush_interval_milliseconds>7500</flush_interval_milliseconds>
|
||||
</query_log>
|
||||
<dictionaries_config>*_dictionary.xml</dictionaries_config>
|
||||
<compression incl="clickhouse_compression">
|
||||
</compression>
|
||||
<distributed_ddl>
|
||||
<path>/clickhouse/task_queue/ddl</path>
|
||||
</distributed_ddl>
|
||||
<format_schema_path>{tmp_dir}/data/format_schemas/</format_schema_path>
|
||||
</yandex>
|
||||
"""
|
||||
|
||||
USERS_CONFIG = \
|
||||
"""\
|
||||
<?xml version="1.0"?>
|
||||
<yandex>
|
||||
<profiles>
|
||||
<default>
|
||||
<max_memory_usage>10000000000</max_memory_usage>
|
||||
<use_uncompressed_cache>0</use_uncompressed_cache>
|
||||
<load_balancing>random</load_balancing>
|
||||
</default>
|
||||
|
||||
<readonly>
|
||||
<readonly>1</readonly>
|
||||
</readonly>
|
||||
</profiles>
|
||||
|
||||
<users>
|
||||
<default>
|
||||
<password></password>
|
||||
<networks incl="networks" replace="replace">
|
||||
<ip>::/0</ip>
|
||||
</networks>
|
||||
|
||||
<profile>default</profile>
|
||||
|
||||
<quota>default</quota>
|
||||
</default>
|
||||
|
||||
<readonly>
|
||||
<password></password>
|
||||
<networks incl="networks" replace="replace">
|
||||
<ip>::1</ip>
|
||||
<ip>127.0.0.1</ip>
|
||||
</networks>
|
||||
<profile>readonly</profile>
|
||||
<quota>default</quota>
|
||||
</readonly>
|
||||
</users>
|
||||
|
||||
<quotas>
|
||||
<default>
|
||||
<interval>
|
||||
<duration>3600</duration>
|
||||
<queries>0</queries>
|
||||
<errors>0</errors>
|
||||
<result_rows>0</result_rows>
|
||||
<read_rows>0</read_rows>
|
||||
<execution_time>0</execution_time>
|
||||
</interval>
|
||||
</default>
|
||||
</quotas>
|
||||
</yandex>
|
||||
"""
|
||||
|
||||
|
||||
@pytest.fixture(scope='module', params=[f for f in os.listdir(QUERIES_PATH) if f.endswith('.reference')])
|
||||
def test_case(request):
|
||||
return request.param
|
||||
|
||||
|
||||
def test_query(test_case, cmdopts):
|
||||
port_base = random.SystemRandom().randrange(10000, 60000)
|
||||
|
||||
tcp_port = port_base + 1
|
||||
http_port = port_base + 2
|
||||
inter_port = port_base + 3
|
||||
tcps_port = port_base + 4
|
||||
https_port = port_base + 5
|
||||
odbc_port = port_base + 6
|
||||
|
||||
bin_prefix = 'clickhouse'
|
||||
if cmdopts['builddir'] is not None:
|
||||
bin_prefix = os.path.join(cmdopts['builddir'], 'dbms', 'programs', bin_prefix)
|
||||
|
||||
tmp_dir = tempfile.mkdtemp(prefix='clickhouse.test..')
|
||||
log_dir = os.path.join(tmp_dir, 'log')
|
||||
etc_dir = os.path.join(tmp_dir, 'etc')
|
||||
config_path = os.path.join(etc_dir, 'server-config.xml')
|
||||
users_path = os.path.join(etc_dir, 'users.xml')
|
||||
|
||||
os.makedirs(log_dir)
|
||||
os.makedirs(etc_dir)
|
||||
|
||||
with open(config_path, 'w') as f:
|
||||
f.write(SERVER_CONFIG.format(tmp_dir=tmp_dir, log_dir=log_dir, etc_dir=etc_dir))
|
||||
|
||||
with open(users_path, 'w') as f:
|
||||
f.write(USERS_CONFIG)
|
||||
|
||||
class ServerThread(threading.Thread):
|
||||
def __init__(self, bin_prefix, args):
|
||||
self._bin = bin_prefix + '-server'
|
||||
self._args = args
|
||||
self._lock = threading.Lock()
|
||||
threading.Thread.__init__(self)
|
||||
self._lock.acquire()
|
||||
self._out = ''
|
||||
self._err = ''
|
||||
|
||||
def run(self):
|
||||
self._proc = subprocess.Popen([self._bin] + self._args, shell=False, bufsize=1,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
while self._proc.poll() is None:
|
||||
line = self._proc.stderr.readline()
|
||||
self._err += line
|
||||
# if 'Application: Ready for connections' in line:
|
||||
break
|
||||
|
||||
# If process has died then try to fetch output before releasing lock
|
||||
if self._proc.returncode is not None:
|
||||
stdout, stderr = self._proc.communicate()
|
||||
if stdout:
|
||||
self._out += stdout
|
||||
if stderr:
|
||||
self._err += stderr
|
||||
|
||||
self._lock.release()
|
||||
|
||||
if self._proc.returncode is None:
|
||||
stdout, stderr = self._proc.communicate()
|
||||
if stdout:
|
||||
self._out += stdout
|
||||
if stderr:
|
||||
self._err += stderr
|
||||
|
||||
def wait(self):
|
||||
self._lock.acquire()
|
||||
if self._proc.returncode is not None:
|
||||
return False
|
||||
self._lock.release()
|
||||
return True
|
||||
|
||||
def stop(self):
|
||||
if self._proc.returncode is None:
|
||||
self._proc.send_signal(signal.SIGINT)
|
||||
|
||||
print 'Start clickhouse-server with args:'
|
||||
|
||||
server_args = [
|
||||
'--config-file={config_path}'.format(config_path=config_path),
|
||||
'--',
|
||||
'--tcp_port={tcp_port}'.format(tcp_port=tcp_port),
|
||||
'--http_port={http_port}'.format(http_port=http_port),
|
||||
'--interserver_http_port={inter_port}'.format(inter_port=inter_port),
|
||||
'--tcp_port_secure={tcps_port}'.format(tcps_port=tcps_port),
|
||||
'--https_port={https_port}'.format(https_port=https_port),
|
||||
'--odbc_bridge.port={odbc_port}'.format(odbc_port=odbc_port),
|
||||
]
|
||||
print server_args
|
||||
|
||||
server_thread = ServerThread(bin_prefix, server_args)
|
||||
server_thread.start()
|
||||
if not server_thread.wait():
|
||||
print server_thread._out
|
||||
print server_thread._err
|
||||
pytest.fail('Server died unexpectedly')
|
||||
return
|
||||
|
||||
# TODO: run test
|
||||
|
||||
server_thread.stop()
|
||||
server_thread.join()
|
||||
|
||||
print server_thread._out
|
||||
print server_thread._err
|
||||
|
||||
print 'Stop clickhouse-server'
|
Loading…
Reference in New Issue
Block a user