http://git-wip-us.apache.org/repos/asf/cassandra/blob/14d62ab1/pylib/cqlshlib/test/test_cqlsh_output.py ---------------------------------------------------------------------- diff --git a/pylib/cqlshlib/test/test_cqlsh_output.py b/pylib/cqlshlib/test/test_cqlsh_output.py new file mode 100644 index 0000000..dbce527 --- /dev/null +++ b/pylib/cqlshlib/test/test_cqlsh_output.py @@ -0,0 +1,965 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# to configure behavior, define $CQL_TEST_HOST to the destination address +# for Thrift connections, and $CQL_TEST_PORT to the associated port. + +from __future__ import with_statement + +import re +from itertools import izip +from .basecase import (BaseTestCase, cqlshlog, dedent, at_a_time, cql, + TEST_HOST, TEST_PORT) +from .cassconnect import (get_test_keyspace, testrun_cqlsh, testcall_cqlsh, + cassandra_cursor, split_cql_commands, quote_name) +from .ansi_colors import (ColoredText, lookup_colorcode, lookup_colorname, + lookup_colorletter, ansi_seq) + +CONTROL_C = '\x03' +CONTROL_D = '\x04' + +class TestCqlshOutput(BaseTestCase): + def setUp(self): + pass + + def tearDown(self): + pass + + def assertNoHasColors(self, text, msg=None): + self.assertNotRegexpMatches(text, ansi_seq, msg='ANSI CSI sequence found in %r' % text) + + def assertHasColors(self, text, msg=None): + self.assertRegexpMatches(text, ansi_seq, msg=msg) + + def assertColored(self, coloredtext, colorname): + wanted_colorcode = lookup_colorcode(colorname) + for num, c in enumerate(coloredtext): + if not c.isspace(): + ccolor = c.colorcode() + self.assertEqual(ccolor, wanted_colorcode, + msg='Output text %r (char #%d) is colored %s, not %s' + % (coloredtext, num, lookup_colorname(ccolor), colorname)) + + def assertColorFromTags(self, coloredtext, tags): + for (char, tag) in izip(coloredtext, tags): + if char.isspace(): + continue + if tag.isspace(): + tag = 'n' # neutral + self.assertEqual(char.colorcode(), lookup_colorletter(tag), + msg='Coloring mismatch.\nExpected coloring: %s\n' + 'Actually got: %s\ncolor code: %s' + % (tags, coloredtext.colored_version(), coloredtext.colortags())) + + def assertCqlverQueriesGiveColoredOutput(self, queries_and_expected_outputs, + cqlver=(), **kwargs): + if not isinstance(cqlver, (tuple, list)): + cqlver = (cqlver,) + for ver in cqlver: + self.assertQueriesGiveColoredOutput(queries_and_expected_outputs, cqlver=ver, **kwargs) + + def assertQueriesGiveColoredOutput(self, queries_and_expected_outputs, + sort_results=False, **kwargs): + """ + Allow queries and expected output to be specified in structured tuples, + along with expected color information. + """ + with testrun_cqlsh(tty=True, **kwargs) as c: + for query, expected in queries_and_expected_outputs: + cqlshlog.debug('Testing %r' % (query,)) + output = c.cmd_and_response(query).lstrip("\r\n") + c_output = ColoredText(output) + pairs = at_a_time(dedent(expected).split('\n'), 2) + outlines = c_output.splitlines() + if sort_results: + if outlines[1].plain().startswith('---'): + firstpart = outlines[:2] + sortme = outlines[2:] + else: + firstpart = [] + sortme = outlines + lastpart = [] + while sortme[-1].plain() == '': + lastpart.append(sortme.pop(-1)) + outlines = firstpart + sorted(sortme, key=lambda c:c.plain()) + lastpart + for (plain, colorcodes), outputline in zip(pairs, outlines): + self.assertEqual(outputline.plain().rstrip(), plain) + self.assertColorFromTags(outputline, colorcodes) + + def test_no_color_output(self): + for termname in ('', 'dumb', 'vt100'): + cqlshlog.debug('TERM=%r' % termname) + with testrun_cqlsh(tty=True, env={'TERM': termname}) as c: + c.send('select * from has_all_types;\n') + self.assertNoHasColors(c.read_to_next_prompt()) + c.send('select * from has_value_encoding_errors;\n') + self.assertNoHasColors(c.read_to_next_prompt()) + c.send('select count(*) from has_all_types;\n') + self.assertNoHasColors(c.read_to_next_prompt()) + c.send('totally invalid cql;\n') + self.assertNoHasColors(c.read_to_next_prompt()) + + def test_no_prompt_or_colors_output(self): + # CQL queries and number of lines expected in output: + queries = (('select * from has_all_types limit 1;', 5), + ('select * from has_value_encoding_errors limit 1;', 6)) + for termname in ('', 'dumb', 'vt100', 'xterm'): + cqlshlog.debug('TERM=%r' % termname) + for cql, lines_expected in queries: + output, result = testcall_cqlsh(prompt=None, env={'TERM': termname}, + tty=False, input=cql + '\n') + output = output.splitlines() + for line in output: + self.assertNoHasColors(line) + self.assertNotRegexpMatches(line, r'^cqlsh\S*>') + self.assertEqual(len(output), lines_expected, + msg='output: %r' % '\n'.join(output)) + self.assertEqual(output[0], '') + self.assertNicelyFormattedTableHeader(output[1]) + self.assertNicelyFormattedTableRule(output[2]) + self.assertNicelyFormattedTableData(output[3]) + self.assertEqual(output[4].strip(), '') + + def test_color_output(self): + for termname in ('xterm', 'unknown-garbage'): + cqlshlog.debug('TERM=%r' % termname) + with testrun_cqlsh(tty=True, env={'TERM': termname}) as c: + c.send('select * from has_all_types;\n') + self.assertHasColors(c.read_to_next_prompt()) + c.send('select * from has_value_encoding_errors;\n') + self.assertHasColors(c.read_to_next_prompt()) + c.send('select count(*) from has_all_types;\n') + self.assertHasColors(c.read_to_next_prompt()) + c.send('totally invalid cql;\n') + self.assertHasColors(c.read_to_next_prompt()) + + def test_count_output(self): + self.assertCqlverQueriesGiveColoredOutput(( + ('select count(*) from has_all_types;', """ + count + MMMMM + ------- + + 4 + G + + """), + + ('select COUNT(*) FROM empty_table;', """ + count + MMMMM + ------- + + 0 + G + + """), + + ('select COUNT(*) FROM empty_composite_table;', """ + count + MMMMM + ------- + + 0 + G + + """), + + ('select COUNT(*) FROM twenty_rows_table limit 10;', """ + count + MMMMM + ------- + + 10 + GG + + """), + + ('select COUNT(*) FROM twenty_rows_table limit 1000000;', """ + count + MMMMM + ------- + + 20 + GG + + """), + ), cqlver=(2, 3)) + + # different results cql2/cql3 + q = 'select COUNT(*) FROM twenty_rows_composite_table limit 1000000;' + + self.assertQueriesGiveColoredOutput(( + (q, """ + count + MMMMM + ------- + + 1 + GG + + """), + ), cqlver=2) + + self.assertQueriesGiveColoredOutput(( + (q, """ + count + MMMMM + ------- + + 20 + GG + + """), + ), cqlver=3) + + def test_dynamic_cf_output(self): + self.assertQueriesGiveColoredOutput(( + ('select * from dynamic_columns;', """ + somekey,1 | 1.2,one point two + nMMMMMMM G MMM YYYYYYYYYYYYY + somekey,2 | 2.3,two point three + MMMMMMM G MMM YYYYYYYYYYYYYYY + somekey,3 | -0.0001,negative ten thousandth | 3.46,three point four six | 99,ninety-nine point oh + MMMMMMM G MMMMMMM YYYYYYYYYYYYYYYYYYYYYYY MMMM YYYYYYYYYYYYYYYYYYYY n MM YYYYYYYYYYYYYYYYYYYY + + """), + + ('select somekey, 2.3 from dynamic_columns;', """ + somekey | 2.3 + MMMMMMM MMM + ---------+----------------- + + 1 | null + G RRRR + 2 | two point three + G YYYYYYYYYYYYYYY + 3 | null + G RRRR + + """), + + ('select first 1 * from dynamic_columns where somekey = 2;', """ + 2.3 + MMM + ----------------- + + two point three + YYYYYYYYYYYYYYY + + """), + + ('select * from dynamic_columns where somekey = 2;', """ + somekey | 2.3 + MMMMMMM MMM + ---------+----------------- + + 2 | two point three + G YYYYYYYYYYYYYYY + + """), + ), cqlver=2, sort_results=True) + + def test_static_cf_output(self): + self.assertCqlverQueriesGiveColoredOutput(( + ('select a, b from twenty_rows_table where a in (1, 13, 2);', """ + a | b + MM MM + ----+---- + + 1 | 1 + YY YY + 13 | 13 + YY YY + 2 | 2 + YY YY + + """), + ), cqlver=(2, 3)) + + self.assertQueriesGiveColoredOutput(( + ('select * from dynamic_columns;', """ + somekey | column1 | value + MMMMMMM MMMMMMM MMMMM + ---------+---------+------------------------- + + 1 | 1.2 | one point two + GG GGGGGGG YYYYYYYYYYYYYYYYYYYYYYY + 2 | 2.3 | two point three + GG GGGGGGG YYYYYYYYYYYYYYYYYYYYYYY + 3 | 99 | ninety-nine point oh + GG GGGGGGG YYYYYYYYYYYYYYYYYYYYYYY + 3 | 3.46 | three point four six + GG GGGGGGG YYYYYYYYYYYYYYYYYYYYYYY + 3 | -0.0001 | negative ten thousandth + GG GGGGGGG YYYYYYYYYYYYYYYYYYYYYYY + + """), + ), cqlver=3, sort_results=True) + + def test_empty_cf_output(self): + self.assertCqlverQueriesGiveColoredOutput(( + ('select * from empty_table;', """ + """), + ), cqlver=(2, 3)) + + q = 'select * from has_all_types where num = 999;' + self.assertQueriesGiveColoredOutput(( + (q, """ + num + MMM + ----- + + 999 + GGG + + """), + ), cqlver=2) + + # same query should show up as empty in cql 3 + self.assertQueriesGiveColoredOutput(( + (q, """ + """), + ), cqlver=3) + + def test_columnless_key_output(self): + q = 'select a from twenty_rows_table where a in (1, 2, -9192);' + self.assertQueriesGiveColoredOutput(( + (q, """ + a + MMMMM + ------- + + 1 + YYYYY + 2 + YYYYY + -9192 + YYYYY + + """), + ), cqlver=2) + + self.assertQueriesGiveColoredOutput(( + (q, """ + a + M + --- + + 1 + Y + 2 + Y + + """), + ), cqlver=3) + + def test_numeric_output(self): + self.assertCqlverQueriesGiveColoredOutput(( + ('''select intcol, bigintcol, varintcol \ + from has_all_types \ + where num in (0, 1, 2, 3);''', """ + intcol | bigintcol | varintcol + MMMMMM MMMMMMMMM MMMMMMMMM + -------------+----------------------+----------------------------- + + -12 | 1234567890123456789 | 10000000000000000000000000 + GGGGGGGGGGG GGGGGGGGGGGGGGGGGGGG GGGGGGGGGGGGGGGGGGGGGGGGGGG + 2147483647 | 9223372036854775807 | 9 + GGGGGGGGGGG GGGGGGGGGGGGGGGGGGGG GGGGGGGGGGGGGGGGGGGGGGGGGGG + 0 | 0 | 0 + GGGGGGGGGGG GGGGGGGGGGGGGGGGGGGG GGGGGGGGGGGGGGGGGGGGGGGGGGG + -2147483648 | -9223372036854775808 | -10000000000000000000000000 + GGGGGGGGGGG GGGGGGGGGGGGGGGGGGGG GGGGGGGGGGGGGGGGGGGGGGGGGGG + + """), + + ('''select decimalcol, doublecol, floatcol \ + from has_all_types \ + where num in (0, 1, 2, 3);''', """ + decimalcol | doublecol | floatcol + MMMMMMMMMM MMMMMMMMM MMMMMMMM + ------------------+-----------+---------- + + 19952.11882 | 1 | -2.1 + GGGGGGGGGGGGGGGG GGGGGGG GGGGG + 1E-14 | 1e+07 | 1e+05 + GGGGGGGGGGGGGGGG GGGGGGG GGGGG + 0.0 | 0 | 0 + GGGGGGGGGGGGGGGG GGGGGGG GGGGG + 10.0000000000000 | -1004.1 | 1e+08 + GGGGGGGGGGGGGGGG GGGGGGG GGGGG + + """), + ), cqlver=(2, 3)) + + self.assertQueriesGiveColoredOutput(( + ('''select * from dynamic_columns where somekey = 3;''', """ + somekey | -0.0001 | 3.46 | 99 + MMMMMMM MMMMMMM MMMM MM + ---------+-------------------------+----------------------+---------------------- + + 3 | negative ten thousandth | three point four six | ninety-nine point oh + G YYYYYYYYYYYYYYYYYYYYYYY YYYYYYYYYYYYYYYYYYYY YYYYYYYYYYYYYYYYYYYY + + """), + ), cqlver=2) + + def test_timestamp_output(self): + self.assertQueriesGiveColoredOutput(( + ('''select timestampcol from has_all_types where num = 0;''', """ + timestampcol + MMMMMMMMMMMM + -------------------------- + + 2012-05-14 12:53:20+0000 + GGGGGGGGGGGGGGGGGGGGGGGG + + """), + ), env={'TZ': 'Etc/UTC'}) + + self.assertQueriesGiveColoredOutput(( + ('''select timestampcol from has_all_types where num = 0;''', """ + timestampcol + MMMMMMMMMMMM + -------------------------- + + 2012-05-14 07:53:20-0500 + GGGGGGGGGGGGGGGGGGGGGGGG + + """), + ), env={'TZ': 'EST'}) + + def test_boolean_output(self): + self.assertCqlverQueriesGiveColoredOutput(( + ('select num, booleancol from has_all_types where num in (0, 1, 2, 3);', """ + num | booleancol + MMM MMMMMMMMMM + -----+------------ + + 0 | True + G GGGGG + 1 | True + G GGGGG + 2 | False + G GGGGG + 3 | False + G GGGGG + + """), + ), cqlver=(2, 3)) + + def test_null_output(self): + # column with metainfo but no values + self.assertCqlverQueriesGiveColoredOutput(( + ("select k, c, notthere from undefined_values_table where k in ('k1', 'k2');", """ + k | c | notthere + M M MMMMMMMM + ----+----+---------- + + k1 | c1 | null + YY YY RRRR + k2 | c2 | null + YY YY RRRR + + """), + ), cqlver=(2, 3)) + + # all-columns, including a metainfo column has no values (cql3) + self.assertQueriesGiveColoredOutput(( + ("select * from undefined_values_table where k in ('k1', 'k2');", """ + k | c | notthere + M M MMMMMMMM + ----+----+---------- + + k1 | c1 | null + YY YY RRRR + k2 | c2 | null + YY YY RRRR + + """), + ), cqlver=3) + + # all-columns, including a metainfo column has no values (cql2) + self.assertQueriesGiveColoredOutput(( + ("select * from undefined_values_table where k in ('k1', 'k2');", """ + k | c + M M + ----+---- + + k1 | c1 + YY YY + k2 | c2 + YY YY + + """), + ), cqlver=2) + + # column not in metainfo, which has no values (invalid query in cql3) + self.assertQueriesGiveColoredOutput(( + ("select num, fakecol from has_all_types where num in (1, 2);", """ + num + MMM + ----- + + 1 + GGG + 2 + GGG + + """), + ), cqlver=2) + + def test_string_output_ascii(self): + self.assertCqlverQueriesGiveColoredOutput(( + ("select * from ascii_with_invalid_and_special_chars where k in (0, 1, 2, 3, 4);", r""" + k | val + M MMM + ---+----------------------------------------------- + + 0 | newline:\n + G YYYYYYYYmm + 1 | return\rand null\x00! + G YYYYYYmmYYYYYYYYmmmmY + 2 | \x00\x01\x02\x03\x04\x05control chars\x06\x07 + G mmmmmmmmmmmmmmmmmmmmmmmmYYYYYYYYYYYYYmmmmmmmm + 3 | \xfe\xffbyte order mark + G mmmmmmmmYYYYYYYYYYYYYYY + 4 | fake special chars\x00\n + G YYYYYYYYYYYYYYYYYYYYYYYY + + """), + ), cqlver=(2, 3)) + + def test_string_output_utf8(self): + # many of these won't line up visually here, to keep the source code + # here ascii-only. note that some of the special Unicode characters + # here will render as double-width or zero-width in unicode-aware + # terminals, but the color-checking machinery here will still treat + # it as one character, so those won't seem to line up visually either. + + self.assertCqlverQueriesGiveColoredOutput(( + ("select * from utf8_with_special_chars where k in (0, 1, 2, 3, 4, 5, 6);", u""" + k | val + M MMM + ---+------------------------------- + + 0 | Normal string + G YYYYYYYYYYYYY + 1 | Text with\\nnewlines\\n + G YYYYYYYYYmmYYYYYYYYmm + 2 | Text with embedded \\x01 char + G YYYYYYYYYYYYYYYYYYYmmmmYYYYY + 3 | \u24c8\u24c5\u24ba\u24b8\u24be\u24b6\u24c1\u2008\u249e\u24a3\u249c\u24ad\u24ae and normal ones + G YYYYYYYYYYYYYYYYYYYYYYYYYYYYY + 4 | double wides: \u2f91\u2fa4\u2f9a + G YYYYYYYYYYYYYYYYY + 5 | zero width\u200bspace + G YYYYYYYYYYYYYYYY + 6 | fake special chars\\x00\\n + G YYYYYYYYYYYYYYYYYYYYYYYY + + """.encode('utf-8')), + ), cqlver=(2, 3), env={'LANG': 'en_US.UTF-8'}) + + def test_blob_output(self): + self.assertCqlverQueriesGiveColoredOutput(( + ("select num, blobcol from has_all_types where num in (0, 1, 2, 3);", r""" + num | blobcol + MMM MMMMMMM + -----+-------------------- + + 0 | 000102030405fffefd + G mmmmmmmmmmmmmmmmmm + 1 | ffffffffffffffffff + G mmmmmmmmmmmmmmmmmm + 2 | + G mmmmmmmmmmmmmmmmmm + 3 | 80 + G mmmmmmmmmmmmmmmmmm + + """), + ), cqlver=(2, 3)) + + def test_colname_decoding_errors(self): + # not clear how to achieve this situation in the first place. the + # validator works pretty well, and we can't change the comparator + # after insertion. + # + # guess we could monkey-patch cqlsh or python-cql source to + # explicitly generate an exception on the deserialization of type X.. + pass + + def test_colval_decoding_errors(self): + self.assertCqlverQueriesGiveColoredOutput(( + ("select * from has_value_encoding_errors;", r""" + pkey | utf8col + MMMM MMMMMMM + ------+-------------------- + + A | '\x00\xff\x00\xff' + Y RRRRRRRRRRRRRRRRRR + + + Failed to decode value '\x00\xff\x00\xff' (for column 'utf8col') as text: 'utf8' codec can't decode byte 0xff in position 1: invalid start byte + RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR + """), + ), cqlver=(2, 3)) + + def test_key_decoding_errors(self): + self.assertCqlverQueriesGiveColoredOutput(( + ("select * from has_key_encoding_errors;", r""" + pkey | col + MMMM MMM + --------------------+---------- + + '\x00\xff\x02\x8f' | whatever + RRRRRRRRRRRRRRRRRR YYYYYYYY + + + Failed to decode value '\x00\xff\x02\x8f' (for column 'pkey') as text: 'utf8' codec can't decode byte 0xff in position 1: invalid start byte + RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR + """), + ), cqlver=(2, 3)) + + def test_prompt(self): + with testrun_cqlsh(tty=True, keyspace=None, cqlver=2) as c: + self.assertEqual(c.output_header.splitlines()[-1], 'cqlsh> ') + + c.send('\n') + output = c.read_to_next_prompt().replace('\r\n', '\n') + self.assertEqual(output, '\ncqlsh> ') + + cmd = "USE '%s';\n" % get_test_keyspace().replace("'", "''") + c.send(cmd) + output = c.read_to_next_prompt().replace('\r\n', '\n') + self.assertEqual(output, '%scqlsh:%s> ' % (cmd, get_test_keyspace())) + + c.send('use system;\n') + output = c.read_to_next_prompt().replace('\r\n', '\n') + self.assertEqual(output, 'use system;\ncqlsh:system> ') + + c.send('use NONEXISTENTKEYSPACE;\n') + outputlines = c.read_to_next_prompt().splitlines() + self.assertEqual(outputlines[0], 'use NONEXISTENTKEYSPACE;') + self.assertEqual(outputlines[3], 'cqlsh:system> ') + midline = ColoredText(outputlines[1]) + self.assertEqual(midline.plain(), + "Bad Request: Keyspace 'NONEXISTENTKEYSPACE' does not exist") + self.assertColorFromTags(midline, + "RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR") + + def check_describe_keyspace(self, fullcqlver): + with testrun_cqlsh(tty=True, cqlver=fullcqlver) as c: + ks = get_test_keyspace() + qks = quote_name(fullcqlver, ks) + for cmd in ('describe keyspace', 'desc keyspace'): + for givename in ('system', '', qks): + for semicolon in ('', ';'): + fullcmd = cmd + (' ' if givename else '') + givename + semicolon + desc = c.cmd_and_response(fullcmd) + self.check_describe_keyspace_output(desc, givename or qks, fullcqlver) + + # try to actually execute that last keyspace description, with a + # new keyspace name + new_ks_name = 'COPY_OF_' + ks + copy_desc = desc.replace(ks, new_ks_name) + statements = split_cql_commands(copy_desc, cqlver=fullcqlver) + do_drop = True + + with cassandra_cursor(cql_version=fullcqlver) as curs: + try: + for stmt in statements: + cqlshlog.debug('TEST EXEC: %s' % stmt) + try: + curs.execute(stmt) + except cql.ProgrammingError, e: + # expected sometimes under cql3, since some existing + # tables made under cql2 are not recreatable with cql3 + for errmsg in ('No definition found that is not part of the PRIMARY KEY', + "mismatched input '<' expecting EOF"): + if errmsg in str(e): + do_drop = False + break + else: + raise + # maybe we should do some checks that the two keyspaces + # match up as much as we expect at this point? + finally: + curs.execute('use system') + if do_drop: + curs.execute('drop keyspace %s' % quote_name(fullcqlver, new_ks_name)) + + def check_describe_keyspace_output(self, output, qksname, fullcqlver): + expected_bits = [r'(?im)^CREATE KEYSPACE %s WITH\b' % re.escape(qksname), + r'(?im)^USE \S+;$', + r';\s*$'] + if fullcqlver == '3.0.0': + expected_bits.append(r'\breplication = {\n \'class\':') + else: + expected_bits.append(r'\bstrategy_class =') + for expr in expected_bits: + self.assertRegexpMatches(output, expr) + + def test_describe_keyspace_output(self): + for v in ('2.0.0', '3.0.0'): + self.check_describe_keyspace(v) + + def test_describe_columnfamily_output(self): + # we can change these to regular expressions if/when it makes sense + # to do so; these will likely be subject to lots of adjustments. + + table_desc2 = dedent(""" + + CREATE TABLE has_all_types ( + num int PRIMARY KEY, + intcol int, + timestampcol timestamp, + floatcol float, + uuidcol uuid, + bigintcol bigint, + doublecol double, + booleancol boolean, + decimalcol decimal, + asciicol ascii, + blobcol blob, + varcharcol text, + textcol text, + varintcol varint + ) WITH + comment='' AND + comparator=text AND + read_repair_chance=0.100000 AND + gc_grace_seconds=864000 AND + default_validation=text AND + min_compaction_threshold=4 AND + max_compaction_threshold=32 AND + replicate_on_write='true' AND + compaction_strategy_class='SizeTieredCompactionStrategy' AND + compression_parameters:sstable_compression='SnappyCompressor'; + + """) + + # note columns are now comparator-ordered instead of original-order. + table_desc3 = dedent(""" + + CREATE TABLE has_all_types ( + num int PRIMARY KEY, + asciicol ascii, + bigintcol bigint, + blobcol blob, + booleancol boolean, + decimalcol decimal, + doublecol double, + floatcol float, + intcol int, + textcol text, + timestampcol timestamp, + uuidcol uuid, + varcharcol text, + varintcol varint + ) WITH COMPACT STORAGE AND + bloom_filter_fp_chance=0.010000 AND + caching='KEYS_ONLY' AND + comment='' AND + dclocal_read_repair_chance=0.000000 AND + gc_grace_seconds=864000 AND + read_repair_chance=0.100000 AND + replicate_on_write='true' AND + compaction={'class': 'SizeTieredCompactionStrategy'} AND + compression={'sstable_compression': 'SnappyCompressor'}; + + """) + + for v in ('2.0.0', '3.0.0'): + with testrun_cqlsh(tty=True, cqlver=v) as c: + for cmdword in ('describe table', 'desc columnfamily'): + for semicolon in (';', ''): + output = c.cmd_and_response('%s has_all_types%s' % (cmdword, semicolon)) + self.assertNoHasColors(output) + self.assertEqual(output, {'2.0.0': table_desc2, '3.0.0': table_desc3}[v]) + + def test_describe_columnfamilies_output(self): + output_re = r''' + \n + Keyspace [ ] (?P<ksname> \S+ ) \n + -----------* \n + (?P<cfnames> .*? ) + \n + ''' + + ks = get_test_keyspace() + + with testrun_cqlsh(tty=True, keyspace=None, cqlver=3) as c: + + # when not in a keyspace + for cmdword in ('DESCRIBE COLUMNFAMILIES', 'desc tables'): + for semicolon in (';', ''): + ksnames = [] + output = c.cmd_and_response(cmdword + semicolon) + self.assertNoHasColors(output) + self.assertRegexpMatches(output, '(?xs) ^ ( %s )+ $' % output_re) + + for section in re.finditer('(?xs)' + output_re, output): + ksname = section.group('ksname') + ksnames.append(ksname) + cfnames = section.group('cfnames') + self.assertNotIn('\n\n', cfnames) + if ksname == ks: + self.assertIn('ascii_with_invalid_and_special_chars', cfnames) + + self.assertIn('system', ksnames) + self.assertIn(quote_name('3.0.0', ks), ksnames) + + # when in a keyspace + c.send('USE %s;\n' % quote_name('3.0.0', ks)) + c.read_to_next_prompt() + + for cmdword in ('DESCRIBE COLUMNFAMILIES', 'desc tables'): + for semicolon in (';', ''): + output = c.cmd_and_response(cmdword + semicolon) + self.assertNoHasColors(output) + self.assertEqual(output[0], '\n') + self.assertEqual(output[-1], '\n') + self.assertNotIn('Keyspace %s' % quote_name('3.0.0', ks), output) + self.assertIn('has_value_encoding_errors', output) + self.assertIn('undefined_values_table', output) + + def test_describe_cluster_output(self): + output_re = r'''(?x) + ^ + \n + Cluster: [ ] (?P<clustername> .* ) \n + Partitioner: [ ] (?P<partitionername> .* ) \n + Snitch: [ ] (?P<snitchname> .* ) \n + \n + ''' + + ringinfo_re = r''' + Range[ ]ownership: \n + ( + [ ] .*? [ ][ ] \[ ( \d+ \. ){3} \d+ \] \n + )+ + \n + ''' + + with testrun_cqlsh(tty=True, keyspace=None, cqlver=3) as c: + + # not in a keyspace + for semicolon in ('', ';'): + output = c.cmd_and_response('describe cluster' + semicolon) + self.assertNoHasColors(output) + self.assertRegexpMatches(output, output_re + '$') + + c.send('USE %s;\n' % quote_name('3.0.0', get_test_keyspace())) + c.read_to_next_prompt() + + for semicolon in ('', ';'): + output = c.cmd_and_response('describe cluster' + semicolon) + self.assertNoHasColors(output) + self.assertRegexpMatches(output, output_re + ringinfo_re + '$') + + def test_describe_schema_output(self): + with testrun_cqlsh(tty=True) as c: + for semicolon in ('', ';'): + output = c.cmd_and_response('desc schema' + semicolon) + self.assertNoHasColors(output) + self.assertRegexpMatches(output, '^\nCREATE KEYSPACE') + self.assertIn("\nCREATE KEYSPACE system WITH replication = {\n 'class': 'LocalStrategy'\n};\n", + output) + self.assertRegexpMatches(output, ';\s*$') + + def test_show_output(self): + with testrun_cqlsh(tty=True) as c: + output = c.cmd_and_response('show version;') + self.assertRegexpMatches(output, + '^\[cqlsh \S+ \| Cassandra \S+ \| CQL spec \S+ \| Thrift protocol \S+\]$') + + output = c.cmd_and_response('show host;') + self.assertHasColors(output) + self.assertRegexpMatches(output, '^Connected to .* at %s:%d\.$' + % (re.escape(TEST_HOST), TEST_PORT)) + + def test_show_assumptions_output(self): + expected_output = '\nUSE %s;\n\n' % quote_name('', get_test_keyspace()) + + with testrun_cqlsh(tty=True) as c: + output = c.cmd_and_response('show assumptions') + self.assertEqual(output, 'No overrides.\n') + + c.cmd_and_response('assume dynamic_values VALUES aRe uuid;') + expected_output += 'ASSUME dynamic_values VALUES ARE uuid;\n' + output = c.cmd_and_response('show assumptions') + self.assertEqual(output, expected_output + '\n') + + c.cmd_and_response('Assume has_all_types names arE float;') + expected_output += 'ASSUME has_all_types NAMES ARE float;\n' + output = c.cmd_and_response('show assumptions') + self.assertEqual(output, expected_output + '\n') + + c.cmd_and_response('assume twenty_rows_table ( b ) values are decimal;') + expected_output += 'ASSUME twenty_rows_table(b) VALUES ARE decimal;\n' + output = c.cmd_and_response('show assumptions') + self.assertEqual(output, expected_output + '\n') + + def test_eof_prints_newline(self): + with testrun_cqlsh(tty=True) as c: + c.send(CONTROL_D) + out = c.read_lines(1)[0].replace('\r', '') + self.assertEqual(out, '\n') + with self.assertRaises(BaseException) as cm: + c.read_lines(1) + self.assertIn(type(cm.exception), (EOFError, OSError)) + + def test_exit_prints_no_newline(self): + for semicolon in ('', ';'): + with testrun_cqlsh(tty=True) as c: + cmd = 'exit%s\n' % semicolon + c.send(cmd) + out = c.read_lines(1)[0].replace('\r', '') + self.assertEqual(out, cmd) + with self.assertRaises(BaseException) as cm: + c.read_lines(1) + self.assertIn(type(cm.exception), (EOFError, OSError)) + + def test_help_types(self): + with testrun_cqlsh(tty=True) as c: + output = c.cmd_and_response('help types') + + + def test_help(self): + pass + + def test_printing_parse_error(self): + pass + + def test_printing_lex_error(self): + pass + + def test_multiline_statements(self): + pass + + def test_cancel_statement(self): + pass + + def test_printing_integrity_error(self): + pass + + def test_printing_cql_error(self): + pass + + def test_empty_line(self): + pass
http://git-wip-us.apache.org/repos/asf/cassandra/blob/14d62ab1/pylib/cqlshlib/test/test_keyspace_init2.cql ---------------------------------------------------------------------- diff --git a/pylib/cqlshlib/test/test_keyspace_init2.cql b/pylib/cqlshlib/test/test_keyspace_init2.cql new file mode 100644 index 0000000..415becc --- /dev/null +++ b/pylib/cqlshlib/test/test_keyspace_init2.cql @@ -0,0 +1,180 @@ +CREATE TABLE has_all_types ( + num int PRIMARY KEY, + intcol int, + asciicol ascii, + bigintcol bigint, + blobcol blob, + booleancol boolean, + decimalcol decimal, + doublecol double, + floatcol float, + textcol text, + timestampcol timestamp, + uuidcol uuid, + varcharcol varchar, + varintcol varint +); + +INSERT INTO has_all_types (num, intcol, asciicol, bigintcol, blobcol, booleancol, + decimalcol, doublecol, floatcol, textcol, + timestampcol, uuidcol, varcharcol, varintcol) +VALUES (0, -12, 'abcdefg', 1234567890123456789, '000102030405fffefd', 'true', + 19952.11882, 1.0, -2.1, 'Voilá!', '2012-05-14 12:53:20+0000', + bd1924e1-6af8-44ae-b5e1-f24131dbd460, '"', 10000000000000000000000000); + +INSERT INTO has_all_types (num, intcol, asciicol, bigintcol, blobcol, booleancol, + decimalcol, doublecol, floatcol, textcol, + timestampcol, uuidcol, varcharcol, varintcol) +VALUES (1, 2147483647, '__!''$#@!~"', 9223372036854775807, 'ffffffffffffffffff', true, + 0.00000000000001, 9999999.999, 99999.99, 'âǶâ®à¸â³â''', '1900-01-01+0000', + ffffffff-ffff-ffff-ffff-ffffffffffff, 'newline-> +<-', 9); + +INSERT INTO has_all_types (num, intcol, asciicol, bigintcol, blobcol, booleancol, + decimalcol, doublecol, floatcol, textcol, + timestampcol, uuidcol, varcharcol, varintcol) +VALUES (2, 0, '', 0, '', false, + 0.0, 0.0, 0.0, '', 0, + 00000000-0000-0000-0000-000000000000, '', 0); + +INSERT INTO has_all_types (num, intcol, asciicol, bigintcol, blobcol, booleancol, + decimalcol, doublecol, floatcol, textcol, + timestampcol, uuidcol, varcharcol, varintcol) +VALUES (3, -2147483648, '''''''', -9223372036854775808, '80', 'false', + 10.0000000000000, -1004.10, 100000000.9, 'é¾é¦é¬±', '2038-01-19T03:14-1200', + ffffffff-ffff-1fff-8fff-ffffffffffff, '''', -10000000000000000000000000); + + + +CREATE TABLE has_value_encoding_errors ( + pkey ascii PRIMARY KEY, + utf8col blob +); + +INSERT INTO has_value_encoding_errors (pkey, utf8col) VALUES ('A', '00ff00ff'); +ALTER TABLE has_value_encoding_errors ALTER utf8col TYPE text; + +CREATE TABLE has_key_encoding_errors ( + pkey blob PRIMARY KEY, + col text +); + +INSERT INTO has_key_encoding_errors (pkey, col) VALUES ('00ff028f', 'whatever'); +ALTER TABLE has_key_encoding_errors ALTER pkey TYPE text; + + + +CREATE TABLE empty_table ( + lonelykey float primary key, + lonelycol text +); + + + +CREATE COLUMNFAMILY dynamic_columns ( + somekey int PRIMARY KEY +) WITH comparator = float + AND default_validation = text; + +INSERT INTO dynamic_columns (somekey, 1.2) VALUES (1, 'one point two'); +INSERT INTO dynamic_columns (somekey, 2.3) VALUES (2, 'two point three'); +INSERT INTO dynamic_columns (somekey, 3.46) VALUES (3, 'three point four six'); +INSERT INTO dynamic_columns (somekey, 99.0) VALUES (3, 'ninety-nine point oh'); +INSERT INTO dynamic_columns (somekey, -0.0001) VALUES (3, 'negative ten thousandth'); + + + +CREATE TABLE twenty_rows_table ( + a text primary key, + b text +); + +INSERT INTO twenty_rows_table (a, b) VALUES (1, 1); +INSERT INTO twenty_rows_table (a, b) VALUES (2, 2); +INSERT INTO twenty_rows_table (a, b) VALUES (3, 3); +INSERT INTO twenty_rows_table (a, b) VALUES (4, 4); +INSERT INTO twenty_rows_table (a, b) VALUES (5, 5); +INSERT INTO twenty_rows_table (a, b) VALUES (6, 6); +INSERT INTO twenty_rows_table (a, b) VALUES (7, 7); +INSERT INTO twenty_rows_table (a, b) VALUES (8, 8); +INSERT INTO twenty_rows_table (a, b) VALUES (9, 9); +INSERT INTO twenty_rows_table (a, b) VALUES (10, 10); +INSERT INTO twenty_rows_table (a, b) VALUES (11, 11); +INSERT INTO twenty_rows_table (a, b) VALUES (12, 12); +INSERT INTO twenty_rows_table (a, b) VALUES (13, 13); +INSERT INTO twenty_rows_table (a, b) VALUES (14, 14); +INSERT INTO twenty_rows_table (a, b) VALUES (15, 15); +INSERT INTO twenty_rows_table (a, b) VALUES (16, 16); +INSERT INTO twenty_rows_table (a, b) VALUES (17, 17); +INSERT INTO twenty_rows_table (a, b) VALUES (18, 18); +INSERT INTO twenty_rows_table (a, b) VALUES (19, 19); +INSERT INTO twenty_rows_table (a, b) VALUES (20, 20); + +-- one row is twenty cols wide +INSERT INTO twenty_rows_table (a, b1) VALUES (1, 1); +INSERT INTO twenty_rows_table (a, b2) VALUES (1, 2); +INSERT INTO twenty_rows_table (a, b3) VALUES (1, 3); +INSERT INTO twenty_rows_table (a, b4) VALUES (1, 4); +INSERT INTO twenty_rows_table (a, b5) VALUES (1, 5); +INSERT INTO twenty_rows_table (a, b6) VALUES (1, 6); +INSERT INTO twenty_rows_table (a, b7) VALUES (1, 7); +INSERT INTO twenty_rows_table (a, b8) VALUES (1, 8); +INSERT INTO twenty_rows_table (a, b9) VALUES (1, 9); +INSERT INTO twenty_rows_table (a, b10) VALUES (1, 10); +INSERT INTO twenty_rows_table (a, b11) VALUES (1, 11); +INSERT INTO twenty_rows_table (a, b12) VALUES (1, 12); +INSERT INTO twenty_rows_table (a, b13) VALUES (1, 13); +INSERT INTO twenty_rows_table (a, b14) VALUES (1, 14); +INSERT INTO twenty_rows_table (a, b15) VALUES (1, 15); +INSERT INTO twenty_rows_table (a, b16) VALUES (1, 16); +INSERT INTO twenty_rows_table (a, b17) VALUES (1, 17); +INSERT INTO twenty_rows_table (a, b18) VALUES (1, 18); +INSERT INTO twenty_rows_table (a, b19) VALUES (1, 19); + + + +CREATE TABLE undefined_values_table ( + k text PRIMARY KEY, + c text, + notthere text +); + +INSERT INTO undefined_values_table (k, c) VALUES ('k1', 'c1'); +INSERT INTO undefined_values_table (k, c) VALUES ('k2', 'c2'); + + + +CREATE TABLE ascii_with_invalid_and_special_chars ( + k int PRIMARY KEY, + val blob +); + +-- "newline:\n" +INSERT INTO ascii_with_invalid_and_special_chars (k, val) VALUES (0, '6e65776c696e653a0a'); +-- "return\rand null\0!" +INSERT INTO ascii_with_invalid_and_special_chars (k, val) VALUES (1, '72657475726e0d616e64206e756c6c0021'); +-- "\x00\x01\x02\x03\x04\x05control chars\x06\x07" +INSERT INTO ascii_with_invalid_and_special_chars (k, val) VALUES (2, '000102030405636f6e74726f6c2063686172730607'); +-- "\xfe\xffbyte order mark" +INSERT INTO ascii_with_invalid_and_special_chars (k, val) VALUES (3, 'feff62797465206f72646572206d61726b'); +-- "fake special chars\\x00\\n" +INSERT INTO ascii_with_invalid_and_special_chars (k, val) VALUES (4, '66616b65207370656369616c2063686172735c7830305c6e'); + +ALTER TABLE ascii_with_invalid_and_special_chars ALTER val TYPE ascii; + + + +CREATE TABLE utf8_with_special_chars ( + k int PRIMARY KEY, + val text +); + +INSERT INTO utf8_with_special_chars (k, val) VALUES (0, 'Normal string'); +INSERT INTO utf8_with_special_chars (k, val) VALUES (1, 'Text with +newlines +'); +INSERT INTO utf8_with_special_chars (k, val) VALUES (2, 'Text with embedded char'); +INSERT INTO utf8_with_special_chars (k, val) VALUES (3, 'ââ âºâ¸â¾â¶ââââ£âââ® and normal ones'); +INSERT INTO utf8_with_special_chars (k, val) VALUES (4, 'double wides: â¾â¾¤â¾'); +INSERT INTO utf8_with_special_chars (k, val) VALUES (5, 'zero widthâspace'); +INSERT INTO utf8_with_special_chars (k, val) VALUES (6, 'fake special chars\x00\n'); http://git-wip-us.apache.org/repos/asf/cassandra/blob/14d62ab1/pylib/cqlshlib/test/test_keyspace_init3.cql ---------------------------------------------------------------------- diff --git a/pylib/cqlshlib/test/test_keyspace_init3.cql b/pylib/cqlshlib/test/test_keyspace_init3.cql new file mode 100644 index 0000000..8c5e4be --- /dev/null +++ b/pylib/cqlshlib/test/test_keyspace_init3.cql @@ -0,0 +1,36 @@ +CREATE TABLE empty_composite_table ( + lonelykey float, + lonelycol text, + lonelyval int, + primary key (lonelykey, lonelycol) +); + +CREATE TABLE twenty_rows_composite_table ( + a text, + b text, + c text, + primary key (a, b) +); + +-- all in the same storage engine row: + +INSERT INTO twenty_rows_composite_table (a, b, c) VALUES ('A', 1, 1); +INSERT INTO twenty_rows_composite_table (a, b, c) VALUES ('A', 2, 2); +INSERT INTO twenty_rows_composite_table (a, b, c) VALUES ('A', 3, 3); +INSERT INTO twenty_rows_composite_table (a, b, c) VALUES ('A', 4, 4); +INSERT INTO twenty_rows_composite_table (a, b, c) VALUES ('A', 5, 5); +INSERT INTO twenty_rows_composite_table (a, b, c) VALUES ('A', 6, 6); +INSERT INTO twenty_rows_composite_table (a, b, c) VALUES ('A', 7, 7); +INSERT INTO twenty_rows_composite_table (a, b, c) VALUES ('A', 8, 8); +INSERT INTO twenty_rows_composite_table (a, b, c) VALUES ('A', 9, 9); +INSERT INTO twenty_rows_composite_table (a, b, c) VALUES ('A', 10, 10); +INSERT INTO twenty_rows_composite_table (a, b, c) VALUES ('A', 11, 11); +INSERT INTO twenty_rows_composite_table (a, b, c) VALUES ('A', 12, 12); +INSERT INTO twenty_rows_composite_table (a, b, c) VALUES ('A', 13, 13); +INSERT INTO twenty_rows_composite_table (a, b, c) VALUES ('A', 14, 14); +INSERT INTO twenty_rows_composite_table (a, b, c) VALUES ('A', 15, 15); +INSERT INTO twenty_rows_composite_table (a, b, c) VALUES ('A', 16, 16); +INSERT INTO twenty_rows_composite_table (a, b, c) VALUES ('A', 17, 17); +INSERT INTO twenty_rows_composite_table (a, b, c) VALUES ('A', 18, 18); +INSERT INTO twenty_rows_composite_table (a, b, c) VALUES ('A', 19, 19); +INSERT INTO twenty_rows_composite_table (a, b, c) VALUES ('A', 20, 20);
