about summary refs log tree commit diff stats
path: root/092socket.mu
diff options
context:
space:
mode:
Diffstat (limited to '092socket.mu')
-rw-r--r--092socket.mu230
1 files changed, 105 insertions, 125 deletions
diff --git a/092socket.mu b/092socket.mu
index 1fd2586b..6a5c427a 100644
--- a/092socket.mu
+++ b/092socket.mu
@@ -1,87 +1,69 @@
-# Wrappers around socket primitives that are easier to test.
+# Wrappers around socket primitives that take a 'local-network' object and are
+# thus easier to test.
 #
-# To test client operations, use `assume-resources` with a filename that
-# begins with a hostname. (Filenames starting with '/' are assumed to be
-# local.)
+# The current semantics of fake port-connections don't match UNIX socket ones,
+# but we'll improve them as we learn more.
+
+container local-network [
+  data:&:@:port-connection
+]
+
+# Port connections represent connections to ports on localhost.
+# Before passing a local-network object to network functions
+# `start-reading-socket` and `start-writing-socket`, add port-connections to
+# the local-network.
+#
+# For reading, `receive-from-socket` will check for a
+# port-connection on the port parameter that's been passed in. If there's
+# no port-connection for that port, it will return nothing and log an error.
+# If there is a port-connection for that port, it will transmit the contents
+# to the provided sink.
 #
-# To test server operations, just run a real client against localhost.
+# For writing, `start-writing-socket` returns a sink connecting the
+# caller to the socket on the passed-in port.
+container port-connection [
+  port:num
+  contents:text
+]
 
