Skip to content
This repository has been archived by the owner on Oct 15, 2022. It is now read-only.

Commit

Permalink
tests/daemon: Add test sending a message over socket
Browse files Browse the repository at this point in the history
  • Loading branch information
Profpatsch committed May 15, 2019
1 parent 8d06cda commit 42aa124
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 17 deletions.
41 changes: 28 additions & 13 deletions src/ops/daemon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,40 +13,50 @@ use std::path::PathBuf;
use std::sync::mpsc;
use std::thread;

const SOCKET_FILE_NAME: &str = "/tmp/lorri-socket";

// TODO: make private again
/// Instructs the daemon to start a build
struct StartBuild {
nix_file: PathBuf,
pub struct StartBuild {
pub nix_file: PathBuf,
}

/// See the documentation for lorri::cli::Command::Shell for more
/// details.
pub fn main() -> OpResult {
let listener = listener::Listener::new(Path::new("/tmp/lorri-socket"))
let socket_path = Path::new(SOCKET_FILE_NAME);
// TODO: move listener into Daemon struct?
let listener = listener::Listener::new(socket_path)
// TODO
.unwrap();
// TODO: set up socket path, make it settable by the user
let (mut daemon, build_messages_rx) = Daemon::new();

// messages sent from accept handlers
let (accept_messages_tx, accept_messages_rx) = mpsc::channel();

// TODO join handle
let _accept_loop_handle = thread::spawn(move || loop {
let tx = accept_messages_tx.clone();
let accept_messages_tx = accept_messages_tx.clone();
let _handle = listener
.accept(|unix_stream, comm_type| match comm_type {
CommunicationType::Ping => ping(ReadWriter::new(unix_stream), tx),
CommunicationType::Ping => ping(ReadWriter::new(unix_stream), accept_messages_tx),
})
// TODO
.unwrap();
});

// TODO: join handle
let _start_build_loop_handle = thread::spawn(|| {
for msg in build_messages_rx {
println!("{:#?}", msg);
}
});

// For each build instruction, add the corresponding file
// to the watch list.
for start_build in accept_messages_rx {
daemon.start_build(start_build.nix_file)
daemon.add(start_build.nix_file)
}

ok()
Expand All @@ -55,10 +65,11 @@ pub fn main() -> OpResult {
// handle.join().unwrap();
}

struct Daemon {
// TODO: move from ops to internals
pub struct Daemon {
// TODO: PathBuf is a nix file
pub handlers: HashMap<PathBuf, std::thread::JoinHandle<()>>,
pub build_events_tx: mpsc::Sender<build_loop::Event>,
handlers: HashMap<PathBuf, std::thread::JoinHandle<()>>,
build_events_tx: mpsc::Sender<build_loop::Event>,
}

impl Daemon {
Expand All @@ -76,8 +87,9 @@ impl Daemon {
)
}

/// Start a build in a new thread
pub fn start_build(&mut self, nix_file: PathBuf) {
/// Add nix file to the set of files this daemon watches
/// & builds if they change.
pub fn add(&mut self, nix_file: PathBuf) {
let tx = self.build_events_tx.clone();

self.handlers.entry(nix_file.clone()).or_insert_with(|| {
Expand All @@ -97,9 +109,12 @@ impl Daemon {
}
}

/// Handle the ping
/// Accept handler for `socket::communicate::Ping` messages.
/// For a valid ping message, it sends an instruction to start
/// the build to `build_chan`.
// TODO: make private again
// the ReadWriter here has to be the inverse of the `Client.ping()`, which is `ReadWriter<!, Ping>`
fn ping(rw: ReadWriter<Ping, NoMessage>, build_chan: mpsc::Sender<StartBuild>) {
pub fn ping(rw: ReadWriter<Ping, NoMessage>, build_chan: mpsc::Sender<StartBuild>) {
// TODO: read timeout
let ping: Result<Ping, ReadError> = rw.read(None);
match ping {
Expand Down
4 changes: 4 additions & 0 deletions src/socket/communicate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ pub mod listener {
/// Accept a new connection on the socket,
/// read the communication type and then delegate to the
/// corresponding handling subroutine.
///
/// The handler is start in a thread, the thread handle is returned.
///
/// This method blocks until a client tries to connect.
pub fn accept<F: 'static>(
&self,
handler: F,
Expand Down
81 changes: 81 additions & 0 deletions tests/daemon/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
extern crate lorri;
extern crate tempfile;

use lorri::build_loop;
use lorri::ops::daemon::Daemon;
use lorri::socket::communicate::{client, listener};
use lorri::socket::communicate::{CommunicationType, Ping};
use lorri::socket::ReadWriter;
use std::io::{Error, ErrorKind};
use std::path::PathBuf;
use std::sync::mpsc;
use std::thread;
use std::time::Duration;

/// This tests the basic working of the client/daemon setup.
///
/// The daemon starts listening, the client sends a message
/// to request watching a nix file, the daemon starts a `build_loop`
/// and the test is successful once the `build_loop` signals
/// that the build is starting up (`Event::Started`).
#[test]
pub fn start_job_with_ping() -> std::io::Result<()> {
// TODO: this code is a mess because Daeomon is not
// nicely abstracted yet.

// messages returned by the `daemon.accept()` handler
let (accept_messages_tx, accept_messages_rx) = mpsc::channel();

let tempdir = tempfile::tempdir()?;
let socket_file = tempdir.path().join("socket");

// create unix socket file
let listener = listener::Listener::new(&socket_file).unwrap();

// listen for incoming messages
// TODO: put this listener stuff into the Daemon
let accept_handle = thread::spawn(move || {
listener
.accept(|unix_stream, comm_type| match comm_type {
CommunicationType::Ping => {
lorri::ops::daemon::ping(ReadWriter::new(unix_stream), accept_messages_tx)
}
})
.unwrap()
});

// The daemon knows how to build stuff
let (mut daemon, build_events_rx) = Daemon::new();

// connect to socket and send a ping message
client::ping(None)
.connect(&socket_file)
.unwrap()
.write(&Ping {
nix_file: PathBuf::from("/who/cares"),
})
.unwrap();

// The client pinged, so now a message should have arrived
let daemon_subroutine_handle = accept_handle.join().unwrap();
let start_build = accept_messages_rx
.recv_timeout(Duration::from_millis(100))
.unwrap();
daemon.add(start_build.nix_file);

// Read the first build event, which should be a `Started` message
match build_events_rx
.recv_timeout(Duration::from_millis(100))
.unwrap()
{
build_loop::Event::Started => Ok(()),
ev => Err(Error::new(
ErrorKind::Other,
format!("didn’t expect event {:?}", ev),
)),
}?;

drop(tempdir);
daemon_subroutine_handle.join().unwrap();
Ok(())
}
1 change: 1 addition & 0 deletions tests/daemon/trivial.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
with import ../../nix/bogus-nixpkgs {}; mkShell {}
6 changes: 2 additions & 4 deletions tests/direnv/direnvtestcase.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use lorri::{
use std::collections::HashMap;
use std::fs::File;
use std::io::Write;
use std::iter::FromIterator;
use std::path::PathBuf;
use std::process::Command;
use tempfile::{tempdir, TempDir};
Expand All @@ -24,10 +25,7 @@ impl DirenvTestCase {
pub fn new(name: &str) -> DirenvTestCase {
let tempdir = tempdir().expect("tempfile::tempdir() failed us!");

let test_root = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("tests")
.join("direnv")
.join(name);
let test_root = PathBuf::from_iter(&[env!("CARGO_MANIFEST_DIR"), "tests", "direnv", name]);

let project =
Project::load(test_root.join("shell.nix"), tempdir.path().to_path_buf()).unwrap();
Expand Down

0 comments on commit 42aa124

Please sign in to comment.