import pytest

# Convenience function to execute a scylla command in gdb, returning its
# output as a string - or a gdb.error exception.
def scylla(gdb, cmd):
    gdb.execute('scylla ' + cmd, from_tty=False, to_string=True)

# Check that trying an unknown subcommand of the "scylla" subcommand
# produces the right error message.
def test_nonexistent_scylla_command(gdb):
        with pytest.raises(gdb.error, match='Undefined scylla command'):
            scylla(gdb, 'nonexistent_command')

# Minimal test for some of the commands. Each only checks that the command
# does not fail - but not what it does or returns. These tests are still
# useful - importantly, they can detect that one of the commands relies on
# some internal implementation detail which no longer works, and needs to
# be fixed.

def test_features(gdb):
    scylla(gdb, 'features')

def test_compaction_tasks(gdb):
    scylla(gdb, 'compaction-tasks')

def test_databases(gdb):
    scylla(gdb, 'databases')

def test_column_families(gdb):
    scylla(gdb, 'column_families')

def test_keyspaces(gdb):
    scylla(gdb, 'keyspaces')

def test_active_sstables(gdb):
    scylla(gdb, 'active-sstables')

def test_sstables(gdb):
    scylla(gdb, 'sstables')

def test_memtables(gdb):
    scylla(gdb, 'memtables')

def test_repairs(gdb):
    scylla(gdb, 'repairs')

def test_gms(gdb):
    scylla(gdb, 'gms')

def test_heapprof(gdb):
    scylla(gdb, 'heapprof')

def test_io_queues(gdb):
    scylla(gdb, 'io-queues')

def test_cache(gdb):
    scylla(gdb, 'cache')

def test_mem_range(gdb):
    scylla(gdb, 'mem-range')

def test_mem_ranges(gdb):
    scylla(gdb, 'mem-ranges')

def test_memory(gdb):
    scylla(gdb, 'memory')

def test_segment_descs(gdb):
    scylla(gdb, 'segment-descs')

def test_small_object_1(gdb):
    scylla(gdb, 'small-object -o 32 --random-page')

def test_small_object_2(gdb):
    scylla(gdb, 'small-object -o 64 --summarize')

def test_lsa(gdb):
    scylla(gdb, 'lsa')

def test_netw(gdb):
    scylla(gdb, 'netw')

def test_smp_queues(gdb):
    scylla(gdb, 'smp-queues')

def test_task_queues(gdb):
    scylla(gdb, 'task-queues')

def test_task_histogram(gdb):
    scylla(gdb, 'task_histogram')

def test_tasks(gdb):
    scylla(gdb, 'tasks')

def test_threads(gdb):
    scylla(gdb, 'threads')

def test_timers(gdb):
    scylla(gdb, 'timers')

# Some commands need a schema to work on. The following fixture finds
# one (the schema of the first table - note that even without any user
# tables, we will always have system tables).
@pytest.fixture(scope="module")
def schema(gdb, scylla_gdb):
    db = scylla_gdb.sharded(gdb.parse_and_eval('::debug::the_database')).local()
    table = next(scylla_gdb.for_each_table(db))
    gdb.set_convenience_variable('schema', 
        table['_schema']['_p'].reinterpret_cast(gdb.lookup_type('schema').pointer()))
    yield '$schema'

def test_schema(gdb, schema):
    scylla(gdb, f'schema {schema}')

def test_find(gdb, schema):
    scylla(gdb, f'find -r {schema}')

def test_ptr(gdb, schema):
    scylla(gdb, f'ptr {schema}')

def test_generate_object_graph(gdb, schema, request):
    tmpdir = request.config.getoption('scylla_tmp_dir')
    scylla(gdb, f'generate-object-graph -o {tmpdir}/og.dot -d 2 -t 10 {schema}')

# Some commands need a task to work on. This finds one.
# FIXME: Currently, this fails because get_local_tasks() finds no task.
# The problem is that we attached gdb to Scylla while Scylla was idle,
# and had no task to run :-(
@pytest.fixture(scope="module")
def task(gdb, scylla_gdb):
    gdb.set_convenience_variable('task', 
        next(scylla_gdb.get_local_tasks()))
    yield '$task'

@pytest.mark.xfail(reason="FIXME: get_local_tasks() returns nothing when we attach gdb :-(")
def test_fiber(gdb, task):
    scylla(gdb, f'fiber {task}')

# FIXME: need a simple test for lsa-segment

@@ -152,24 +152,31 @@
 , cmd):%0A    
+return 
 gdb.execute(
@@ -3290,193 +3290,321 @@
 . Th
-is finds one.%0A# FIXME: Currently, this fails because get_local_tasks() finds no task.%0A# The problem is that we attached gdb to Scylla while Scylla was idle,%0A# and had no task to run :-(
+e following fixture finds one.%0A# Because we stopped Scylla while it was idle, we don't expect to find%0A# any ready task with get_local_tasks(), but we can find one with a%0A# find_vptrs() loop. I noticed that a nice one (with multiple tasks chained%0A# to it for %22scylla fiber%22) is one from http_server::do_accept_one.
 %0A@py
@@ -3667,45 +3667,61 @@
     
-gdb.set_convenience_variable('task', 
+for obj_addr, vtable_addr in scylla_gdb.find_vptrs():
 %0A   
@@ -3726,20 +3726,22 @@
        n
-ext(
+ame = 
 scylla_g
@@ -3747,139 +3747,184 @@
 gdb.
-get_local_tasks()))%0A    yield '$task'%0A%0A@pytest.mark.xfail(reason=%22FIXME: get_local_tasks() returns nothing when we attach gdb :-(%22)
+resolve(vtable_addr, startswith='vtable for seastar::continuation')%0A        if name and 'do_accept_one' in name:%0A            return obj_addr.cast(gdb.lookup_type('uintptr_t'))%0A
 %0Adef

enable test for "scylla fiber"