view rust/hg-core/src/utils/debug.rs @ 51616:b08c5fbe0e70 stable

rust: blanket implementation of Graph for Graph references The need comes from the fact that `AncestorsIterator` and many Graph-related algorithms take ownership of the `Graph` they work with. This, in turn is due to them needing to accept the `Index` instances that are provided by the Python layers (that neither rhg nor `RHGitaly` use, of course): the fact that nowadays the Python layer holds an object that is itself implemented in Rust does not change the core problem that they cannot be tracked by the borrow checker. Even though it looks like cloning `Changelog` would be cheap, it seems hard to guarantee that on the long run. The object is already too rich for us to be comfortable with it, when using references is the most natural and guaranteed way of proceeding. The added test seems a bit superfleous, but it will act as a reminder that this feature is really useful until something in the Mercurial code base actually uses it.
author Georges Racinet <georges.racinet@octobus.net>
date Mon, 22 Apr 2024 19:47:08 +0200
parents a6b8b1ab9116
children
line wrap: on
line source

//! Utils for debugging hg-core

use crate::config::Config;

/// Write the file path given by the config option `devel.<config_option>` with
/// the suffix `.waiting`, then wait for the file path given by the
/// config option `devel.<config_option>` to appear on disk
/// up to `devel.<config_option>-timeout` seconds.
/// Note that the timeout may be higher because we scale it if global
/// `run-tests` timeouts are raised to prevent flakiness on slower hardware.
///
/// Useful for testing race conditions.
pub fn debug_wait_for_file(
    config: &Config,
    config_option: &str,
) -> Result<(), String> {
    let path_opt = format!("sync.{config_option}");
    let file_path = match config.get_str(b"devel", path_opt.as_bytes()).ok() {
        Some(Some(file_path)) => file_path,
        _ => return Ok(()),
    };

    // TODO make it so `configitems` is shared between Rust and Python so that
    // defaults work out of the box, etc.
    let default_timeout = 2;
    let timeout_opt = format!("sync.{config_option}-timeout");
    let timeout_seconds =
        match config.get_u32(b"devel", timeout_opt.as_bytes()) {
            Ok(Some(timeout)) => timeout,
            Err(e) => {
                log::debug!("{e}");
                default_timeout
            }
            _ => default_timeout,
        };
    let timeout_seconds = timeout_seconds as u64;

    log::debug!(
        "Config option `{config_option}` found, \
             waiting for file `{file_path}` to be created"
    );
    std::fs::File::create(format!("{file_path}.waiting")).ok();
    // If the test timeout have been extended, scale the timer relative
    // to the normal timing.
    let global_default_timeout: u64 = std::env::var("HGTEST_TIMEOUT_DEFAULT")
        .map(|t| t.parse())
        .unwrap_or(Ok(0))
        .unwrap();
    let global_timeout_override: u64 = std::env::var("HGTEST_TIMEOUT")
        .map(|t| t.parse())
        .unwrap_or(Ok(0))
        .unwrap();
    let timeout_seconds = if global_default_timeout < global_timeout_override {
        timeout_seconds * global_timeout_override / global_default_timeout
    } else {
        timeout_seconds
    };
    let timeout = std::time::Duration::from_secs(timeout_seconds);

    let start = std::time::Instant::now();
    let path = std::path::Path::new(file_path);
    let mut found = false;
    while start.elapsed() < timeout {
        if path.exists() {
            log::debug!("File `{file_path}` was created");
            found = true;
            break;
        } else {
            std::thread::sleep(std::time::Duration::from_millis(10));
        }
    }
    if !found {
        let msg = format!(
            "File `{file_path}` set by `{config_option}` was not found \
            within the allocated {timeout_seconds} seconds timeout"
        );
        Err(msg)
    } else {
        Ok(())
    }
}

pub fn debug_wait_for_file_or_print(config: &Config, config_option: &str) {
    if let Err(e) = debug_wait_for_file(config, config_option) {
        eprintln!("{e}");
    };
}