Skip to content

Commit 5946b0a

Browse files
authored
Merge pull request #6986 from ZeaLoVe/improve-logictest-output
ci: improve logictest output
2 parents 71981a2 + 829276c commit 5946b0a

File tree

2 files changed

+93
-29
lines changed

2 files changed

+93
-29
lines changed

tests/logictest/README.md

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,12 @@ Get help with:
3939
python main.py -h
4040
```
4141

42+
Useful arguments:
43+
1. --run-dir ydb will only run the suites in dir ./suites/ydb/
44+
2. --skip-dir ydb will skip the suites in dir ./suites/ydb
45+
3. --suites other_dir wiil use suites file in dir ./other_dir
46+
4. Run files by pattern string like: python main.py "03_0001"
47+
4248
## Docker
4349

4450
### Build image
@@ -59,6 +65,56 @@ docker build -t sqllogic/test:latest .
5965
- ADDITIONAL_HEADERS (for security scenario)
6066
3. docker run --name logictest --rm --network host datafuselabs/sqllogictest:latest
6167

68+
## How to write logic test
69+
70+
Fast start, you can follow this demo: https://github.com/datafuselabs/databend/blob/main/tests/logictest/suites/select_0
71+
72+
Runner supported: mysql handler, http handler, clickhouse handler.
73+
74+
- ok
75+
- Returns no error, don't care about the result
76+
- error
77+
- Returns with error and expected error message, usually with an error code, but also with a message string; the way to determine whether the specified string is in the returned message
78+
- query
79+
- Return result and check the result with expected, follow by query_type and query_label
80+
- query_type is a char represent a column in result, multi char means multi column
81+
- B Boolean
82+
- T text
83+
- F floating point
84+
- I integer
85+
- query_label If different runner return inconsistency, you can write like this(suppose that mysql handler is get different result)
86+
87+
This is a query demo(query_label is optional):
88+
89+
```
90+
statement query III label(mysql)
91+
select number, number + 1, number + 999 from numbers(10);
92+
93+
----
94+
0 1 999
95+
1 2 1000
96+
2 3 1001
97+
3 4 1002
98+
4 5 1003
99+
5 6 1004
100+
6 7 1005
101+
7 8 1006
102+
8 9 1007
103+
9 10 1008.0
104+
105+
---- mysql
106+
0 1 999
107+
1 2 1000
108+
2 3 1001
109+
3 4 1002
110+
4 5 1003
111+
5 6 1004
112+
6 7 1005
113+
7 8 1006
114+
8 9 1007
115+
9 10 1008
116+
```
117+
62118
## Write logic test tips
63119

64120
1. skipif help you skip test of given handler
@@ -91,8 +147,10 @@ select 1;
91147
```
92148

93149
**tips** If you do not care about result, use statement ok instead of statement query
150+
**tips** Add ORDER BY to ensure that the order of returned results is always consistent
94151
**warning** A statement query need result, and even if you want to skip a case, you still need to keep the results in the test content
95152

96153
# Learn More
97154

98-
Ref pr: https://github.com/datafuselabs/databend/pull/5048
155+
RFC: https://github.com/datafuselabs/databend/blob/main/docs/doc/60-contributing/03-rfcs/20220425-new_sql_logic_test_framework.md
156+
Migration discussion: https://github.com/datafuselabs/databend/discussions/5838

tests/logictest/logictest.py

Lines changed: 34 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -94,12 +94,13 @@ def parse_token_args(tokens, arg):
9494

9595
class LogicError(Exception):
9696

97-
def __init__(self, message, expected):
97+
def __init__(self, message, errorType, runner):
9898
self.message = message
99-
self.expected = expected
99+
self.errorType = errorType
100+
self.runner = runner
100101

101102
def __str__(self):
102-
return f"Expected: {self.expected}\nMessage: {self.message}"
103+
return f"Ruuner: {self.runner}\nErrorType: {self.errorType}\nMessage: {self.message}"
103104

104105

105106
class Statement:
@@ -225,15 +226,12 @@ def get_statements(suite_path, suite_name):
225226

226227
def format_value(vals, val_num):
227228
row = len(vals) // val_num
228-
width = len(str(vals[0])) + 2
229-
for i in range(len(vals)):
230-
width = max(width, len(vals[i]) + 2)
231229
table = ""
232230
for i in range(row):
233231
ans = []
234232
for j in range(val_num):
235-
ans.append('{: >{w}}'.format(str(vals[i * val_num + j]), w=width))
236-
table += "".join(ans)
233+
ans.append(str(vals[i * val_num + j]))
234+
table += " ".join(ans)
237235
table += "\n"
238236
return table
239237

@@ -256,7 +254,7 @@ def __init__(self, kind, args):
256254
self.driver = None
257255
self.statement_files = []
258256
self.kind = kind
259-
self.show_query_on_execution = True
257+
self.show_query_on_execution = False
260258
self.on_error_return = False
261259
self.dir = dir
262260
self.args = args
@@ -279,7 +277,7 @@ def fetch_files(self):
279277

