mirror of
https://github.com/polserver/polserver
synced 2026-08-13 08:23:08 -04:00
* Format escript testsuite
* Manual formatting for escript testsuite
- Add format-off to places where formatter does what it should, but we don't like the results
- Manually change places where formatter outputs weird things _without_ format-off, so next format-all would show changes if not fixed
* Update .err files; add update_err.py helper script
Use the format `:line:col: message` for all .err file checks. This
allows us to change the file names without needing to edit the .err
contents.
Note that `in/inc0{1,2}.err` need have error messages between POSIX and
Windows, so their messages are a bit more "generic" than the other errs.
* Format pol testsuite
* Manual formatting for pol testsuite
* Format pol-core modules
* Manual formatting for pol-core modules
* Additional format pol-core modules
Use FormatterLineWidth=10000 since module functions must be on one line
because of the parse_modules target
* Address Discord comments
- Support multi-line module function declarations in parse_modules
* Format on df07831
648 lines
19 KiB
Text
648 lines
19 KiB
Text
use os;
|
|
use file;
|
|
|
|
include "testutil";
|
|
|
|
const C_RED := "\x1b[31m";
|
|
const C_GREEN := "\x1b[32m";
|
|
const C_YELLOW := "\x1b[33m";
|
|
const C_CYAN := "\x1b[36m";
|
|
const C_MAGENTA_LIGHT := "\x1b[95m";
|
|
const C_RESET := "\x1b[0m";
|
|
|
|
const CONTENT_LENGTH_HEADER_KEY := "Content-Length: ";
|
|
const CONTENT_LENGTH_HEADER_KEY_LENGTH := 16;
|
|
const CONTENT_LENGTH_HEADER_VALUE_START := CONTENT_LENGTH_HEADER_KEY_LENGTH + 1;
|
|
|
|
// List of events
|
|
var dapEvents := {};
|
|
|
|
// Maps a request sequence to a response object
|
|
var dapResponses := dictionary{};
|
|
|
|
// Global aux script connection
|
|
var connection;
|
|
|
|
// Next request sequence number
|
|
var nextSequence := 1;
|
|
var recvBuffer := "";
|
|
|
|
program auxservice( conn, params )
|
|
connection := conn;
|
|
|
|
var result;
|
|
var test_script_pid := params.test_script_pid;
|
|
var test_type := params.test_type;
|
|
|
|
if ( test_type == "attach" )
|
|
result := run_attach_test( params.debuggee_pid );
|
|
elseif ( test_type == "launch" )
|
|
result := run_launch_test();
|
|
else
|
|
result := ret_error( $"Unknown debugger test type '{test_type}'" );
|
|
endif
|
|
GetProcess( test_script_pid ).SendEvent( struct{ result := result } );
|
|
connection := 0;
|
|
endprogram
|
|
|
|
function run_launch_test()
|
|
// Get script source
|
|
var source := ReadFile( "debuggee.src" );
|
|
if ( !source )
|
|
return ret_error( source );
|
|
endif
|
|
|
|
var print_linenum;
|
|
foreach line in source
|
|
if ( line.find( "/* print */" ) )
|
|
print_linenum := _line_iter;
|
|
endif
|
|
endforeach
|
|
|
|
var err;
|
|
|
|
// Send request: initialize
|
|
if ( !request_succeeded( err, "initialize", struct{ adapterID := "pol", password := "debugme" } ) )
|
|
return err;
|
|
endif
|
|
|
|
// Wait for event: initialized
|
|
if ( !event_received( err, "initialized" ) )
|
|
return err;
|
|
endif
|
|
|
|
// Send request: launch
|
|
// Note: Sets the program's parameter as "stop"
|
|
if ( !request_succeeded( err, "launch", struct{ "script" := ":testdebugger:debuggee",
|
|
arg := "S4:stop" /* packed */ }
|
|
) )
|
|
return err;
|
|
endif
|
|
|
|
// Wait for event: stopped
|
|
if ( !event_received( err, "stopped" ) )
|
|
return err;
|
|
endif
|
|
|
|
// Send request: threads
|
|
// Note: Since we only have one thread that never changes, test sends this message only once.
|
|
var threads;
|
|
if ( !( threads := request_succeeded( err, "threads" ) ) )
|
|
return err;
|
|
endif
|
|
var threadId := threads.threads[1].id;
|
|
|
|
// Send request: stackTrace
|
|
var stackTrace;
|
|
if ( !( stackTrace := request_succeeded( err, "stackTrace", struct{ threadId := threadId } ) ) )
|
|
return err;
|
|
endif
|
|
|
|
// Send request: scopes
|
|
var scopes;
|
|
if ( !( scopes := request_succeeded( err, "scopes", struct{ frameId := stackTrace.stackFrames[1].id }
|
|
) ) )
|
|
return err;
|
|
endif
|
|
|
|
var evaluate;
|
|
if ( !( evaluate := request_succeeded( err, "evaluate", struct{ expression := "running",
|
|
frameId := scopes.scopes[1].variablesReference }
|
|
) ) )
|
|
return err;
|
|
endif
|
|
|
|
if ( evaluate.result != "\"stop\"" )
|
|
return ret_error( $"Program argument from launch request `running` does not equal '\"stop\"': '{evaluate.result}' != '\"stop\"'" );
|
|
endif
|
|
|
|
// Send request: continue
|
|
if ( !request_succeeded( err, "continue", struct{ threadId := threadId } ) )
|
|
return err;
|
|
endif
|
|
|
|
// Wait for event: output
|
|
var output;
|
|
if ( !( output := event_received( err, "output" ) ) )
|
|
return err;
|
|
endif
|
|
|
|
var expected := struct{ category := "stdout",
|
|
output := "Loop finished!\n",
|
|
line := print_linenum,
|
|
source := struct{ path := stackTrace.stackFrames[1].source.path } };
|
|
|
|
if ( output.category != expected.category || output.output != expected.output ||
|
|
output.line != expected.line || output.source.path != expected.source.path )
|
|
return ret_error( $"Incorrect output event. Expected: {expected}, actual {output}" );
|
|
endif
|
|
|
|
// Wait for event: exited
|
|
if ( !event_received( err, "exited" ) )
|
|
return err;
|
|
endif
|
|
|
|
return 1;
|
|
endfunction
|
|
|
|
function run_attach_test( debuggee_pid )
|
|
var err;
|
|
|
|
// Send request: initialize
|
|
if ( !request_succeeded( err, "initialize", struct{ adapterID := "pol", password := "debugme" } ) )
|
|
return err;
|
|
endif
|
|
|
|
// Wait for event: initialized
|
|
if ( !event_received( err, "initialized" ) )
|
|
return err;
|
|
endif
|
|
|
|
// Send request: processes
|
|
var processes;
|
|
if ( !( processes := request_succeeded( err, "processes", struct{ filter := "debuggee" } ) ) )
|
|
return err;
|
|
endif
|
|
|
|
if ( processes.processes[1].id != debuggee_pid )
|
|
return ret_error( $"Process id does not match! Expected {processes.processes[1].id} == {debuggee_pid}" );
|
|
endif
|
|
|
|
// Send request: attach
|
|
if ( !request_succeeded( err, "attach", struct{ pid := debuggee_pid } ) )
|
|
return err;
|
|
endif
|
|
|
|
// Wait for event: stopped
|
|
if ( !event_received( err, "stopped" ) )
|
|
return err;
|
|
endif
|
|
|
|
// Send request: threads
|
|
// Note: Since we only have one thread that never changes, test sends this message only once.
|
|
var threads;
|
|
if ( !( threads := request_succeeded( err, "threads" ) ) )
|
|
return err;
|
|
endif
|
|
var threadId := threads.threads[1].id;
|
|
|
|
// Send request: stackTrace
|
|
var stackTrace;
|
|
if ( !( stackTrace := request_succeeded( err, "stackTrace", struct{ threadId := threadId } ) ) )
|
|
return err;
|
|
endif
|
|
|
|
// Send request: scopes
|
|
var scopes;
|
|
if ( !( scopes := request_succeeded( err, "scopes", struct{ frameId := stackTrace.stackFrames[1].id }
|
|
) ) )
|
|
return err;
|
|
endif
|
|
|
|
var scope_varibles := array{};
|
|
foreach scope in ( scopes.scopes )
|
|
var variables;
|
|
if ( !( variables := request_succeeded( err, "variables", struct{ variablesReference := scope.variablesReference }
|
|
) ) )
|
|
return err;
|
|
endif
|
|
|
|
foreach variable in ( variables.variables )
|
|
var evaluate;
|
|
if ( !( evaluate := request_succeeded( err, "evaluate", struct{ expression := variable.name,
|
|
frameId := scope.variablesReference }
|
|
) ) )
|
|
return err;
|
|
endif
|
|
|
|
if ( variable.value != evaluate.result )
|
|
return ret_error( $"Variables value does not match evaluate result: '{variable.value}' != '{evaluate.result}'" );
|
|
endif
|
|
|
|
if ( variable.variablesReference )
|
|
var inner_variables;
|
|
if ( !( inner_variables := request_succeeded( err, "variables", struct{ variablesReference := variable.variablesReference }
|
|
) ) )
|
|
return err;
|
|
endif
|
|
endif
|
|
endforeach
|
|
endforeach
|
|
|
|
// Get script source
|
|
var source := ReadFile( "debuggee.src" );
|
|
if ( !source )
|
|
return ret_error( source );
|
|
endif
|
|
|
|
var setting_breakpoints := array{};
|
|
foreach line in source
|
|
if ( line.find( "/* set_bp */" ) )
|
|
setting_breakpoints.append( struct{ line := _line_iter } );
|
|
endif
|
|
endforeach
|
|
|
|
// Send request: setBreakpoints
|
|
if ( !request_succeeded( err, "setBreakpoints", struct{ source := struct{
|
|
path := stackTrace.stackFrames[1].source.path },
|
|
breakpoints := setting_breakpoints }
|
|
) )
|
|
return err;
|
|
endif
|
|
|
|
// Send request: continue
|
|
if ( !request_succeeded( err, "continue", struct{ threadId := threadId } ) )
|
|
return err;
|
|
endif
|
|
|
|
// Wait for event: stopped
|
|
if ( !event_received( err, "stopped" ) )
|
|
return err;
|
|
endif
|
|
|
|
// Send request: stackTrace
|
|
if ( !( stackTrace := request_succeeded( err, "stackTrace", struct{ threadId := threadId } ) ) )
|
|
return err;
|
|
endif
|
|
|
|
if ( stackTrace.stackFrames[1].name != "func2" )
|
|
return ret_error( $"Expected to be in `func2` after first continue: '{stackTrace.stackFrames[1].name}' != 'func2'" );
|
|
endif
|
|
|
|
// Send request: stepIn
|
|
if ( !request_succeeded( err, "stepIn", struct{ threadId := threadId } ) )
|
|
return err;
|
|
endif
|
|
|
|
// Wait for event: stopped
|
|
if ( !event_received( err, "stopped" ) )
|
|
return err;
|
|
endif
|
|
|
|
// Send request: stackTrace
|
|
if ( !( stackTrace := request_succeeded( err, "stackTrace", struct{ threadId := threadId } ) ) )
|
|
return err;
|
|
endif
|
|
|
|
if ( stackTrace.stackFrames[1].name != "func3" )
|
|
return ret_error( $"Expected to be in `func3` after first stepIn: '{stackTrace.stackFrames[1].name}' != 'func3'" );
|
|
endif
|
|
|
|
if ( stackTrace.stackFrames[2].name != "func2" )
|
|
return ret_error( $"Expected previous frame to be in `func2` after first stepIn: '{stackTrace.stackFrames[2].name}' != 'func2'" );
|
|
endif
|
|
|
|
// Send request: continue
|
|
if ( !request_succeeded( err, "continue", struct{ threadId := threadId } ) )
|
|
return err;
|
|
endif
|
|
|
|
// Wait for event: stopped
|
|
if ( !event_received( err, "stopped" ) )
|
|
return err;
|
|
endif
|
|
|
|
// Send request: stackTrace
|
|
if ( !( stackTrace := request_succeeded( err, "stackTrace", struct{ threadId := threadId } ) ) )
|
|
return err;
|
|
endif
|
|
|
|
if ( stackTrace.stackFrames[1].name != "funcShadowed" )
|
|
return ret_error( $"Expected to be in `funcShadowed` after second continue: '{stackTrace.stackFrames[1].name}' != 'funcShadowed'" );
|
|
endif
|
|
|
|
var expectedNextLine := stackTrace.stackFrames[1].line + 1;
|
|
|
|
// Send request: evaluate
|
|
var evaluate;
|
|
if ( !( evaluate := request_succeeded( err, "evaluate", struct{ expression := "shadowed_variable",
|
|
frameId := stackTrace.stackFrames[1].id }
|
|
) ) )
|
|
return err;
|
|
endif
|
|
|
|
if ( evaluate.result != "\"funcShadowed:param1\"" )
|
|
return ret_error( $"Expected value of `shadowed_variable` to be '\"funcShadowed:param1\"': '{evaluate.result}' != '\"funcShadowed:param1\"'" );
|
|
endif
|
|
|
|
// Send request: next
|
|
if ( !request_succeeded( err, "next", struct{ threadId := threadId } ) )
|
|
return err;
|
|
endif
|
|
|
|
// Wait for event: stopped
|
|
if ( !event_received( err, "stopped" ) )
|
|
return err;
|
|
endif
|
|
|
|
// Send request: stackTrace
|
|
if ( !( stackTrace := request_succeeded( err, "stackTrace", struct{ threadId := threadId } ) ) )
|
|
return err;
|
|
endif
|
|
|
|
if ( stackTrace.stackFrames[1].line != expectedNextLine )
|
|
return ret_error( $"Expected to be on line {expectedNextLine} after next: {stackTrace.stackFrames[1].line} != ${expectedNextLine}" );
|
|
endif
|
|
|
|
// Send request: continue
|
|
if ( !request_succeeded( err, "continue", struct{ threadId := threadId } ) )
|
|
return err;
|
|
endif
|
|
|
|
// Send request: pause
|
|
if ( !request_succeeded( err, "pause", struct{ threadId := threadId } ) )
|
|
return err;
|
|
endif
|
|
|
|
// Wait for event: stopped
|
|
if ( !event_received( err, "stopped" ) )
|
|
return err;
|
|
endif
|
|
|
|
// Send request: stackTrace
|
|
if ( !( stackTrace := request_succeeded( err, "stackTrace", struct{ threadId := threadId } ) ) )
|
|
return err;
|
|
endif
|
|
|
|
if ( stackTrace.stackFrames[1].name != "funcShadowed" )
|
|
return ret_error( $"Expected to be in `funcShadowed` after third continue: '{stackTrace.stackFrames[1].name}' != 'funcShadowed'" );
|
|
endif
|
|
|
|
// Send request: scopes
|
|
if ( !( scopes := request_succeeded( err, "scopes", struct{ frameId := stackTrace.stackFrames[1].id }
|
|
) ) )
|
|
return err;
|
|
endif
|
|
|
|
// Send request: setVariable [local]
|
|
var setVariable;
|
|
if ( !( setVariable := request_succeeded( err, "setVariable", struct{ name := "was_paused",
|
|
value := "1",
|
|
variablesReference := scopes.scopes[1].variablesReference
|
|
}
|
|
) ) )
|
|
return err;
|
|
endif
|
|
|
|
if ( setVariable.type != "number" || setVariable.value != "1" )
|
|
return ret_error( $"Expected setVariable for `was_paused` to be type = number, value = '1': {setVariable.type} != 'number' || {setVariable.value} != '1'" );
|
|
endif
|
|
|
|
// Send request: stepOut
|
|
if ( !request_succeeded( err, "stepOut", struct{ threadId := threadId } ) )
|
|
return err;
|
|
endif
|
|
|
|
// Wait for event: stopped
|
|
if ( !event_received( err, "stopped" ) )
|
|
return err;
|
|
endif
|
|
|
|
// Send request: stackTrace
|
|
if ( !( stackTrace := request_succeeded( err, "stackTrace", struct{ threadId := threadId } ) ) )
|
|
return err;
|
|
endif
|
|
|
|
if ( stackTrace.stackFrames[1].name != "(program)" )
|
|
return ret_error( $"Expected to be in `(program)` after stepOut: '{stackTrace.stackFrames[1].name}' != '(program)'" );
|
|
endif
|
|
|
|
// Send request: next
|
|
if ( !request_succeeded( err, "next", struct{ threadId := threadId } ) )
|
|
return err;
|
|
endif
|
|
|
|
// Wait for event: stopped
|
|
if ( !event_received( err, "stopped" ) )
|
|
return err;
|
|
endif
|
|
|
|
// Send request: stackTrace
|
|
if ( !( stackTrace := request_succeeded( err, "stackTrace", struct{ threadId := threadId } ) ) )
|
|
return err;
|
|
endif
|
|
|
|
if ( stackTrace.stackFrames[1].name != "(program)" )
|
|
return ret_error( $"Expected to be in `(program)` after stepOut: '{stackTrace.stackFrames[1].name}' != '(program)'" );
|
|
endif
|
|
|
|
// Send request: evaluate
|
|
if ( !( evaluate := request_succeeded( err, "evaluate", struct{ expression := "shadowed_result",
|
|
frameId := stackTrace.stackFrames[1].id }
|
|
) ) )
|
|
return err;
|
|
endif
|
|
|
|
if ( evaluate.result != "1" )
|
|
return ret_error( $"Expected value of `shadowed_result` to be '1': '{evaluate.result}' != '1'" );
|
|
endif
|
|
|
|
// Send request: scopes
|
|
if ( !( scopes := request_succeeded( err, "scopes", struct{ frameId := stackTrace.stackFrames[1].id }
|
|
) ) )
|
|
return err;
|
|
endif
|
|
|
|
// Send request: setVariable [global]
|
|
if ( !( setVariable := request_succeeded( err, "setVariable", struct{ name := "global_float",
|
|
value := "1.5",
|
|
variablesReference := scopes.scopes[Len(
|
|
scopes.scopes )].variablesReference
|
|
}
|
|
) ) )
|
|
return err;
|
|
endif
|
|
|
|
// Send request: evaluate
|
|
if ( !( evaluate := request_succeeded( err, "evaluate", struct{ expression := "global_float",
|
|
frameId := stackTrace.stackFrames[Len(
|
|
stackTrace.stackFrames )].id }
|
|
) ) )
|
|
return err;
|
|
endif
|
|
|
|
if ( evaluate.result != "1.5" )
|
|
return ret_error( $"Expected value of `global_float` to be '1.5': '{evaluate.result}' != '1.5'" );
|
|
endif
|
|
|
|
return 1;
|
|
endfunction
|
|
|
|
// Test helper methods
|
|
function fill_buffers( timeout, request_seq := -1, event := "" )
|
|
var ev;
|
|
var found := 0;
|
|
outer: while ( ev := wait_for_event( timeout ) )
|
|
if ( !ev && timeout > 0 )
|
|
return error{ errortext := "Error filling buffer" };
|
|
endif
|
|
recvBuffer += ev.value;
|
|
if ( recvBuffer.length() < CONTENT_LENGTH_HEADER_KEY_LENGTH )
|
|
continue outer;
|
|
elseif ( recvBuffer[1, CONTENT_LENGTH_HEADER_KEY_LENGTH] != CONTENT_LENGTH_HEADER_KEY )
|
|
return error{ errortext := "Invalid header in buffer" };
|
|
else
|
|
var previousBufferLength := recvBuffer.length();
|
|
while ( recvBuffer )
|
|
var newlineIndex := recvBuffer.find( "\n", 3 );
|
|
|
|
if ( newlineIndex == 0 || recvBuffer == "" )
|
|
continue outer;
|
|
endif
|
|
|
|
var content_length := CInt( recvBuffer[CONTENT_LENGTH_HEADER_VALUE_START, newlineIndex] );
|
|
|
|
if ( content_length == 0 )
|
|
continue outer;
|
|
endif
|
|
|
|
var needed_length := newlineIndex + 2 + content_length;
|
|
|
|
if ( recvBuffer.length() >= needed_length )
|
|
var msg := recvBuffer[newlineIndex + 3, content_length];
|
|
|
|
var obj := UnpackJSON( msg );
|
|
|
|
if ( obj.type == "response" )
|
|
print( $"{C_MAGENTA_LIGHT}++ [response#{request_seq}] [{obj.command}]{C_RESET} {PackJSON(
|
|
obj.body )}" );
|
|
|
|
dapResponses[obj.request_seq] := obj;
|
|
if ( request_seq == obj.request_seq )
|
|
found := 1;
|
|
endif
|
|
elseif ( obj.type == "event" )
|
|
dapEvents.append( obj );
|
|
print( $"{C_MAGENTA_LIGHT}++ [event] [{obj.event}]{C_RESET} {PackJSON( obj.body )}" );
|
|
|
|
if ( event != "" && event == obj.event )
|
|
found := 1;
|
|
endif
|
|
endif
|
|
|
|
if ( needed_length == recvBuffer.length() )
|
|
recvBuffer := "";
|
|
else
|
|
recvBuffer := recvBuffer[needed_length + 1, recvBuffer.length()];
|
|
endif
|
|
endif
|
|
if ( previousBufferLength == recvBuffer.length() )
|
|
continue outer;
|
|
endif
|
|
previousBufferLength := recvBuffer.length();
|
|
Sleepms( 100 );
|
|
endwhile
|
|
if ( found )
|
|
return found;
|
|
endif
|
|
endif
|
|
endwhile
|
|
|
|
return found;
|
|
endfunction
|
|
|
|
function fill_buffers_for_request( request_seq )
|
|
var filled := fill_buffers( 0, request_seq := request_seq );
|
|
var found := dapResponses.exists( request_seq );
|
|
if ( found )
|
|
return 1;
|
|
endif
|
|
|
|
filled := fill_buffers( 5, request_seq := request_seq );
|
|
if ( filled == error )
|
|
return filled;
|
|
endif
|
|
|
|
return dapResponses.exists( request_seq );
|
|
endfunction
|
|
|
|
function fill_buffers_for_event( event, timeout )
|
|
var filled := fill_buffers( 0, event := event );
|
|
|
|
foreach ev in dapEvents
|
|
if ( ev.event == event )
|
|
return 1;
|
|
endif
|
|
endforeach
|
|
|
|
filled := fill_buffers( timeout, event := event );
|
|
|
|
foreach ev in dapEvents
|
|
if ( ev.event == event )
|
|
return 1;
|
|
endif
|
|
endforeach
|
|
|
|
if ( filled == error )
|
|
return filled;
|
|
endif
|
|
|
|
return 0;
|
|
endfunction
|
|
|
|
function event_wait( event, timeout := 10 )
|
|
var found := fill_buffers_for_event( event, timeout );
|
|
|
|
if ( found )
|
|
foreach result in dapEvents
|
|
if ( result.event == event )
|
|
dapEvents.erase( _result_iter );
|
|
return result.body;
|
|
endif
|
|
endforeach
|
|
endif
|
|
|
|
return error{ errortext := $"No event for type {event}" };
|
|
endfunction
|
|
|
|
function request( command, arguments )
|
|
var request_seq := nextSequence;
|
|
|
|
var body := struct{ command := command, arguments := arguments,
|
|
type := "request", seq := nextSequence++ };
|
|
|
|
var content := PackJSON( body );
|
|
print( $"{C_GREEN}-> [request#{request_seq}] [{command}]{C_RESET} {PackJSON( arguments )}" );
|
|
|
|
var request_text := "Content-Length: " + ( content.length() ) + CChr( 13 ) + CChr( 10 ) + CChr( 13 )
|
|
+ CChr( 10 ) + content;
|
|
connection.transmit( request_text );
|
|
|
|
var found := fill_buffers_for_request( request_seq );
|
|
|
|
if ( found )
|
|
var result := dapResponses[request_seq];
|
|
dapResponses.erase( request_seq );
|
|
if ( result.success )
|
|
return result.body;
|
|
endif
|
|
print( $"{C_RED}!! [response#{request_seq}]{C_RESET} failed: {result.message}" );
|
|
|
|
return error{ errortext := $"Request {command}#{request_seq} failed: {result.message}" };
|
|
endif
|
|
|
|
print( $"{C_RED}[req seq#{request_seq}]{C_RESET} timed out: {found}" );
|
|
return error{ errortext := $"Request {command}#{request_seq} timed out: {found}" };
|
|
endfunction
|
|
|
|
function request_succeeded( byref err, command, arguments := struct{} )
|
|
err := 0;
|
|
var response := request( command, arguments );
|
|
if ( response )
|
|
print( $"{C_CYAN}<- [response] [{command}]{C_RESET}" );
|
|
return response;
|
|
endif
|
|
err := ret_error( response );
|
|
return 0;
|
|
endfunction
|
|
|
|
function event_received( byref err, event, timeout := 5 )
|
|
err := 0;
|
|
var response := event_wait( event, timeout );
|
|
if ( response )
|
|
print( $"{C_CYAN}<- [event] [{event}]{C_RESET}" );
|
|
return response;
|
|
endif
|
|
err := ret_error( response );
|
|
return 0;
|
|
endfunction
|