<commit_before>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

/// Extract the module name from a path. This is the required prefix for
/// query, fragment, subscription and mutation names.
pub fn extract_module_name(path: &str) -> Option<String> {
    let final_segment = get_final_non_index_js_segment(path)?.to_string();

    let mut iter = final_segment.split('.');
    let mut first_segment = to_camel_case(iter.next()?.to_string());
    let mobile_platform = iter.find_map(|item| {
        if item == "ios" {
            return Some("Ios");
        } else if item == "android" {
            return Some("Android");
        }
        None
    });
    if let Some(mobile_platform) = mobile_platform {
        first_segment.push_str(mobile_platform);
    }
    Some(first_segment)
}

fn get_final_non_index_js_segment(path: &str) -> Option<&str> {
    let mut iter = path.split('/');
    let last_segment = iter.next_back()?;
    if last_segment == "index.js" {
        return iter.next_back();
    }
    Some(last_segment)
}

/// Converts a `String` to a camel case `String`
fn to_camel_case(non_camelized_string: String) -> String {
    // Emulate the following regex, from the Javascript implementation:
    // moduleName = moduleName.replace(/[^a-zA-Z0-9]+(\w?)/g, (match, next) =>
    //     next.toUpperCase(),
    // );

    let mut camelized_string = String::with_capacity(non_camelized_string.len());
    let mut last_character_was_not_alphanumeric = false;
    for ch in non_camelized_string.chars() {
        if !ch.is_alphanumeric() {
            last_character_was_not_alphanumeric = true;
        } else if last_character_was_not_alphanumeric {
            camelized_string.push(ch.to_ascii_uppercase());
            last_character_was_not_alphanumeric = false;
        } else {
            camelized_string.push(ch);
            last_character_was_not_alphanumeric = false;
        }
    }
    camelized_string
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn extract_module_names_test() {
        assert_eq!(
            extract_module_name("/path/Button.ios.js"),
            Some("ButtonIos".to_string())
        );
        assert_eq!(
            extract_module_name("/path/Button.android.js"),
            Some("ButtonAndroid".to_string())
        );
        assert_eq!(
            extract_module_name("/path/Button.hooks.android.js"),
            Some("ButtonAndroid".to_string())
        );
        assert_eq!(
            extract_module_name("/path/Button.js"),
            Some("Button".to_string())
        );
        assert_eq!(
            extract_module_name("/path/Button.react.js"),
            Some("Button".to_string())
        );
        assert_eq!(
            extract_module_name("/path/Button.react-test.js"),
            Some("Button".to_string())
        );
        assert_eq!(
            extract_module_name("/path/Button.react-test.jsx"),
            Some("Button".to_string())
        );
        assert_eq!(
            extract_module_name("/path/Button.react.example.jsx"),
            Some("Button".to_string())
        );
        assert_eq!(
            extract_module_name("/path/Button.react-snapshot-test.js"),
            Some("Button".to_string())
        );
        assert_eq!(
            extract_module_name("/path/Button.kit.js"),
            Some("Button".to_string())
        );
        assert_eq!(
            extract_module_name("/path/Button.hooks.js"),
            Some("Button".to_string())
        );
        assert_eq!(
            extract_module_name("/path/Button.brands.react.js"),
            Some("Button".to_string())
        );
        assert_eq!(
            extract_module_name("/path/Button.my.custom.suffix.js"),
            Some("Button".to_string())
        );
        assert_eq!(
            extract_module_name("/path/Slider.ios.js"),
            Some("SliderIos".to_string())
        );
        assert_eq!(
            extract_module_name("/path/TypeScript.ts"),
            Some("TypeScript".to_string())
        );
        assert_eq!(
            extract_module_name("/path/TypeScript.tsx"),
            Some("TypeScript".to_string())
        );
        assert_eq!(
            extract_module_name("/path/button/index.js"),
            Some("button".to_string())
        );
        assert_eq!(
            extract_module_name("/path/foo-bar/index.js"),
            Some("fooBar".to_string())
        );
        assert_eq!(
            extract_module_name("/path/foo-bar-baz.js"),
            Some("fooBarBaz".to_string())
        );
        assert_eq!(
            extract_module_name("/path/non-numeric-end-.js"),
            Some("nonNumericEnd".to_string())
        );
    }
}
<commit_msg>Correctly resolve module names for TypeScript index files (#3644)<commit_after>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

/// Extract the module name from a path. This is the required prefix for
/// query, fragment, subscription and mutation names.
pub fn extract_module_name(path: &str) -> Option<String> {
    let final_segment = get_final_non_index_js_segment(path)?.to_string();

    let mut iter = final_segment.split('.');
    let mut first_segment = to_camel_case(iter.next()?.to_string());
    let mobile_platform = iter.find_map(|item| {
        if item == "ios" {
            return Some("Ios");
        } else if item == "android" {
            return Some("Android");
        }
        None
    });
    if let Some(mobile_platform) = mobile_platform {
        first_segment.push_str(mobile_platform);
    }
    Some(first_segment)
}

fn get_final_non_index_js_segment(path: &str) -> Option<&str> {
    let mut iter = path.split('/');
    let last_segment = iter.next_back()?;
    if last_segment == "index.js"
        || last_segment == "index.jsx"
        || last_segment == "index.ts"
        || last_segment == "index.tsx"
    {
        return iter.next_back();
    }
    Some(last_segment)
}

/// Converts a `String` to a camel case `String`
fn to_camel_case(non_camelized_string: String) -> String {
    // Emulate the following regex, from the Javascript implementation:
    // moduleName = moduleName.replace(/[^a-zA-Z0-9]+(\w?)/g, (match, next) =>
    //     next.toUpperCase(),
    // );

    let mut camelized_string = String::with_capacity(non_camelized_string.len());
    let mut last_character_was_not_alphanumeric = false;
    for ch in non_camelized_string.chars() {
        if !ch.is_alphanumeric() {
            last_character_was_not_alphanumeric = true;
        } else if last_character_was_not_alphanumeric {
            camelized_string.push(ch.to_ascii_uppercase());
            last_character_was_not_alphanumeric = false;
        } else {
            camelized_string.push(ch);
            last_character_was_not_alphanumeric = false;
        }
    }
    camelized_string
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn extract_module_names_test() {
        assert_eq!(
            extract_module_name("/path/Button.ios.js"),
            Some("ButtonIos".to_string())
        );
        assert_eq!(
            extract_module_name("/path/Button.android.js"),
            Some("ButtonAndroid".to_string())
        );
        assert_eq!(
            extract_module_name("/path/Button.hooks.android.js"),
            Some("ButtonAndroid".to_string())
        );
        assert_eq!(
            extract_module_name("/path/Button.js"),
            Some("Button".to_string())
        );
        assert_eq!(
            extract_module_name("/path/Button.react.js"),
            Some("Button".to_string())
        );
        assert_eq!(
            extract_module_name("/path/Button.react-test.js"),
            Some("Button".to_string())
        );
        assert_eq!(
            extract_module_name("/path/Button.react-test.jsx"),
            Some("Button".to_string())
        );
        assert_eq!(
            extract_module_name("/path/Button.react.example.jsx"),
            Some("Button".to_string())
        );
        assert_eq!(
            extract_module_name("/path/Button.react-snapshot-test.js"),
            Some("Button".to_string())
        );
        assert_eq!(
            extract_module_name("/path/Button.kit.js"),
            Some("Button".to_string())
        );
        assert_eq!(
            extract_module_name("/path/Button.hooks.js"),
            Some("Button".to_string())
        );
        assert_eq!(
            extract_module_name("/path/Button.brands.react.js"),
            Some("Button".to_string())
        );
        assert_eq!(
            extract_module_name("/path/Button.my.custom.suffix.js"),
            Some("Button".to_string())
        );
        assert_eq!(
            extract_module_name("/path/Slider.ios.js"),
            Some("SliderIos".to_string())
        );
        assert_eq!(
            extract_module_name("/path/TypeScript.ts"),
            Some("TypeScript".to_string())
        );
        assert_eq!(
            extract_module_name("/path/TypeScript.tsx"),
            Some("TypeScript".to_string())
        );
        assert_eq!(
            extract_module_name("/path/TypeScript/index.ts"),
            Some("TypeScript".to_string())
        );
        assert_eq!(
            extract_module_name("/path/TypeScript/index.tsx"),
            Some("TypeScript".to_string())
        );
        assert_eq!(
            extract_module_name("/path/button/index.js"),
            Some("button".to_string())
        );
        assert_eq!(
            extract_module_name("/path/button/index.jsx"),
            Some("button".to_string())
        );
        assert_eq!(
            extract_module_name("/path/foo-bar/index.js"),
            Some("fooBar".to_string())
        );
        assert_eq!(
            extract_module_name("/path/foo-bar-baz.js"),
            Some("fooBarBaz".to_string())
        );
        assert_eq!(
            extract_module_name("/path/non-numeric-end-.js"),
            Some("nonNumericEnd".to_string())
        );
    }
}
<|endoftext|>


<commit_before>extern crate msi;

#[macro_use]
mod testutil;

use msi::{Column, Delete, Expr, Insert, Package, PackageType, Select, Value};
use std::error::Error;
use std::io::{Cursor, ErrorKind};

// ========================================================================= //

#[test]
fn nonexistent_table() {
    let cursor = Cursor::new(Vec::new());
    let mut package = Package::create(PackageType::Installer, cursor).unwrap();
    let query = Delete::from("Foobar")
        .with(Expr::col("Foo").eq(Expr::integer(1)));
    assert_error!(package.delete_rows(query),
                  ErrorKind::NotFound,
                  "Table \"Foobar\" does not exist");

}

#[test]
fn nonexistent_column() {
    let cursor = Cursor::new(Vec::new());
    let mut package = Package::create(PackageType::Installer, cursor).unwrap();
    let columns = vec![
        Column::build("Foo").primary_key().int32(),
        Column::build("Bar").nullable().string(6),
    ];
    package.create_table("Foobar", columns).unwrap();
    let query = Delete::from("Foobar")
        .with((Expr::col("Bar").ne(Expr::null()))
                  .and(Expr::col("Baz").eq(Expr::integer(1))));
    assert_error!(package.delete_rows(query),
                  ErrorKind::InvalidInput,
                  "Table \"Foobar\" has no column named \"Baz\"");
}

#[test]
fn delete_one_row() {
    let cursor = Cursor::new(Vec::new());
    let mut package = Package::create(PackageType::Installer, cursor).unwrap();
    let columns = vec![
        Column::build("Foo").primary_key().int32(),
        Column::build("Bar").nullable().string(6),
    ];
    package.create_table("Foobar", columns).unwrap();

    let query = Insert::into("Foobar")
        .row(vec![Value::Int(1), Value::from("One")])
        .row(vec![Value::Int(2), Value::from("Two")])
        .row(vec![Value::Int(3), Value::from("Three")]);
    package.insert_rows(query).unwrap();

    let query = Delete::from("Foobar")
        .with((Expr::col("Foo").eq(Expr::integer(2))));
    package.delete_rows(query).unwrap();

    let cursor = package.into_inner().unwrap();
    let mut package = Package::open(cursor).unwrap();
    let rows = package.select_rows(Select::table("Foobar")).unwrap();
    assert_eq!(rows.len(), 2);
    let keys = rows.map(|row| row[0].as_int().unwrap()).collect::<Vec<i32>>();
    assert_eq!(keys, vec![1, 3]);
}

#[test]
fn delete_multiple_rows() {
    let cursor = Cursor::new(Vec::new());
    let mut package = Package::create(PackageType::Installer, cursor).unwrap();
    let columns = vec![
        Column::build("Key").primary_key().int16(),
        Column::build("Value").nullable().int32(),
    ];
    package.create_table("Mapping", columns).unwrap();

    let query = Insert::into("Mapping")
        .row(vec![Value::Int(1), Value::Int(17)])
        .row(vec![Value::Int(2), Value::Int(42)])
        .row(vec![Value::Int(3), Value::Int(17)]);
    package.insert_rows(query).unwrap();

    let query = Delete::from("Mapping")
        .with(Expr::col("Value").eq(Expr::integer(17)));
    package.delete_rows(query).unwrap();

    let cursor = package.into_inner().unwrap();
    let mut package = Package::open(cursor).unwrap();
    let rows = package.select_rows(Select::table("Mapping")).unwrap();
    let values: Vec<(i32, i32)> =
        rows.map(|row| (row[0].as_int().unwrap(), row[1].as_int().unwrap()))
            .collect();
    assert_eq!(values, vec![(2, 42)]);
}

#[test]
fn delete_all_rows() {
    let cursor = Cursor::new(Vec::new());
    let mut package = Package::create(PackageType::Installer, cursor).unwrap();
    let columns = vec![
        Column::build("Foo").primary_key().int32(),
        Column::build("Bar").nullable().string(6),
    ];
    package.create_table("Foobar", columns).unwrap();

    let query = Insert::into("Foobar")
        .row(vec![Value::Int(1), Value::from("One")])
        .row(vec![Value::Int(2), Value::from("Two")])
        .row(vec![Value::Int(3), Value::from("Three")]);
    package.insert_rows(query).unwrap();

    package.delete_rows(Delete::from("Foobar")).unwrap();

    let cursor = package.into_inner().unwrap();
    let mut package = Package::open(cursor).unwrap();
    let rows = package.select_rows(Select::table("Foobar")).unwrap();
    assert_eq!(rows.len(), 0);
    let keys = rows.map(|row| row[0].as_int().unwrap()).collect::<Vec<i32>>();
    assert!(keys.is_empty());
}

// ========================================================================= //
<commit_msg>Remove unnecessary parentheses<commit_after>extern crate msi;

#[macro_use]
mod testutil;

use msi::{Column, Delete, Expr, Insert, Package, PackageType, Select, Value};
use std::error::Error;
use std::io::{Cursor, ErrorKind};

// ========================================================================= //

#[test]
fn nonexistent_table() {
    let cursor = Cursor::new(Vec::new());
    let mut package = Package::create(PackageType::Installer, cursor).unwrap();
    let query = Delete::from("Foobar")
        .with(Expr::col("Foo").eq(Expr::integer(1)));
    assert_error!(package.delete_rows(query),
                  ErrorKind::NotFound,
                  "Table \"Foobar\" does not exist");

}

#[test]
fn nonexistent_column() {
    let cursor = Cursor::new(Vec::new());
    let mut package = Package::create(PackageType::Installer, cursor).unwrap();
    let columns = vec![
        Column::build("Foo").primary_key().int32(),
        Column::build("Bar").nullable().string(6),
    ];
    package.create_table("Foobar", columns).unwrap();
    let query = Delete::from("Foobar")
        .with((Expr::col("Bar").ne(Expr::null()))
                  .and(Expr::col("Baz").eq(Expr::integer(1))));
    assert_error!(package.delete_rows(query),
                  ErrorKind::InvalidInput,
                  "Table \"Foobar\" has no column named \"Baz\"");
}

#[test]
fn delete_one_row() {
    let cursor = Cursor::new(Vec::new());
    let mut package = Package::create(PackageType::Installer, cursor).unwrap();
    let columns = vec![
        Column::build("Foo").primary_key().int32(),
        Column::build("Bar").nullable().string(6),
    ];
    package.create_table("Foobar", columns).unwrap();

    let query = Insert::into("Foobar")
        .row(vec![Value::Int(1), Value::from("One")])
        .row(vec![Value::Int(2), Value::from("Two")])
        .row(vec![Value::Int(3), Value::from("Three")]);
    package.insert_rows(query).unwrap();

    let query = Delete::from("Foobar")
        .with(Expr::col("Foo").eq(Expr::integer(2)));
    package.delete_rows(query).unwrap();

    let cursor = package.into_inner().unwrap();
    let mut package = Package::open(cursor).unwrap();
    let rows = package.select_rows(Select::table("Foobar")).unwrap();
    assert_eq!(rows.len(), 2);
    let keys = rows.map(|row| row[0].as_int().unwrap()).collect::<Vec<i32>>();
    assert_eq!(keys, vec![1, 3]);
}

#[test]
fn delete_multiple_rows() {
    let cursor = Cursor::new(Vec::new());
    let mut package = Package::create(PackageType::Installer, cursor).unwrap();
    let columns = vec![
        Column::build("Key").primary_key().int16(),
        Column::build("Value").nullable().int32(),
    ];
    package.create_table("Mapping", columns).unwrap();

    let query = Insert::into("Mapping")
        .row(vec![Value::Int(1), Value::Int(17)])
        .row(vec![Value::Int(2), Value::Int(42)])
        .row(vec![Value::Int(3), Value::Int(17)]);
    package.insert_rows(query).unwrap();

    let query = Delete::from("Mapping")
        .with(Expr::col("Value").eq(Expr::integer(17)));
    package.delete_rows(query).unwrap();

    let cursor = package.into_inner().unwrap();
    let mut package = Package::open(cursor).unwrap();
    let rows = package.select_rows(Select::table("Mapping")).unwrap();
    let values: Vec<(i32, i32)> =
        rows.map(|row| (row[0].as_int().unwrap(), row[1].as_int().unwrap()))
            .collect();
    assert_eq!(values, vec![(2, 42)]);
}

#[test]
fn delete_all_rows() {
    let cursor = Cursor::new(Vec::new());
    let mut package = Package::create(PackageType::Installer, cursor).unwrap();
    let columns = vec![
        Column::build("Foo").primary_key().int32(),
        Column::build("Bar").nullable().string(6),
    ];
    package.create_table("Foobar", columns).unwrap();

    let query = Insert::into("Foobar")
        .row(vec![Value::Int(1), Value::from("One")])
        .row(vec![Value::Int(2), Value::from("Two")])
        .row(vec![Value::Int(3), Value::from("Three")]);
    package.insert_rows(query).unwrap();

    package.delete_rows(Delete::from("Foobar")).unwrap();

    let cursor = package.into_inner().unwrap();
    let mut package = Package::open(cursor).unwrap();
    let rows = package.select_rows(Select::table("Foobar")).unwrap();
    assert_eq!(rows.len(), 0);
    let keys = rows.map(|row| row[0].as_int().unwrap()).collect::<Vec<i32>>();
    assert!(keys.is_empty());
}

// ========================================================================= //
<|endoftext|>


<commit_before>use std::io::Cursor;
use FromCursor;
use types::CString;

/// A server authentication challenge.
#[derive(Debug)]
pub struct BodyResAuthenticate {
    pub data: CString
}

impl FromCursor for BodyResAuthenticate {
    fn from_cursor(mut cursor: &mut Cursor<Vec<u8>>) -> BodyResAuthenticate {
        BodyResAuthenticate {
            data: CString::from_cursor(&mut cursor)
        }
    }
}
<commit_msg>Create tests for frame_authenticate<commit_after>use std::io::Cursor;
use FromCursor;
use types::CString;

/// A server authentication challenge.
#[derive(Debug)]
pub struct BodyResAuthenticate {
    pub data: CString
}

impl FromCursor for BodyResAuthenticate {
    fn from_cursor(mut cursor: &mut Cursor<Vec<u8>>) -> BodyResAuthenticate {
        BodyResAuthenticate {
            data: CString::from_cursor(&mut cursor)
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::io::Cursor;
    use FromCursor;

    #[test]
    fn body_res_authenticate() {
        // string "abcde"
        let data: Vec<u8> = vec![0, 5, 97, 98, 99, 100, 101];
        let mut cursor = Cursor::new(data);
        let body = BodyResAuthenticate::from_cursor(&mut cursor);
        assert_eq!(body.data.as_str(), "abcde");
    }
}
<|endoftext|>


<commit_before>/* diosix virtual CPU scheduler
 *
 * This is, for now, really really simple.
 * Making it fairer and adaptive to workloads is the ultimate goal.
 *
 * (c) Chris Williams, 2018-2020.
 *
 * See LICENSE for usage and copying.
 */

use spin::Mutex;
use alloc::collections::vec_deque::VecDeque;
use hashbrown::hash_map::HashMap;
use platform::timer::TimerValue;
use super::error::Cause;
use super::vcore::{VirtualCore, Priority};
use super::pcore::{self, PhysicalCore, PhysicalCoreID};
use super::hardware;
use super::message;

pub type TimesliceCount = u64;

/* prevent physical CPU time starvation: allow a normal virtual core to run after this number of timeslices
have been spent running high priority virtual cores */
const HIGH_PRIO_TIMESLICES_MAX: TimesliceCount = 10;

/* max how long a virtual core is allowed to run before a scheduling decision is made */
const TIMESLICE_LENGTH: TimerValue = TimerValue::Milliseconds(50);

/* define the shortest time between now and another interrupt and rescheduling decision.
this is to stop supervisor kernels spamming the scheduling system with lots of short reschedulings */
const TIMESLICE_MIN_LENGTH: TimerValue = TimerValue::Milliseconds(5);

/* duration a system maintence core (one that can't run supervisor code) must wait
before looking for fixed work to do. also the length in between application cores can
attempt to perform housekeeping */
const MAINTENANCE_LENGTH: TimerValue = TimerValue::Seconds(5);

/* these are the global wait queues. while each physical CPU core gets its own pair
of high-normal wait queues, virtual cores waiting to be assigned to a physical CPU sit in these global queues.
when a physical CPU runs out of queued virtual cores, it pulls one from these global queues.
a physical CPU core can ask fellow CPUs to push virtual cores onto the global queues via messages */
lazy_static!
{
    static ref GLOBAL_QUEUES: Mutex<ScheduleQueues> = Mutex::new(ScheduleQueues::new());
    static ref WORKLOAD: Mutex<HashMap<PhysicalCoreID, usize>> = Mutex::new(HashMap::new());
    static ref LAST_HOUSEKEEP_CHECK: Mutex<TimerValue> = Mutex::new(TimerValue::Exact(0));
}

#[derive(PartialEq, Clone, Copy, Debug)]
pub enum SearchMode
{
    MustFind, /* when searching for something to run, keep looping until successful */
    CheckOnce /* search just once for something else to run, return to environment otherwise */
}

/* queue a virtual core in global wait list */
pub fn queue(to_queue: VirtualCore)
{
    GLOBAL_QUEUES.lock().queue(to_queue);
}

/* activate preemptive multitasking. each physical CPU core should call this
   to start running workloads - be them user/supervisor or management tasks
   <= returns OK, or error code on failure */
pub fn start() -> Result<(), Cause>
{
    hardware::scheduler_timer_start();
    Ok(())
}

/* a timer interrupt has come in -- make a decision */
pub fn ping()
{
    let time_now = hardware::scheduler_get_timer_now();
    let frequency = hardware::scheduler_get_timer_frequency();
    if time_now.is_none() || frequency.is_none()
    {
        /* check to see if anything needs to run and bail out if
        no timer hardware can be found (and yet we're still getting IRQs?) */
        run_next(SearchMode::CheckOnce);
        return;
    }

    /* get down to the exact timer values */
    let frequency = frequency.unwrap();
    let time_now = time_now.unwrap().to_exact(frequency);

    match pcore::PhysicalCore::this().get_timer_sched_last()
    {
        Some(v) =>
        {
            let timeslice_length = TIMESLICE_LENGTH.to_exact(frequency);

            /* check to see if we've reached the end of this physical CPU core's
            time slice. a virtual code has the pcore for TIMESLICE_LENGTH of time
            before a mandatory scheduling decision is made */
            let mut last_scheduled_at = v.to_exact(frequency);
            if time_now - last_scheduled_at >= timeslice_length
            {
                /* it's been a while since we last made a decision, so force one now */
                run_next(SearchMode::CheckOnce);
                pcore::PhysicalCore::this().set_timer_sched_last(Some(TimerValue::Exact(time_now)));
                last_scheduled_at = time_now;
            }

            /* check to make sure timer target is correct for whatever virtual core we're about
            to run. run_next() may have set a new timer irq target, or changed the virtual core
            we're running. there may be a supervisor-level timer IRQ upcoming.
            make sure the physical core timer target value is appropriate. */
            if let Some(timer_target) = hardware::scheduler_get_timer_next_at()
            {
                let mut timer_target = timer_target.to_exact(frequency);

                /* avoid skipping over any pending supervisor timer IRQ: reduce latency between
                capsule timer interrupts being raised and capsule cores scheduled to pick up said IRQs */
                if let Some(supervisor_target) = pcore::PhysicalCore::get_virtualcore_timer_target()
                {
                    timer_target = supervisor_target.to_exact(frequency);
                }

                /* if the target is already behind us, discard it and interrupt at end of this timeslice.
                   if the target is too far ahead, curtail it to the end of this timeslice */
                if timer_target <= time_now || timer_target > last_scheduled_at + timeslice_length
                {
                    timer_target = last_scheduled_at + timeslice_length;
                }

                hardware::scheduler_timer_at(TimerValue::Exact(timer_target));
            }
        },

        /* if not we've not scheduled anything yet, find something to run */
        None =>
        {
            run_next(SearchMode::MustFind);
            pcore::PhysicalCore::this().set_timer_sched_last(Some(TimerValue::Exact(time_now)));
        }
    }
}

/* find something else to run, or return to whatever we were running if allowed.
   call this function when a virtual core's timeslice has expired, or it has crashed
   or stopped running and we can't return to it. this function will return regardless
   if this physical CPU core is unable to run virtual cores.
   => search_mode = define whether or not to continue searching for another
   virtual core to run, or check once to see if something else is waiting */
pub fn run_next(search_mode: SearchMode)
{
    /* check for housekeeping */
    housekeeping();

    /* if this core can run supervisor-level code then find it some work to do */
    if pcore::PhysicalCore::smode_supported()
    {
        /* keep looping until we've found something to switch to if must_switch
        is set to true */
        loop
        {
            let mut something_found = true;

            /* check to see if there's anything waiting to be picked up for this
            physical CPU from a global queue. if so, then adopt it so it can get a chance to run */
            match GLOBAL_QUEUES.lock().dequeue()
            {
                /* we've found a virtual CPU core to run, so switch to that */
                Some(orphan) =>
                {
                    let mut workloads =  WORKLOAD.lock();

                    /* increment counter of how many virtual cores this physical CPU core
                    has taken from the global queue */
                    if let Some(count) = workloads.get_mut(&PhysicalCore::get_id())
                    {
                        *count = *count + 1;
                    }
                    else
                    {
                        workloads.insert(PhysicalCore::get_id(), 1);
                    }

                    pcore::context_switch(orphan);
                },

                /* otherwise, try to take a virtual CPU core waiting for this physical CPU core and run it */
                _ => match PhysicalCore::dequeue()
                {
                    Some(virtcore) => pcore::context_switch(virtcore), /* waiting virtual CPU core found, queuing now */
                    _ => something_found = false /* nothing else to run */
                }
            }

            /* if we've found something, or only searching once, exit the search loop */
            if something_found == true || search_mode == SearchMode::CheckOnce
            {
                break;
            }
        }

        /* we've got a virtual core to run. tell the timer system to call us back soon */
        hardware::scheduler_timer_next_in(TIMESLICE_LENGTH);
    }
    else
    {
        hardware::scheduler_timer_next_in(MAINTENANCE_LENGTH); /* we'll be back some time later */
    }
}

/* perform any housekeeping duties defined by the various parts of the system */
fn housekeeping()
{
    /* don't busy wait for housekeeping lock: check once and move on */
    let mut last_check = match LAST_HOUSEKEEP_CHECK.try_lock()
    {
        Some(value) => value,
        None => return
    };

    /* only perform housekeeping once every MAINTENANCE_LENGTH-long period */
    match (hardware::scheduler_get_timer_now(), hardware::scheduler_get_timer_frequency())
    {
        (Some(time_now), Some(frequency)) =>
        {
            let time_now = time_now.to_exact(frequency);
            let last_check_value = (*last_check).to_exact(frequency);
            let maintence_length = MAINTENANCE_LENGTH.to_exact(frequency);

            /* wait until we're at least MAINTENANCE_LENGTH into boot */
            if time_now > maintence_length
            {
                if time_now - last_check_value < maintence_length
                {
                    /* not enough MAINTENANCE_LENGTH time has passed */
                    return;
                }
                /* mark when we last performed housekeeping */
                *last_check = TimerValue::Exact(time_now);
            }
            else
            {
                /* flush debug and bail out */
                debughousekeeper!();
                return;
            }
        },
        (_, _) =>
        {
            /* no timer. output debug and bail out */
            debughousekeeper!();
            return;
        }
    }

    debughousekeeper!(); /* drain the debug logs to the debug hardware port */
    heaphousekeeper!(); /* return any unused regions of physical memory */
    physmemhousekeeper!(); /* tidy up any physical memory structures */

    /* if the global queues are empty then work out which physical CPU core
    has the most number of virtual cores and is therefore the busiest */
    let global_queue_lock = GLOBAL_QUEUES.lock();
    if global_queue_lock.total_queued() > 0
    {
        let mut highest_count = 0;
        let mut busiest_pcore: Option<PhysicalCoreID> = None;
        for (&pcoreid, &vcore_count) in WORKLOAD.lock().iter()
        {
            if vcore_count > highest_count
            {
                highest_count = vcore_count;
                busiest_pcore = Some(pcoreid);
            }
        }

        /* ask the busiest core to send one virtual core back to the global queue
        but only if it has enough to share: it must have more than one virtual core */
        if highest_count > 1
        {
            if let Some(pid) = busiest_pcore
            {
                let m = message::Message::new(message::Recipient::send_to_pcore(pid),
                                                message::MessageContent::DisownQueuedVirtualCore);
                match message::send(m)
                {
                    Err(e) => hvalert!("Failed to message physical CPU {} during load balancing: {:?}", pid, e),
                    Ok(()) => ()
                };
            }
        }
    }
}

/* maintain a simple two-level round-robin scheduler per physical CPU core. we can make it more fancy later.
the hypervisor tries to dish out physical CPU time fairly among capsules, and let the
capsule supervisors work out how best to allocate their time to userspace code.
picking the next virtual CPU core to run should be O(1) or as close as possible to it. */
pub struct ScheduleQueues
{
    high: VecDeque<VirtualCore>,
    low: VecDeque<VirtualCore>,
    high_timeslices: TimesliceCount
}

impl ScheduleQueues
{
    /* initialize a new set of scheduler queues */
    pub fn new() -> ScheduleQueues
    {
        ScheduleQueues
        {
            high: VecDeque::<VirtualCore>::new(),
            low: VecDeque::<VirtualCore>::new(),
            high_timeslices: 0
        }
    }

    /* run the given virtual core by switching to its supervisor context.
    this also updates NORM_PRIO_TICKS. if the current physical CPU was already running a
    virtual core, that virtual core is queued up in the waiting list by context_switch() */
    pub fn run(&mut self, to_run: VirtualCore)
    {
        /* if we're about to run a normal virtual core, then reset counter since a normal virtual core ran.
        if we're running a non-normal virtual core, then increase the count. */
        match to_run.get_priority()
        {
            Priority::Normal => self.high_timeslices = 0,
            Priority::High => self.high_timeslices = self.high_timeslices + 1
        };

        pcore::context_switch(to_run);
    }

    /* add the given virtual core to the appropriate waiting queue. put it to the back
    so that other virtual cores get a chance to run */
    pub fn queue(&mut self, to_queue: VirtualCore)
    {
        match to_queue.get_priority()
        {
            Priority::High => self.high.push_back(to_queue),
            Priority::Normal => self.low.push_back(to_queue)
        }
    }

    /* remove a virtual core from the waiting list queues, selected by priority with safeguards to
    prevent CPU time starvation. Returns selected virtual core or None for no other virtual cores waiting */
    pub fn dequeue(&mut self) -> Option<VirtualCore>
    {
        /* has a normal virtual core been waiting for ages? */
        if self.high_timeslices > HIGH_PRIO_TIMESLICES_MAX
        {
            match self.low.pop_front()
            {
                Some(t) => return Some(t),
                None => ()
            };
        }

        /* check the high priority queue for anything waiting.
        if not, then try the normal priority queue */
        match self.high.pop_front()
        {
            Some(t) => Some(t),
            None => self.low.pop_front()
        }
    }

    /* return the total number of virtual cores queued */
    pub fn total_queued(&self) -> usize
    {
        self.high.len() + self.low.len()
    }
}
<commit_msg>* Use built-in mutexes rather than spin-rs * Incorporate a capsule's state when making scheduling decisions. Don't return to a virtual core that's in a dying or restarting capsule, and instead find another. The virtual core will be dropped in the subsequent context switch. * Schedule integrity checks in the routine housekeeping<commit_after>/* diosix virtual CPU scheduler
 *
 * This is, for now, really really simple.
 * Making it fairer and adaptive to workloads is the ultimate goal.
 *
 * (c) Chris Williams, 2018-2021.
 *
 * See LICENSE for usage and copying.
 */

use super::lock::Mutex;
use alloc::collections::vec_deque::VecDeque;
use hashbrown::hash_map::HashMap;
use platform::timer::TimerValue;
use super::error::Cause;
use super::vcore::{VirtualCore, Priority};
use super::pcore::{self, PhysicalCore, PhysicalCoreID};
use super::hardware;
use super::message;
use super::capsule::{self, CapsuleState};

pub type TimesliceCount = u64;

/* prevent physical CPU time starvation: allow a normal virtual core to run after this number of timeslices
have been spent running high priority virtual cores */
const HIGH_PRIO_TIMESLICES_MAX: TimesliceCount = 10;

/* max how long a virtual core is allowed to run before a scheduling decision is made */
const TIMESLICE_LENGTH: TimerValue = TimerValue::Milliseconds(50);

/* define the shortest time between now and another interrupt and rescheduling decision.
this is to stop supervisor kernels spamming the scheduling system with lots of short reschedulings */
const TIMESLICE_MIN_LENGTH: TimerValue = TimerValue::Milliseconds(5);

/* duration a system maintence core (one that can't run supervisor code) must wait
before looking for fixed work to do. also the length in between application cores can
attempt to perform housekeeping */
const MAINTENANCE_LENGTH: TimerValue = TimerValue::Seconds(5);

/* these are the global wait queues. while each physical CPU core gets its own pair
of high-normal wait queues, virtual cores waiting to be assigned to a physical CPU sit in these global queues.
when a physical CPU runs out of queued virtual cores, it pulls one from these global queues.
a physical CPU core can ask fellow CPUs to push virtual cores onto the global queues via messages */
lazy_static!
{
    static ref GLOBAL_QUEUES: Mutex<ScheduleQueues> = Mutex::new(ScheduleQueues::new());
    static ref WORKLOAD: Mutex<HashMap<PhysicalCoreID, usize>> = Mutex::new(HashMap::new());
    static ref LAST_HOUSEKEEP_CHECK: Mutex<TimerValue> = Mutex::new(TimerValue::Exact(0));
}

#[derive(PartialEq, Clone, Copy, Debug)]
pub enum SearchMode
{
    MustFind, /* when searching for something to run, keep looping until successful */
    CheckOnce /* search just once for something else to run, return to environment otherwise */
}

/* queue a virtual core in global wait list */
pub fn queue(to_queue: VirtualCore)
{
    GLOBAL_QUEUES.lock().queue(to_queue);
}

/* activate preemptive multitasking. each physical CPU core should call this
   to start running workloads - be them user/supervisor or management tasks
   <= returns OK, or error code on failure */
pub fn start() -> Result<(), Cause>
{
    hardware::scheduler_timer_start();
    Ok(())
}

/* make a decision on whether to run another virtual core,
   or return to the currently running core (if possible).
   ping() is called when a scheduler timer IRQ comes in */
pub fn ping()
{
    let time_now = hardware::scheduler_get_timer_now();
    let frequency = hardware::scheduler_get_timer_frequency();
    if time_now.is_none() || frequency.is_none()
    {
        /* check to see if anything needs to run and bail out if
        no timer hardware can be found (and yet we're still getting IRQs?) */
        run_next(SearchMode::CheckOnce);
        return;
    }

    /* get down to the exact timer values */
    let frequency = frequency.unwrap();
    let time_now = time_now.unwrap().to_exact(frequency);

    match pcore::PhysicalCore::this().get_timer_sched_last()
    {
        Some(v) =>
        {
            let timeslice_length = TIMESLICE_LENGTH.to_exact(frequency);
            let mut last_scheduled_at = v.to_exact(frequency);

            /* if the capsule we're running in is valid then perform a time slice check.
               if it's not valid, ensure the capsule is torn down or restarted for this
               virtual core. when all vcores are removed from the capsule, its
               resources will be released and the capsule deleted */
            let capsule_state = capsule::get_current_state();
            match capsule_state
            {
                Some(CapsuleState::Valid) =>
                {
                    /* check to see if we've reached the end of this physical CPU core's
                    time slice. a virtual code has the pcore for TIMESLICE_LENGTH of time
                    before a mandatory scheduling decision is made */
                    if time_now - last_scheduled_at >= timeslice_length
                    {
                        /* it's been a while since we last made a decision, so force one now */
                        run_next(SearchMode::CheckOnce);
                        pcore::PhysicalCore::this().set_timer_sched_last(Some(TimerValue::Exact(time_now)));
                        last_scheduled_at = time_now;
                    }
                },
                _ =>
                {
                    /* it is safe to call destroy_current() and restart_current() multiple times
                       per vcore until the capsule is dead or restarted */
                    if let Err(_e) = match capsule_state
                    {
                        Some(CapsuleState::Dying) => capsule::destroy_current(),
                        Some(CapsuleState::Restarting) => capsule::restart_current(),
                        _ => Ok(())
                    }
                    {
                        hvalert!("BUG: Capsule update failure {:?} in scheduler ({:?})", _e, capsule_state)
                    }

                    /* capsule we're running in is no longer valid so force a reschedule */
                    run_next(SearchMode::MustFind);
                    pcore::PhysicalCore::this().set_timer_sched_last(Some(TimerValue::Exact(time_now)));
                    last_scheduled_at = time_now;
                }
            }

            /* check to make sure timer target is correct for whatever virtual core we're about
            to run. run_next() may have set a new timer irq target, or changed the virtual core
            we're running. there may be a supervisor-level timer IRQ upcoming.
            make sure the physical core timer target value is appropriate. */
            if let Some(timer_target) = hardware::scheduler_get_timer_next_at()
            {
                let mut timer_target = timer_target.to_exact(frequency);

                /* avoid skipping over any pending supervisor timer IRQ: reduce latency between
                capsule timer interrupts being raised and capsule cores scheduled to pick up said IRQs */
                if let Some(supervisor_target) = pcore::PhysicalCore::get_virtualcore_timer_target()
                {
                    timer_target = supervisor_target.to_exact(frequency);
                }

                /* if the target is already behind us, discard it and interrupt at end of this timeslice.
                   if the target is too far ahead, curtail it to the end of this timeslice */
                if timer_target <= time_now || timer_target > last_scheduled_at + timeslice_length
                {
                    timer_target = last_scheduled_at + timeslice_length;
                }

                hardware::scheduler_timer_at(TimerValue::Exact(timer_target));
            }
        },

        /* if not we've not scheduled anything yet, find something to run */
        None =>
        {
            run_next(SearchMode::MustFind);
            pcore::PhysicalCore::this().set_timer_sched_last(Some(TimerValue::Exact(time_now)));
        }
    }
}

/* find something else to run, or return to whatever we were running if allowed.
   call this function when a virtual core's timeslice has expired, or it has crashed
   or stopped running and we can't return to it. this function will return regardless
   if this physical CPU core is unable to run virtual cores.
   => search_mode = define whether or not to continue searching for another
   virtual core to run, or check once to see if something else is waiting */
fn run_next(search_mode: SearchMode)
{
    /* check for housekeeping */
    housekeeping();

    /* if this core can run supervisor-level code then find it some work to do */
    if pcore::PhysicalCore::smode_supported()
    {
        loop
        {
            let mut something_found = true;

            /* check to see if there's anything waiting to be picked up for this
            physical CPU from a global queue. if so, then adopt it so it can get a chance to run */
            match GLOBAL_QUEUES.lock().dequeue()
            {
                /* we've found a virtual CPU core to run, so switch to that */
                Some(orphan) =>
                {
                    let mut workloads =  WORKLOAD.lock();

                    /* increment counter of how many virtual cores this physical CPU core
                    has taken from the global queue */
                    if let Some(count) = workloads.get_mut(&PhysicalCore::get_id())
                    {
                        *count = *count + 1;
                    }
                    else
                    {
                        workloads.insert(PhysicalCore::get_id(), 1);
                    }

                    pcore::context_switch(orphan);
                },

                /* otherwise, try to take a virtual CPU core waiting for this physical CPU core and run it */
                _ => match PhysicalCore::dequeue()
                {
                    Some(virtcore) => pcore::context_switch(virtcore), /* waiting virtual CPU core found, queuing now */
                    _ => something_found = false /* nothing else to run */
                }
            }

            /* if we've found something, or only searching once, exit the search loop */
            if something_found == true || search_mode == SearchMode::CheckOnce
            {
                break;
            }
        }

        /* we've got a virtual core to run. tell the timer system to call us back soon */
        hardware::scheduler_timer_next_in(TIMESLICE_LENGTH);
    }
    else
    {
        hardware::scheduler_timer_next_in(MAINTENANCE_LENGTH); /* we'll be back some time later */
    }
}

/* perform any housekeeping duties defined by the various parts of the system */
fn housekeeping()
{
    /* perform integrity checks */
    #[cfg(feature = "integritychecks")]
    {
        if let Err(val) = pcore::PhysicalCore::integrity_check()
        {
            hvalert!("CPU private stack overflowed (0x{:x}). Halting!", val);
            loop {}
        }
    }

    /* avoid blocking on the house keeping lock */
    if LAST_HOUSEKEEP_CHECK.is_locked() == true
    {
        return;
    }

    let mut last_check = LAST_HOUSEKEEP_CHECK.lock();

    /* only perform housekeeping once every MAINTENANCE_LENGTH-long period */
    match (hardware::scheduler_get_timer_now(), hardware::scheduler_get_timer_frequency())
    {
        (Some(time_now), Some(frequency)) =>
        {
            let time_now = time_now.to_exact(frequency);
            let last_check_value = (*last_check).to_exact(frequency);
            let maintence_length = MAINTENANCE_LENGTH.to_exact(frequency);

            /* wait until we're at least MAINTENANCE_LENGTH into boot */
            if time_now > maintence_length
            {
                if time_now - last_check_value < maintence_length
                {
                    /* not enough MAINTENANCE_LENGTH time has passed */
                    return;
                }
                /* mark when we last performed housekeeping */
                *last_check = TimerValue::Exact(time_now);
            }
            else
            {
                /* flush debug and bail out */
                debughousekeeper!();
                return;
            }
        },
        (_, _) =>
        {
            /* no timer. output debug and bail out */
            debughousekeeper!();
            return;
        }
    }

    debughousekeeper!(); /* drain the debug logs to the debug hardware port */
    heaphousekeeper!(); /* return any unused regions of physical memory */
    physmemhousekeeper!(); /* tidy up any physical memory structures */

    /* if the global queues are empty then work out which physical CPU core
    has the most number of virtual cores and is therefore the busiest */
    let global_queue_lock = GLOBAL_QUEUES.lock();
    if global_queue_lock.total_queued() > 0
    {
        let mut highest_count = 0;
        let mut busiest_pcore: Option<PhysicalCoreID> = None;
        for (&pcoreid, &vcore_count) in WORKLOAD.lock().iter()
        {
            if vcore_count > highest_count
            {
                highest_count = vcore_count;
                busiest_pcore = Some(pcoreid);
            }
        }

        /* ask the busiest core to send one virtual core back to the global queue
        but only if it has enough to share: it must have more than one virtual core */
        if highest_count > 1
        {
            if let Some(pid) = busiest_pcore
            {
                let m = message::Message::new(message::Recipient::send_to_pcore(pid),
                                                message::MessageContent::DisownQueuedVirtualCore);
                match message::send(m)
                {
                    Err(e) => hvalert!("Failed to message physical CPU {} during load balancing: {:?}", pid, e),
                    Ok(()) => ()
                };
            }
        }
    }
}

/* maintain a simple two-level round-robin scheduler per physical CPU core. we can make it more fancy later.
the hypervisor tries to dish out physical CPU time fairly among capsules, and let the
capsule supervisors work out how best to allocate their time to userspace code.
picking the next virtual CPU core to run should be O(1) or as close as possible to it. */
pub struct ScheduleQueues
{
    high: VecDeque<VirtualCore>,
    low: VecDeque<VirtualCore>,
    high_timeslices: TimesliceCount
}

impl ScheduleQueues
{
    /* initialize a new set of scheduler queues */
    pub fn new() -> ScheduleQueues
    {
        ScheduleQueues
        {
            high: VecDeque::<VirtualCore>::new(),
            low: VecDeque::<VirtualCore>::new(),
            high_timeslices: 0
        }
    }

    /* run the given virtual core by switching to its supervisor context.
    this also updates NORM_PRIO_TICKS. if the current physical CPU was already running a
    virtual core, that virtual core is queued up in the waiting list by context_switch() */
    pub fn run(&mut self, to_run: VirtualCore)
    {
        /* if we're about to run a normal virtual core, then reset counter since a normal virtual core ran.
        if we're running a non-normal virtual core, then increase the count. */
        match to_run.get_priority()
        {
            Priority::Normal => self.high_timeslices = 0,
            Priority::High => self.high_timeslices = self.high_timeslices + 1
        };

        pcore::context_switch(to_run);
    }

    /* add the given virtual core to the appropriate waiting queue. put it to the back
    so that other virtual cores get a chance to run */
    pub fn queue(&mut self, to_queue: VirtualCore)
    {
        match to_queue.get_priority()
        {
            Priority::High => self.high.push_back(to_queue),
            Priority::Normal => self.low.push_back(to_queue)
        }
    }

    /* remove a virtual core from the waiting list queues, selected by priority with safeguards to
    prevent CPU time starvation. Returns selected virtual core or None for no other virtual cores waiting */
    pub fn dequeue(&mut self) -> Option<VirtualCore>
    {
        /* has a normal virtual core been waiting for ages? */
        if self.high_timeslices > HIGH_PRIO_TIMESLICES_MAX
        {
            match self.low.pop_front()
            {
                Some(t) => return Some(t),
                None => ()
            };
        }

        /* check the high priority queue for anything waiting.
        if not, then try the normal priority queue */
        match self.high.pop_front()
        {
            Some(t) => Some(t),
            None => self.low.pop_front()
        }
    }

    /* return the total number of virtual cores queued */
    pub fn total_queued(&self) -> usize
    {
        self.high.len() + self.low.len()
    }
}
<|endoftext|>


<commit_before>// Copyright 2016 TiKV Project Authors. Licensed under Apache-2.0.

use std::sync::{atomic::AtomicBool, mpsc, Arc};
use std::thread;
use std::time::Duration;

use raft::eraftpb::MessageType;
use tikv_util::HandyRwLock;

use test_raftstore::*;

enum FailureType<'a> {
    Partition(&'a [u64], &'a [u64]),
    Reboot(&'a [u64]),
}

fn attach_prevote_notifiers<T: Simulator>(cluster: &Cluster<T>, peer: u64) -> mpsc::Receiver<()> {
    // Setup a notifier
    let (tx, rx) = mpsc::channel();
    let response_notifier = Box::new(MessageTypeNotifier::new(
        MessageType::MsgRequestPreVoteResponse,
        tx.clone(),
        Arc::from(AtomicBool::new(true)),
    ));
    let request_notifier = Box::new(MessageTypeNotifier::new(
        MessageType::MsgRequestPreVote,
        tx.clone(),
        Arc::from(AtomicBool::new(true)),
    ));

    cluster.sim.wl().add_send_filter(peer, response_notifier);
    cluster.sim.wl().add_send_filter(peer, request_notifier);

    rx
}

// Validate that prevote is used in elections after partition or reboot of some nodes.
fn test_prevote<T: Simulator>(
    cluster: &mut Cluster<T>,
    failure_type: FailureType<'_>,
    leader_after_failure_id: impl Into<Option<u64>>,
    detect_during_failure: impl Into<Option<(u64, bool)>>,
    detect_during_recovery: impl Into<Option<(u64, bool)>>,
) {
    cluster.cfg.raft_store.prevote = true;

    let leader_id = 1;
    let detect_during_failure = detect_during_failure.into();
    let detect_during_recovery = detect_during_recovery.into();

    // We must start the cluster before adding send filters, otherwise it panics.
    cluster.run();

    cluster.must_transfer_leader(1, new_peer(leader_id, 1));
    cluster.must_put(b"k1", b"v1");

    // Determine how to fail.
    let rx = if let Some((id, _)) = detect_during_failure {
        let rx = attach_prevote_notifiers(cluster, id);
        debug!("Attached failure prevote notifier.");
        Some(rx)
    } else {
        None
    };

    match failure_type {
        FailureType::Partition(majority, minority) => {
            cluster.partition(majority.to_vec(), minority.to_vec());
        }
        FailureType::Reboot(peers) => {
            peers.iter().for_each(|&peer| cluster.stop_node(peer));
        }
    };

    if let (Some(rx), Some((_, should_detect))) = (rx, detect_during_failure) {
        // Once we see a response on the wire we know a prevote round is happening.
        let received = rx.recv_timeout(Duration::from_secs(5));
        debug!("Done with failure prevote notifier, got {:?}", received);
        assert_eq!(
            received.is_ok(),
            should_detect,
            "Sends a PreVote or PreVoteResponse during failure.",
        );
    }

    // Let the cluster recover.
    match failure_type {
        FailureType::Partition(_, _) => {
            cluster.clear_send_filters();
        }
        FailureType::Reboot(peers) => {
            cluster.clear_send_filters();
            peers.iter().for_each(|&peer| {
                cluster.run_node(peer).unwrap();
            });
        }
    };

    // Prepare to listen.
    let rx = if let Some((id, _)) = detect_during_recovery {
        let rx = attach_prevote_notifiers(cluster, id);
        debug!("Attached recovery prevote notifier.");
        Some(rx)
    } else {
        None
    };

    if let Some(leader_id) = leader_after_failure_id.into() {
        cluster.must_transfer_leader(1, new_peer(leader_id, 1));
    };

    // Once we see a response on the wire we know a prevote round is happening.
    if let (Some(rx), Some((_, should_detect))) = (rx, detect_during_failure) {
        let received = rx.recv_timeout(Duration::from_secs(5));
        debug!("Done with recovery prevote notifier, got {:?}", received);

        assert_eq!(
            received.is_ok(),
            should_detect,
            "Sends a PreVote or PreVoteResponse during recovery.",
        );
    };

    cluster.must_put(b"k3", b"v3");
    assert_eq!(cluster.must_get(b"k1"), Some(b"v1".to_vec()));
}

#[test]
fn test_prevote_partition_leader_in_majority_detect_in_majority() {
    let mut cluster = new_server_cluster(0, 5);
    // Since the leader is in the majority and not rebooted, it sees no prevote.
    test_prevote(
        &mut cluster,
        FailureType::Partition(&[1, 2, 3], &[4, 5]),
        None,
        (1, false),
        (1, false),
    );
}

// TODO: Enable detect after failure when we can reliably capture the prevote.
#[test]
fn test_prevote_partition_leader_in_majority_detect_in_minority() {
    let mut cluster = new_server_cluster(0, 5);
    // The follower is in the minority and is part of a prevote process. On rejoin it adopts the
    // old leader.
    test_prevote(
        &mut cluster,
        FailureType::Partition(&[1, 2, 3], &[4, 5]),
        None,
        (4, true),
        None,
    );
}

// TODO: Enable detect after failure when we can reliably capture the prevote.
#[test]
fn test_prevote_partition_leader_in_minority_detect_in_majority() {
    let mut cluster = new_server_cluster(0, 5);
    // The follower is in the minority and is part of a prevote process. On rejoin it adopts the
    // old leader.
    test_prevote(
        &mut cluster,
        FailureType::Partition(&[1, 2], &[3, 4, 5]),
        None,
        (4, true),
        None,
    );
}

// TODO: Enable detect after failure when we can reliably capture the prevote.
#[test]
fn test_prevote_partition_leader_in_minority_detect_in_minority() {
    let mut cluster = new_server_cluster(0, 5);
    // The follower is in the minority and is part of a prevote process. On rejoin it adopts the
    // old leader.
    test_prevote(
        &mut cluster,
        FailureType::Partition(&[1, 2, 3], &[3, 4, 5]),
        None,
        (4, true),
        None,
    );
}

#[test]
fn test_prevote_reboot_majority_followers() {
    let mut cluster = new_server_cluster(0, 5);
    // A prevote round will start, but nothing will succeed.
    test_prevote(
        &mut cluster,
        FailureType::Reboot(&[3, 4, 5]),
        1,
        (1, true),
        None,
    );
}

#[test]
fn test_prevote_reboot_minority_followers() {
    let mut cluster = new_server_cluster(0, 5);
    // A prevote round will start, but nothing will succeed until recovery.
    test_prevote(
        &mut cluster,
        FailureType::Reboot(&[4, 5]),
        None,
        (2, false),
        (2, false),
    );
}

// Test isolating a minority of the cluster and make sure that the remove themselves.
fn test_pair_isolated<T: Simulator>(cluster: &mut Cluster<T>) {
    let region = 1;
    let pd_client = Arc::clone(&cluster.pd_client);

    // Given some nodes A, B, C, D, E, we partition the cluster such that D, E are isolated from the rest.
    cluster.run();
    // Choose a predictable leader so we don't accidentally partition the leader.
    cluster.must_transfer_leader(region, new_peer(1, 1));
    cluster.partition(vec![1, 2, 3], vec![4, 5]);

    // Then, add a policy to PD that it should ask the Raft leader to remove the peer from the group.
    pd_client.must_remove_peer(region, new_peer(4, 4));
    pd_client.must_remove_peer(region, new_peer(5, 5));

    // Verify the nodes have self removed.
    cluster.must_remove_region(4, region);
    cluster.must_remove_region(5, region);
}

#[test]
fn test_server_pair_isolated() {
    let mut cluster = new_server_cluster(0, 5);
    test_pair_isolated(&mut cluster);
}

fn test_isolated_follower_leader_does_not_change<T: Simulator>(cluster: &mut Cluster<T>) {
    cluster.run();
    cluster.must_transfer_leader(1, new_peer(1, 1));
    cluster.must_put(b"k1", b"v1");
    let region_status = new_status_request(1, new_peer(1, 1), new_region_leader_cmd());
    let resp = cluster
        .call_command(region_status.clone(), Duration::from_secs(5))
        .unwrap();
    let term = resp.get_header().get_current_term();
    // Isolate peer5.
    cluster.partition(vec![1, 2, 3, 4], vec![5]);
    let election_timeout = cluster.cfg.raft_store.raft_base_tick_interval.0
        * cluster.cfg.raft_store.raft_election_timeout_ticks as u32;
    // Peer5 should not increase its term.
    thread::sleep(election_timeout * 2);
    // Now peer5 can send messages to others
    cluster.clear_send_filters();
    thread::sleep(election_timeout * 2);
    cluster.must_put(b"k1", b"v1");
    // Peer1 is still the leader.
    let leader = cluster.leader_of_region(1);
    assert_eq!(leader, Some(new_peer(1, 1)));
    // And the term is not changed.
    let resp = cluster
        .call_command(region_status, Duration::from_secs(5))
        .unwrap();
    let current_term = resp.get_header().get_current_term();
    assert_eq!(term, current_term);
}

#[test]
fn test_server_isolated_follower_leader_does_not_change() {
    let mut cluster = new_server_cluster(0, 5);
    test_isolated_follower_leader_does_not_change(&mut cluster);
}

fn test_create_peer_from_pre_vote<T: Simulator>(cluster: &mut Cluster<T>) {
    let pd_client = Arc::clone(&cluster.pd_client);
    pd_client.disable_default_operator();

    let r1 = cluster.run_conf_change();
    cluster.must_put(b"k1", b"v1");

    let rx = attach_prevote_notifiers(cluster, 1);
    cluster.add_send_filter(IsolationFilterFactory::new(2));
    pd_client.must_add_peer(r1, new_peer(2, 2));

    if rx.recv_timeout(Duration::from_secs(3)).is_err() {
        panic!("peer 1 should send pre vote");
    }

    // The peer 2 should be created.
    cluster.clear_send_filters();
    must_get_equal(&cluster.get_engine(2), b"k1", b"v1");
}

#[test]
fn test_node_create_peer_from_pre_vote() {
    let mut cluster = new_node_cluster(0, 2);
    cluster.cfg.raft_store.prevote = true;
    test_create_peer_from_pre_vote(&mut cluster);
}
<commit_msg>tests: fix unstatble test test_prevote_reboot_minority_followers (#5731)<commit_after>// Copyright 2016 TiKV Project Authors. Licensed under Apache-2.0.

use std::sync::{atomic::AtomicBool, mpsc, Arc};
use std::thread;
use std::time::Duration;

use raft::eraftpb::MessageType;
use tikv_util::HandyRwLock;

use test_raftstore::*;

enum FailureType<'a> {
    Partition(&'a [u64], &'a [u64]),
    Reboot(&'a [u64]),
}

fn attach_prevote_notifiers<T: Simulator>(cluster: &Cluster<T>, peer: u64) -> mpsc::Receiver<()> {
    // Setup a notifier
    let (tx, rx) = mpsc::channel();
    let response_notifier = Box::new(MessageTypeNotifier::new(
        MessageType::MsgRequestPreVoteResponse,
        tx.clone(),
        Arc::from(AtomicBool::new(true)),
    ));
    let request_notifier = Box::new(MessageTypeNotifier::new(
        MessageType::MsgRequestPreVote,
        tx.clone(),
        Arc::from(AtomicBool::new(true)),
    ));

    cluster.sim.wl().add_send_filter(peer, response_notifier);
    cluster.sim.wl().add_send_filter(peer, request_notifier);

    rx
}

// Validate that prevote is used in elections after partition or reboot of some nodes.
fn test_prevote<T: Simulator>(
    cluster: &mut Cluster<T>,
    failure_type: FailureType<'_>,
    leader_after_failure_id: impl Into<Option<u64>>,
    detect_during_failure: impl Into<Option<(u64, bool)>>,
    detect_during_recovery: impl Into<Option<(u64, bool)>>,
) {
    cluster.cfg.raft_store.prevote = true;
    // To stable the test, we use a large election timeout to make
    // leader's readiness get handle within an election timeout
    configure_for_lease_read(cluster, Some(10), Some(50));

    let leader_id = 1;
    let detect_during_failure = detect_during_failure.into();
    let detect_during_recovery = detect_during_recovery.into();

    // We must start the cluster before adding send filters, otherwise it panics.
    cluster.run();

    cluster.must_transfer_leader(1, new_peer(leader_id, 1));
    cluster.must_put(b"k1", b"v1");

    // Determine how to fail.
    let rx = if let Some((id, _)) = detect_during_failure {
        let rx = attach_prevote_notifiers(cluster, id);
        debug!("Attached failure prevote notifier.");
        Some(rx)
    } else {
        None
    };

    match failure_type {
        FailureType::Partition(majority, minority) => {
            cluster.partition(majority.to_vec(), minority.to_vec());
        }
        FailureType::Reboot(peers) => {
            peers.iter().for_each(|&peer| cluster.stop_node(peer));
        }
    };

    if let (Some(rx), Some((_, should_detect))) = (rx, detect_during_failure) {
        // Once we see a response on the wire we know a prevote round is happening.
        let received = rx.recv_timeout(Duration::from_secs(5));
        debug!("Done with failure prevote notifier, got {:?}", received);
        assert_eq!(
            received.is_ok(),
            should_detect,
            "Sends a PreVote or PreVoteResponse during failure.",
        );
    }

    // Let the cluster recover.
    match failure_type {
        FailureType::Partition(_, _) => {
            cluster.clear_send_filters();
        }
        FailureType::Reboot(peers) => {
            cluster.clear_send_filters();
            peers.iter().for_each(|&peer| {
                cluster.run_node(peer).unwrap();
            });
        }
    };

    // Prepare to listen.
    let rx = if let Some((id, _)) = detect_during_recovery {
        let rx = attach_prevote_notifiers(cluster, id);
        debug!("Attached recovery prevote notifier.");
        Some(rx)
    } else {
        None
    };

    if let Some(leader_id) = leader_after_failure_id.into() {
        cluster.must_transfer_leader(1, new_peer(leader_id, 1));
    };

    // Once we see a response on the wire we know a prevote round is happening.
    if let (Some(rx), Some((_, should_detect))) = (rx, detect_during_failure) {
        let received = rx.recv_timeout(Duration::from_secs(5));
        debug!("Done with recovery prevote notifier, got {:?}", received);

        assert_eq!(
            received.is_ok(),
            should_detect,
            "Sends a PreVote or PreVoteResponse during recovery.",
        );
    };

    cluster.must_put(b"k3", b"v3");
    assert_eq!(cluster.must_get(b"k1"), Some(b"v1".to_vec()));
}

#[test]
fn test_prevote_partition_leader_in_majority_detect_in_majority() {
    let mut cluster = new_server_cluster(0, 5);
    // Since the leader is in the majority and not rebooted, it sees no prevote.
    test_prevote(
        &mut cluster,
        FailureType::Partition(&[1, 2, 3], &[4, 5]),
        None,
        (1, false),
        (1, false),
    );
}

// TODO: Enable detect after failure when we can reliably capture the prevote.
#[test]
fn test_prevote_partition_leader_in_majority_detect_in_minority() {
    let mut cluster = new_server_cluster(0, 5);
    // The follower is in the minority and is part of a prevote process. On rejoin it adopts the
    // old leader.
    test_prevote(
        &mut cluster,
        FailureType::Partition(&[1, 2, 3], &[4, 5]),
        None,
        (4, true),
        None,
    );
}

// TODO: Enable detect after failure when we can reliably capture the prevote.
#[test]
fn test_prevote_partition_leader_in_minority_detect_in_majority() {
    let mut cluster = new_server_cluster(0, 5);
    // The follower is in the minority and is part of a prevote process. On rejoin it adopts the
    // old leader.
    test_prevote(
        &mut cluster,
        FailureType::Partition(&[1, 2], &[3, 4, 5]),
        None,
        (4, true),
        None,
    );
}

// TODO: Enable detect after failure when we can reliably capture the prevote.
#[test]
fn test_prevote_partition_leader_in_minority_detect_in_minority() {
    let mut cluster = new_server_cluster(0, 5);
    // The follower is in the minority and is part of a prevote process. On rejoin it adopts the
    // old leader.
    test_prevote(
        &mut cluster,
        FailureType::Partition(&[1, 2, 3], &[3, 4, 5]),
        None,
        (4, true),
        None,
    );
}

#[test]
fn test_prevote_reboot_majority_followers() {
    let mut cluster = new_server_cluster(0, 5);
    // A prevote round will start, but nothing will succeed.
    test_prevote(
        &mut cluster,
        FailureType::Reboot(&[3, 4, 5]),
        1,
        (1, true),
        None,
    );
}

#[test]
fn test_prevote_reboot_minority_followers() {
    let mut cluster = new_server_cluster(0, 5);
    // A prevote round will start, but nothing will succeed until recovery.
    test_prevote(
        &mut cluster,
        FailureType::Reboot(&[4, 5]),
        None,
        (2, false),
        (2, false),
    );
}

// Test isolating a minority of the cluster and make sure that the remove themselves.
fn test_pair_isolated<T: Simulator>(cluster: &mut Cluster<T>) {
    let region = 1;
    let pd_client = Arc::clone(&cluster.pd_client);

    // Given some nodes A, B, C, D, E, we partition the cluster such that D, E are isolated from the rest.
    cluster.run();
    // Choose a predictable leader so we don't accidentally partition the leader.
    cluster.must_transfer_leader(region, new_peer(1, 1));
    cluster.partition(vec![1, 2, 3], vec![4, 5]);

    // Then, add a policy to PD that it should ask the Raft leader to remove the peer from the group.
    pd_client.must_remove_peer(region, new_peer(4, 4));
    pd_client.must_remove_peer(region, new_peer(5, 5));

    // Verify the nodes have self removed.
    cluster.must_remove_region(4, region);
    cluster.must_remove_region(5, region);
}

#[test]
fn test_server_pair_isolated() {
    let mut cluster = new_server_cluster(0, 5);
    test_pair_isolated(&mut cluster);
}

fn test_isolated_follower_leader_does_not_change<T: Simulator>(cluster: &mut Cluster<T>) {
    cluster.run();
    cluster.must_transfer_leader(1, new_peer(1, 1));
    cluster.must_put(b"k1", b"v1");
    let region_status = new_status_request(1, new_peer(1, 1), new_region_leader_cmd());
    let resp = cluster
        .call_command(region_status.clone(), Duration::from_secs(5))
        .unwrap();
    let term = resp.get_header().get_current_term();
    // Isolate peer5.
    cluster.partition(vec![1, 2, 3, 4], vec![5]);
    let election_timeout = cluster.cfg.raft_store.raft_base_tick_interval.0
        * cluster.cfg.raft_store.raft_election_timeout_ticks as u32;
    // Peer5 should not increase its term.
    thread::sleep(election_timeout * 2);
    // Now peer5 can send messages to others
    cluster.clear_send_filters();
    thread::sleep(election_timeout * 2);
    cluster.must_put(b"k1", b"v1");
    // Peer1 is still the leader.
    let leader = cluster.leader_of_region(1);
    assert_eq!(leader, Some(new_peer(1, 1)));
    // And the term is not changed.
    let resp = cluster
        .call_command(region_status, Duration::from_secs(5))
        .unwrap();
    let current_term = resp.get_header().get_current_term();
    assert_eq!(term, current_term);
}

#[test]
fn test_server_isolated_follower_leader_does_not_change() {
    let mut cluster = new_server_cluster(0, 5);
    test_isolated_follower_leader_does_not_change(&mut cluster);
}

fn test_create_peer_from_pre_vote<T: Simulator>(cluster: &mut Cluster<T>) {
    let pd_client = Arc::clone(&cluster.pd_client);
    pd_client.disable_default_operator();

    let r1 = cluster.run_conf_change();
    cluster.must_put(b"k1", b"v1");

    let rx = attach_prevote_notifiers(cluster, 1);
    cluster.add_send_filter(IsolationFilterFactory::new(2));
    pd_client.must_add_peer(r1, new_peer(2, 2));

    if rx.recv_timeout(Duration::from_secs(3)).is_err() {
        panic!("peer 1 should send pre vote");
    }

    // The peer 2 should be created.
    cluster.clear_send_filters();
    must_get_equal(&cluster.get_engine(2), b"k1", b"v1");
}

#[test]
fn test_node_create_peer_from_pre_vote() {
    let mut cluster = new_node_cluster(0, 2);
    cluster.cfg.raft_store.prevote = true;
    test_create_peer_from_pre_vote(&mut cluster);
}
<|endoftext|>


<commit_before>#[macro_use]
mod macros;

use syn::{Meta, MetaList, MetaNameValue, NestedMeta};

#[test]
fn test_parse_meta_item_word() {
    let input = "hello";

    snapshot!(input as Meta, @r###"
    Path(Path {
        segments: [
            PathSegment {
                ident: "hello",
                arguments: None,
            },
        ],
    })
    "###);
}

#[test]
fn test_parse_meta_name_value() {
    let input = "foo = 5";
    let (inner, meta) = (input, input);

    snapshot!(inner as MetaNameValue, @r###"
    MetaNameValue {
        path: Path {
            segments: [
                PathSegment {
                    ident: "foo",
                    arguments: None,
                },
            ],
        },
        lit: 5,
    }
    "###);

    snapshot!(meta as Meta, @r###"
    Meta::NameValue {
        path: Path {
            segments: [
                PathSegment {
                    ident: "foo",
                    arguments: None,
                },
            ],
        },
        lit: 5,
    }
    "###);

    assert_eq!(meta, inner.into());
}

#[test]
fn test_parse_meta_name_value_with_keyword() {
    let input = "static = 5";
    let (inner, meta) = (input, input);

    snapshot!(inner as MetaNameValue, @r###"
    MetaNameValue {
        path: Path {
            segments: [
                PathSegment {
                    ident: "static",
                    arguments: None,
                },
            ],
        },
        lit: 5,
    }
    "###);

    snapshot!(meta as Meta, @r###"
    Meta::NameValue {
        path: Path {
            segments: [
                PathSegment {
                    ident: "static",
                    arguments: None,
                },
            ],
        },
        lit: 5,
    }
    "###);

    assert_eq!(meta, inner.into());
}

#[test]
fn test_parse_meta_name_value_with_bool() {
    let input = "true = 5";
    let (inner, meta) = (input, input);

    snapshot!(inner as MetaNameValue, @r###"
    MetaNameValue {
        path: Path {
            segments: [
                PathSegment {
                    ident: "true",
                    arguments: None,
                },
            ],
        },
        lit: 5,
    }
    "###);

    snapshot!(meta as Meta, @r###"
    Meta::NameValue {
        path: Path {
            segments: [
                PathSegment {
                    ident: "true",
                    arguments: None,
                },
            ],
        },
        lit: 5,
    }
    "###);

    assert_eq!(meta, inner.into());
}

#[test]
fn test_parse_meta_item_list_lit() {
    let input = "foo(5)";
    let (inner, meta) = (input, input);

    snapshot!(inner as MetaList, @r###"
    MetaList {
        path: Path {
            segments: [
                PathSegment {
                    ident: "foo",
                    arguments: None,
                },
            ],
        },
        nested: [
            Lit(5),
        ],
    }
    "###);

    snapshot!(meta as Meta, @r###"
    Meta::List {
        path: Path {
            segments: [
                PathSegment {
                    ident: "foo",
                    arguments: None,
                },
            ],
        },
        nested: [
            Lit(5),
        ],
    }
    "###);

    assert_eq!(meta, inner.into());
}

#[test]
fn test_parse_meta_item_multiple() {
    let input = "foo(word, name = 5, list(name2 = 6), word2)";
    let (inner, meta) = (input, input);

    snapshot!(inner as MetaList, @r###"
    MetaList {
        path: Path {
            segments: [
                PathSegment {
                    ident: "foo",
                    arguments: None,
                },
            ],
        },
        nested: [
            Meta(Path(Path {
                segments: [
                    PathSegment {
                        ident: "word",
                        arguments: None,
                    },
                ],
            })),
            Meta(Meta::NameValue {
                path: Path {
                    segments: [
                        PathSegment {
                            ident: "name",
                            arguments: None,
                        },
                    ],
                },
                lit: 5,
            }),
            Meta(Meta::List {
                path: Path {
                    segments: [
                        PathSegment {
                            ident: "list",
                            arguments: None,
                        },
                    ],
                },
                nested: [
                    Meta(Meta::NameValue {
                        path: Path {
                            segments: [
                                PathSegment {
                                    ident: "name2",
                                    arguments: None,
                                },
                            ],
                        },
                        lit: 6,
                    }),
                ],
            }),
            Meta(Path(Path {
                segments: [
                    PathSegment {
                        ident: "word2",
                        arguments: None,
                    },
                ],
            })),
        ],
    }
    "###);

    snapshot!(meta as Meta, @r###"
    Meta::List {
        path: Path {
            segments: [
                PathSegment {
                    ident: "foo",
                    arguments: None,
                },
            ],
        },
        nested: [
            Meta(Path(Path {
                segments: [
                    PathSegment {
                        ident: "word",
                        arguments: None,
                    },
                ],
            })),
            Meta(Meta::NameValue {
                path: Path {
                    segments: [
                        PathSegment {
                            ident: "name",
                            arguments: None,
                        },
                    ],
                },
                lit: 5,
            }),
            Meta(Meta::List {
                path: Path {
                    segments: [
                        PathSegment {
                            ident: "list",
                            arguments: None,
                        },
                    ],
                },
                nested: [
                    Meta(Meta::NameValue {
                        path: Path {
                            segments: [
                                PathSegment {
                                    ident: "name2",
                                    arguments: None,
                                },
                            ],
                        },
                        lit: 6,
                    }),
                ],
            }),
            Meta(Path(Path {
                segments: [
                    PathSegment {
                        ident: "word2",
                        arguments: None,
                    },
                ],
            })),
        ],
    }
    "###);

    assert_eq!(meta, inner.into());
}

#[test]
fn test_parse_nested_meta() {
    let input = "5";
    snapshot!(input as NestedMeta, @"Lit(5)");

    let input = "list(name2 = 6)";
    snapshot!(input as NestedMeta, @r###"
    Meta(Meta::List {
        path: Path {
            segments: [
                PathSegment {
                    ident: "list",
                    arguments: None,
                },
            ],
        },
        nested: [
            Meta(Meta::NameValue {
                path: Path {
                    segments: [
                        PathSegment {
                            ident: "name2",
                            arguments: None,
                        },
                    ],
                },
                lit: 6,
            }),
        ],
    })
    "###);
}
<commit_msg>Add test of parsing absolute path as Meta<commit_after>#[macro_use]
mod macros;

use syn::{Meta, MetaList, MetaNameValue, NestedMeta};

#[test]
fn test_parse_meta_item_word() {
    let input = "hello";

    snapshot!(input as Meta, @r###"
    Path(Path {
        segments: [
            PathSegment {
                ident: "hello",
                arguments: None,
            },
        ],
    })
    "###);
}

#[test]
fn test_parse_meta_name_value() {
    let input = "foo = 5";
    let (inner, meta) = (input, input);

    snapshot!(inner as MetaNameValue, @r###"
    MetaNameValue {
        path: Path {
            segments: [
                PathSegment {
                    ident: "foo",
                    arguments: None,
                },
            ],
        },
        lit: 5,
    }
    "###);

    snapshot!(meta as Meta, @r###"
    Meta::NameValue {
        path: Path {
            segments: [
                PathSegment {
                    ident: "foo",
                    arguments: None,
                },
            ],
        },
        lit: 5,
    }
    "###);

    assert_eq!(meta, inner.into());
}

#[test]
fn test_parse_meta_name_value_with_keyword() {
    let input = "static = 5";
    let (inner, meta) = (input, input);

    snapshot!(inner as MetaNameValue, @r###"
    MetaNameValue {
        path: Path {
            segments: [
                PathSegment {
                    ident: "static",
                    arguments: None,
                },
            ],
        },
        lit: 5,
    }
    "###);

    snapshot!(meta as Meta, @r###"
    Meta::NameValue {
        path: Path {
            segments: [
                PathSegment {
                    ident: "static",
                    arguments: None,
                },
            ],
        },
        lit: 5,
    }
    "###);

    assert_eq!(meta, inner.into());
}

#[test]
fn test_parse_meta_name_value_with_bool() {
    let input = "true = 5";
    let (inner, meta) = (input, input);

    snapshot!(inner as MetaNameValue, @r###"
    MetaNameValue {
        path: Path {
            segments: [
                PathSegment {
                    ident: "true",
                    arguments: None,
                },
            ],
        },
        lit: 5,
    }
    "###);

    snapshot!(meta as Meta, @r###"
    Meta::NameValue {
        path: Path {
            segments: [
                PathSegment {
                    ident: "true",
                    arguments: None,
                },
            ],
        },
        lit: 5,
    }
    "###);

    assert_eq!(meta, inner.into());
}

#[test]
fn test_parse_meta_item_list_lit() {
    let input = "foo(5)";
    let (inner, meta) = (input, input);

    snapshot!(inner as MetaList, @r###"
    MetaList {
        path: Path {
            segments: [
                PathSegment {
                    ident: "foo",
                    arguments: None,
                },
            ],
        },
        nested: [
            Lit(5),
        ],
    }
    "###);

    snapshot!(meta as Meta, @r###"
    Meta::List {
        path: Path {
            segments: [
                PathSegment {
                    ident: "foo",
                    arguments: None,
                },
            ],
        },
        nested: [
            Lit(5),
        ],
    }
    "###);

    assert_eq!(meta, inner.into());
}

#[test]
fn test_parse_meta_item_multiple() {
    let input = "foo(word, name = 5, list(name2 = 6), word2)";
    let (inner, meta) = (input, input);

    snapshot!(inner as MetaList, @r###"
    MetaList {
        path: Path {
            segments: [
                PathSegment {
                    ident: "foo",
                    arguments: None,
                },
            ],
        },
        nested: [
            Meta(Path(Path {
                segments: [
                    PathSegment {
                        ident: "word",
                        arguments: None,
                    },
                ],
            })),
            Meta(Meta::NameValue {
                path: Path {
                    segments: [
                        PathSegment {
                            ident: "name",
                            arguments: None,
                        },
                    ],
                },
                lit: 5,
            }),
            Meta(Meta::List {
                path: Path {
                    segments: [
                        PathSegment {
                            ident: "list",
                            arguments: None,
                        },
                    ],
                },
                nested: [
                    Meta(Meta::NameValue {
                        path: Path {
                            segments: [
                                PathSegment {
                                    ident: "name2",
                                    arguments: None,
                                },
                            ],
                        },
                        lit: 6,
                    }),
                ],
            }),
            Meta(Path(Path {
                segments: [
                    PathSegment {
                        ident: "word2",
                        arguments: None,
                    },
                ],
            })),
        ],
    }
    "###);

    snapshot!(meta as Meta, @r###"
    Meta::List {
        path: Path {
            segments: [
                PathSegment {
                    ident: "foo",
                    arguments: None,
                },
            ],
        },
        nested: [
            Meta(Path(Path {
                segments: [
                    PathSegment {
                        ident: "word",
                        arguments: None,
                    },
                ],
            })),
            Meta(Meta::NameValue {
                path: Path {
                    segments: [
                        PathSegment {
                            ident: "name",
                            arguments: None,
                        },
                    ],
                },
                lit: 5,
            }),
            Meta(Meta::List {
                path: Path {
                    segments: [
                        PathSegment {
                            ident: "list",
                            arguments: None,
                        },
                    ],
                },
                nested: [
                    Meta(Meta::NameValue {
                        path: Path {
                            segments: [
                                PathSegment {
                                    ident: "name2",
                                    arguments: None,
                                },
                            ],
                        },
                        lit: 6,
                    }),
                ],
            }),
            Meta(Path(Path {
                segments: [
                    PathSegment {
                        ident: "word2",
                        arguments: None,
                    },
                ],
            })),
        ],
    }
    "###);

    assert_eq!(meta, inner.into());
}

#[test]
fn test_parse_nested_meta() {
    let input = "5";
    snapshot!(input as NestedMeta, @"Lit(5)");

    let input = "list(name2 = 6)";
    snapshot!(input as NestedMeta, @r###"
    Meta(Meta::List {
        path: Path {
            segments: [
                PathSegment {
                    ident: "list",
                    arguments: None,
                },
            ],
        },
        nested: [
            Meta(Meta::NameValue {
                path: Path {
                    segments: [
                        PathSegment {
                            ident: "name2",
                            arguments: None,
                        },
                    ],
                },
                lit: 6,
            }),
        ],
    })
    "###);
}

#[test]
fn test_parse_path() {
    let input = "::serde::Serialize";
    snapshot!(input as Meta, @r###"
    Path(Path {
        leading_colon: Some,
        segments: [
            PathSegment {
                ident: "serde",
                arguments: None,
            },
            PathSegment {
                ident: "Serialize",
                arguments: None,
            },
        ],
    })
    "###);

    let input = "::serde::Serialize";
    snapshot!(input as NestedMeta, @r###"
    Meta(Path(Path {
        leading_colon: Some,
        segments: [
            PathSegment {
                ident: "serde",
                arguments: None,
            },
            PathSegment {
                ident: "Serialize",
                arguments: None,
            },
        ],
    }))
    "###);
}
<|endoftext|>


<commit_before>use value::AMQPValue;

use std::collections::BTreeMap;
use std::fmt;

#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub enum AMQPType {
    Boolean,
    ShortShortInt,
    ShortShortUInt,
    ShortInt,
    ShortUInt,
    LongInt,
    LongUInt,
    LongLongInt,
    LongLongUInt,
    Float,
    Double,
    DecimalValue,
    ShortString,
    LongString,
    FieldArray,
    Timestamp,
    FieldTable,
    Void,
}

impl AMQPType {
    pub fn from_id(id: char) -> Option<AMQPType> {
        match id {
            't' => Some(AMQPType::Boolean),
            'b' => Some(AMQPType::ShortShortInt),
            'B' => Some(AMQPType::ShortShortUInt),
            'U' => Some(AMQPType::ShortInt),
            'u' => Some(AMQPType::ShortUInt),
            'I' => Some(AMQPType::LongInt),
            'i' => Some(AMQPType::LongUInt),
            'L' => Some(AMQPType::LongLongInt),
            'l' => Some(AMQPType::LongLongUInt),
            'f' => Some(AMQPType::Float),
            'd' => Some(AMQPType::Double),
            'D' => Some(AMQPType::DecimalValue),
            's' => Some(AMQPType::ShortString),
            'S' => Some(AMQPType::LongString),
            'A' => Some(AMQPType::FieldArray),
            'T' => Some(AMQPType::Timestamp),
            'F' => Some(AMQPType::FieldTable),
            'V' => Some(AMQPType::Void),
            _   => None,
        }
    }

    pub fn get_id(&self) -> char {
        match *self {
            AMQPType::Boolean => 't',
            AMQPType::ShortShortInt => 'b',
            AMQPType::ShortShortUInt => 'B',
            AMQPType::ShortInt => 'U',
            AMQPType::ShortUInt => 'u',
            AMQPType::LongInt => 'I',
            AMQPType::LongUInt => 'i',
            AMQPType::LongLongInt => 'L',
            AMQPType::LongLongUInt => 'l',
            AMQPType::Float => 'f',
            AMQPType::Double => 'd',
            AMQPType::DecimalValue => 'D',
            AMQPType::ShortString => 's',
            AMQPType::LongString => 'S',
            AMQPType::FieldArray => 'A',
            AMQPType::Timestamp => 'T',
            AMQPType::FieldTable => 'F',
            AMQPType::Void => 'V',
        }
    }

    pub fn to_string(&self) -> String {
        format!("{}", self)
    }
}

impl fmt::Display for AMQPType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:?}", self)
    }
}

pub type Boolean        = bool;
pub type ShortShortInt  = i8;
pub type ShortShortUInt = u8;
pub type ShortInt       = i16;
pub type ShortUInt      = u16;
pub type LongInt        = i32;
pub type LongUInt       = u32;
pub type LongLongInt    = i64;
pub type LongLongUInt   = u64;
pub type Float          = f32;
pub type Double         = f64;
pub type ShortString    = String; /* TODO: don't allow size >= 255 */
pub type LongString     = String;
pub type FieldArray     = Vec<AMQPValue>;
pub type Timestamp      = LongLongUInt;
pub type FieldTable     = BTreeMap<ShortString, AMQPValue>;
pub type Void           = ();

#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub struct DecimalValue {
    pub scale: ShortShortUInt,
    pub value: LongUInt,
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_type_to_string() {
        assert_eq!(AMQPType::Boolean.to_string(), "Boolean");
        assert_eq!(AMQPType::Void.to_string(),    "Void");
    }
}
<commit_msg>types: add a test for from_id<commit_after>use value::AMQPValue;

use std::collections::BTreeMap;
use std::fmt;

#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub enum AMQPType {
    Boolean,
    ShortShortInt,
    ShortShortUInt,
    ShortInt,
    ShortUInt,
    LongInt,
    LongUInt,
    LongLongInt,
    LongLongUInt,
    Float,
    Double,
    DecimalValue,
    ShortString,
    LongString,
    FieldArray,
    Timestamp,
    FieldTable,
    Void,
}

impl AMQPType {
    pub fn from_id(id: char) -> Option<AMQPType> {
        match id {
            't' => Some(AMQPType::Boolean),
            'b' => Some(AMQPType::ShortShortInt),
            'B' => Some(AMQPType::ShortShortUInt),
            'U' => Some(AMQPType::ShortInt),
            'u' => Some(AMQPType::ShortUInt),
            'I' => Some(AMQPType::LongInt),
            'i' => Some(AMQPType::LongUInt),
            'L' => Some(AMQPType::LongLongInt),
            'l' => Some(AMQPType::LongLongUInt),
            'f' => Some(AMQPType::Float),
            'd' => Some(AMQPType::Double),
            'D' => Some(AMQPType::DecimalValue),
            's' => Some(AMQPType::ShortString),
            'S' => Some(AMQPType::LongString),
            'A' => Some(AMQPType::FieldArray),
            'T' => Some(AMQPType::Timestamp),
            'F' => Some(AMQPType::FieldTable),
            'V' => Some(AMQPType::Void),
            _   => None,
        }
    }

    pub fn get_id(&self) -> char {
        match *self {
            AMQPType::Boolean => 't',
            AMQPType::ShortShortInt => 'b',
            AMQPType::ShortShortUInt => 'B',
            AMQPType::ShortInt => 'U',
            AMQPType::ShortUInt => 'u',
            AMQPType::LongInt => 'I',
            AMQPType::LongUInt => 'i',
            AMQPType::LongLongInt => 'L',
            AMQPType::LongLongUInt => 'l',
            AMQPType::Float => 'f',
            AMQPType::Double => 'd',
            AMQPType::DecimalValue => 'D',
            AMQPType::ShortString => 's',
            AMQPType::LongString => 'S',
            AMQPType::FieldArray => 'A',
            AMQPType::Timestamp => 'T',
            AMQPType::FieldTable => 'F',
            AMQPType::Void => 'V',
        }
    }

    pub fn to_string(&self) -> String {
        format!("{}", self)
    }
}

impl fmt::Display for AMQPType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{:?}", self)
    }
}

pub type Boolean        = bool;
pub type ShortShortInt  = i8;
pub type ShortShortUInt = u8;
pub type ShortInt       = i16;
pub type ShortUInt      = u16;
pub type LongInt        = i32;
pub type LongUInt       = u32;
pub type LongLongInt    = i64;
pub type LongLongUInt   = u64;
pub type Float          = f32;
pub type Double         = f64;
pub type ShortString    = String; /* TODO: don't allow size >= 255 */
pub type LongString     = String;
pub type FieldArray     = Vec<AMQPValue>;
pub type Timestamp      = LongLongUInt;
pub type FieldTable     = BTreeMap<ShortString, AMQPValue>;
pub type Void           = ();

#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub struct DecimalValue {
    pub scale: ShortShortUInt,
    pub value: LongUInt,
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_type_from_id() {
        assert_eq!(AMQPType::from_id('T'), Some(AMQPType::Timestamp));
        assert_eq!(AMQPType::from_id('z'), None);
    }

    #[test]
    fn test_type_to_string() {
        assert_eq!(AMQPType::Boolean.to_string(), "Boolean");
        assert_eq!(AMQPType::Void.to_string(),    "Void");
    }
}
<|endoftext|>