Skip to content

Commit

Permalink
WIP shell polling
Browse files Browse the repository at this point in the history
  • Loading branch information
ilyash-b committed Oct 28, 2024
1 parent ba479af commit 4b97566
Show file tree
Hide file tree
Showing 3 changed files with 137 additions and 12 deletions.
2 changes: 1 addition & 1 deletion lib/autoload/globals/net.ngs
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ ns {
}
} catch(e) {
debug('server', "ThreadedServerDelegate before logging")
log("ThreadedServerDelegate - exception in thread: ${e}")
log("ThreadedServerDelegate - exception in thread: ${Str(e) tor '<Unable to convert exception to string>'}")
debug('server', "ThreadedServerDelegate after logging")
guard false
}
Expand Down
50 changes: 48 additions & 2 deletions lib/shell.ngs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,52 @@ ns {
})
}

section "Shell Client" {
type Client()


type ShellClientDelegate(net::ClientDelegate)

global on_connect
F on_connect(scd:ShellClientDelegate, c:net::Connection) {
debug('client', 'ShellClientDelegate#on_connect()')
scd.jrc = IO::handlers::JsonRpcClient()
c.pipeline.push(IO::handlers::Splitter("\n"))
c.pipeline.push(scd.jrc)
c.pipeline.fire(IO::events::Active())
}

global init
F init(c:Client) {
c.last_seen_version = null
c.last_eval_result = null
c.scd = ShellClientDelegate()
c.client = net::UnixStreamClient()
c.client.connect(SOCK_FILE, c.scd)
}

global eval
F eval(c:Client, cmd:Str) {
c.last_eval_result = c.scd.jrc.call('eval', [cmd])::{
debug('client', {"Eval result ${A.SafeStr()}"})
}
}

global poll
F poll(c:Client, cb:Fun) {
while true {
debug('client', "Polling last_seen_version=${c.last_seen_version}")
p = c.scd.jrc.call('poll', {
'last_seen_version': c.last_seen_version
})
debug('client', "Poll result - timeline (ver. ${p.version})")
# echo(p.timeline.repr().Lines())
c.last_seen_version = p.version
not(cb(p)) returns p
}
}
}

doc Rudimentary client as phase 1.
doc Send a command and poll for results.
F eval(cmd:Str, exit:Bool=true) {
Expand Down Expand Up @@ -56,7 +102,7 @@ ns {
log("Poll result - timeline (ver. ${p.version})")
echo(p.timeline.repr().Lines())
last_seen_version = p.version
exit breaks
exit returns p
}
}

Expand Down Expand Up @@ -164,7 +210,7 @@ ns {

# TODO: handle broken pipe
F poll(last_seen_version) {
log("poll(last_seen_version=${last_seen_version}) - work in progress - returning the whole Timeline")
debug('client', "poll(last_seen_version=${last_seen_version}) - work in progress - returning the whole Timeline")
section "Wait for later version before retuning the result" block b {
not(last_seen_version) returns
while true {
Expand Down
97 changes: 88 additions & 9 deletions ngsfile
Original file line number Diff line number Diff line change
@@ -1,14 +1,93 @@
#!/usr/bin/env ngs
ns {
F server() {
require("./lib/shell.ngs")::server(false)
}
ns(t=test) {

F ui() {
require("./lib/shell.ngs")::server(true)
}
sh = require("./lib/shell.ngs")

F server() sh::server(false)
F ui() sh::server(true)

# "eval" would conflict with the global eval(c:Client, ...)
# which is called from tests below.
F _eval(cmd:Str, exit:Bool=true) sh::eval(cmd, exit)
_exports.eval = _eval

F test() {
# Tests where the server and the client run in the same process
# fail sporadically. Therefore, running the server in external process.
# srv = Thread("test-server", {
# log("Starting server")
# server()
# })
srv = $(ngs . server &)
$(sleep 1)
c = sh::Client()

F make_textual_command_timeline_result_pattern(ti:Str, cmd:Str, pat) {
{
'timeline': {
'children': Present({
'$type': 'GroupTimelineItem'
'children': [{
'$type': 'TextualCommandTimelineItem'
'id': ti
'command': cmd
}, {
'$type': 'ResultTimelineItem'
'children': [
pat
]
}]
})
}
}
}

test_ti = null
t("basic eval", {
test_ti = c.eval("{1+1}").assert({
'ti': Pfx('ti-')
}).ti
})

t("poll after basic eval", {
c.poll(F(response) {
# The response was observed to be already there. I don't think it's guaranteed. May need refactoring.
assert(response, make_textual_command_timeline_result_pattern(test_ti, '{1+1}', { '$type': 'Scalar', 'value': 2 }))
false
})
})

t("eval with sleep", {
# Valid JSON for decode_json()
test_ti = c.eval("sleep 2 | echo 3").assert({
'ti': Pfx('ti-')
}).ti
})

t("poll immediately after eval with sleep", {
c.poll(F(response) {
assert(response, {
'timeline': {
'children': Present({
'$type': 'GroupTimelineItem'
'children': [{
'$type': 'TextualCommandTimelineItem'
'id': test_ti
'command': 'sleep 2 | echo 3'
}]
})
}
})
false
})
})

F eval(cmd:Str, exit:Bool=true) {
require("./lib/shell.ngs")::eval(cmd, exit)
t("poll later after eval with sleep", {
$(sleep 2)
c.poll(F(response) {
assert(response, make_textual_command_timeline_result_pattern(test_ti, 'sleep 2 | echo 3', { '$type': 'Scalar', 'value': 3 }))
false
})
})
}
}

0 comments on commit 4b97566

Please sign in to comment.