2016-12-10 22:45:21 +08:00
|
|
|
"""Tests the json history backend."""
|
2024-01-30 12:23:50 +01:00
|
|
|
|
2015-11-14 19:56:26 -08:00
|
|
|
# pylint: disable=protected-access
|
2021-09-07 00:08:37 +05:30
|
|
|
|
2016-07-18 16:30:28 +03:00
|
|
|
import shlex
|
2015-08-11 20:16:02 -04:00
|
|
|
|
2017-01-26 18:24:33 +02:00
|
|
|
import pytest
|
|
|
|
|
2020-06-25 20:26:47 -04:00
|
|
|
from xonsh.history.json import (
|
|
|
|
JsonHistory,
|
2022-01-31 21:26:34 +05:30
|
|
|
_xhj_gc_bytes_to_rmfiles,
|
2020-06-25 20:26:47 -04:00
|
|
|
_xhj_gc_commands_to_rmfiles,
|
|
|
|
_xhj_gc_files_to_rmfiles,
|
|
|
|
_xhj_gc_seconds_to_rmfiles,
|
|
|
|
)
|
2022-01-31 21:26:34 +05:30
|
|
|
from xonsh.history.main import HistoryAlias, history_main
|
2021-09-07 00:08:37 +05:30
|
|
|
from xonsh.lazyjson import LazyJSON
|
2016-07-16 21:39:32 +03:00
|
|
|
|
2018-08-30 09:18:49 -05:00
|
|
|
CMDS = ["ls", "cat hello kitty", "abc", "def", "touch me", "grep from me"]
|
2021-01-25 15:37:29 -06:00
|
|
|
IGNORE_OPTS = ",".join(["ignoredups", "ignoreerr", "ignorespace"])
|
2018-08-30 09:18:49 -05:00
|
|
|
|
2015-11-27 13:17:57 -07:00
|
|
|
|
2018-11-19 20:22:18 -05:00
|
|
|
@pytest.fixture
|
2021-09-07 00:08:37 +05:30
|
|
|
def hist(tmpdir, xession, monkeypatch):
|
2021-05-20 15:44:26 +05:30
|
|
|
file = tmpdir / "xonsh-HISTORY-TEST.json"
|
|
|
|
h = JsonHistory(filename=str(file), here="yup", sessionid="SESSIONID", gc=False)
|
2021-09-07 00:08:37 +05:30
|
|
|
monkeypatch.setattr(xession, "history", h)
|
2016-07-16 21:39:32 +03:00
|
|
|
yield h
|
|
|
|
|
|
|
|
|
2021-05-20 15:44:26 +05:30
|
|
|
def test_hist_init(hist, xession):
|
2015-11-14 19:56:26 -08:00
|
|
|
"""Test initialization of the shell history."""
|
2016-07-16 21:39:32 +03:00
|
|
|
with LazyJSON(hist.filename) as lj:
|
2018-08-30 09:18:49 -05:00
|
|
|
obs = lj["here"]
|
|
|
|
assert "yup" == obs
|
2015-08-11 20:16:02 -04:00
|
|
|
|
|
|
|
|
2021-05-20 15:44:26 +05:30
|
|
|
def test_hist_append(hist, xession):
|
2015-11-14 19:56:26 -08:00
|
|
|
"""Verify appending to the history works."""
|
2021-05-20 15:44:26 +05:30
|
|
|
xession.env["HISTCONTROL"] = set()
|
2018-08-30 09:18:49 -05:00
|
|
|
hf = hist.append({"inp": "still alive", "rtn": 0})
|
2016-06-22 17:41:13 -04:00
|
|
|
assert hf is None
|
2018-08-30 09:18:49 -05:00
|
|
|
assert "still alive" == hist.buffer[0]["inp"]
|
|
|
|
assert 0 == hist.buffer[0]["rtn"]
|
2016-12-10 22:45:21 +08:00
|
|
|
assert 0 == hist.rtns[-1]
|
2018-08-30 09:18:49 -05:00
|
|
|
hf = hist.append({"inp": "dead now", "rtn": 1})
|
|
|
|
assert "dead now" == hist.buffer[1]["inp"]
|
|
|
|
assert 1 == hist.buffer[1]["rtn"]
|
2016-12-10 22:45:21 +08:00
|
|
|
assert 1 == hist.rtns[-1]
|
2018-08-30 09:18:49 -05:00
|
|
|
hf = hist.append({"inp": "reborn", "rtn": 0})
|
|
|
|
assert "reborn" == hist.buffer[2]["inp"]
|
|
|
|
assert 0 == hist.buffer[2]["rtn"]
|
2016-12-10 22:45:21 +08:00
|
|
|
assert 0 == hist.rtns[-1]
|
2015-08-11 20:16:02 -04:00
|
|
|
|
|
|
|
|
2021-05-20 15:44:26 +05:30
|
|
|
def test_hist_flush(hist, xession):
|
2015-11-14 19:56:26 -08:00
|
|
|
"""Verify explicit flushing of the history works."""
|
2015-08-11 20:16:02 -04:00
|
|
|
hf = hist.flush()
|
2016-06-22 17:41:13 -04:00
|
|
|
assert hf is None
|
2021-05-20 15:44:26 +05:30
|
|
|
xession.env["HISTCONTROL"] = set()
|
2018-08-30 09:18:49 -05:00
|
|
|
hist.append({"inp": "still alive?", "rtn": 0, "out": "yes"})
|
2015-08-11 20:16:02 -04:00
|
|
|
hf = hist.flush()
|
2016-06-22 17:41:13 -04:00
|
|
|
assert hf is not None
|
2015-08-11 20:16:02 -04:00
|
|
|
while hf.is_alive():
|
|
|
|
pass
|
2016-07-16 21:39:32 +03:00
|
|
|
with LazyJSON(hist.filename) as lj:
|
2018-08-30 09:18:49 -05:00
|
|
|
assert len(lj["cmds"]) == 1
|
|
|
|
cmd = lj["cmds"][0]
|
|
|
|
assert cmd["inp"] == "still alive?"
|
|
|
|
assert not cmd.get("out", None)
|
2016-12-11 16:33:45 +08:00
|
|
|
|
|
|
|
|
2024-05-16 07:31:38 +02:00
|
|
|
def test_hist_flush_on_xonsh_unload(hist, xession):
|
|
|
|
hf = hist.flush()
|
|
|
|
assert hf is None
|
|
|
|
xession.env["HISTCONTROL"] = set()
|
|
|
|
hist.append({"inp": "save me if you unload", "rtn": 0, "out": "yes"})
|
|
|
|
xession.unload()
|
|
|
|
with LazyJSON(hist.filename) as lj:
|
|
|
|
assert len(lj["cmds"]) == 1
|
|
|
|
cmd = lj["cmds"][0]
|
|
|
|
assert cmd["inp"] == "save me if you unload"
|
|
|
|
|
|
|
|
|
2021-05-20 15:44:26 +05:30
|
|
|
def test_hist_flush_with_store_stdout(hist, xession):
|
2016-12-11 16:33:45 +08:00
|
|
|
"""Verify explicit flushing of the history works."""
|
|
|
|
hf = hist.flush()
|
|
|
|
assert hf is None
|
2021-05-20 15:44:26 +05:30
|
|
|
xession.env["HISTCONTROL"] = set()
|
|
|
|
xession.env["XONSH_STORE_STDOUT"] = True
|
2018-08-30 09:18:49 -05:00
|
|
|
hist.append({"inp": "still alive?", "rtn": 0, "out": "yes"})
|
2016-12-11 16:33:45 +08:00
|
|
|
hf = hist.flush()
|
|
|
|
assert hf is not None
|
|
|
|
while hf.is_alive():
|
|
|
|
pass
|
|
|
|
with LazyJSON(hist.filename) as lj:
|
2018-08-30 09:18:49 -05:00
|
|
|
assert len(lj["cmds"]) == 1
|
|
|
|
assert lj["cmds"][0]["inp"] == "still alive?"
|
|
|
|
assert lj["cmds"][0]["out"].strip() == "yes"
|
2016-12-11 16:33:45 +08:00
|
|
|
|
|
|
|
|
2021-05-20 15:44:26 +05:30
|
|
|
def test_hist_flush_with_store_cwd(hist, xession):
|
2021-05-11 22:23:57 +03:00
|
|
|
hf = hist.flush()
|
|
|
|
assert hf is None
|
|
|
|
|
|
|
|
hist.save_cwd = True
|
|
|
|
hist.append({"inp": "# saving with cwd", "rtn": 0, "out": "yes", "cwd": "/tmp"})
|
|
|
|
hf = hist.flush()
|
|
|
|
assert hf is not None
|
|
|
|
|
|
|
|
hist.save_cwd = False
|
|
|
|
hist.append({"inp": "# saving without cwd", "rtn": 0, "out": "yes", "cwd": "/tmp"})
|
|
|
|
hf = hist.flush()
|
|
|
|
assert hf is not None
|
|
|
|
|
|
|
|
while hf.is_alive():
|
|
|
|
pass
|
|
|
|
with LazyJSON(hist.filename) as lj:
|
|
|
|
assert len(lj["cmds"]) == 2
|
|
|
|
assert lj["cmds"][0]["cwd"] == "/tmp"
|
2021-05-20 15:44:26 +05:30
|
|
|
assert "cwd" not in lj["cmds"][1]
|
2021-05-11 22:23:57 +03:00
|
|
|
|
|
|
|
|
2021-05-20 15:44:26 +05:30
|
|
|
def test_hist_flush_with_hist_control(hist, xession):
|
2016-12-11 16:33:45 +08:00
|
|
|
"""Verify explicit flushing of the history works."""
|
|
|
|
hf = hist.flush()
|
|
|
|
assert hf is None
|
2021-05-20 15:44:26 +05:30
|
|
|
xession.env["HISTCONTROL"] = IGNORE_OPTS
|
2018-08-30 09:18:49 -05:00
|
|
|
hist.append({"inp": "ls foo1", "rtn": 0})
|
|
|
|
hist.append({"inp": "ls foo1", "rtn": 1})
|
|
|
|
hist.append({"inp": "ls foo1", "rtn": 0})
|
|
|
|
hist.append({"inp": "ls foo2", "rtn": 2})
|
|
|
|
hist.append({"inp": "ls foo3", "rtn": 0})
|
2021-01-25 15:37:29 -06:00
|
|
|
hist.append({"inp": "ls secret", "rtn": 0, "spc": True})
|
2016-12-11 16:33:45 +08:00
|
|
|
hf = hist.flush()
|
|
|
|
assert hf is not None
|
|
|
|
while hf.is_alive():
|
|
|
|
pass
|
|
|
|
assert len(hist.buffer) == 0
|
|
|
|
with LazyJSON(hist.filename) as lj:
|
2018-08-30 09:18:49 -05:00
|
|
|
cmds = list(lj["cmds"])
|
2016-12-11 16:33:45 +08:00
|
|
|
assert len(cmds) == 2
|
2018-08-30 09:18:49 -05:00
|
|
|
assert [x["inp"] for x in cmds] == ["ls foo1", "ls foo3"]
|
|
|
|
assert [x["rtn"] for x in cmds] == [0, 0]
|
2015-08-11 20:16:02 -04:00
|
|
|
|
|
|
|
|
2021-05-20 15:44:26 +05:30
|
|
|
def test_cmd_field(hist, xession):
|
2015-08-16 21:41:02 -04:00
|
|
|
# in-memory
|
2021-05-20 15:44:26 +05:30
|
|
|
xession.env["HISTCONTROL"] = set()
|
2018-08-30 09:18:49 -05:00
|
|
|
hf = hist.append({"inp": "ls foo", "rtn": 1})
|
2016-06-22 17:41:13 -04:00
|
|
|
assert hf is None
|
|
|
|
assert 1 == hist.rtns[0]
|
|
|
|
assert 1 == hist.rtns[-1]
|
2020-05-05 06:42:28 -04:00
|
|
|
assert hist.outs[-1] is None
|
2015-08-16 21:41:02 -04:00
|
|
|
# slice
|
2016-06-22 17:41:13 -04:00
|
|
|
assert [1] == hist.rtns[:]
|
2015-08-16 21:41:02 -04:00
|
|
|
# on disk
|
|
|
|
hf = hist.flush()
|
2016-06-22 17:41:13 -04:00
|
|
|
assert hf is not None
|
|
|
|
assert 1 == hist.rtns[0]
|
|
|
|
assert 1 == hist.rtns[-1]
|
2020-05-05 06:42:28 -04:00
|
|
|
assert hist.outs[-1] is None
|
2015-08-11 20:16:02 -04:00
|
|
|
|
2016-07-23 11:38:20 +03:00
|
|
|
|
2018-08-30 09:18:49 -05:00
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"inp, commands, offset",
|
|
|
|
[
|
|
|
|
("", CMDS, (0, 1)),
|
|
|
|
("-r", list(reversed(CMDS)), (len(CMDS) - 1, -1)),
|
|
|
|
("0", CMDS[0:1], (0, 1)),
|
|
|
|
("1", CMDS[1:2], (1, 1)),
|
|
|
|
("-2", CMDS[-2:-1], (len(CMDS) - 2, 1)),
|
|
|
|
("1:3", CMDS[1:3], (1, 1)),
|
|
|
|
("1::2", CMDS[1::2], (1, 2)),
|
|
|
|
("-4:-2", CMDS[-4:-2], (len(CMDS) - 4, 1)),
|
|
|
|
],
|
|
|
|
)
|
2021-05-20 15:44:26 +05:30
|
|
|
def test_show_cmd_numerate(inp, commands, offset, hist, xession, capsys):
|
2015-11-14 19:56:26 -08:00
|
|
|
"""Verify that CLI history commands work."""
|
2016-07-23 11:38:20 +03:00
|
|
|
base_idx, step = offset
|
2021-05-20 15:44:26 +05:30
|
|
|
xession.env["HISTCONTROL"] = set()
|
2016-12-19 20:59:37 +08:00
|
|
|
for ts, cmd in enumerate(CMDS): # populate the shell history
|
2018-08-30 09:18:49 -05:00
|
|
|
hist.append({"inp": cmd, "rtn": 0, "ts": (ts + 1, ts + 1.5)})
|
2015-11-27 13:17:57 -07:00
|
|
|
|
2021-12-07 01:12:26 +05:30
|
|
|
exp = (f"{base_idx + idx * step}: {cmd}" for idx, cmd in enumerate(list(commands)))
|
2018-08-30 09:18:49 -05:00
|
|
|
exp = "\n".join(exp)
|
2016-07-23 11:38:20 +03:00
|
|
|
|
2018-08-30 09:18:49 -05:00
|
|
|
history_main(["show", "-n"] + shlex.split(inp))
|
2016-07-23 11:38:20 +03:00
|
|
|
out, err = capsys.readouterr()
|
|
|
|
assert out.rstrip() == exp
|
2015-11-05 19:35:41 -08:00
|
|
|
|
2016-07-16 21:39:32 +03:00
|
|
|
|
2021-09-07 00:08:37 +05:30
|
|
|
def test_history_diff(tmpdir, xession, monkeypatch, capsys):
|
|
|
|
files = [tmpdir / f"xonsh-HISTORY-TEST-{idx}.json" for idx in range(2)]
|
|
|
|
for file in files:
|
|
|
|
hist = JsonHistory(
|
|
|
|
filename=str(file), here="yup", sessionid="SESSIONID", gc=False
|
|
|
|
)
|
|
|
|
monkeypatch.setattr(xession, "history", hist)
|
|
|
|
xession.env["HISTCONTROL"] = set()
|
|
|
|
for ts, cmd in enumerate(CMDS): # populate the shell history
|
|
|
|
hist.append({"inp": cmd, "rtn": 0, "ts": (ts + 1, ts + 1.5)})
|
|
|
|
flush = hist.flush()
|
|
|
|
if flush.queue:
|
|
|
|
# make sure that flush is complete
|
|
|
|
time.sleep(0.1)
|
|
|
|
|
2021-12-07 01:12:26 +05:30
|
|
|
left, right = (str(f) for f in files)
|
2021-09-07 00:08:37 +05:30
|
|
|
history_main(["diff", left, right])
|
|
|
|
out, err = capsys.readouterr()
|
|
|
|
# make sure it is called
|
|
|
|
assert out.rstrip()
|
|
|
|
|
|
|
|
|
2021-05-20 15:44:26 +05:30
|
|
|
def test_histcontrol(hist, xession):
|
2021-01-25 15:37:29 -06:00
|
|
|
"""Test HISTCONTROL=ignoredups,ignoreerr,ignorespacee"""
|
2015-11-27 13:17:57 -07:00
|
|
|
|
2021-05-20 15:44:26 +05:30
|
|
|
xession.env["HISTCONTROL"] = IGNORE_OPTS
|
2016-06-28 13:47:35 +03:00
|
|
|
assert len(hist.buffer) == 0
|
|
|
|
|
|
|
|
# An error, buffer remains empty
|
2018-08-30 09:18:49 -05:00
|
|
|
hist.append({"inp": "ls foo", "rtn": 2})
|
2016-12-11 16:33:45 +08:00
|
|
|
assert len(hist.buffer) == 1
|
2016-12-10 22:45:21 +08:00
|
|
|
assert hist.rtns[-1] == 2
|
2018-08-30 09:18:49 -05:00
|
|
|
assert hist.inps[-1] == "ls foo"
|
2016-06-28 13:47:35 +03:00
|
|
|
|
|
|
|
# Success
|
2018-08-30 09:18:49 -05:00
|
|
|
hist.append({"inp": "ls foobazz", "rtn": 0})
|
2016-12-11 16:33:45 +08:00
|
|
|
assert len(hist.buffer) == 2
|
2018-08-30 09:18:49 -05:00
|
|
|
assert "ls foobazz" == hist.buffer[-1]["inp"]
|
|
|
|
assert 0 == hist.buffer[-1]["rtn"]
|
2016-12-10 22:45:21 +08:00
|
|
|
assert hist.rtns[-1] == 0
|
2018-08-30 09:18:49 -05:00
|
|
|
assert hist.inps[-1] == "ls foobazz"
|
2016-06-28 13:47:35 +03:00
|
|
|
|
|
|
|
# Error
|
2018-08-30 09:18:49 -05:00
|
|
|
hist.append({"inp": "ls foo", "rtn": 2})
|
2016-12-11 16:33:45 +08:00
|
|
|
assert len(hist.buffer) == 3
|
2018-08-30 09:18:49 -05:00
|
|
|
assert "ls foo" == hist.buffer[-1]["inp"]
|
|
|
|
assert 2 == hist.buffer[-1]["rtn"]
|
2016-12-10 22:45:21 +08:00
|
|
|
assert hist.rtns[-1] == 2
|
2018-08-30 09:18:49 -05:00
|
|
|
assert hist.inps[-1] == "ls foo"
|
2016-06-28 13:47:35 +03:00
|
|
|
|
|
|
|
# File now exists, success
|
2018-08-30 09:18:49 -05:00
|
|
|
hist.append({"inp": "ls foo", "rtn": 0})
|
2016-12-11 16:33:45 +08:00
|
|
|
assert len(hist.buffer) == 4
|
2018-08-30 09:18:49 -05:00
|
|
|
assert "ls foo" == hist.buffer[-1]["inp"]
|
|
|
|
assert 0 == hist.buffer[-1]["rtn"]
|
2016-12-10 22:45:21 +08:00
|
|
|
assert hist.rtns[-1] == 0
|
2018-08-30 09:18:49 -05:00
|
|
|
assert hist.inps[-1] == "ls foo"
|
2016-06-28 13:47:35 +03:00
|
|
|
|
|
|
|
# Success
|
2018-08-30 09:18:49 -05:00
|
|
|
hist.append({"inp": "ls", "rtn": 0})
|
2016-12-11 16:33:45 +08:00
|
|
|
assert len(hist.buffer) == 5
|
2018-08-30 09:18:49 -05:00
|
|
|
assert "ls" == hist.buffer[-1]["inp"]
|
|
|
|
assert 0 == hist.buffer[-1]["rtn"]
|
2016-12-10 22:45:21 +08:00
|
|
|
assert hist.rtns[-1] == 0
|
2018-08-30 09:18:49 -05:00
|
|
|
assert hist.inps[-1] == "ls"
|
2016-06-28 13:47:35 +03:00
|
|
|
|
|
|
|
# Dup
|
2018-08-30 09:18:49 -05:00
|
|
|
hist.append({"inp": "ls", "rtn": 0})
|
2016-12-11 16:33:45 +08:00
|
|
|
assert len(hist.buffer) == 6
|
2016-12-10 22:45:21 +08:00
|
|
|
assert hist.rtns[-1] == 0
|
2018-08-30 09:18:49 -05:00
|
|
|
assert hist.inps[-1] == "ls"
|
2016-06-28 13:47:35 +03:00
|
|
|
|
|
|
|
# Success
|
2018-08-30 09:18:49 -05:00
|
|
|
hist.append({"inp": "/bin/ls", "rtn": 0})
|
2016-12-11 16:33:45 +08:00
|
|
|
assert len(hist.buffer) == 7
|
2018-08-30 09:18:49 -05:00
|
|
|
assert "/bin/ls" == hist.buffer[-1]["inp"]
|
|
|
|
assert 0 == hist.buffer[-1]["rtn"]
|
2016-12-10 22:45:21 +08:00
|
|
|
assert hist.rtns[-1] == 0
|
2018-08-30 09:18:49 -05:00
|
|
|
assert hist.inps[-1] == "/bin/ls"
|
2016-06-28 13:47:35 +03:00
|
|
|
|
|
|
|
# Error
|
2018-08-30 09:18:49 -05:00
|
|
|
hist.append({"inp": "ls bazz", "rtn": 1})
|
2016-12-11 16:33:45 +08:00
|
|
|
assert len(hist.buffer) == 8
|
2018-08-30 09:18:49 -05:00
|
|
|
assert "ls bazz" == hist.buffer[-1]["inp"]
|
|
|
|
assert 1 == hist.buffer[-1]["rtn"]
|
2016-12-10 22:45:21 +08:00
|
|
|
assert hist.rtns[-1] == 1
|
2018-08-30 09:18:49 -05:00
|
|
|
assert hist.inps[-1] == "ls bazz"
|
2016-06-28 13:47:35 +03:00
|
|
|
|
|
|
|
# Error
|
2018-08-30 09:18:49 -05:00
|
|
|
hist.append({"inp": "ls bazz", "rtn": -1})
|
2016-12-11 16:33:45 +08:00
|
|
|
assert len(hist.buffer) == 9
|
2018-08-30 09:18:49 -05:00
|
|
|
assert "ls bazz" == hist.buffer[-1]["inp"]
|
|
|
|
assert -1 == hist.buffer[-1]["rtn"]
|
2016-12-10 22:45:21 +08:00
|
|
|
assert hist.rtns[-1] == -1
|
2018-08-30 09:18:49 -05:00
|
|
|
assert hist.inps[-1] == "ls bazz"
|
2016-07-18 16:30:28 +03:00
|
|
|
|
2021-01-25 15:37:29 -06:00
|
|
|
# Success
|
|
|
|
hist.append({"inp": "echo not secret", "rtn": 0, "spc": False})
|
|
|
|
assert len(hist.buffer) == 10
|
|
|
|
assert "echo not secret" == hist.buffer[-1]["inp"]
|
|
|
|
assert 0 == hist.buffer[-1]["rtn"]
|
|
|
|
assert hist.rtns[-1] == 0
|
|
|
|
assert hist.inps[-1] == "echo not secret"
|
|
|
|
|
|
|
|
# Space
|
|
|
|
hist.append({"inp": "echo secret command", "rtn": 0, "spc": True})
|
|
|
|
assert len(hist.buffer) == 10
|
|
|
|
assert hist.rtns[-1] == 0
|
|
|
|
assert hist.inps[-1] == "echo not secret"
|
|
|
|
|
2016-07-18 16:30:28 +03:00
|
|
|
|
2018-08-30 09:18:49 -05:00
|
|
|
@pytest.mark.parametrize("args", ["-h", "--help", "show -h", "show --help"])
|
2016-07-18 19:23:13 +03:00
|
|
|
def test_parse_args_help(args, capsys):
|
|
|
|
with pytest.raises(SystemExit):
|
2021-09-07 00:08:37 +05:30
|
|
|
history_main(shlex.split(args))
|
2018-08-30 09:18:49 -05:00
|
|
|
assert "show this help message and exit" in capsys.readouterr()[0]
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
2021-09-07 00:08:37 +05:30
|
|
|
"args, session, slice, numerate, reverse",
|
2018-08-30 09:18:49 -05:00
|
|
|
[
|
2021-09-07 00:08:37 +05:30
|
|
|
("", "session", [], False, False),
|
|
|
|
("1:5", "session", ["1:5"], False, False),
|
|
|
|
("show", "session", [], False, False),
|
|
|
|
("show 15", "session", ["15"], False, False),
|
|
|
|
("show bash 3:5 15:66", "bash", ["3:5", "15:66"], False, False),
|
|
|
|
("show -r", "session", [], False, True),
|
|
|
|
("show -rn bash", "bash", [], True, True),
|
|
|
|
("show -n -r -30:20", "session", ["-30:20"], True, True),
|
|
|
|
("show -n zsh 1:2:3", "zsh", ["1:2:3"], True, False),
|
2018-08-30 09:18:49 -05:00
|
|
|
],
|
|
|
|
)
|
2021-09-07 00:08:37 +05:30
|
|
|
def test_parser_show(args, session, slice, numerate, reverse, mocker, hist, xession):
|
2016-07-29 19:24:08 +03:00
|
|
|
# use dict instead of argparse.Namespace for pretty pytest diff
|
2018-08-30 09:18:49 -05:00
|
|
|
exp_ns = {
|
2021-09-07 00:08:37 +05:30
|
|
|
"session": session,
|
|
|
|
"slices": slice,
|
|
|
|
"numerate": numerate,
|
|
|
|
"reverse": reverse,
|
2018-08-30 09:18:49 -05:00
|
|
|
"start_time": None,
|
|
|
|
"end_time": None,
|
|
|
|
"datetime_format": None,
|
|
|
|
"timestamp": False,
|
|
|
|
"null_byte": False,
|
|
|
|
}
|
2021-09-07 00:08:37 +05:30
|
|
|
|
|
|
|
# clear parser instance, so that patched func can take place
|
|
|
|
from xonsh.history import main as mod
|
|
|
|
|
|
|
|
main = HistoryAlias()
|
2024-05-27 18:00:15 +02:00
|
|
|
spy = mocker.spy(mod.xcli, "run_with_partial_args")
|
2021-09-07 00:08:37 +05:30
|
|
|
|
|
|
|
# action
|
|
|
|
main(shlex.split(args))
|
|
|
|
|
|
|
|
# assert
|
|
|
|
spy.assert_called_once()
|
|
|
|
|
|
|
|
# assemble
|
|
|
|
args, _ = spy.call_args
|
|
|
|
_, kwargs = args
|
|
|
|
called_with = {attr: kwargs[attr] for attr in exp_ns}
|
|
|
|
if kwargs["_unparsed"]:
|
|
|
|
called_with["slices"] = kwargs["_unparsed"]
|
|
|
|
|
|
|
|
# assert
|
|
|
|
assert called_with == exp_ns
|
2016-08-27 17:30:53 +03:00
|
|
|
|
|
|
|
|
2018-08-30 09:18:49 -05:00
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"index, exp",
|
|
|
|
[
|
|
|
|
(-1, ("grep from me", "out", 0, (5, 6))),
|
|
|
|
(1, ("cat hello kitty", "out", 0, (1, 2))),
|
|
|
|
(
|
|
|
|
slice(1, 3),
|
|
|
|
[("cat hello kitty", "out", 0, (1, 2)), ("abc", "out", 0, (2, 3))],
|
|
|
|
),
|
|
|
|
],
|
|
|
|
)
|
2021-05-20 15:44:26 +05:30
|
|
|
def test_history_getitem(index, exp, hist, xession):
|
|
|
|
xession.env["HISTCONTROL"] = set()
|
2018-08-30 09:18:49 -05:00
|
|
|
attrs = ("inp", "out", "rtn", "ts")
|
2016-08-27 17:30:53 +03:00
|
|
|
|
2018-08-30 09:18:49 -05:00
|
|
|
for ts, cmd in enumerate(CMDS): # populate the shell history
|
|
|
|
entry = {k: v for k, v in zip(attrs, [cmd, "out", 0, (ts, ts + 1)])}
|
2017-01-29 15:22:46 +02:00
|
|
|
hist.append(entry)
|
|
|
|
|
|
|
|
entry = hist[index]
|
|
|
|
if isinstance(entry, list):
|
|
|
|
assert [(e.cmd, e.out, e.rtn, e.ts) for e in entry] == exp
|
|
|
|
else:
|
|
|
|
assert (entry.cmd, entry.out, entry.rtn, entry.ts) == exp
|
2017-02-05 22:02:27 +08:00
|
|
|
|
|
|
|
|
2020-06-25 20:26:47 -04:00
|
|
|
import calendar
|
2022-01-31 21:26:34 +05:30
|
|
|
import time
|
2020-06-25 20:26:47 -04:00
|
|
|
|
|
|
|
HF_FIRST_DAY = calendar.timegm(time.struct_time((2018, 5, 13, 0, 0, 0, 0, 0, 0)))
|
|
|
|
|
|
|
|
|
|
|
|
def history_files_list(gen_count) -> (float, int, str, int):
|
|
|
|
"""Generate a list of history file tuples"""
|
|
|
|
# generate test list:
|
|
|
|
# 2 files every day in range
|
|
|
|
# morning file has 100 commands, evening 50
|
|
|
|
# first file size 10000, 2nd 2500
|
|
|
|
# first file time 0900, 2nd 2300
|
|
|
|
# for sanity in reproducable test results, all date arithmetic ignores astronomy.
|
|
|
|
# time zone is UTC, all days have 24 h and 0 sec, no leap year or leap sec.
|
|
|
|
|
|
|
|
retval = []
|
|
|
|
for i in range(int((gen_count + 1) / 2)):
|
|
|
|
retval.append(
|
|
|
|
(
|
|
|
|
# first day in sec + #days * 24hr + #hr * 60min + # sec * 60sec + sec= sec to date.
|
|
|
|
HF_FIRST_DAY + (((((i * 24) + 9) * 60) + 0) * 60) + 0, # mod dt,
|
|
|
|
100,
|
|
|
|
f".argle/xonsh-{2*i:05n}.json",
|
|
|
|
10000,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
retval.append(
|
|
|
|
(
|
|
|
|
# first day in sec + #days * 24hr + #hr * 60min + # sec * 60sec + sec= sec to date.
|
|
|
|
HF_FIRST_DAY + (((((i * 24) + 23) * 60) + 0) * 60) + 0, # mod dt,
|
|
|
|
50,
|
|
|
|
f".argle/xonsh-{2*i+1:05n}.json",
|
|
|
|
2500,
|
|
|
|
)
|
|
|
|
)
|
|
|
|
return retval
|
|
|
|
|
|
|
|
|
|
|
|
# generate 100 files, 50 days.
|
|
|
|
HISTORY_FILES_LIST = history_files_list(100)
|
|
|
|
|
|
|
|
# TS of newest history file
|
|
|
|
SEC_FROM_LATEST = time.time() - (HISTORY_FILES_LIST[-1][0])
|
|
|
|
SEC_PER_DAY = 24 * 60 * 60
|
2021-03-22 13:20:36 -07:00
|
|
|
# "now" gets evaluated above at test definition time and later when the tests
|
|
|
|
# are executed. If the difference between these equals half the smallest time
|
|
|
|
# interval between log files, the test will fail. We constructed logs at 9a and
|
|
|
|
# 11p every day. The smallest interval is thus ten hours (from 11p to 9a), so
|
|
|
|
# we can't spend more than five hours executing the tests.
|
|
|
|
MAX_RUNTIME = 30 * 60
|
2021-05-20 15:44:26 +05:30
|
|
|
MIN_DIFF = min(
|
|
|
|
HISTORY_FILES_LIST[i + 1][0] - HISTORY_FILES_LIST[i][0]
|
|
|
|
for i in range(len(HISTORY_FILES_LIST) - 1)
|
|
|
|
)
|
2021-03-22 13:20:36 -07:00
|
|
|
assert MAX_RUNTIME < MIN_DIFF / 2
|
2020-06-25 20:26:47 -04:00
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"fn, hsize, in_files, exp_size, exp_files",
|
|
|
|
[
|
|
|
|
# xhj_gc_commands_to_rmfiles
|
|
|
|
(
|
|
|
|
_xhj_gc_commands_to_rmfiles,
|
|
|
|
1001 * (100 + 50), # Limit > history, no trimming.
|
|
|
|
HISTORY_FILES_LIST,
|
|
|
|
0, # nothing trimmed
|
|
|
|
[], #
|
|
|
|
),
|
|
|
|
(
|
|
|
|
_xhj_gc_commands_to_rmfiles,
|
|
|
|
20 * (100 + 50), # keep 20 full days (40 files)
|
|
|
|
HISTORY_FILES_LIST,
|
|
|
|
30 * (100 + 50),
|
|
|
|
HISTORY_FILES_LIST[: 2 * (30)], # trim 30 full days
|
|
|
|
),
|
|
|
|
(
|
|
|
|
_xhj_gc_commands_to_rmfiles,
|
|
|
|
20 * (100 + 50) + 100, # keep 20 full newest and evening before (41 files)
|
|
|
|
HISTORY_FILES_LIST,
|
|
|
|
30 * (100 + 50) - 50, # trim 30 full oldest, keep newest evening (59 files)
|
|
|
|
HISTORY_FILES_LIST[: 2 * 30 - 1],
|
|
|
|
),
|
|
|
|
# xhj_gc_files_to_rmfiles
|
|
|
|
(
|
|
|
|
_xhj_gc_files_to_rmfiles,
|
|
|
|
1001, # Limit > history, no trimming.
|
|
|
|
HISTORY_FILES_LIST,
|
|
|
|
0, # nothing trimmed
|
|
|
|
[], #
|
|
|
|
),
|
|
|
|
(
|
|
|
|
_xhj_gc_files_to_rmfiles,
|
|
|
|
40, # keep 20 full days (40 files)
|
|
|
|
HISTORY_FILES_LIST,
|
|
|
|
60,
|
|
|
|
HISTORY_FILES_LIST[:60], # trim 30 full days
|
|
|
|
),
|
|
|
|
(
|
|
|
|
_xhj_gc_files_to_rmfiles,
|
|
|
|
41, # keep 20 full newest and evening before (41 files)
|
|
|
|
HISTORY_FILES_LIST,
|
|
|
|
59, # trim 30 full oldest, keep newest evening (59 files)
|
|
|
|
HISTORY_FILES_LIST[: 2 * 30 - 1],
|
|
|
|
),
|
|
|
|
# xhj_gc_bytes_to_rmfiles
|
|
|
|
(
|
|
|
|
_xhj_gc_bytes_to_rmfiles,
|
|
|
|
1001 * (10000 + 2500), # Limit > history, no trimming.
|
|
|
|
HISTORY_FILES_LIST,
|
|
|
|
0, # nothing trimmed
|
|
|
|
[], #
|
|
|
|
),
|
|
|
|
(
|
|
|
|
_xhj_gc_bytes_to_rmfiles,
|
|
|
|
20 * (10000 + 2500), # keep 20 full days (40 files)
|
|
|
|
HISTORY_FILES_LIST,
|
|
|
|
30 * (10000 + 2500),
|
|
|
|
HISTORY_FILES_LIST[: 2 * (30)], # trim 30 full days
|
|
|
|
),
|
|
|
|
(
|
|
|
|
_xhj_gc_bytes_to_rmfiles,
|
|
|
|
20 * (10000 + 2500)
|
|
|
|
+ 10000, # keep 20 full newest and evening before (41 files)
|
|
|
|
HISTORY_FILES_LIST,
|
|
|
|
30 * (10000 + 2500)
|
|
|
|
- 2500, # trim 30 full oldest, keep newest evening (59 files)
|
|
|
|
HISTORY_FILES_LIST[: 2 * 30 - 1],
|
|
|
|
),
|
|
|
|
# xhj_gc_seconds_to_rmfiles
|
|
|
|
(
|
|
|
|
_xhj_gc_seconds_to_rmfiles,
|
|
|
|
SEC_FROM_LATEST + 1001 * SEC_PER_DAY, # Limit > history, no trimming.
|
|
|
|
HISTORY_FILES_LIST,
|
|
|
|
0, # nothing trimmed
|
|
|
|
[], #
|
|
|
|
),
|
|
|
|
(
|
|
|
|
_xhj_gc_seconds_to_rmfiles,
|
2021-03-22 13:20:36 -07:00
|
|
|
SEC_FROM_LATEST + MAX_RUNTIME, # keep just the latest file (night)
|
2020-06-25 20:26:47 -04:00
|
|
|
HISTORY_FILES_LIST,
|
2021-03-22 13:20:36 -07:00
|
|
|
HISTORY_FILES_LIST[-1][0] - HISTORY_FILES_LIST[0][0],
|
|
|
|
HISTORY_FILES_LIST[:-1],
|
2020-06-25 20:26:47 -04:00
|
|
|
),
|
|
|
|
(
|
|
|
|
_xhj_gc_seconds_to_rmfiles,
|
2021-03-22 13:20:36 -07:00
|
|
|
# keep the latest file and previous day, so we'll get three files
|
|
|
|
# including the previous morning and night before
|
|
|
|
SEC_FROM_LATEST + SEC_PER_DAY + MAX_RUNTIME,
|
2020-06-25 20:26:47 -04:00
|
|
|
HISTORY_FILES_LIST,
|
2021-03-22 13:20:36 -07:00
|
|
|
HISTORY_FILES_LIST[-3][0] - HISTORY_FILES_LIST[0][0],
|
|
|
|
HISTORY_FILES_LIST[:-3],
|
2020-06-25 20:26:47 -04:00
|
|
|
),
|
|
|
|
],
|
|
|
|
)
|
2021-05-20 15:44:26 +05:30
|
|
|
def test__xhj_gc_xx_to_rmfiles(fn, hsize, in_files, exp_size, exp_files, xession):
|
2020-06-25 20:26:47 -04:00
|
|
|
act_size, act_files = fn(hsize, in_files)
|
|
|
|
|
|
|
|
assert act_files == exp_files
|
|
|
|
|
|
|
|
# comparing age is approximate, because xhj_gc_seconds_to_rmfiles computes 'now' on each call.
|
|
|
|
# We find multi-minute variations in CI environments.
|
|
|
|
# This should cover some amount of think time sitting at a breakpoint, too.
|
|
|
|
if fn == _xhj_gc_seconds_to_rmfiles:
|
2021-03-22 13:20:36 -07:00
|
|
|
assert abs(act_size - exp_size) < MAX_RUNTIME
|
2020-06-25 20:26:47 -04:00
|
|
|
else:
|
|
|
|
assert act_size == exp_size
|
2020-09-17 00:26:19 +01:00
|
|
|
|
|
|
|
|
2021-05-20 15:44:26 +05:30
|
|
|
def test_hist_clear_cmd(hist, xession, capsys, tmpdir):
|
2020-09-17 00:26:19 +01:00
|
|
|
"""Verify that the CLI history clear command works."""
|
2021-05-20 15:44:26 +05:30
|
|
|
xession.env.update({"XONSH_DATA_DIR": str(tmpdir)})
|
|
|
|
xession.env["HISTCONTROL"] = set()
|
2020-09-17 00:26:19 +01:00
|
|
|
|
|
|
|
for ts, cmd in enumerate(CMDS): # populate the shell history
|
|
|
|
hist.append({"inp": cmd, "rtn": 0, "ts": (ts + 1, ts + 1.5)})
|
2021-05-20 15:44:26 +05:30
|
|
|
assert len(xession.history) == 6
|
2020-09-17 00:26:19 +01:00
|
|
|
|
|
|
|
history_main(["clear"])
|
|
|
|
|
|
|
|
out, err = capsys.readouterr()
|
|
|
|
assert err.rstrip() == "History cleared"
|
2021-05-20 15:44:26 +05:30
|
|
|
assert len(xession.history) == 0
|
2020-09-17 00:26:19 +01:00
|
|
|
|
|
|
|
|
2021-05-20 15:44:26 +05:30
|
|
|
def test_hist_off_cmd(hist, xession, capsys, tmpdir):
|
2020-09-17 00:26:19 +01:00
|
|
|
"""Verify that the CLI history off command works."""
|
2021-05-20 15:44:26 +05:30
|
|
|
xession.env.update({"XONSH_DATA_DIR": str(tmpdir)})
|
|
|
|
xession.env["HISTCONTROL"] = set()
|
2020-09-17 00:26:19 +01:00
|
|
|
|
|
|
|
for ts, cmd in enumerate(CMDS): # populate the shell history
|
|
|
|
hist.append({"inp": cmd, "rtn": 0, "ts": (ts + 1, ts + 1.5)})
|
2021-05-20 15:44:26 +05:30
|
|
|
assert len(xession.history) == 6
|
2020-09-17 00:26:19 +01:00
|
|
|
|
|
|
|
history_main(["off"])
|
|
|
|
|
|
|
|
out, err = capsys.readouterr()
|
|
|
|
assert err.rstrip() == "History off"
|
2021-05-20 15:44:26 +05:30
|
|
|
assert len(xession.history) == 0
|
2020-09-17 00:26:19 +01:00
|
|
|
|
|
|
|
for ts, cmd in enumerate(CMDS): # attempt to populate the shell history
|
|
|
|
hist.append({"inp": cmd, "rtn": 0, "ts": (ts + 1, ts + 1.5)})
|
|
|
|
|
2021-05-20 15:44:26 +05:30
|
|
|
assert len(xession.history) == 0
|
2020-09-17 00:26:19 +01:00
|
|
|
|
|
|
|
|
2021-05-20 15:44:26 +05:30
|
|
|
def test_hist_on_cmd(hist, xession, capsys, tmpdir):
|
2020-09-17 00:26:19 +01:00
|
|
|
"""Verify that the CLI history on command works."""
|
2021-05-20 15:44:26 +05:30
|
|
|
xession.env.update({"XONSH_DATA_DIR": str(tmpdir)})
|
|
|
|
xession.env["HISTCONTROL"] = set()
|
2020-09-17 00:26:19 +01:00
|
|
|
|
|
|
|
for ts, cmd in enumerate(CMDS): # populate the shell history
|
|
|
|
hist.append({"inp": cmd, "rtn": 0, "ts": (ts + 1, ts + 1.5)})
|
2021-05-20 15:44:26 +05:30
|
|
|
assert len(xession.history) == 6
|
2020-09-17 00:26:19 +01:00
|
|
|
|
|
|
|
history_main(["off"])
|
|
|
|
history_main(["on"])
|
|
|
|
|
|
|
|
out, err = capsys.readouterr()
|
|
|
|
assert err.rstrip().endswith("History on")
|
2021-05-20 15:44:26 +05:30
|
|
|
assert len(xession.history) == 0
|
2020-09-17 00:26:19 +01:00
|
|
|
|
2020-09-20 19:28:54 +01:00
|
|
|
for ts, cmd in enumerate(CMDS): # populate the shell history
|
2020-09-17 00:26:19 +01:00
|
|
|
hist.append({"inp": cmd, "rtn": 0, "ts": (ts + 1, ts + 1.5)})
|
|
|
|
|
2021-05-20 15:44:26 +05:30
|
|
|
assert len(xession.history) == 6
|