From 7314a1bda6e6bdce203eca6fca044a80cfdffaa5 Mon Sep 17 00:00:00 2001
From: Timur Safin <tsafin@tarantool.org>
Date: Thu, 15 Dec 2022 02:24:13 +0300
Subject: [PATCH] debugger: refactor debugging scenarios

Refactor the way how we run various debugger scenarios, getting rid
of external static file, and producing debuggee scripts on the fly.
The idea is to have everything in the single source: both source
script to be used for debugging, and corresponding debugger commands.

NO_DOC=internal
NO_CHANGELOG=internal
---
 .../console_debugger_session_test.lua         | 205 ++++++++++++++----
 test/app-luatest/debug-self-target.lua        |   8 -
 test/app-luatest/debug-target.lua             |  12 -
 3 files changed, 158 insertions(+), 67 deletions(-)
 delete mode 100644 test/app-luatest/debug-self-target.lua
 delete mode 100644 test/app-luatest/debug-target.lua

diff --git a/test/app-luatest/console_debugger_session_test.lua b/test/app-luatest/console_debugger_session_test.lua
index 8fc2284503..83f588fd20 100644
--- a/test/app-luatest/console_debugger_session_test.lua
+++ b/test/app-luatest/console_debugger_session_test.lua
@@ -1,10 +1,7 @@
 local t = require('luatest')
 local popen = require('popen')
 local tnt = require('tarantool')
-
-local function normalize_path(s)
-    return s:gsub("^@", ""):gsub("[^/]+$", "")
-end
+local fio = require('fio')
 
 local function unescape(s)
     return s and s:gsub('[\27\155][][()#;?%d]*[A-PRZcf-ntqry=><~]', '') or ''
@@ -22,7 +19,6 @@ local function tarantool_path(arg)
 end
 
 local TARANTOOL_PATH = tarantool_path(arg)
-local path_to_script = normalize_path(debug.getinfo(1, 'S').source)
 
 local DEBUGGER = 'luadebug'
 local dbg_header = tnt.package .. " debugger " .. tnt.version
@@ -41,6 +37,47 @@ local function debuglog(...)
     print('--', ...)
 end
 
