# 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
# and $CQL_TEST_PORT to the associated port.


import locale
import os
import re
from .basecase import BaseTestCase
from .cassconnect import create_db, remove_db, cqlsh_testrun
from .run_cqlsh import TimeoutError
from cqlshlib.cql3handling import CqlRuleSet

BEL = '\x07'  # the terminal-bell character
CTRL_C = '\x03'
TAB = '\t'

# completions not printed out in this many seconds may not be acceptable.
# tune if needed for a slow system, etc, but be aware that the test will
# need to wait this long for each completion test, to make sure more info
# isn't coming
COMPLETION_RESPONSE_TIME = 0.5

completion_separation_re = re.compile(r'\s+')


class CqlshCompletionCase(BaseTestCase):

    @classmethod
    def setUpClass(cls):
        create_db()

    @classmethod
    def tearDownClass(cls):
        remove_db()

    def setUp(self):
        env = os.environ.copy()
        env['COLUMNS'] = '100000'
        if (locale.getpreferredencoding() != 'UTF-8'):
            env['LC_CTYPE'] = 'en_US.utf8'
        self.cqlsh_runner = cqlsh_testrun(cqlver=None, env=env)
        self.cqlsh = self.cqlsh_runner.__enter__()

    def tearDown(self):
        self.cqlsh_runner.__exit__(None, None, None)

    def _get_completions(self, inputstring, split_completed_lines=True):
        """
        Get results of tab completion in cqlsh. Returns a bare string if a
        string completes immediately. Otherwise, returns a set of all
        whitespace-separated tokens in the offered completions by default, or a
        list of the lines in the offered completions if split_completed_lines is
        False.
        """
        self.cqlsh.send(inputstring)
        self.cqlsh.send(TAB)
        immediate = self.cqlsh.read_up_to_timeout(COMPLETION_RESPONSE_TIME)
        immediate = immediate.replace(' \b', '')
        self.assertEqual(immediate[:len(inputstring)], inputstring)
        immediate = immediate[len(inputstring):]
        immediate = immediate.replace(BEL, '')

        if immediate:
            return immediate

        self.cqlsh.send(TAB)
        choice_output = self.cqlsh.read_up_to_timeout(COMPLETION_RESPONSE_TIME)
        if choice_output == BEL:
            choice_output = ''

        self.cqlsh.send(CTRL_C)  # cancel any current line
        self.cqlsh.read_to_next_prompt()

        choice_lines = choice_output.splitlines()
        if choice_lines:
            # ensure the last line of the completion is the prompt
            prompt_regex = self.cqlsh.prompt.lstrip() + re.escape(inputstring)
            msg = ('Double-tab completion '
                   'does not print prompt for input "{}"'.format(inputstring))
            self.assertRegex(choice_lines[-1], prompt_regex, msg=msg)

        choice_lines = [line.strip() for line in choice_lines[:-1]]
        choice_lines = [line for line in choice_lines if line]

        if split_completed_lines:
            completed_lines = list(map(set, (completion_separation_re.split(line.strip())
                                             for line in choice_lines)))

            if not completed_lines:
                return set()

            completed_tokens = set.union(*completed_lines)
            return completed_tokens - {''}
        else:
            return choice_lines

        assert False

    def _trycompletions_inner(self, inputstring, immediate='', choices=(),
                              other_choices_ok=False,
                              split_completed_lines=True,
                              ignore_system_keyspaces=False):
        """
        Test tab completion in cqlsh. Enters in the text in inputstring, then
        simulates a tab keypress to see what is immediately completed (this
        should only happen when there is only one completion possible). If
        there is an immediate completion, the new text is expected to match
        'immediate'. If there is no immediate completion, another tab keypress
        is simulated in order to get a list of choices, which are expected to
        match the items in 'choices' (order is not important, but case is).
        """
        completed = self._get_completions(inputstring,
                                          split_completed_lines=split_completed_lines)

        if immediate:
            msg = 'cqlsh completed %r (%d), but we expected %r (%d)' % (completed, len(completed), immediate, len(immediate))
            self.assertEqual(completed, immediate, msg=msg)
            return

        if ignore_system_keyspaces:
            completed = list(filter(lambda s: not s.startswith('system'), completed))

        if other_choices_ok:
            self.assertEqual(set(choices), completed.intersection(choices))
        else:
            self.assertEqual(set(choices), set(completed))

    def trycompletions(self, inputstring, immediate='', choices=(),
                       other_choices_ok=False, split_completed_lines=True,
                       ignore_system_keyspaces=False):
        try:
            self._trycompletions_inner(inputstring, immediate, choices,
                                       other_choices_ok=other_choices_ok,
                                       split_completed_lines=split_completed_lines,
                                       ignore_system_keyspaces=ignore_system_keyspaces)
        finally:
            try:
                self.cqlsh.send(CTRL_C)  # cancel any current line
                self.cqlsh.read_to_next_prompt(timeout=1.0)
            except TimeoutError:
                # retry once
                self.cqlsh.send(CTRL_C)
                self.cqlsh.read_to_next_prompt(timeout=10.0)

    def strategies(self):
        return CqlRuleSet.replication_strategies


