|
1 | 1 | """sqlite3 CLI tests."""
|
2 | 2 | import sqlite3
|
| 3 | +import sys |
| 4 | +import textwrap |
3 | 5 | import unittest
|
4 | 6 |
|
5 | 7 | from sqlite3.__main__ import main as cli
|
| 8 | +from test.support.import_helper import import_module |
6 | 9 | from test.support.os_helper import TESTFN, unlink
|
| 10 | +from test.support.pty_helper import run_pty |
7 | 11 | from test.support import (
|
8 | 12 | captured_stdout,
|
9 | 13 | captured_stderr,
|
10 | 14 | captured_stdin,
|
11 | 15 | force_not_colorized_test_class,
|
| 16 | + requires_subprocess, |
12 | 17 | )
|
13 | 18 |
|
14 | 19 |
|
@@ -200,5 +205,98 @@ def test_color(self):
|
200 | 205 | self.assertIn('\x1b[1;35mOperationalError (SQLITE_ERROR)\x1b[0m: '
|
201 | 206 | '\x1b[35mnear "sel": syntax error\x1b[0m', err)
|
202 | 207 |
|
| 208 | + |
| 209 | +@requires_subprocess() |
| 210 | +@force_not_colorized_test_class |
| 211 | +class Completion(unittest.TestCase): |
| 212 | + PS1 = "sqlite> " |
| 213 | + |
| 214 | + @classmethod |
| 215 | + def setUpClass(cls): |
| 216 | + _sqlite3 = import_module("_sqlite3") |
| 217 | + if not hasattr(_sqlite3, "SQLITE_KEYWORDS"): |
| 218 | + raise unittest.SkipTest("unable to determine SQLite keywords") |
| 219 | + |
| 220 | + readline = import_module("readline") |
| 221 | + if readline.backend == "editline": |
| 222 | + raise unittest.SkipTest("libedit readline is not supported") |
| 223 | + |
| 224 | + def write_input(self, input_, env=None): |
| 225 | + script = textwrap.dedent(""" |
| 226 | + import readline |
| 227 | + from sqlite3.__main__ import main |
| 228 | +
|
| 229 | + readline.parse_and_bind("set colored-completion-prefix off") |
| 230 | + main() |
| 231 | + """) |
| 232 | + return run_pty(script, input_, env) |
| 233 | + |
| 234 | + def test_complete_sql_keywords(self): |
| 235 | + # List candidates starting with 'S', there should be multiple matches. |
| 236 | + input_ = b"S\t\tEL\t 1;\n.quit\n" |
| 237 | + output = self.write_input(input_) |
| 238 | + self.assertIn(b"SELECT", output) |
| 239 | + self.assertIn(b"SET", output) |
| 240 | + self.assertIn(b"SAVEPOINT", output) |
| 241 | + self.assertIn(b"(1,)", output) |
| 242 | + |
| 243 | + # Keywords are completed in upper case for even lower case user input. |
| 244 | + input_ = b"sel\t\t 1;\n.quit\n" |
| 245 | + output = self.write_input(input_) |
| 246 | + self.assertIn(b"SELECT", output) |
| 247 | + self.assertIn(b"(1,)", output) |
| 248 | + |
| 249 | + @unittest.skipIf(sys.platform.startswith("freebsd"), |
| 250 | + "Two actual tabs are inserted when there are no matching" |
| 251 | + " completions in the pseudo-terminal opened by run_pty()" |
| 252 | + " on FreeBSD") |
| 253 | + def test_complete_no_match(self): |
| 254 | + input_ = b"xyzzy\t\t\b\b\b\b\b\b\b.quit\n" |
| 255 | + # Set NO_COLOR to disable coloring for self.PS1. |
| 256 | + output = self.write_input(input_, env={"NO_COLOR": "1"}) |
| 257 | + lines = output.decode().splitlines() |
| 258 | + indices = ( |
| 259 | + i for i, line in enumerate(lines, 1) |
| 260 | + if line.startswith(f"{self.PS1}xyzzy") |
| 261 | + ) |
| 262 | + line_num = next(indices, -1) |
| 263 | + self.assertNotEqual(line_num, -1) |
| 264 | + # Completions occupy lines, assert no extra lines when there is nothing |
| 265 | + # to complete. |
| 266 | + self.assertEqual(line_num, len(lines)) |
| 267 | + |
| 268 | + def test_complete_no_input(self): |
| 269 | + from _sqlite3 import SQLITE_KEYWORDS |
| 270 | + |
| 271 | + script = textwrap.dedent(""" |
| 272 | + import readline |
| 273 | + from sqlite3.__main__ import main |
| 274 | +
|
| 275 | + # Configure readline to ...: |
| 276 | + # - hide control sequences surrounding each candidate |
| 277 | + # - hide "Display all xxx possibilities? (y or n)" |
| 278 | + # - hide "--More--" |
| 279 | + # - show candidates one per line |
| 280 | + readline.parse_and_bind("set colored-completion-prefix off") |
| 281 | + readline.parse_and_bind("set colored-stats off") |
| 282 | + readline.parse_and_bind("set completion-query-items 0") |
| 283 | + readline.parse_and_bind("set page-completions off") |
| 284 | + readline.parse_and_bind("set completion-display-width 0") |
| 285 | +
|
| 286 | + main() |
| 287 | + """) |
| 288 | + input_ = b"\t\t.quit\n" |
| 289 | + output = run_pty(script, input_, env={"NO_COLOR": "1"}) |
| 290 | + lines = output.decode().splitlines() |
| 291 | + indices = [ |
| 292 | + i for i, line in enumerate(lines) |
| 293 | + if line.startswith(self.PS1) |
| 294 | + ] |
| 295 | + self.assertEqual(len(indices), 2) |
| 296 | + start, end = indices |
| 297 | + candidates = [l.strip() for l in lines[start+1:end]] |
| 298 | + self.assertEqual(candidates, sorted(SQLITE_KEYWORDS)) |
| 299 | + |
| 300 | + |
203 | 301 | if __name__ == "__main__":
|
204 | 302 | unittest.main()
|
0 commit comments