+--[[
+    Convert passed multi-line literal script
+    to the temporary file. Get rid of line annotations,
+    leaving code alone.
+
+    Annotated lines are in a form:
+        1 | local date = require 'datetime'
+        2 | local T = date.new{hour = 3, tzoffset = '+0300'}
+]]
+local function dbg_script(content)
+    assert(content ~= nil)
+    local tmpname = os.tmpname()
+    local f = assert(io.open(tmpname, "wb"))
+    debuglog("Temporary script name:", tmpname)
+    -- process multi-line literal
+    for line in string.gmatch(content, "[^\n]+") do
+        --[[
+            Process annotated code line, delimited with '|'.
+
+        +-------- virtual line number (for easier maintaining)
+        | +------ delimiter
+        | | +---- Lua code to be executed
+        V V V
+        1 | local date = require 'datetime'
+
+        ]]
+        local delim = line:find('|')
+
+        -- if there is no delimiter - write it as-is
+        if not delim then
+            f:write(line..'\n')
+        else
+            -- TODO - assert if there is incorrect line numbers
+            -- at the moment just ignore annotation entirely.
+            f:write(line:sub(delim + 1)..'\n')
+        end
+    end
+    f:close()
+    return tmpname
+end
+
 local cmd_aliases = {
     ['b'] = 'b|break|breakpoint|add_break|add_breakpoint',
     ['bd'] = 'bd|bdelete|delete_break|delete_breakpoint',
@@ -83,17 +120,31 @@ local function get_key_arg_pair(cmd)
 end
 
 local g = t.group('debug', {
-    {sequence = 'full scenario'},
-    {sequence = 'successful breakpoints additions'},
-    {sequence = 'failed breakpoints additions'},
-    {sequence = 'successful breakpoints removals'},
-    {sequence = 'failed breakpoints removals'},
+    {sequence = 'full'},
+    {sequence = 'bp_set_ok'},
+    {sequence = 'bp_set_fail'},
+    {sequence = 'bp_delete_ok'},
+    {sequence = 'bp_delete_fail'},
 })
 
 local sequences = {
 
     -- full, complex sequence
-    ['full scenario'] = {
+    full = {
+        dbg_script [[
+     1 | local date = require 'datetime'
+     2 |
+     3 | local T = date.new{hour = 3, tzoffset = '+0300'}
+     4 | print(T)
+     5 |
+     6 | local fmt = '%Y-%m-%dT%H%M%z'
+     7 | local S  = T:format(fmt)
+     8 | print(S)
+     9 | local T1 = date.parse(S, {format = fmt})
+    10 | print(T1)
+    11 |
+    12 | os.exit(0)
+]],
         { ['\t'] = '' }, -- \t is a special value for start
         { ['n'] = dbg_prompt },
         { ['s'] = dbg_prompt },
@@ -107,11 +158,11 @@ local sequences = {
         { ['bogus;'] = 'is not recognized.' },
         { ['h'] = dbg_prompt },
         { ['t'] = '=> builtin/datetime.lua' },
-        { ['u'] = 'debug-target.lua:3 in chunk at' },
+        { ['u'] = '{file}:3 in chunk at' },
         -- FIXME (gh-8190) - we should not show calling side at luadebug.lua
         -- { ['u'] = 'Already at the bottom of the stack.' },
         { ['u'] = 'Inspecting frame: builtin/luadebug.lua' },
-        { ['d'] = 'debug-target.lua:3 in chunk at' },
+        { ['d'] = '{file}:3 in chunk at' },
         { ['d'] = 'Inspecting frame: builtin/datetime.lua' },
         { ['l'] = 'obj => {"tzoffset" = "+0300", "hour" = 3}' },
         { ['f'] = dbg_prompt },
@@ -126,57 +177,98 @@ local sequences = {
     },
 
     -- partial sequence with successful breakpoints added
-    ['successful breakpoints additions'] = {
+    bp_set_ok = {
+        dbg_script [[
+     1 | local date = require 'datetime'
+     2 |
+     3 | local T = date.new{hour = 3, tzoffset = '+0300'}
+     4 | print(T)
+     5 |
+     6 | local fmt = '%Y-%m-%dT%H%M%z'
+     7 | local S  = T:format(fmt)
+     8 | print(S)
+     9 | local T1 = date.parse(S, {format = fmt})
+    10 | print(T1)
+    11 |
+    12 | os.exit(0)
+]],
         { ['\t'] = '' }, -- \t is a special value for start
-        { ['b +10'] = dbg_prompt },
-        { ['c'] = 'debug-target.lua:10' },
+        { ['b +9'] = dbg_prompt },
+        { ['c'] = '{file}:9' },
         { ['n'] = dbg_prompt },
         { ['p S'] = 'S => "1970-01-01T0300+0300"' },
         { ['p T'] = 'T => 1970-01-01T03:00:00+0300' },
         { ['c'] = '' },
     },
 
-    -- partial sequence with failed breakpoint additions
-    ['failed breakpoints additions'] = {
+    -- partial sequence with failed breakpoint addsitions
+    bp_set_fail = {
+        dbg_script [[
+    1 | local date = require 'datetime'
+    2 | local T = date.new{hour = 3, tzoffset = '+0300'}
+    3 | print(T)
+    4 | os.exit(0)
+]],
         { ['\t'] = '' }, -- \t is a special value for start
         { ['b'] = 'expects argument, but none received' },
         { ['b 11'] = dbg_failed_bp },
-        { ['b debug-target.lua'] = dbg_failed_bp },
-        { ['b debug-target.lua:'] = dbg_failed_bp },
-        { ['b debug-target.lua:-10'] = dbg_failed_bp },
-        { ['b debug-target.lua+0'] = dbg_failed_range(0) },
+        { ['b {file}'] = dbg_failed_bp },
+        { ['b {file}:'] = dbg_failed_bp },
+        { ['b {file}:-10'] = dbg_failed_bp },
+        { ['b {file}+0'] = dbg_failed_range(0) },
         { ['b +0'] = dbg_failed_range(0) },
-        { ['b debug-target.lua+2147483632'] = dbg_failed_range(2147483632) },
+        { ['b {file}+2147483632'] = dbg_failed_range(2147483632) },
         { ['b +21474836480'] = dbg_failed_range(21474836480) },
     },
 
     -- partial sequence with breakpoints additions and removals
-    ['successful breakpoints removals'] = {
+    bp_delete_ok = {
+        dbg_script [[
+     1 | local date = require 'datetime'
+     2 |
+     3 | local T = date.new{hour = 3, tzoffset = '+0300'}
+     4 | print(T)
+     5 |
+     6 | local fmt = '%Y-%m-%dT%H%M%z'
+     7 | local S  = T:format(fmt)
+     8 | print(S)
+     9 | local T1 = date.parse(S, {format = fmt})
+    10 | print(T1)
+    11 |
+    12 | os.exit(0)
+]],
         { ['\t'] = '' }, -- \t is a special value for start
-        { ['b +7'] = 'debug-target.lua:7' },
-        { ['bl'] = 'debug-target.lua:7' },
-        { ['b :5'] = 'debug-target.lua:5' },
-        { ['bl'] = 'debug-target.lua:5' },
-        { ['bd :5'] = 'debug-target.lua:5' },
-        { ['bl'] = 'debug-target.lua:7' },
-        { ['bd +7'] = 'debug-target.lua:7' },
-        { ['b :6'] = 'debug-target.lua:6' },
-        { ['bl'] = 'debug-target.lua:6' },
+        { ['b +7'] = '{file}:7' },
+        { ['bl'] = '{file}:7' },
+        { ['b :5'] = '{file}:5' },
+        { ['bl'] = '{file}:5' },
+        { ['bd :5'] = '{file}:5' },
+        { ['bl'] = '{file}:7' },
+        { ['bd +7'] = '{file}:7' },
+        { ['b :6'] = '{file}:6' },
+        { ['bl'] = '{file}:6' },
         { ['bd *'] = 'Removed all breakpoints' },
         { ['bl'] = 'No active breakpoints defined' },
     },
     -- partial sequence with failed breakpoints removals
-    ['failed breakpoints removals'] = {
+    bp_delete_fail = {
+        dbg_script [[
+    1 | local date = require 'datetime'
+    2 | local T = date.new{hour = 3, tzoffset = '+0300'}
+    3 | print(T)
+    4 | os.exit(0)
+]],
         { ['\t'] = '' }, -- \t is a special value for start
         { ['bd'] = 'expects argument, but none received' },
         { ['bd 11'] = dbg_failed_bpd },
-        { ['bd debug-target.lua'] = dbg_failed_bpd },
-        { ['bd debug-target.lua:'] = dbg_failed_bpd },
-        { ['bd debug-target.lua:-10'] = dbg_failed_bpd },
+        { ['bd {file}'] = dbg_failed_bpd },
+        { ['bd {file}:'] = dbg_failed_bpd },
+        { ['bd {file}:-10'] = dbg_failed_bpd },
     },
 }
 
-local function run_debug_session(cmdline, sequence, header)
+local function run_debug_session(cmdline, sequence, tmpfile, header)
+    local short_src = fio.basename(tmpfile)
     --[[
         repeat multiple times to check all command aliases
     ]]
@@ -192,6 +284,8 @@ local function run_debug_session(cmdline, sequence, header)
         local first = header ~= nil
         for _, row in pairs(sequence) do
             local cmd, expected = next(row)
+            -- interpret template strings {file}
+            expected = expected:gsub('{file}', short_src)
             if cmd ~= '\t' then
                 if cmd ~= '' then
                     local key, arg = get_key_arg_pair(cmd)
@@ -227,21 +321,36 @@ local function run_debug_session(cmdline, sequence, header)
         end
         fh:close()
     end
+    debuglog('Delete temporary file ', tmpfile)
+    os.remove(tmpfile)
 end
 
 -- `tarantool -d debug-target.lua`
 g.test_debugger = function(cg)
-    local debug_target_script = path_to_script .. 'debug-target.lua'
-    local cmd = { TARANTOOL_PATH, '-d', debug_target_script }
     local scenario_name = cg.params.sequence
     t.assert(scenario_name)
-    t.assert(sequences[scenario_name])
-    run_debug_session(cmd, sequences[scenario_name], dbg_header)
+    local sequence = sequences[scenario_name]
+    t.assert(sequence)
+    local tmpfile = table.remove(sequence, 1)
+    t.assert(type(tmpfile) == 'string')
+
+    local cmd = { TARANTOOL_PATH, '-d', tmpfile }
+    run_debug_session(cmd, sequences[scenario_name], tmpfile, dbg_header)
 end
 
 local g_self = t.group('self-debug')
 
 local shorter_sequence = {
+        dbg_script [[
+    1 | local dbg = require 'luadebug'
+    2 | dbg()
+    3 | local date = require 'datetime'
+    4 |
+    5 | local T = date.new{hour = 3, tzoffset = '+0300'}
+    6 | print(T)
+    7 |
+    8 | os.exit(0)
+]],
     { ['\t'] = '' }, -- \t is a special value for start
     { ['n'] = dbg_prompt },
     { ['s'] = dbg_prompt },
@@ -249,18 +358,20 @@ local shorter_sequence = {
     { ['n'] = dbg_prompt },
     { ['p'] = 'expects argument, but none received' },
     { ['p obj'] = 'obj => {"tzoffset" = "+0300", "hour" = 3}' },
-    { ['u'] = 'debug-self-target.lua:5 in chunk at' },
+    { ['u'] = '{file}:5 in chunk at' },
     { ['u'] = 'Already at the bottom of the stack.' },
     { ['d'] = 'Inspecting frame: builtin/datetime.lua' },
     { ['l'] = 'obj => {"tzoffset" = "+0300", "hour" = 3}' },
-    { ['f'] = 'debug-self-target.lua:6 in chunk at' },
+    { ['f'] = '{file}:6 in chunk at' },
     { ['c'] = '' },
 }
 
 -- `tarantool debug-self-target.lua`, where
 -- initiate debugging session via `require 'luadebug'()`
 g_self.test_debug_self_invoke = function()
-    local debug_target_script = path_to_script .. 'debug-self-target.lua'
-    local cmd = { TARANTOOL_PATH, debug_target_script }
-    run_debug_session(cmd, shorter_sequence)
+    local tmpfile = table.remove(shorter_sequence, 1)
+    t.assert(type(tmpfile) == 'string')
+
+    local cmd = { TARANTOOL_PATH, tmpfile }
+    run_debug_session(cmd, shorter_sequence, tmpfile)
 end
diff --git a/test/app-luatest/debug-self-target.lua b/test/app-luatest/debug-self-target.lua
deleted file mode 100644
index 8aed6a967a..0000000000
--- a/test/app-luatest/debug-self-target.lua
+++ /dev/null
@@ -1,8 +0,0 @@
-local dbg = require 'luadebug'
-dbg()
-local date = require 'datetime'
-
-local T = date.new{hour = 3, tzoffset = '+0300'}
-print(T)
-
-os.exit(0)
diff --git a/test/app-luatest/debug-target.lua b/test/app-luatest/debug-target.lua
deleted file mode 100644
index 3546ac2dde..0000000000
--- a/test/app-luatest/debug-target.lua
+++ /dev/null
@@ -1,12 +0,0 @@
-local date = require 'datetime'
-
-local T = date.new{hour = 3, tzoffset = '+0300'}
-print(T)
-
-local fmt = '%Y-%m-%dT%H%M%z'
-local S  = T:format(fmt)
-print(S)
-local T1 = date.parse(S, {format = fmt})
-print(T1)
-
-os.exit(0)
-- 
GitLab