-scenario example-server-test [
+def new-port-connection port:num, contents:text -> p:&:port-connection [
   local-scope
-  # test server without a fake on a random (real) port
-  # that way repeatedly running the test will give ports time to timeout and
-  # close before reusing them
-  make-random-nondeterministic
-  port:num <- random-in-range 0/real-random-numbers, 8000, 8100
-  run [
-    socket:num <- $open-server-socket port
-    $print [server socket: ], socket, 10/newline
-    assert socket, [ 
-F - example-server-test: $open-server-socket failed]
-    $print [starting up server routine], 10/newline
-    handler-routine:number <- start-running serve-one-request socket, example-handler
-  ]
-  $print [starting to read from port ], port, 10/newline
-  source:&:source:char <- start-reading-from-network 0/real-resources, [localhost], [/], port
-  response:text <- drain source
-  10:@:char/raw <- copy *response
-  memory-should-contain [
-    10:array:character <- [abc]
-  ]
+  load-ingredients
+  p:&:port-connection <- new port-connection:type
+  *p <- merge port, contents
 ]
-# helper just for this scenario
-def example-handler query:text -> response:text [
+
+def new-fake-network -> n:&:local-network [
   local-scope
   load-ingredients
-  reply [abc]
+  n:&:local-network <- new local-network:type
+  local-network-ports:&:@:port-connection <- new port-connection:type, 0
+  *n <- put *n, data:offset, local-network-ports
 ]
 
-#? scenario example-client-test [
-#?   local-scope
-#?   assume-resources [
-#?     [example.com/] -> [abc]
-#?   ]
-#?   run [
-#?     source:&:source:char <- start-reading-from-network resources, [example.com], [/]
-#?   ]
-#?   contents:text <- drain source
-#?   10:@:char/raw <- copy *contents
-#?   memory-should-contain [
-#?     10:address:character <- [abc]
-#?   ]
-#? ]
-
-type request-handler = (recipe text -> text)
-
-def serve-one-request socket:num, request-handler:request-handler [
+scenario write-to-fake-socket [
   local-scope
-  load-ingredients
-  session:num <- $accept socket
-  $print [server session socket: ], session, 10/newline
-  assert session, [ 
-F - example-server-test: $accept failed]
-  contents:&:source:char, sink:&:sink:char <- new-channel 30
-  sink <- start-running receive-from-socket session, sink
-  query:text <- drain contents
-  response:text <- call request-handler, query
-  write-to-socket session, response
-  $close-socket session
+  single-port-network:&:local-network <- new-fake-network
+  sink:&:sink:char, writer:num/routine <- start-writing-socket single-port-network, 8080
+  sink <- write sink, 120/x
+  close sink
+  wait-for-routine writer
+  tested-port-connections:&:@:port-connection <- get *single-port-network, data:offset
+  tested-port-connection:port-connection <- index *tested-port-connections, 0
+  contents:text <- get tested-port-connection, contents:offset
+  10:@:char/raw <- copy *contents
+  memory-should-contain [
+    10:array:character <- [x]
+  ]
 ]
 
 def start-reading-from-network resources:&:resources, host:text, path:text -> contents:&:source:char [
   local-scope
   load-ingredients
-  $print [running start-reading-from-network], 10/newline
-  {
-    port:num, port-found?:boolean <- next-ingredient
-    break-if port-found?
-    port <- copy 80/http-port
-  }
   {
     break-if resources
     # real network
-    socket:num <- $open-client-socket host, port
-    $print [client socket: ], socket, 10/newline
+    socket:num <- $open-client-socket host, 80/http-port
     assert socket, [contents]
     req:text <- interpolate [GET _ HTTP/1.1], path
     request-socket socket, req
@@ -120,72 +102,70 @@ def request-socket socket:num, s:text -> socket:num [
   $write-to-socket socket, 10/lf
 ]
 
-#? def start-writing-socket network:&:local-network, port:num -> sink:&:sink:char, routine-id:num [
-#?   local-scope
-#?   load-ingredients
-#?   source:&:source:char, sink:&:sink:char <- new-channel 30
-#?   {
-#?     break-if network
-#?     socket:num <- $open-server-socket port
-#?     session:num <- $accept socket
-#?     # TODO Create channel implementation of write-to-socket.
-#?     return sink, 0/routine-id
-#?   }
-#?   # fake network
-#?   routine-id <- start-running transmit-to-fake-socket network, port, source
-#? ]
+def start-writing-socket network:&:local-network, port:num -> sink:&:sink:char, routine-id:num [
+  local-scope
+  load-ingredients
+  source:&:source:char, sink:&:sink:char <- new-channel 30
+  {
+    break-if network
+    socket:num <- $open-server-socket port
+    session:num <- $accept socket
+    # TODO Create channel implementation of write-to-socket.
+    return sink, 0/routine-id
+  }
+  # fake network
+  routine-id <- start-running transmit-to-fake-socket network, port, source
+]
 
-#? def transmit-to-fake-socket network:&:local-network, port:num, source:&:source:char -> network:&:local-network, source:&:source:char [
-#?   local-scope
-#?   load-ingredients
-#?   # compute new port connection contents
-#?   buf:&:buffer <- new-buffer 30
-#?   {
-#?     c:char, done?:bool, source <- read source
-#?     break-unless c
-#?     buf <- append buf, c
-#?     break-if done?
-#?     loop
-#?   }
-#?   contents:text <- buffer-to-array buf
-#?   new-port-connection:&:port-connection <- new-port-connection port, contents
-#?   # Got the contents of the channel, time to write to fake port.
-#?   i:num <- copy 0
-#?   port-connections:&:@:port-connection <- get *network, data:offset
-#?   len:num <- length *port-connections
-#?   {
-#?     done?:bool <- greater-or-equal i, len
-#?     break-if done?
-#?     current:port-connection <- index *port-connections, i
-#?     current-port:num <- get current, port:offset
-#?     ports-match?:bool <- equal current-port, port
-#?     i <- add i, 1
-#?     loop-unless ports-match?
-#?     # Found an existing connection on this port, overwrite.
-#?     put-index *port-connections, i, *new-port-connection
-#?     reply
-#?   }
-#?   # Couldn't find an existing connection on this port, initialize a new one.
-#?   new-len:num <- add len, 1
-#?   new-port-connections:&:@:port-connection <- new port-connection:type, new-len
-#?   put *network, data:offset, new-port-connections
-#?   i:num <- copy 0
-#?   {
-#?     done?:bool <- greater-or-equal i, len
-#?     break-if done?
-#?     tmp:port-connection <- index *port-connections, i
-#?     put-index *new-port-connections, i, tmp
-#?   }
-#?   put-index *new-port-connections, len, *new-port-connection
-#? ]
+def transmit-to-fake-socket network:&:local-network, port:num, source:&:source:char -> network:&:local-network, source:&:source:char [
+  local-scope
+  load-ingredients
+  # compute new port connection contents
+  buf:&:buffer <- new-buffer 30
+  {
+    c:char, done?:bool, source <- read source
+    break-unless c
+    buf <- append buf, c
+    break-if done?
+    loop
+  }
+  contents:text <- buffer-to-array buf
+  new-port-connection:&:port-connection <- new-port-connection port, contents
+  # Got the contents of the channel, time to write to fake port.
+  i:num <- copy 0
+  port-connections:&:@:port-connection <- get *network, data:offset
+  len:num <- length *port-connections
+  {
+    done?:bool <- greater-or-equal i, len
+    break-if done?
+    current:port-connection <- index *port-connections, i
+    current-port:num <- get current, port:offset
+    ports-match?:bool <- equal current-port, port
+    i <- add i, 1
+    loop-unless ports-match?
+    # Found an existing connection on this port, overwrite.
+    put-index *port-connections, i, *new-port-connection
+    reply
+  }
+  # Couldn't find an existing connection on this port, initialize a new one.
+  new-len:num <- add len, 1
+  new-port-connections:&:@:port-connection <- new port-connection:type, new-len
+  put *network, data:offset, new-port-connections
+  i:num <- copy 0
+  {
+    done?:bool <- greater-or-equal i, len
+    break-if done?
+    tmp:port-connection <- index *port-connections, i
+    put-index *new-port-connections, i, tmp
+  }
+  put-index *new-port-connections, len, *new-port-connection
+]
 
 def receive-from-socket socket:num, sink:&:sink:char -> sink:&:sink:char [
   local-scope
   load-ingredients
   {
-    $print [read-from-socket ], socket, 10/newline
     req:text, eof?:bool <- $read-from-socket socket, 4096/bytes
-    loop-unless req
     bytes-read:num <- length *req
     i:num <- copy 0
     {
ass="n">features, "fourth:feature", g_strcmp0)); assert_true(g_list_find_custom(features, "fifth:feature", g_strcmp0)); g_list_free(features); disco_close(); } void does_not_add_duplicate_feature(void **state) { disco_close(); disco_add_feature("my_plugin", "my:feature"); disco_add_feature("some_other_plugin", "my:feature"); GList *features = disco_get_features(); assert_int_equal(g_list_length(features), 1); g_list_free(features); disco_close(); } void removes_plugin_features(void **state) { disco_close(); disco_add_feature("plugin1", "plugin1:feature1"); disco_add_feature("plugin1", "plugin1:feature2"); disco_add_feature("plugin2", "plugin2:feature1"); GList *features = disco_get_features(); assert_int_equal(g_list_length(features), 3); g_list_free(features); disco_remove_features("plugin1"); features = disco_get_features(); assert_int_equal(g_list_length(features), 1); g_list_free(features); disco_close(); } void does_not_remove_feature_when_more_than_one_reference(void **state) { disco_close(); disco_add_feature("plugin1", "feature1"); disco_add_feature("plugin1", "feature2"); disco_add_feature("plugin2", "feature1"); GList *features = disco_get_features(); assert_int_equal(g_list_length(features), 2); g_list_free(features); disco_remove_features("plugin1"); features = disco_get_features(); assert_int_equal(g_list_length(features), 1); g_list_free(features); disco_close(); }