class TestCqlshCompletion(CqlshCompletionCase):
    cqlver = '3.1.6'

    def test_complete_on_empty_string(self):
        self.trycompletions('', choices=('?', 'ADD', 'ALTER', 'BEGIN', 'CAPTURE', 'COMMENT', 'CONSISTENCY',
                                         'COPY', 'CREATE', 'DEBUG', 'DELETE', 'DESC', 'DESCRIBE',
                                         'DROP', 'GRANT', 'HELP', 'INSERT', 'LIST', 'LOGIN', 'PAGING', 'REVOKE',
                                         'SECURITY', 'SELECT', 'SHOW', 'SOURCE', 'TRACING', 'ELAPSED', 'EXPAND', 'SERIAL', 'TRUNCATE',
                                         'UPDATE', 'USE', 'exit', 'quit', 'CLEAR', 'CLS', 'history'))

    def test_complete_command_words(self):
        self.trycompletions('alt', '\b\b\bALTER ')
        self.trycompletions('I', 'NSERT INTO ')
        self.trycompletions('exit', ' ')

    def test_complete_in_uuid(self):
        pass

    def test_complete_in_select(self):
        self.trycompletions('SELECT ',
                            choices=('*', '<colname>',
                                     '-', '<blobLiteral>', '<float>', '<wholenumber>', '<uuid>',
                                     '<identifier>', '<pgStringLiteral>', '<quotedStringLiteral>',
                                     'ABS', 'AVG', 'CAST', 'COUNT', 'DISTINCT',
                                     'EXP', 'JSON', 'LOG', 'LOG10',
                                     'MAP_KEYS', 'MAP_VALUES',
                                     'MIN', 'MAX',
                                     'MIN_WRITETIME', 'MAX_WRITETIME',
                                     'ROUND', 'SUM', 'TOKEN',
                                     'TO_DATE', 'TO_TIMESTAMP', 'TO_UNIX_TIMESTAMP',
                                     'TTL', 'WRITETIME',
                                     'COLLECTION_AVG', 'COLLECTION_COUNT', 'COLLECTION_MAX',
                                     'COLLECTION_MIN', 'COLLECTION_SUM',
                                     'MASK_DEFAULT', 'MASK_HASH', 'MASK_INNER', 'MASK_NULL',
                                     'MASK_OUTER', 'MASK_REPLACE',
                                     '[', '{', 'false', 'true', 'NULL'
                                     ),
                            other_choices_ok=True
                            )

    def test_complete_in_select_where(self):
        self.trycompletions('SELECT * FROM system.peers WHERE ',
                            choices=('<identifier>', '<quotedName>', 'peer', 'CURRENT_DATE()', 'CURRENT_TIME()',
                                     'CURRENT_TIMEUUID()', 'CURRENT_TIMESTAMP()', 'TOKEN',
                                     'MIN_TIMEUUID', 'MAX_TIMEUUID')
                            )

    def test_complete_in_select_where_equal(self):
        self.trycompletions('SELECT * FROM system.peers WHERE rack = ',
                            choices=('-', '<blobLiteral>', '<float>', '<wholenumber>', '<uuid>',
                                     '<identifier>', '<pgStringLiteral>', '<quotedStringLiteral>',
                                     '[', '{', 'false', 'true', 'NULL',
                                     'TOKEN', 'MIN_TIMEUUID', 'MAX_TIMEUUID',
                                     'CURRENT_DATE()', 'CURRENT_TIME()', 'CURRENT_TIMEUUID()', 'CURRENT_TIMESTAMP()'
                                     )
                            )

    def test_complete_in_insert(self):
        self.trycompletions('INSERT INTO  ',
                            choices=('twenty_rows_table',
                                     'ascii_with_special_chars',
                                     'users',
                                     'has_all_types',
                                     'system.',
                                     'empty_composite_table',
                                     'empty_table',
                                     'undefined_values_table',
                                     'dynamic_columns',
                                     'twenty_rows_composite_table',
                                     'utf8_with_special_chars',
                                     'system_traces.',
                                     'songs'),
                            other_choices_ok=True)
        self.trycompletions('INSERT INTO twenty_rows_composite_table',
                            immediate=' ')
        self.trycompletions('INSERT INTO twenty_rows_composite_table ',
                            choices=['(', 'JSON'])
        self.trycompletions('INSERT INTO twenty_rows_composite_table (a, b ',
                            choices=(')', ','))
        self.trycompletions('INSERT INTO twenty_rows_composite_table (a, b, ',
                            immediate='c ')
        self.trycompletions('INSERT INTO twenty_rows_composite_table (a, b, c ',
                            choices=(',', ')'))
        self.trycompletions('INSERT INTO twenty_rows_composite_table (a, b)',
                            immediate=' VALUES ( ')
        self.trycompletions('INSERT INTO twenty_rows_composite_table (a, b, c) VAL',
                            immediate='UES ( ')

        self.trycompletions(
            'INSERT INTO twenty_rows_composite_table (a, b, c) VALUES (',
            choices=['<value for a (text)>'],
            split_completed_lines=False)

        self.trycompletions(
            "INSERT INTO twenty_rows_composite_table (a, b, c) VALUES ('",
            choices=['<value for a (text)>'],
            split_completed_lines=False)

        self.trycompletions(
            "INSERT INTO twenty_rows_composite_table (a, b, c) VALUES ( 'eggs",
            choices=['<value for a (text)>'],
            split_completed_lines=False)

        self.trycompletions(
            "INSERT INTO twenty_rows_composite_table (a, b, c) VALUES ('eggs'",
            immediate=', ')

        self.trycompletions(
            ("INSERT INTO twenty_rows_composite_table (a, b, c) "
             "VALUES ( 'eggs',"),
            choices=['<value for b (text)>'],
            split_completed_lines=False)

        self.trycompletions(
            ("INSERT INTO twenty_rows_composite_table (a, b, c) "
             "VALUES ( 'eggs', 'sausage', 'spam')"),
            immediate=' ')

        self.trycompletions(
            ("INSERT INTO twenty_rows_composite_table (a, b, c) "
             "VALUES ( 'eggs', 'sausage', 'spam') "),
            choices=[';', 'USING', 'IF'])

        self.trycompletions(
            ("INSERT INTO twenty_rows_composite_table (a, b, c) "
             "VALUES ( 'eggs', 'sausage', 'spam');"),
            choices=['?', 'ADD', 'ALTER', 'BEGIN', 'CAPTURE', 'COMMENT', 'CONSISTENCY', 'COPY',
                     'CREATE', 'DEBUG', 'DELETE', 'DESC', 'DESCRIBE', 'DROP',
                     'ELAPSED', 'EXPAND', 'GRANT', 'HELP', 'INSERT', 'LIST', 'LOGIN', 'PAGING',
                     'REVOKE', 'SECURITY', 'SELECT', 'SHOW', 'SOURCE', 'SERIAL', 'TRACING',
                     'TRUNCATE', 'UPDATE', 'USE', 'exit', 'history', 'quit',
                     'CLEAR', 'CLS'])

        self.trycompletions(
            ("INSERT INTO twenty_rows_composite_table (a, b, c) "
             "VALUES ( 'eggs', 'sausage', 'spam') US"),
            immediate='ING T')

        self.trycompletions(
            ("INSERT INTO twenty_rows_composite_table (a, b, c) "
             "VALUES ( 'eggs', 'sausage', 'spam') USING"),
            immediate=' T')

        self.trycompletions(
            ("INSERT INTO twenty_rows_composite_table (a, b, c) "
             "VALUES ( 'eggs', 'sausage', 'spam') USING T"),
            choices=['TTL', 'TIMESTAMP'])

        self.trycompletions(
            ("INSERT INTO twenty_rows_composite_table (a, b, c) "
             "VALUES ( 'eggs', 'sausage', 'spam') USING TT"),
            immediate='L ')

        self.trycompletions(
            ("INSERT INTO twenty_rows_composite_table (a, b, c) "
             "VALUES ( 'eggs', 'sausage', 'spam') USING TI"),
            immediate='MESTAMP ')

        self.trycompletions(
            ("INSERT INTO twenty_rows_composite_table (a, b, c) "
             "VALUES ( 'eggs', 'sausage', 'spam') USING TIMESTAMP "),
            choices=['<wholenumber>'])

        self.trycompletions(
            ("INSERT INTO twenty_rows_composite_table (a, b, c) "
             "VALUES ( 'eggs', 'sausage', 'spam') USING TTL "),
            choices=['<wholenumber>'])

        self.trycompletions(
            ("INSERT INTO twenty_rows_composite_table (a, b, c) "
             "VALUES ( 'eggs', 'sausage', 'spam') USING TIMESTAMP 0 "),
            choices=['AND', ';'])

        self.trycompletions(
            ("INSERT INTO twenty_rows_composite_table (a, b, c) "
             "VALUES ( 'eggs', 'sausage', 'spam') USING TTL 0 "),
            choices=['AND', ';'])

        self.trycompletions(
            ("INSERT INTO twenty_rows_composite_table (a, b, c) "
             "VALUES ( 'eggs', 'sausage', 'spam') USING TIMESTAMP 0 A"),
            immediate='ND TTL ')

        self.trycompletions(
            ("INSERT INTO twenty_rows_composite_table (a, b, c) "
             "VALUES ( 'eggs', 'sausage', 'spam') USING TTL 0 A"),
            immediate='ND TIMESTAMP ')

        self.trycompletions(
            ("INSERT INTO twenty_rows_composite_table (a, b, c) "
             "VALUES ( 'eggs', 'sausage', 'spam') USING TTL 0 AND TIMESTAMP "),
            choices=['<wholenumber>'])

        self.trycompletions(
            ("INSERT INTO twenty_rows_composite_table (a, b, c) "
             "VALUES ( 'eggs', 'sausage', 'spam') USING TTL 0 AND TIMESTAMP 0 "),
            choices=['AND', ';'])

        self.trycompletions(
            ("INSERT INTO twenty_rows_composite_table (a, b, c) "
             "VALUES ( 'eggs', 'sausage', 'spam') USING TTL 0 AND TIMESTAMP 0 AND "),
            choices=[])

        self.trycompletions(
            ("INSERT INTO has_all_types (num, setcol) VALUES (0, "),
            immediate="{ ")

        self.trycompletions(
            ("INSERT INTO has_all_types (num, mapcol) VALUES (0, "),
            immediate="{ ")

        self.trycompletions(
            ("INSERT INTO has_all_types (num, listcol) VALUES (0, "),
            immediate="[ ")

        self.trycompletions(
            ("INSERT INTO has_all_types (num, vectorcol) VALUES (0, "),
            immediate="[ ")

    def test_complete_in_update(self):
        self.trycompletions("UPD", immediate="ATE ")
        self.trycompletions("UPDATE ",
                            choices=['twenty_rows_table',
                                     'users', 'has_all_types', 'system.',
                                     'ascii_with_special_chars',
                                     'empty_composite_table', 'empty_table',
                                     'undefined_values_table',
                                     'dynamic_columns',
                                     'twenty_rows_composite_table',
                                     'utf8_with_special_chars',
                                     'system_traces.', 'songs'],
                            other_choices_ok=True)

        self.trycompletions("UPDATE empty_table ", choices=['USING', 'SET'])

        self.trycompletions("UPDATE empty_table S",
                            immediate='ET lonelycol = ')
        self.trycompletions("UPDATE empty_table SET lon",
                            immediate='elycol = ')
        self.trycompletions("UPDATE empty_table SET lonelycol",
                            immediate=' = ')

        self.trycompletions("UPDATE empty_table U", immediate='SING T')
        self.trycompletions("UPDATE empty_table USING T",
                            choices=["TTL", "TIMESTAMP"])

        self.trycompletions("UPDATE empty_table SET lonelycol = ",
                            choices=['<term (text)>'],
                            split_completed_lines=False)

        self.trycompletions("UPDATE empty_table SET lonelycol = 'eg",
                            choices=['<term (text)>'],
                            split_completed_lines=False)
        self.trycompletions("UPDATE empty_table SET lonelycol = 'eggs'",
                            choices=[',', 'WHERE'])
        self.trycompletions("UPDATE empty_table SET lonelycol = 'eggs' WHERE ",
                            choices=['CURRENT_DATE()', 'CURRENT_TIME()', 'CURRENT_TIMESTAMP()',
                                     'CURRENT_TIMEUUID()', 'TOKEN', 'MIN_TIMEUUID', 'MAX_TIMEUUID', 'lonelykey'])

        self.trycompletions("UPDATE empty_table SET lonelycol = 'eggs' WHERE lonel",
                            immediate='ykey ')
        self.trycompletions("UPDATE empty_table SET lonelycol = 'eggs' WHERE lonelykey ",
                            choices=['=', '<=', '>=', '>', '<', '!=', 'BETWEEN', 'CONTAINS', 'IN', 'NOT', '['])
        self.trycompletions("UPDATE empty_table SET lonelycol = 'eggs' WHERE lonelykey = 0.0 ",
                            choices=['AND', 'IF', ';'])
        self.trycompletions("UPDATE empty_table SET lonelycol = 'eggs' WHERE lonelykey = 0.0 AND ",
                            choices=['CURRENT_DATE()', 'CURRENT_TIME()', 'CURRENT_TIMESTAMP()',
                                     'CURRENT_TIMEUUID()', 'TOKEN', 'MIN_TIMEUUID', 'MAX_TIMEUUID', 'lonelykey'])

        self.trycompletions("UPDATE empty_table SET lonelycol = 'eggs' WHERE TOKEN(lonelykey ",
                            choices=[',', ')'])
        self.trycompletions("UPDATE empty_table SET lonelycol = 'eggs' WHERE TOKEN(lonelykey) ",
                            choices=['=', '<=', '>=', '<', '>'])
        self.trycompletions("UPDATE empty_table SET lonelycol = 'eggs' WHERE TOKEN(lonelykey) <= TOKEN(13) ",
                            choices=[';', 'AND', 'IF'])
        self.trycompletions("UPDATE empty_table SET lonelycol = 'eggs' WHERE TOKEN(lonelykey) <= TOKEN(13) IF ",
                            choices=['EXISTS', '<quotedName>', '<identifier>'])

        self.trycompletions("UPDATE empty_table SET lonelycol = 'eggs' WHERE TOKEN(lonelykey) <= TOKEN(13) IF EXISTS ",
                            choices=['>=', '!=', '<=', 'IN', '[', ';', '=', '<', '>', '.', 'CONTAINS'])

        self.trycompletions("UPDATE empty_table SET lonelycol = 'eggs' WHERE TOKEN(lonelykey) <= TOKEN(13) IF lonelykey ",
                            choices=['>=', '!=', '<=', 'IN', '=', '<', '>', 'CONTAINS'])

        self.trycompletions("UPDATE empty_table SET lonelycol = 'eggs' WHERE TOKEN(lonelykey) <= TOKEN(13) IF lonelykey CONTAINS ",
                            choices=['false', 'true', '<pgStringLiteral>',
                                     '-', '<float>', 'TOKEN', '<identifier>',
                                     '<uuid>', '{', '[', 'NULL', '<quotedStringLiteral>',
                                     '<blobLiteral>', '<wholenumber>', 'KEY'])

    def test_complete_in_delete(self):
        self.trycompletions('DELETE F', choices=['FROM', '<identifier>', '<quotedName>'])

        self.trycompletions('DELETE a ', choices=['FROM', '[', '.', ','])
        self.trycompletions('DELETE a [',
                            choices=['<wholenumber>', 'false', '-', '<uuid>',
                                     '<pgStringLiteral>', '<float>', 'TOKEN',
                                     '<identifier>', '<quotedStringLiteral>',
                                     '{', '[', 'NULL', 'true', '<blobLiteral>'])

        self.trycompletions('DELETE a, ',
                            choices=['<identifier>', '<quotedName>'])

        self.trycompletions('DELETE a FROM ',
                            choices=['twenty_rows_table',
                                     'ascii_with_special_chars', 'users',
                                     'has_all_types', 'system.',
                                     'empty_composite_table', 'empty_table',
                                     'system_auth.', 'undefined_values_table',
                                     'dynamic_columns',
                                     'twenty_rows_composite_table',
                                     'utf8_with_special_chars',
                                     'system_traces.', 'songs',
                                     self.cqlsh.keyspace + '.'],
                            other_choices_ok=True)

        self.trycompletions('DELETE FROM ',
                            choices=['twenty_rows_table',
                                     'ascii_with_special_chars', 'users',
                                     'has_all_types', 'system.',
                                     'empty_composite_table', 'empty_table',
                                     'system_auth.', 'undefined_values_table',
                                     'dynamic_columns',
                                     'twenty_rows_composite_table',
                                     'utf8_with_special_chars',
                                     'system_traces.', 'songs',
                                     'system_auth.', 'system_distributed.',
                                     'system_schema.', 'system_traces.',
                                     self.cqlsh.keyspace + '.'],
                            other_choices_ok=True)
        self.trycompletions('DELETE FROM twenty_rows_composite_table ',
                            choices=['USING', 'WHERE'])

        self.trycompletions('DELETE FROM twenty_rows_composite_table U',
                            immediate='SING TIMESTAMP ')

        self.trycompletions('DELETE FROM twenty_rows_composite_table USING TIMESTAMP ',
                            choices=['<wholenumber>'])
        self.trycompletions('DELETE FROM twenty_rows_composite_table USING TIMESTAMP 0',
                            choices=['<wholenumber>'])
        self.trycompletions('DELETE FROM twenty_rows_composite_table USING TIMESTAMP 0 ',
                            immediate='WHERE ')
        self.trycompletions('DELETE FROM twenty_rows_composite_table USING TIMESTAMP 0 WHERE ',
                            choices=['a', 'b', 'CURRENT_DATE()', 'CURRENT_TIME()', 'CURRENT_TIMESTAMP()',
                                     'CURRENT_TIMEUUID()', 'MAX_TIMEUUID', 'MIN_TIMEUUID', 'TOKEN'])

        self.trycompletions('DELETE FROM twenty_rows_composite_table USING TIMESTAMP 0 WHERE a ',
                            choices=['<=', '>=', 'BETWEEN', 'CONTAINS', 'IN', 'NOT', '[', '=', '<', '>', '!='])

        self.trycompletions('DELETE FROM twenty_rows_composite_table USING TIMESTAMP 0 WHERE TOKEN(',
                            immediate='a ')
        self.trycompletions('DELETE FROM twenty_rows_composite_table USING TIMESTAMP 0 WHERE TOKEN(a',
                            immediate=' ')
        self.trycompletions('DELETE FROM twenty_rows_composite_table USING TIMESTAMP 0 WHERE TOKEN(a ',
                            choices=[')', ','])
        self.trycompletions('DELETE FROM twenty_rows_composite_table USING TIMESTAMP 0 WHERE TOKEN(a) ',
                            choices=['>=', '<=', '=', '<', '>'])
        self.trycompletions('DELETE FROM twenty_rows_composite_table USING TIMESTAMP 0 WHERE TOKEN(a) >= ',
                            choices=['false', 'true', '<pgStringLiteral>',
                                     '-', '<float>', 'TOKEN',
                                     '<identifier>', '<uuid>', '{', '[', 'NULL',
                                     '<quotedStringLiteral>', '<blobLiteral>',
                                     '<wholenumber>'])
        self.trycompletions(('DELETE FROM twenty_rows_composite_table USING TIMESTAMP 0 WHERE '
                             'TOKEN(a) >= TOKEN(0) '),
                            choices=['AND', 'IF', ';'])
        self.trycompletions(('DELETE FROM twenty_rows_composite_table USING TIMESTAMP 0 WHERE '
                             'TOKEN(a) >= TOKEN(0) IF '),
                            choices=['EXISTS', '<identifier>', '<quotedName>'])
        self.trycompletions(('DELETE FROM twenty_rows_composite_table USING TIMESTAMP 0 WHERE '
                             'TOKEN(a) >= TOKEN(0) IF b '),
                            choices=['>=', '!=', '<=', 'IN', '=', '<', '>', 'CONTAINS'])
        self.trycompletions(('DELETE FROM twenty_rows_composite_table USING TIMESTAMP 0 WHERE '
                             'TOKEN(a) >= TOKEN(0) IF b CONTAINS '),
                            choices=['false', 'true', '<pgStringLiteral>',
                                     '-', '<float>', 'TOKEN', '<identifier>',
                                     '<uuid>', '{', '[', 'NULL', '<quotedStringLiteral>',
                                     '<blobLiteral>', '<wholenumber>', 'KEY'])
        self.trycompletions(('DELETE FROM twenty_rows_composite_table USING TIMESTAMP 0 WHERE '
                             'TOKEN(a) >= TOKEN(0) IF b < 0 '),
                            choices=['AND', ';'])
        self.trycompletions(('DELETE FROM twenty_rows_composite_table USING TIMESTAMP 0 WHERE '
                             'TOKEN(a) >= TOKEN(0) IF b < 0 AND '),
                            choices=['<identifier>', '<quotedName>'])
        self.trycompletions(("DELETE FROM twenty_rows_composite_table USING TIMESTAMP 0 WHERE "
                             "b = 'eggs'"),
                            choices=['AND', 'IF', ';'])

    def test_complete_in_begin_batch(self):
        self.trycompletions('BEGIN ', choices=['BATCH', 'COUNTER', 'UNLOGGED'])
        self.trycompletions('BEGIN BATCH ', choices=['DELETE', 'INSERT', 'UPDATE', 'USING'])
        self.trycompletions('BEGIN BATCH INSERT ', immediate='INTO ')

    def test_complete_in_create_keyspace(self):
        self.trycompletions('create keyspace ', '', choices=('<identifier>', '<quotedName>', 'IF'))
        self.trycompletions('create keyspace moo ',
                            "WITH replication = {'class': '")
        self.trycompletions('create keyspace "12SomeName" with ',
                            "replication = {'class': '")
        self.trycompletions("create keyspace fjdkljf with foo=bar ", "",
                            choices=('AND', ';'))
        self.trycompletions("create keyspace fjdkljf with foo=bar AND ",
                            "replication = {'class': '")
        self.trycompletions("create keyspace moo with replication", " = {'class': '")
        self.trycompletions("create keyspace moo with replication=", " {'class': '")
        self.trycompletions("create keyspace moo with replication={", "'class':'")
        self.trycompletions("create keyspace moo with replication={'class'", ":'")
        self.trycompletions("create keyspace moo with replication={'class': ", "'")
        self.trycompletions("create keyspace moo with replication={'class': '", "",
                            choices=self.strategies())
        # ttl is an "unreserved keyword". should work
        self.trycompletions("create keySPACE ttl with replication ="
                            "{ 'class' : 'SimpleStrategy'", ", 'replication_factor': ")
        self.trycompletions("create   keyspace ttl with replication ="
                            "{'class':'SimpleStrategy',", " 'replication_factor': ")
        self.trycompletions("create keyspace \"ttl\" with replication ="
                            "{'class': 'SimpleStrategy', ", "'replication_factor': ")
        self.trycompletions("create keyspace \"ttl\" with replication ="
                            "{'class': 'SimpleStrategy', 'repl", "ication_factor'")
        self.trycompletions("create keyspace foo with replication ="
                            "{'class': 'SimpleStrategy', 'replication_factor': ", '',
                            choices=('<term>',))
        self.trycompletions("create keyspace foo with replication ="
                            "{'class': 'SimpleStrategy', 'replication_factor': 1", '',
                            choices=('<term>',))
        self.trycompletions("create keyspace foo with replication ="
                            "{'class': 'SimpleStrategy', 'replication_factor': 1 ", '}')
        self.trycompletions("create keyspace foo with replication ="
                            "{'class': 'SimpleStrategy', 'replication_factor': 1, ",
                            '', choices=())
        self.trycompletions("create keyspace foo with replication ="
                            "{'class': 'SimpleStrategy', 'replication_factor': 1} ",
                            '', choices=('AND', ';'))
        self.trycompletions("create keyspace foo with replication ="
                            "{'class': 'NetworkTopologyStrategy', ", '',
                            choices=('<dc_name>',))
        self.trycompletions("create keyspace \"PB and J\" with replication={"
                            "'class': 'NetworkTopologyStrategy'", ', ')
        self.trycompletions("create keyspace PBJ with replication={"
                            "'class': 'NetworkTopologyStrategy'} and ",
                            "durable_writes = '")

    def test_complete_in_string_literals(self):
        # would be great if we could get a space after this sort of completion,
        # but readline really wants to make things difficult for us
        self.trycompletions("create keyspace blah with replication = {'class': 'Sim",
                            "pleStrategy'")

    def test_complete_in_drop(self):
        self.trycompletions('DR', immediate='OP ')
        self.trycompletions('DROP ',
                            choices=['AGGREGATE', 'COLUMNFAMILY', 'FUNCTION', 'IDENTITY',
                                     'INDEX', 'KEYSPACE', 'ROLE', 'TABLE',
                                     'TRIGGER', 'TYPE', 'USER', 'MATERIALIZED'])

    def test_complete_in_drop_keyspace(self):
        self.trycompletions('DROP K', immediate='EYSPACE ')
        quoted_keyspace = '"' + self.cqlsh.keyspace + '"'
        self.trycompletions('DROP KEYSPACE ',
                            choices=['IF', self.cqlsh.keyspace])

        self.trycompletions('DROP KEYSPACE ' + quoted_keyspace,
                            choices=[';'])

        self.trycompletions('DROP KEYSPACE I',
                            immediate='F EXISTS ' + self.cqlsh.keyspace + ' ;')

    def test_complete_in_create_type(self):
        self.trycompletions('CREATE TYPE foo ', choices=['(', '.'])

    def test_complete_in_drop_type(self):
        self.trycompletions('DROP TYPE ',
                            choices=['IF', 'system_views.', 'system_metrics.',
                                     'tags', 'system_traces.', 'system_distributed.', 'system_cluster_metadata.',
                                     'phone_number', 'quote_udt', 'band_info_type', 'address', 'system.', 'system_schema.',
                                     'system_auth.', 'system_virtual_schema.', self.cqlsh.keyspace + '.'])

    def test_complete_in_create_trigger(self):
        self.trycompletions('CREATE TRIGGER ', choices=['<identifier>', '<quotedName>', 'IF'])
        self.trycompletions('CREATE TRIGGER foo ', immediate='ON ')
        self.trycompletions('CREATE TRIGGER foo ON ', choices=['system.', 'system_auth.', 'system_distributed.',
                                                               'system_schema.', 'system_traces.', 'system_views.',
                                                               'system_virtual_schema.'], other_choices_ok=True)

    def create_columnfamily_table_template(self, name):
        """Parameterized test for CREATE COLUMNFAMILY and CREATE TABLE. Since
        they're synonyms, they should have the same completion behavior, so this
        test avoids duplication between tests for the two statements."""
        prefix = 'CREATE ' + name + ' '
        quoted_keyspace = '"' + self.cqlsh.keyspace + '"'
        self.trycompletions(prefix + '',
                            choices=['IF', self.cqlsh.keyspace, '<new_table_name>'])
        self.trycompletions(prefix + 'IF ',
                            immediate='NOT EXISTS ')
        self.trycompletions(prefix + 'IF NOT EXISTS ',
                            choices=['<new_table_name>', self.cqlsh.keyspace])
        self.trycompletions(prefix + 'IF NOT EXISTS new_table ',
                            choices=['(', '.', 'LIKE'])

        self.trycompletions(prefix + quoted_keyspace, choices=['.', '(', 'LIKE'])

        self.trycompletions(prefix + quoted_keyspace + '( ',
                            choices=['<new_column_name>', '<identifier>',
                                     '<quotedName>'])

        self.trycompletions(prefix + quoted_keyspace + '.',
                            choices=['<new_table_name>'])
        self.trycompletions(prefix + quoted_keyspace + '.new_table ',
                            choices=['(', 'LIKE'])
        self.trycompletions(prefix + quoted_keyspace + '.new_table ( ',
                            choices=['<new_column_name>', '<identifier>',
                                     '<quotedName>'])

        self.trycompletions(prefix + ' new_table ( ',
                            choices=['<new_column_name>', '<identifier>',
                                     '<quotedName>'])
        self.trycompletions(prefix + ' new_table (col_a ine',
                            immediate='t ')
        self.trycompletions(prefix + ' new_table (col_a int ',
                            choices=[',', 'CHECK', 'MASKED', 'PRIMARY'])
        self.trycompletions(prefix + ' new_table (col_a int M',
                            immediate='ASKED WITH ')
        self.trycompletions(prefix + ' new_table (col_a int MASKED WITH ',
                            choices=['DEFAULT', self.cqlsh.keyspace + '.', 'system.'],
                            other_choices_ok=True)
        self.trycompletions(prefix + ' new_table (col_a int P',
                            immediate='RIMARY KEY ')
        self.trycompletions(prefix + ' new_table (col_a int PRIMARY KEY ',
                            choices=[')', ','])

        self.trycompletions(prefix + ' new_table (col_a v',
                            choices=['varchar', 'varint', 'vector'])
        self.trycompletions(prefix + ' new_table (col_a ve',
                            immediate='ctor ')
        self.trycompletions(prefix + ' new_table (col_a vector<',
                            choices=['address', 'boolean', 'duration', 'list'],
                            other_choices_ok=True)
        self.trycompletions(prefix + ' new_table (col_a vector<float, ',
                            choices=['<wholenumber>'])
        self.trycompletions(prefix + ' new_table (col_a vector<float, 2 ',
                            immediate='>')

        self.trycompletions(prefix + ' new_table (col_a int PRIMARY KEY,',
                            choices=['<identifier>', '<quotedName>'])
        self.trycompletions(prefix + ' new_table (col_a int PRIMARY KEY)',
                            immediate=' ')
        self.trycompletions(prefix + ' new_table (col_a int PRIMARY KEY) ',
                            choices=[';', 'WITH'])
        self.trycompletions(prefix + ' new_table (col_a int PRIMARY KEY) W',
                            immediate='ITH ')
        self.trycompletions(prefix + ' new_table (col_a int PRIMARY KEY) WITH ',
                            choices=['allow_auto_snapshot',
                                     'bloom_filter_fp_chance', 'compaction',
                                     'compression',
                                     'default_time_to_live', 'gc_grace_seconds',
                                     'incremental_backups',
                                     'max_index_interval',
                                     'memtable',
                                     'memtable_flush_period_in_ms',
                                     'CLUSTERING',
                                     'COMPACT', 'caching', 'comment',
                                     'min_index_interval', 'speculative_retry', 'additional_write_policy', 'cdc', 'read_repair'])
        self.trycompletions(prefix + ' new_table (col_a int PRIMARY KEY) WITH ',
                            choices=['allow_auto_snapshot',
                                     'bloom_filter_fp_chance', 'compaction',
                                     'compression',
                                     'default_time_to_live', 'gc_grace_seconds',
                                     'incremental_backups',
                                     'max_index_interval',
                                     'memtable',
                                     'memtable_flush_period_in_ms',
                                     'CLUSTERING',
                                     'COMPACT', 'caching', 'comment',
                                     'min_index_interval', 'speculative_retry', 'additional_write_policy', 'cdc', 'read_repair'])
        self.trycompletions(prefix + ' new_table (col_a int PRIMARY KEY) WITH bloom_filter_fp_chance ',
                            immediate='= ')
        self.trycompletions(prefix + ' new_table (col_a int PRIMARY KEY) WITH bloom_filter_fp_chance = ',
                            choices=['<float_between_0_and_1>'])

        self.trycompletions(prefix + ' new_table (col_a int PRIMARY KEY) WITH compaction ',
                            immediate="= {'class': '")
        self.trycompletions(prefix + " new_table (col_a int PRIMARY KEY) WITH compaction = "
                            + "{'class': '",
                            choices=['SizeTieredCompactionStrategy',
                                     'LeveledCompactionStrategy',
                                     'TimeWindowCompactionStrategy',
                                     'UnifiedCompactionStrategy'])
        self.trycompletions(prefix + " new_table (col_a int PRIMARY KEY) WITH compaction = "
                            + "{'class': 'S",
                            immediate="izeTieredCompactionStrategy'")
        self.trycompletions(prefix + " new_table (col_a int PRIMARY KEY) WITH compaction = "
                            + "{'class': 'SizeTieredCompactionStrategy",
                            immediate="'")
        self.trycompletions(prefix + " new_table (col_a int PRIMARY KEY) WITH compaction = "
                            + "{'class': 'SizeTieredCompactionStrategy'",
                            choices=['}', ','])
        self.trycompletions(prefix + " new_table (col_a int PRIMARY KEY) WITH compaction = "
                            + "{'class': 'SizeTieredCompactionStrategy', ",
                            immediate="'")
        self.trycompletions(prefix + " new_table (col_a int PRIMARY KEY) WITH compaction = "
                            + "{'class': 'SizeTieredCompactionStrategy', '",
                            choices=['bucket_high', 'bucket_low', 'class',
                                     'enabled', 'max_threshold',
                                     'min_sstable_size', 'min_threshold',
                                     'tombstone_compaction_interval',
                                     'tombstone_threshold',
                                     'unchecked_tombstone_compaction',
                                     'only_purge_repaired_tombstones',
                                     'provide_overlapping_tombstones'])
        self.trycompletions(prefix + " new_table (col_a int PRIMARY KEY) WITH compaction = "
                            + "{'class': 'SizeTieredCompactionStrategy'}",
                            choices=[';', 'AND'])
        self.trycompletions(prefix + " new_table (col_a int PRIMARY KEY) WITH compaction = "
                            + "{'class': 'SizeTieredCompactionStrategy'} AND ",
                            choices=['allow_auto_snapshot', 'bloom_filter_fp_chance', 'compaction',
                                     'compression',
                                     'default_time_to_live', 'gc_grace_seconds',
                                     'incremental_backups',
                                     'max_index_interval',
                                     'memtable',
                                     'memtable_flush_period_in_ms',
                                     'CLUSTERING',
                                     'COMPACT', 'caching', 'comment',
                                     'min_index_interval', 'speculative_retry', 'additional_write_policy', 'cdc', 'read_repair'])
        self.trycompletions(prefix + " new_table (col_a int PRIMARY KEY) WITH compaction = "
                            + "{'class': 'TimeWindowCompactionStrategy', '",
                            choices=['compaction_window_unit', 'compaction_window_size',
                                     'timestamp_resolution', 'min_threshold', 'class', 'max_threshold',
                                     'tombstone_compaction_interval', 'tombstone_threshold',
                                     'enabled', 'unchecked_tombstone_compaction',
                                     'only_purge_repaired_tombstones', 'provide_overlapping_tombstones'])
        self.trycompletions(prefix + " new_table (col_a int PRIMARY KEY) WITH compaction = "
                            + "{'class': 'UnifiedCompactionStrategy', '",
                            choices=['scaling_parameters', 'min_sstable_size',
                                     'flush_size_override', 'base_shard_count', 'class', 'target_sstable_size',
                                     'sstable_growth', 'max_sstables_to_compact',
                                     'enabled', 'expired_sstable_check_frequency_seconds',
                                     'unsafe_aggressive_sstable_expiration', 'overlap_inclusion_method',
                                     'tombstone_threshold', 'tombstone_compaction_interval',
                                     'unchecked_tombstone_compaction', 'provide_overlapping_tombstones',
                                     'max_threshold', 'only_purge_repaired_tombstones'])

    def test_complete_in_create_columnfamily(self):
        self.trycompletions('CREATE C', choices=['COLUMNFAMILY', 'CUSTOM'])
        self.trycompletions('CREATE CO', immediate='LUMNFAMILY ')
        self.create_columnfamily_table_template('COLUMNFAMILY')

    def test_complete_in_create_materializedview(self):
        self.trycompletions('CREATE MAT', immediate='ERIALIZED VIEW ')
        self.trycompletions('CREATE MATERIALIZED VIEW AS ', choices=['AS', 'SELECT'])
        self.trycompletions('CREATE MATERIALIZED VIEW AS SELECT * ', immediate='FROM ')
        self.trycompletions('CREATE MATERIALIZED VIEW AS SELECT * FROM system.peers ', immediate='WHERE ')
        self.trycompletions('CREATE MATERIALIZED VIEW AS SELECT * FROM system.peers WHERE host_id ', immediate='IS NOT NULL ')
        self.trycompletions('CREATE MATERIALIZED VIEW AS SELECT * FROM system.peers WHERE host_id IS NOT NULL PR', immediate='IMARY KEY ( ')
        self.trycompletions('CREATE MATERIALIZED VIEW AS SELECT * FROM system.peers WHERE host_id IS NOT NULL PRIMARY KEY (host_id) ', choices=[';', 'WITH'])
        self.trycompletions('CREATE MATERIALIZED VIEW AS SELECT * FROM system.peers WHERE host_id IS NOT NULL PRIMARY KEY (a, b) ', choices=[';', 'WITH'])
        self.trycompletions('CREATE MATERIALIZED VIEW AS SELECT * FROM system.peers WHERE host_id IS NOT NULL PRIMARY KEY ((a,b), c) ', choices=[';', 'WITH'])

    def test_complete_in_create_table(self):
        self.trycompletions('CREATE T', choices=['TRIGGER', 'TABLE', 'TYPE'])
        self.trycompletions('CREATE TA', immediate='BLE ')
        self.create_columnfamily_table_template('TABLE')

    def test_complete_in_create_table_like(self):
        self.trycompletions('CREATE T', choices=['TRIGGER', 'TABLE', 'TYPE'])
        self.trycompletions('CREATE TA', immediate='BLE ')
        quoted_keyspace = '"' + self.cqlsh.keyspace + '"'
        self.trycompletions('CREATE TABLE ',
                            choices=['IF', self.cqlsh.keyspace, '<new_table_name>'])
        self.trycompletions('CREATE TABLE IF ',
                            immediate='NOT EXISTS ')
        self.trycompletions('CREATE TABLE IF NOT EXISTS ',
                            choices=['<new_table_name>', self.cqlsh.keyspace])
        self.trycompletions('CREATE TABLE IF NOT EXISTS new_table L',
                            immediate='IKE ')
        self.trycompletions('CREATE TABLE ' + quoted_keyspace + '.',
                            choices=['<new_table_name>'])
        self.trycompletions('CREATE TABLE ' + quoted_keyspace + '.new_table L',
                            immediate='IKE ')
        self.trycompletions('CREATE TABLE new_table LIKE old_table W',
                            immediate='ITH ')
        self.trycompletions('CREATE TABLE new_table LIKE old_table WITH ',
                            choices=['allow_auto_snapshot',
                                     'bloom_filter_fp_chance', 'compaction',
                                     'compression',
                                     'default_time_to_live', 'gc_grace_seconds',
                                     'incremental_backups',
                                     'max_index_interval',
                                     'memtable',
                                     'memtable_flush_period_in_ms',
                                     'caching', 'comment',
                                     'min_index_interval',
                                     'speculative_retry', 'additional_write_policy',
                                     'cdc', 'read_repair',
                                     'INDEXES', 'COMMENTS', 'SECURITY'])
        self.trycompletions('CREATE TABLE new_table LIKE old_table WITH INDEXES ',
                            choices=[';' , '=', 'AND'])
        self.trycompletions('CREATE TABLE ' + 'new_table LIKE old_table WITH bloom_filter_fp_chance ',
                            immediate='= ')
        self.trycompletions('CREATE TABLE ' + 'new_table LIKE old_table WITH bloom_filter_fp_chance = ',
                            choices=['<float_between_0_and_1>'])
        self.trycompletions('CREATE TABLE ' + 'new_table LIKE old_table WITH compaction ',
                            immediate="= {'class': '")
        self.trycompletions('CREATE TABLE ' + "new_table LIKE old_table WITH compaction = "
                            + "{'class': '",
                            choices=['SizeTieredCompactionStrategy',
                                     'LeveledCompactionStrategy',
                                     'TimeWindowCompactionStrategy',
                                     'UnifiedCompactionStrategy'])
        self.trycompletions('CREATE TABLE ' + "new_table LIKE old_table WITH compaction = "
                            + "{'class': 'S",
                            immediate="izeTieredCompactionStrategy'")
        self.trycompletions('CREATE TABLE ' + "new_table LIKE old_table WITH compaction = "
                            + "{'class': 'SizeTieredCompactionStrategy",
                            immediate="'")
        self.trycompletions('CREATE TABLE ' + "new_table LIKE old_table WITH compaction = "
                            + "{'class': 'SizeTieredCompactionStrategy'",
                            choices=['}', ','])
        self.trycompletions('CREATE TABLE ' + "new_table LIKE old_table WITH compaction = "
                            + "{'class': 'SizeTieredCompactionStrategy', ",
                            immediate="'")
        self.trycompletions('CREATE TABLE ' + "new_table LIKE old_table WITH compaction = "
                            + "{'class': 'SizeTieredCompactionStrategy', '",
                            choices=['bucket_high', 'bucket_low', 'class',
                                     'enabled', 'max_threshold',
                                     'min_sstable_size', 'min_threshold',
                                     'tombstone_compaction_interval',
                                     'tombstone_threshold',
                                     'unchecked_tombstone_compaction',
                                     'only_purge_repaired_tombstones',
                                     'provide_overlapping_tombstones'])
        self.trycompletions('CREATE TABLE ' + "new_table LIKE old_table WITH compaction = "
                            + "{'class': 'SizeTieredCompactionStrategy'}",
                            choices=[';', 'AND'])
        self.trycompletions('CREATE TABLE ' + "new_table LIKE old_table WITH compaction = "
                            + "{'class': 'SizeTieredCompactionStrategy'} AND ",
                            choices=['allow_auto_snapshot', 'bloom_filter_fp_chance', 'compaction',
                                     'compression',
                                     'default_time_to_live', 'gc_grace_seconds',
                                     'incremental_backups',
                                     'max_index_interval',
                                     'memtable',
                                     'memtable_flush_period_in_ms',
                                     'caching', 'comment',
                                     'min_index_interval',
                                     'speculative_retry', 'additional_write_policy',
                                     'cdc', 'read_repair',
                                     'INDEXES', 'COMMENTS', 'SECURITY'])
        self.trycompletions('CREATE TABLE ' + "new_table LIKE old_table WITH compaction = "
                            + "{'class': 'TimeWindowCompactionStrategy', '",
                            choices=['compaction_window_unit', 'compaction_window_size',
                                     'timestamp_resolution', 'min_threshold', 'class', 'max_threshold',
                                     'tombstone_compaction_interval', 'tombstone_threshold',
                                     'enabled', 'unchecked_tombstone_compaction',
                                     'only_purge_repaired_tombstones', 'provide_overlapping_tombstones'])
        self.trycompletions('CREATE TABLE ' + "new_table LIKE old_table WITH compaction = "
                            + "{'class': 'UnifiedCompactionStrategy', '",
                            choices=['scaling_parameters', 'min_sstable_size',
                                     'flush_size_override', 'base_shard_count', 'class', 'target_sstable_size',
                                     'sstable_growth', 'max_sstables_to_compact',
                                     'enabled', 'expired_sstable_check_frequency_seconds',
                                     'unsafe_aggressive_sstable_expiration', 'overlap_inclusion_method',
                                     'tombstone_threshold', 'tombstone_compaction_interval',
                                     'unchecked_tombstone_compaction', 'provide_overlapping_tombstones',
                                     'max_threshold', 'only_purge_repaired_tombstones'])

    def test_complete_in_describe(self):  # Cassandra-10733
        self.trycompletions('DES', immediate='C')
        # quoted_keyspace = '"' + self.cqlsh.keyspace + '"'
        self.trycompletions('DESCR', immediate='IBE ')
        self.trycompletions('DESC TABLE ',
                            choices=['twenty_rows_table',
                                     'ascii_with_special_chars', 'users',
                                     'has_all_types', 'system.',
                                     'empty_composite_table', 'empty_table',
                                     'system_auth.', 'undefined_values_table',
                                     'dynamic_columns',
                                     'twenty_rows_composite_table',
                                     'utf8_with_special_chars',
                                     'system_traces.', 'songs',
                                     'system_distributed.',
                                     self.cqlsh.keyspace + '.'],
                            other_choices_ok=True)

        self.trycompletions('DESC TYPE ',
                            choices=['system.',
                                     'system_auth.',
                                     'system_traces.',
                                     'system_distributed.',
                                     'address',
                                     'phone_number',
                                     'band_info_type',
                                     'tags'],
                            other_choices_ok=True)

        self.trycompletions('DESC FUNCTION ',
                            choices=['system.',
                                     'system_auth.',
                                     'system_traces.',
                                     'system_distributed.',
                                     'fbestband',
                                     'fbestsong',
                                     'fmax',
                                     'fmin',
                                     self.cqlsh.keyspace + '.'],
                            other_choices_ok=True)

        self.trycompletions('DESC AGGREGATE ',
                            choices=['system.',
                                     'system_auth.',
                                     'system_traces.',
                                     'system_distributed.',
                                     'aggmin',
                                     'aggmax',
                                     self.cqlsh.keyspace + '.'],
                            other_choices_ok=True)

        # Unfortunately these commented tests will not work. This is due to the keyspace name containing quotes;
        # cqlsh auto-completes a DESC differently when the keyspace contains quotes. I'll leave the
        # test here though in case we ever change this script to test using keyspace names without
        # quotes

        # self.trycompletions('DESC TABLE ' + '"' + self.cqlsh.keyspace + '"', immediate='.')

        self.trycompletions('DESC TABLE ' + '"' + self.cqlsh.keyspace + '".',
                            choices=['twenty_rows_table',
                                     'ascii_with_special_chars',
                                     'users',
                                     'has_all_types',
                                     'empty_composite_table',
                                     'empty_table',
                                     'undefined_values_table',
                                     'dynamic_columns',
                                     'twenty_rows_composite_table',
                                     'utf8_with_special_chars',
                                     'songs'],
                            other_choices_ok=True)

        # See comment above for DESC TABLE
        # self.trycompletions('DESC TYPE ' + '"' + self.cqlsh.keyspace + '"', immediate='.')

        self.trycompletions('DESC TYPE ' + '"' + self.cqlsh.keyspace + '".',
                            choices=['address',
                                     'phone_number',
                                     'band_info_type',
                                     'tags'],
                            other_choices_ok=True)

        # See comment above for DESC TABLE
        # self.trycompletions('DESC FUNCTION ' + '"' + self.cqlsh.keyspace + '"', immediate='.f')

        self.trycompletions('DESC FUNCTION ' + '"' + self.cqlsh.keyspace + '".', immediate='f')

        self.trycompletions('DESC FUNCTION ' + '"' + self.cqlsh.keyspace + '".f',
                            choices=['fbestband',
                                     'fbestsong',
                                     'fmax',
                                     'fmin'],
                            other_choices_ok=True)

        # See comment above for DESC TABLE
        # self.trycompletions('DESC AGGREGATE ' + '"' + self.cqlsh.keyspace + '"', immediate='.aggm')

        self.trycompletions('DESC AGGREGATE ' + '"' + self.cqlsh.keyspace + '".', immediate='aggm')

        self.trycompletions('DESC AGGREGATE ' + '"' + self.cqlsh.keyspace + '".aggm',
                            choices=['aggmin',
                                     'aggmax'],
                            other_choices_ok=True)

    def test_complete_in_drop_table(self):
        self.trycompletions('DROP T', choices=['TABLE', 'TRIGGER', 'TYPE'])
        self.trycompletions('DROP TA', immediate='BLE ')

    def test_complete_in_truncate(self):
        self.trycompletions('TR', choices=['TRACING', 'TRUNCATE'])
        self.trycompletions('TRU', immediate='NCATE ')
        self.trycompletions('TRUNCATE T', choices=['TABLE', 'twenty_rows_composite_table', 'twenty_rows_table'])

    def test_complete_in_use(self):
        self.trycompletions('US', immediate='E ')
        self.trycompletions('USE ', choices=[self.cqlsh.keyspace, 'system', 'system_auth', 'system_metrics',
                                             'system_distributed', 'system_schema', 'system_traces', 'system_views',
                                             'system_virtual_schema', 'system_cluster_metadata'])

    def test_complete_in_create_index(self):
        self.trycompletions('CREATE I', immediate='NDEX ')
        self.trycompletions('CREATE INDEX ', choices=['<new_index_name>', 'IF', 'ON'])
        self.trycompletions('CREATE INDEX example ', immediate='ON ')

    def test_complete_in_drop_index(self):
        self.trycompletions('DROP IN', immediate='DEX ')

    def test_complete_in_alter_keyspace(self):
        self.trycompletions('ALTER KEY', 'SPACE ')
        self.trycompletions('ALTER KEYSPACE ', '', choices=[self.cqlsh.keyspace, 'system_auth',
                                                            'system_distributed', 'system_traces', 'IF'])
        self.trycompletions('ALTER KEYSPACE I', immediate='F EXISTS ')
        self.trycompletions('ALTER KEYSPACE system_trac', "es WITH replication = {'class': '")
        self.trycompletions("ALTER KEYSPACE system_traces WITH replication = {'class': '", '',
                            choices=['NetworkTopologyStrategy', 'SimpleStrategy'])

    def test_complete_in_grant(self):
        self.trycompletions("GR",
                            immediate='ANT ')
        self.trycompletions("GRANT ",
                            choices=['ALL', 'ALTER', 'AUTHORIZE', 'CREATE', 'DESCRIBE', 'DROP', 'EXECUTE', 'MODIFY', 'SELECT', 'UNMASK', 'SELECT_MASKED'],
                            other_choices_ok=True)
        self.trycompletions("GRANT MODIFY ",
                            choices=[',', 'ON', 'PERMISSION'])
        self.trycompletions("GRANT MODIFY P",
                            immediate='ERMISSION ')
        self.trycompletions("GRANT MODIFY PERMISSION ",
                            choices=[',', 'ON'])
        self.trycompletions("GRANT MODIFY PERMISSION, ",
                            choices=['ALTER', 'AUTHORIZE', 'CREATE', 'DESCRIBE', 'DROP', 'EXECUTE', 'SELECT', 'UNMASK', 'SELECT_MASKED'])
        self.trycompletions("GRANT MODIFY PERMISSION, D",
                            choices=['DESCRIBE', 'DROP'])
        self.trycompletions("GRANT MODIFY PERMISSION, DR",
                            immediate='OP ')
        self.trycompletions("GRANT MODIFY PERMISSION, DROP O",
                            immediate='N ')
        self.trycompletions("GRANT MODIFY, DROP ON ",
                            choices=['ALL', 'KEYSPACE', 'MBEANS', 'ROLE', 'FUNCTION', 'MBEAN', 'TABLE'],
                            other_choices_ok=True)
        self.trycompletions("GRANT MODIFY, DROP ON ALL ",
                            choices=['KEYSPACES', 'TABLES'],
                            other_choices_ok=True)
        self.trycompletions("GRANT MODIFY PERMISSION ON KEY",
                            immediate='SPACE ')
        self.trycompletions("GRANT MODIFY PERMISSION ON KEYSPACE system_tr",
                            immediate='aces TO ')

    def test_complete_in_revoke(self):
        self.trycompletions("RE",
                            immediate='VOKE ')
        self.trycompletions("REVOKE ",
                            choices=['ALL', 'ALTER', 'AUTHORIZE', 'CREATE', 'DESCRIBE', 'DROP', 'EXECUTE', 'MODIFY', 'SELECT', 'UNMASK', 'SELECT_MASKED'],
                            other_choices_ok=True)
        self.trycompletions("REVOKE MODIFY ",
                            choices=[',', 'ON', 'PERMISSION'])
        self.trycompletions("REVOKE MODIFY P",
                            immediate='ERMISSION ')
        self.trycompletions("REVOKE MODIFY PERMISSION ",
                            choices=[',', 'ON'])
        self.trycompletions("REVOKE MODIFY PERMISSION, ",
                            choices=['ALTER', 'AUTHORIZE', 'CREATE', 'DESCRIBE', 'DROP', 'EXECUTE', 'SELECT', 'UNMASK', 'SELECT_MASKED'])
        self.trycompletions("REVOKE MODIFY PERMISSION, D",
                            choices=['DESCRIBE', 'DROP'])
        self.trycompletions("REVOKE MODIFY PERMISSION, DR",
                            immediate='OP ')
        self.trycompletions("REVOKE MODIFY PERMISSION, DROP ",
                            choices=[',', 'ON', 'PERMISSION'])
        self.trycompletions("REVOKE MODIFY PERMISSION, DROP O",
                            immediate='N ')
        self.trycompletions("REVOKE MODIFY PERMISSION, DROP ON ",
                            choices=['ALL', 'KEYSPACE', 'MBEANS', 'ROLE', 'FUNCTION', 'MBEAN', 'TABLE'],
                            other_choices_ok=True)
        self.trycompletions("REVOKE MODIFY, DROP ON ALL ",
                            choices=['KEYSPACES', 'TABLES'],
                            other_choices_ok=True)
        self.trycompletions("REVOKE MODIFY PERMISSION, DROP ON KEY",
                            immediate='SPACE ')
        self.trycompletions("REVOKE MODIFY PERMISSION, DROP ON KEYSPACE system_tr",
                            immediate='aces FROM ')

    def test_complete_in_alter_table(self):
        self.trycompletions('ALTER TABLE I', immediate='F EXISTS ')
        self.trycompletions('ALTER TABLE IF', immediate=' EXISTS ')
        self.trycompletions('ALTER TABLE ', choices=['IF', 'twenty_rows_table',
                                                     'ascii_with_special_chars', 'users',
                                                     'has_all_types', 'system.',
                                                     'empty_composite_table', 'escape_quotes', 'empty_table',
                                                     'system_auth.', 'undefined_values_table',
                                                     'dynamic_columns',
                                                     'twenty_rows_composite_table',
                                                     'utf8_with_special_chars',
                                                     'system_traces.', 'songs', 'system_views.', 'system_metrics.',
                                                     'system_virtual_schema.',
                                                     'system_schema.', 'system_distributed.',
                                                     'system_cluster_metadata.',
                                                     self.cqlsh.keyspace + '.'])
        self.trycompletions('ALTER TABLE IF EXISTS new_table ADD ', choices=['<new_column_name>', 'IF'])
        self.trycompletions('ALTER TABLE IF EXISTS new_table ADD IF NOT EXISTS ', choices=['<new_column_name>'])
        self.trycompletions('ALTER TABLE new_table ADD IF NOT EXISTS ', choices=['<new_column_name>'])
        self.trycompletions('ALTER TABLE new_table ADD col int ', choices=[';', 'MASKED', 'CHECK', 'static'])
        self.trycompletions('ALTER TABLE new_table ADD col int M', immediate='ASKED WITH ')
        self.trycompletions('ALTER TABLE new_table ADD col int MASKED WITH ',
                            choices=['DEFAULT', self.cqlsh.keyspace + '.', 'system.'],
                            other_choices_ok=True)
        self.trycompletions('ALTER TABLE new_table ADD col int C', immediate='HECK ')
        self.trycompletions('ALTER TABLE new_table ADD col int CHECK ',
                            choices=['<identifier>', '<quotedName>', 'JSON', 'LENGTH', 'NOT', 'OCTET_LENGTH', 'REGEXP'],
                            other_choices_ok=True)
        self.trycompletions('ALTER TABLE IF EXISTS new_table RENAME ', choices=['IF', '<quotedName>', '<identifier>'])
        self.trycompletions('ALTER TABLE new_table RENAME ', choices=['IF', '<quotedName>', '<identifier>'])
        self.trycompletions('ALTER TABLE IF EXISTS new_table DROP ', choices=['IF', '<quotedName>', '<identifier>'])
        self.trycompletions('ALTER TABLE IF EXISTS new_table ALTER ', choices=['IF', '<quotedName>', '<identifier>'])
        self.trycompletions('ALTER TABLE IF EXISTS new_table ALTER IF E', immediate='XISTS ')
        self.trycompletions('ALTER TABLE IF EXISTS new_table ALTER IF EXISTS col ', choices=['CHECK', 'MASKED', 'DROP'])
        self.trycompletions('ALTER TABLE IF EXISTS new_table ALTER IF EXISTS col C', immediate='HECK ')
        self.trycompletions('ALTER TABLE IF EXISTS new_table ALTER IF EXISTS col M', immediate='ASKED WITH ')
        self.trycompletions('ALTER TABLE IF EXISTS new_table ALTER IF EXISTS col MASKED WITH ',
                            choices=['DEFAULT', self.cqlsh.keyspace + '.', 'system.'],
                            other_choices_ok=True)
        self.trycompletions('ALTER TABLE IF EXISTS new_table ALTER IF EXISTS col D', immediate='ROP ')
        self.trycompletions('ALTER TABLE IF EXISTS new_table ALTER IF EXISTS col DROP ', choices=['CHECK', 'MASKED'])
        self.trycompletions('ALTER TABLE IF EXISTS new_table ALTER IF EXISTS col DROP C', immediate='HECK ;')
        self.trycompletions('ALTER TABLE IF EXISTS new_table ALTER IF EXISTS col DROP M', immediate='ASKED ;')

    def test_complete_in_alter_type(self):
        self.trycompletions('ALTER TYPE I', immediate='F EXISTS ')
        self.trycompletions('ALTER TYPE ', choices=['IF', 'system_views.',
                                                    'tags', 'system_traces.', 'system_distributed.', 'system_metrics.',
                                                    'phone_number', 'quote_udt', 'band_info_type', 'address', 'system.', 'system_schema.',
                                                    'system_auth.', 'system_virtual_schema.', 'system_cluster_metadata.',
                                                    self.cqlsh.keyspace + '.'
                                                    ])
        self.trycompletions('ALTER TYPE IF EXISTS new_type ADD ', choices=['<new_field_name>', 'IF'])
        self.trycompletions('ALTER TYPE IF EXISTS new_type ADD IF NOT EXISTS ', choices=['<new_field_name>'])
        self.trycompletions('ALTER TYPE IF EXISTS new_type RENAME ', choices=['IF', '<quotedName>', '<identifier>'])

    def test_complete_in_alter_user(self):
        self.trycompletions('ALTER USER ', choices=['<identifier>', 'IF', '<pgStringLiteral>', '<quotedStringLiteral>'])

    def test_complete_in_generated(self):
        self.trycompletions('CREATE GENERATED ', immediate='ROLE ')

    def test_complete_in_create_role(self):
        self.trycompletions('CREATE ROLE ', choices=['<rolename>', 'IF'])
        self.trycompletions('CREATE ROLE IF ', immediate='NOT EXISTS ')
        self.trycompletions('CREATE ROLE foo WITH ', choices=['ACCESS', 'HASHED', 'LOGIN', 'OPTIONS', 'PASSWORD', 'SUPERUSER', 'GENERATED'])
        self.trycompletions('CREATE ROLE foo WITH HASHED ', immediate='PASSWORD = ')
        self.trycompletions('CREATE ROLE foo WITH ACCESS TO ', choices=['ALL', 'DATACENTERS'])
        self.trycompletions('CREATE ROLE foo WITH ACCESS TO ALL ', immediate='DATACENTERS ')
        self.trycompletions('CREATE ROLE foo WITH ACCESS FROM ', choices=['ALL', 'CIDRS'])
        self.trycompletions('CREATE ROLE foo WITH ACCESS FROM ALL ', immediate='CIDRS ')
        self.trycompletions('CREATE GENERATED ROLE WITH ', choices=['ACCESS', 'HASHED', 'LOGIN', 'OPTIONS', 'PASSWORD', 'SUPERUSER', 'GENERATED'])

    def test_complete_in_alter_role(self):
        self.trycompletions('ALTER ROLE ', choices=['<identifier>', 'IF', '<quotedName>'])
        self.trycompletions('ALTER ROLE IF ', immediate='EXISTS ')
        self.trycompletions('ALTER ROLE foo ', immediate='WITH ')
        self.trycompletions('ALTER ROLE foo WITH ', choices=['ACCESS', 'HASHED', 'LOGIN', 'OPTIONS', 'PASSWORD', 'SUPERUSER', 'GENERATED'])
        self.trycompletions('ALTER ROLE foo WITH ACCESS TO ', choices=['ALL', 'DATACENTERS'])
        self.trycompletions('ALTER ROLE foo WITH ACCESS FROM ', choices=['ALL', 'CIDRS'])

    def test_complete_in_create_user(self):
        self.trycompletions('CREATE USER ', choices=['<username>', 'IF'])
        self.trycompletions('CREATE USER IF ', immediate='NOT EXISTS ')

    def test_complete_in_drop_role(self):
        self.trycompletions('DROP ROLE ', choices=['<identifier>', 'IF', '<quotedName>'])

    # IDENTITY checks
    def test_complete_in_add_identity(self):
        self.trycompletions('A', choices=['ADD', 'ALTER'])
        self.trycompletions('AD', immediate='D IDENTITY ')
        self.trycompletions('ADD ID', immediate='ENTITY ')
        self.trycompletions('ADD IDENTITY IF ', immediate='NOT EXISTS ')
        self.trycompletions('ADD IDENTITY IF NOT ', immediate='EXISTS ')
        self.trycompletions('ADD IDENTITY ',
            choices=['<pgStringLiteral>', '<quotedStringLiteral>', 'IF'])
        self.trycompletions("ADD IDENTITY 'testIdentifier' TO R", immediate='OLE ')
        self.trycompletions("ADD IDENTITY 'testIdentifier' ", immediate='TO ROLE ')
        self.trycompletions("ADD IDENTITY 'testIdentifier' TO ROLE ",
                            choices=['<identifier>', '<quotedName>'], other_choices_ok=True)
        self.trycompletions("ADD IDENTITY 'testIdentifier' TO ROLE cassandra ",
                                    choices=[';'])
        self.trycompletions("ADD IDENTITY IF NOT EXISTS 'stestIdentifier' TO ROLE cassandra ",
                            choices=[';'])

    def test_complete_in_drop_identity(self):
        self.trycompletions('D', choices=['DESCRIBE', 'DEBUG', 'DESC', 'DROP', 'DELETE'])
        self.trycompletions('DR', immediate='OP ')
        self.trycompletions('DRO', immediate='P ')
        self.trycompletions('DROP ID', immediate='ENTITY ')
        self.trycompletions('DROP', choices=' ')
        self.trycompletions('DROP IDENTITY ',
                            choices=['<pgStringLiteral>', '<quotedStringLiteral>', 'IF'])
        self.trycompletions('DROP IDENTITY IF ', immediate='EXISTS ')
        self.trycompletions('DROP IDENTITY IF EXISTS ',
                            choices=['<pgStringLiteral>', '<quotedStringLiteral>'])
        self.trycompletions("DROP IDENTITY 'testIdentifier' ", choices=[';'])
        self.trycompletions("DROP IDENTITY IF EXISTS 'testIdentifier' ", choices=[';'])

    def test_complete_in_list(self):
        self.trycompletions('LIST ',
                            choices=['ALL', 'AUTHORIZE', 'DESCRIBE', 'EXECUTE', 'ROLES', 'USERS', 'ALTER',
                                     'CREATE', 'DROP', 'MODIFY', 'SELECT', 'UNMASK', 'SELECT_MASKED', 'SUPERUSERS'])

    # Non-CQL Shell Commands

    def test_complete_in_capture(self):
        self.trycompletions('CAPTURE ', choices=['OFF', ';', '<enter>'], other_choices_ok=True)

    def test_complete_in_paging(self):
        self.trycompletions('PAGING ', choices=['ON', 'OFF', ';', '<enter>', '<wholenumber>'])
        self.trycompletions('PAGING 50 ', choices=[';', '<enter>'])

    def test_complete_in_serial(self):
        self.trycompletions('SERIAL CONSISTENCY ', choices=[';', '<enter>', 'LOCAL_SERIAL', 'SERIAL'])

    def test_complete_in_show(self):
        self.trycompletions('SHOW ', choices=['HOST', 'REPLICAS', 'SESSION', 'VERSION'])
        self.trycompletions('SHOW SESSION ', choices=['<uuid>'])
        self.trycompletions('SHOW REPLICAS ', choices=['-', '<wholenumber>'])

    def test_complete_in_tracing(self):
        self.trycompletions('TRACING ', choices=[';', '<enter>', 'OFF', 'ON'])

    def test_complete_in_select_include_exclude_index(self):
        self.trycompletions('SELECT * FROM system.peers WHERE native_port = 8100 WITH i', immediate='ncluded_indexes = { ',)
        self.trycompletions('SELECT * FROM system.peers WHERE native_port = 8100 WITH e', immediate='xcluded_indexes = { ',)