280278
if self.args.skip_dir and any(
281279
s in dirs for s in self.args.skip_dir):
282-
log.info(f"Skip test file {filename}, skip test in dirs {self.args.skip_dir}")
280+
log.info(f"Skip test file {filename}, in dirs {self.args.skip_dir}")
283281
continue
284282

285283
if self.args.skip and any(
@@ -305,9 +303,6 @@ def execute(self):
305303
# case batch
306304
for (file_path, suite_name) in self.statement_files:
307305
self.suite_now = suite_name
308-
log.info(
309-
f"Run query with the same session every suite file, suite file path:{file_path}"
310-
)
311306
statement_list = list()
312307
for state in get_statements(file_path, suite_name):
313308
statement_list.append(state)
@@ -320,6 +315,10 @@ def execute(self):
320315
global_statistics.add_failed(self.kind, self.suite_now,
321316
e)
322317
continue
318+
319+
log.info(
320+
f"Suite file:{file_path} pass!"
321+
)
323322
else:
324323
raise RuntimeError(
325324
f"batch_execute is not implement in runner {self.kind}")
@@ -331,7 +330,7 @@ def execute_statement(self, statement):
331330
)
332331
return
333332
if self.show_query_on_execution:
334-
log.debug(
333+
log.Info(
335334
f"executing statement, type {statement.s_type.type}\n{statement.text}\n"
336335
)
337336
start = time.perf_counter()
@@ -354,22 +353,24 @@ def assert_execute_ok(self, statement):
354353
statement)
355354
if actual is not None:
356355
raise LogicError(
356+
runner=self.kind,
357357
message=str(statement),
358-
expected=
359-
"statement ok must get success response, not error code in response"
358+
errorType="statement ok get error in response"
360359
)
361360

362361
def assert_query_equal(self, f, resultset, statement):
363362
# use join after split instead of strip
364363
compare_f = "".join(f.split())
365364
compare_result = "".join(resultset[2].split())
366365
if compare_f != compare_result:
367-
raise LogicError(message="\n Expected:\n{}\n Actual:\n{}\n Statement:{}\n Start " \
366+
raise LogicError(message="\n Expected:\n{:<80}\n Actual:\n{:<80}\n Statement:{}\n Start " \
368367
"Line: {}, Result Label: {}".format(resultset[2].rstrip(),
369368
f.rstrip(),
370369
str(statement), resultset[1],
371370
resultset[0].group("label")),
372-
expected="statement query must get result equal to expected")
371+
errorType="statement query get result not equal to expected",
372+
runner=self.kind,
373+
)
373374

374375
def assert_execute_query(self, statement):
375376
if statement.s_type.query_type == "skipped":
@@ -381,11 +382,13 @@ def assert_execute_query(self, statement):
381382
except Exception:
382383
raise LogicError(
383384
message=f"{statement} statement type is query but get no result",
384-
expected="query get result")
385+
errorType="statement query get no result",
386+
runner=self.kind)
385387

386388
if statement.results is None or len(statement.results) == 0:
387-
raise LogicError(message=f"No result found {statement}",
388-
expected="query get result")
389+
raise LogicError(message=f"{statement} no result found by query",
390+
errorType="statement query get empty result",
391+
runner=self.kind)
389392
hasResult = False
390393
for resultset in statement.results:
391394
if resultset[0].group("label") is not None and resultset[0].group(
@@ -399,26 +402,29 @@ def assert_execute_query(self, statement):
399402
self.assert_query_equal(f, resultset, statement)
400403
hasResult = True
401404
if not hasResult:
402-
raise LogicError(message=f"No result found {statement}",
403-
expected="query get result")
405+
raise LogicError(message=f"{statement} no result found in test file",
406+
errorType="statement query has no result in test file",
407+
runner=self.kind)
404408

405409
# expect the query just return error
406410
def assert_execute_error(self, statement):
407411
actual = safe_execute(lambda: self.execute_error(statement.text),
408412
statement)
409413
if actual is None:
410-
raise Exception("Expected error but got none")
414+
raise LogicError(message=f"{str(statement)}",
415+
errorType="statement error get no error message",
416+
runner=self.kind)
411417
match = re.search(statement.s_type.expect_error, actual.msg)
412418
if match is None:
413419
raise LogicError(
414420
message=
415-
f"statement {str(statement)}, expect error regex {statement.s_type.expect_error}, found {actual}",
416-
expected=
417-
f"statement error must get error message in expected string")
421+
f"\n expected error regex is {statement.s_type.expect_error}\n actual found {actual}{str(statement)}",
422+
errorType=f"statement error get error message not equal to expected",
423+
runner=self.kind)
418424

419425
def run_sql_suite(self):
420426
log.info(
421-
f"run_sql_suite for {self.kind} on base {os.path.abspath(self.args.suites)}"
427+
f"run_sql_suite for {self.kind} with suites dir {os.path.abspath(self.args.suites)}"
422428
)
423429
self.fetch_files()
424430
self.execute()

0 commit comments

Comments
 (0)