use ::cache::disk::DiskCache;
use ::client::{
connect_to_server,
};
use ::commands::{
do_compile,
request_shutdown,
request_stats,
};
use env_logger;
use futures::sync::oneshot::{self, Sender};
use futures_cpupool::CpuPool;
use ::mock_command::*;
use ::server::{
ServerMessage,
SccacheServer,
};
use std::fs::File;
use std::io::{
Cursor,
Write,
};
use std::net::TcpListener;
use std::path::Path;
use std::process::Command;
use std::sync::{Arc,Mutex,mpsc};
use std::thread;
use std::time::Duration;
use std::usize;
use test::utils::*;
use tokio_core::reactor::Core;
#[derive(Default)]
struct ServerOptions {
idle_timeout: Option<u64>,
cache_size: Option<usize>,
}
fn run_server_thread<T>(cache_dir: &Path, options: T)
-> (u16, Sender<ServerMessage>, Arc<Mutex<MockCommandCreator>>, thread::JoinHandle<()>)
where T: Into<Option<ServerOptions>> + Send + 'static
{
let options = options.into();
let cache_dir = cache_dir.to_path_buf();
let cache_size = options.as_ref()
.and_then(|o| o.cache_size.as_ref())
.map(|s| *s)
.unwrap_or(usize::MAX);
let pool = CpuPool::new(1);
let storage = Arc::new(DiskCache::new(&cache_dir, cache_size, &pool));
let (tx, rx) = mpsc::channel();
let (shutdown_tx, shutdown_rx) = oneshot::channel();
let handle = thread::spawn(move || {
let core = Core::new().unwrap();
let srv = SccacheServer::new(0, pool, core, storage).unwrap();
let mut srv: SccacheServer<Arc<Mutex<MockCommandCreator>>> = srv;
assert!(srv.port() > 0);
if let Some(options) = options {
if let Some(timeout) = options.idle_timeout {
srv.set_idle_timeout(Duration::from_millis(timeout));
}
}
let port = srv.port();
let creator = srv.command_creator().clone();
tx.send((port, creator)).unwrap();
srv.run(shutdown_rx).unwrap();
});
let (port, creator) = rx.recv().unwrap();
(port, shutdown_tx, creator, handle)
}
#[test]
fn test_server_shutdown() {
let f = TestFixture::new();
let (port, _sender, _storage, child) = run_server_thread(&f.tempdir.path(), None);
let conn = connect_to_server(port).unwrap();
request_shutdown(conn).unwrap();
child.join().unwrap();
}
#[test]
fn test_server_idle_timeout() {
let f = TestFixture::new();
let (_port, _sender, _storage, child) = run_server_thread(&f.tempdir.path(), ServerOptions { idle_timeout: Some(1), .. Default::default() });
child.join().unwrap();
}
#[test]
fn test_server_stats() {
let f = TestFixture::new();
let (port, sender, _storage, child) = run_server_thread(&f.tempdir.path(), None);
let conn = connect_to_server(port).unwrap();
let info = request_stats(conn).unwrap();
assert_eq!(0, info.stats.compile_requests);
sender.send(ServerMessage::Shutdown).ok().unwrap();
child.join().unwrap();
}
#[test]
fn test_server_unsupported_compiler() {
let f = TestFixture::new();
let (port, sender, server_creator, child) = run_server_thread(&f.tempdir.path(), None);
let conn = connect_to_server(port).unwrap();
{
let mut c = server_creator.lock().unwrap();
c.next_command_spawns(Ok(MockChild::new(exit_status(0), "hello", "error")));
}
let exe = &f.bins[0];
let cmdline = vec!["-c".into(), "file.c".into(), "-o".into(), "file.o".into()];
let cwd = f.tempdir.path();
let client_creator = new_creator();
const COMPILER_STDOUT: &'static [u8] = b"some stdout";
const COMPILER_STDERR: &'static [u8] = b"some stderr";
{
let mut c = client_creator.lock().unwrap();
c.next_command_spawns(Ok(MockChild::new(exit_status(0), COMPILER_STDOUT, COMPILER_STDERR)));
}
let mut stdout = Cursor::new(Vec::new());
let mut stderr = Cursor::new(Vec::new());
let path = Some(f.paths);
let mut core = Core::new().unwrap();
assert_eq!(0, do_compile(client_creator.clone(), &mut core, conn, exe, cmdline, cwd, path, vec![], &mut stdout, &mut stderr).unwrap());
assert_eq!(0, server_creator.lock().unwrap().children.len());
assert_eq!(0, client_creator.lock().unwrap().children.len());
assert_eq!(COMPILER_STDOUT, &stdout.into_inner()[..]);
assert_eq!(COMPILER_STDERR, &stderr.into_inner()[..]);
sender.send(ServerMessage::Shutdown).ok().unwrap();
child.join().unwrap();
}
#[test]
fn test_server_compile() {
match env_logger::init() {
Ok(_) => {},
Err(_) => {},
}
let f = TestFixture::new();
let (port, sender, server_creator, child) = run_server_thread(&f.tempdir.path(), None);
const PREPROCESSOR_STDOUT : &'static [u8] = b"preprocessor stdout";
const PREPROCESSOR_STDERR : &'static [u8] = b"preprocessor stderr";
const STDOUT : &'static [u8] = b"some stdout";
const STDERR : &'static [u8] = b"some stderr";
let conn = connect_to_server(port).unwrap();
{
let mut c = server_creator.lock().unwrap();
c.next_command_spawns(Ok(MockChild::new(exit_status(0), "gcc", "")));
c.next_command_spawns(Ok(MockChild::new(exit_status(0), PREPROCESSOR_STDOUT, PREPROCESSOR_STDERR)));
let obj = f.tempdir.path().join("file.o");
c.next_command_calls(move |_| {
match File::create(&obj)
.and_then(|mut f| f.write_all(b"file contents")) {
Ok(_) => Ok(MockChild::new(exit_status(0), STDOUT, STDERR)),
Err(e) => Err(e),
}
});
}
let exe = &f.bins[0];
let cmdline = vec!["-c".into(), "file.c".into(), "-o".into(), "file.o".into()];
let cwd = f.tempdir.path();
let client_creator = new_creator();
let mut stdout = Cursor::new(Vec::new());
let mut stderr = Cursor::new(Vec::new());
let path = Some(f.paths);
let mut core = Core::new().unwrap();
assert_eq!(0, do_compile(client_creator.clone(), &mut core, conn, exe, cmdline, cwd, path, vec![], &mut stdout, &mut stderr).unwrap());
assert_eq!(0, server_creator.lock().unwrap().children.len());
assert_eq!(STDOUT, stdout.into_inner().as_slice());
assert_eq!(STDERR, stderr.into_inner().as_slice());
sender.send(ServerMessage::Shutdown).ok().unwrap();
child.join().unwrap();
}
#[test]
fn test_server_port_in_use() {
let listener = TcpListener::bind("127.0.0.1:0").unwrap();
let sccache = find_sccache_binary();
let output = Command::new(&sccache)
.arg("--start-server")
.env("SCCACHE_SERVER_PORT", listener.local_addr().unwrap().port().to_string())
.output()
.unwrap();
assert!(!output.status.success());
let s = String::from_utf8_lossy(&output.stderr);
assert!(s.contains("Server startup failed:"),
"Output did not contain 'Failed to start server:':\n========\n{}\n========",
s);
}