summaryrefslogtreecommitdiffhomepage
path: root/test/test-manager/src/package.rs
blob: f3fd5356738639c873d857f130aaf7543ba7efa9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
use crate::config::{Architecture, OsType, PackageType, VmConfig};
use anyhow::{Context, Result};
use once_cell::sync::Lazy;
use regex::Regex;
use std::path::{Path, PathBuf};

static VERSION_REGEX: Lazy<Regex> =
    Lazy::new(|| Regex::new(r"\d{4}\.\d+(-beta\d+)?(-dev)?-([0-9a-z])+").unwrap());

#[derive(Debug, Clone)]
pub struct Manifest {
    pub app_package_path: PathBuf,
    pub app_package_to_upgrade_from_path: Option<PathBuf>,
    pub ui_e2e_tests_path: Option<PathBuf>,
}

/// Obtain app packages and their filenames
/// If it's a path, use the path.
/// If it corresponds to a file in packages/, use that package.
/// TODO: If it's a git tag or rev, download it.
pub fn get_app_manifest(
    config: &VmConfig,
    app_package: String,
    app_package_to_upgrade_from: Option<String>,
    package_folder: Option<PathBuf>,
) -> Result<Manifest> {
    let package_type = (config.os_type, config.package_type, config.architecture);

    let app_package_path = find_app(&app_package, false, package_type, package_folder.as_ref())?;
    log::info!("App package: {}", app_package_path.display());

    let app_package_to_upgrade_from_path = app_package_to_upgrade_from
        .map(|app| find_app(&app, false, package_type, package_folder.as_ref()))
        .transpose()?;
    log::info!("App package to upgrade from: {app_package_to_upgrade_from_path:?}");

    let capture = VERSION_REGEX
        .captures(app_package_path.to_str().unwrap())
        .with_context(|| format!("Cannot parse version: {}", app_package_path.display()))?
        .get(0)
        .map(|c| c.as_str())
        .expect("Could not parse version from package name: {app_package}");

    let ui_e2e_tests_path = find_app(capture, true, package_type, package_folder.as_ref()).ok();
    log::info!("GUI e2e test binary: {ui_e2e_tests_path:?}");

    Ok(Manifest {
        app_package_path,
        app_package_to_upgrade_from_path,
        ui_e2e_tests_path,
    })
}

fn find_app(
    app: &str,
    e2e_bin: bool,
    package_type: (OsType, Option<PackageType>, Option<Architecture>),
    package_folder: Option<&PathBuf>,
) -> Result<PathBuf> {
    // If it's a path, use that path
    let app_path = Path::new(app);
    if app_path.is_file() {
        // TODO: Copy to packages?
        return Ok(app_path.to_path_buf());
    }

    let mut app = app.to_owned();
    app.make_ascii_lowercase();

    let current_dir = std::env::current_dir().expect("Unable to get current directory");
    let packages_dir = package_folder.unwrap_or(&current_dir);
    std::fs::create_dir_all(packages_dir)?;
    let dir = std::fs::read_dir(packages_dir.clone()).context("Failed to list packages")?;

    dir
        .filter_map(|entry| entry.ok())
        .map(|entry| entry.path())
        .filter(|entry| entry.is_file())
        .filter(|path| {
            e2e_bin ||
            path
                .extension()
                .map(|m_ext| m_ext.eq_ignore_ascii_case(get_ext(package_type)))
                .unwrap_or(false)
        }) // Filter out irrelevant platforms
        .map(|path| {
            let u8_path = path.as_os_str().to_string_lossy().to_ascii_lowercase();
            (path, u8_path)
        })
        .filter(|(_path, u8_path)| !(e2e_bin ^ u8_path.contains("app-e2e-tests"))) // Skip non-UI-e2e binaries or vice versa
        .filter(|(_path, u8_path)| !e2e_bin || u8_path.contains(get_os_name(package_type))) // Filter out irrelevant platforms
        .filter(|(_path, u8_path)| {
            let linux = e2e_bin || package_type.0 == OsType::Linux;
            let matching_ident = package_type.2.map(|arch| arch.get_identifiers().iter().any(|id| u8_path.contains(id))).unwrap_or(true);
            // Skip for non-Linux, because there's only one package
            !linux || matching_ident
        }) // Skip file if it doesn't match the architecture
        .find(|(_path, u8_path)| u8_path.contains(&app)) //  Find match
        .map(|(path, _)| path).context(if e2e_bin {
            format!(
                "Could not find UI/e2e test for package: {app}.\n\
                Expecting a binary named like `app-e2e-tests-{app}_ARCH` to exist in {package_dir}/\n\
                Example ARCH: `amd64-unknown-linux-gnu`, `x86_64-unknown-linux-gnu`",
                package_dir = packages_dir.display()
            )
        } else {
            format!("Could not find package for app: {app}")
        })
}

fn get_ext(package_type: (OsType, Option<PackageType>, Option<Architecture>)) -> &'static str {
    match package_type.0 {
        OsType::Windows => "exe",
        OsType::Macos => "pkg",
        OsType::Linux => match package_type.1.expect("must specify package type") {
            PackageType::Deb => "deb",
            PackageType::Rpm => "rpm",
        },
    }
}

fn get_os_name(package_type: (OsType, Option<PackageType>, Option<Architecture>)) -> &'static str {
    match package_type.0 {
        OsType::Windows => "windows",
        OsType::Macos => "apple",
        OsType::Linux => "linux",
    }
}