metadata-backup 0.1.0

Program to back up file system metadata.
Documentation
// Copyright 2019 metadata-backup Authors (see AUTHORS.md)

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

//     http://www.apache.org/licenses/LICENSE-2.0

// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

extern crate csv;
extern crate proptest;

use metadata_backup::metadata::Metadata;

use std::path::PathBuf;

fn get_base_path() -> PathBuf {
    let mut out = PathBuf::from(env!["CARGO_MANIFEST_DIR"]);
    out.push("tests");
    out.push("tree");
    out
}

fn get_resource(path_components: &Vec<&str>) -> PathBuf {
    let mut out = get_base_path();

    for component in path_components.iter() {
        out.push(component);
    }
    out
}

fn get_metadata(path_components: &Vec<&str>) -> Result<Metadata, std::io::Error> {
    let pathbuf = get_resource(path_components);
    let path = pathbuf.as_path();
    Metadata::new(&path)
}

#[test]
fn test_size() {
    let test_cases = [
        (vec!["f1.txt"], 0),
        (vec!["f2.txt"], 256),
        (vec!["a", "a1.txt"], 117),
    ];

    for (path_components, expected_size) in test_cases.iter() {
        let meta = get_metadata(path_components).unwrap();
        assert_eq!(meta.size, *expected_size);
    }
}

#[test]
fn test_name() {
    let test_cases = [
        (vec!["f1.txt"], "f1.txt"),
        (vec!["f2.txt"], "f2.txt"),
        (vec!["a1.txt"], "a1.txt"),
        (vec!["a", "a1.txt"], "a1.txt"),
        (vec!["b", "b1.txt"], "b1.txt"),
    ];

    for (path_components, expected_name) in test_cases.iter() {
        let meta = get_metadata(path_components).unwrap();
        assert_eq!(meta.name, expected_name.to_string());
    }
}

#[test]
fn test_is_dir() {
    let test_cases = [
        (vec!["a"], true),
        (vec!["b"], true),
        (vec!["f1.txt"], false),
        (vec!["f2.txt"], false),
        (vec!["a1.txt"], false),
        (vec!["a", "a1.txt"], false),
        (vec!["b", "b1.txt"], false),
    ];

    for (path_components, expected) in test_cases.iter() {
        let meta = get_metadata(path_components).unwrap();
        assert_eq!(meta.is_dir, *expected);
    }
}

mod prop_tests {
    extern crate strmode;

    use metadata_backup::metadata::Metadata;
    use proptest::prelude::*;
    use std::time::{Duration, SystemTime, UNIX_EPOCH};

    prop_compose! {
        fn system_time_truncated()
            (seconds in 0..(2u64^64 - 1)) ->
                SystemTime {
                    UNIX_EPOCH + Duration::from_secs(seconds)
                }
    }

    prop_compose! {
        fn arb_metadata()
            (name in ".*",
             size in any::<u64>(),
             is_dir in any::<bool>(),
             atime in prop::option::of(system_time_truncated()),
             mtime in prop::option::of(system_time_truncated()),
             ctime in prop::option::of(system_time_truncated()),
             st_mode in prop::option::of(any::<u32>()),
             uid in prop::option::of(any::<u32>()),
             gid in prop::option::of(any::<u32>()),
             link in prop::option::of("[^\0]+"),
             ) ->
                Metadata {
                Metadata {
                    name: name,
                    size: size,
                    is_dir: is_dir,
                    atime: atime,
                    mtime: mtime,
                    ctime: ctime,
                    st_mode: st_mode,
                    st_mode_string: st_mode.map(strmode::strmode),
                    uid: uid,
                    gid: gid,
                    link: link,
                }
                }
    }

    proptest! {
        #[test]
        fn serialize_deserialize_records(metadata_records in
                                         prop::collection::vec(arb_metadata(), 1..100)) {
            let mut wtr = csv::Writer::from_writer(vec![]);

            for record in &metadata_records {
                wtr.serialize(record).unwrap();
            };

            let data = String::from_utf8(wtr.into_inner().unwrap()).unwrap();

            let mut reader = csv::Reader::from_reader(data.as_bytes());
            let mut deserialized_vec : Vec<Metadata> =
                Vec::with_capacity(metadata_records.len());
            for result in reader.deserialize::<Metadata>() {
                let record = result.unwrap();
                deserialized_vec.push(record);
            }

            let deserialized_vec = deserialized_vec;

            assert_eq!(metadata_records.len(), deserialized_vec.len());
            for (record_in, record_out) in metadata_records.iter().zip(deserialized_vec) {
                assert_eq!(*record_in, record_out);
            }
        }
    }
}