Teardown

In the Rust test framework, there's no setup() and teardown() functions like there are in the test frameworks of many other languages. And here, we need to run some code when the test is done: we need to kill our FTP server. So, we need some kind of teardown function. We cannot simply say child.kill() at the end of the function because, if the test panics before that, the FTP server will continue running after the test ends. To make sure the cleanup code is always called, no matter how the function ended, we'll have to use the RAII pattern that we discovered in Chapter 6, Implementing the Engine of the Music Player.

Let's write a simple teardown structure:

struct ProcessController {
    child: Child,
}

The structure contains the child process that will be killed in the destructor. So, if the test panics, this destructor will be called. It will also be called if the function ends normally.

We'll also create a constructor and the utility method that we used in the test function:

impl ProcessController {
    fn new(child: Child) -> Self {
        ProcessController {
            child,
        }
    }

    fn is_running(&mut self) -> bool {
        let status = self.child.try_wait().unwrap();
        status.is_none()
    }
}

The function is_running() is used to ensure that the FTP server we launched is actually running; if another instance of the application is already running, our instance will not run. That's why we used an assert in the test function.

Finally, we need to create a destructor:

impl Drop for ProcessController {
    fn drop(&mut self) {
        let _ = self.child.kill();
    }
}

We're now ready to write the test function:

#[test]
fn test_pwd() {
    // …

    let mut ftp = FtpStream::connect("127.0.0.1:1234").unwrap();

    let pwd = ftp.pwd().unwrap();
    assert_eq!("/", pwd);

    ftp.login("ferris", "").unwrap();

    ftp.cwd("src").unwrap();
    let pwd = ftp.pwd().unwrap();
    assert_eq!("/src", pwd);

    let _ = ftp.cdup();
    let pwd = ftp.pwd().unwrap();
    assert_eq!("/", pwd);

    ftp.quit().unwrap();
}

In this function, we issue some FTP commands and make sure the server state is correct by calling the assert_eq!() macro. When we run cargo test, we see the following output:

    Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs
     Running target/debug/deps/ftp_server-47386d9089111729

running 2 tests
test codec::tests::test_decoder ... ok
test codec::tests::test_encoder ... ok

test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

     Running target/debug/deps/server-1b5cda64792f5f82

running 1 test
Waiting clients on port 1234...
New client: [address : 127.0.0.1:43280]
Waiting another client...
Received command: Pwd
Received command: User("ferris")
Received command: Cwd("src")
Received command: Pwd
Received command: CdUp
Received command: Pwd
Received command: Quit
test test_pwd ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

A new section is added for our integration test.