kbvm 0.1.5

An implementation of the XKB specification
Documentation
use {
    crate::{
        keysym::{
            Keysym,
            generated::{LEN, LONGEST_NAME},
        },
        syms,
    },
    bstr::ByteSlice,
    libxkbcommon_test_linker::{
        xkb_keysym_from_name, xkb_keysym_get_name, xkb_keysym_to_lower, xkb_keysym_to_upper,
        xkb_keysym_to_utf32, xkb_utf32_to_keysym,
    },
};

#[test]
fn keysyms_len() {
    assert_eq!(Keysym::all().count(), LEN);
}

#[test]
fn from_char() {
    for cp in 0..=0x10ffff {
        let Some(c) = char::from_u32(cp) else {
            continue;
        };
        let l = Keysym::from_char(c).0;
        let r = xkb_utf32_to_keysym(c as _);
        if l != r {
            panic!("for {c:?}: self=0x{l:x}, xkbcommon=0x{r:x}");
        }
    }
}

#[test]
fn to_char() {
    for sym in Keysym::all() {
        assert_eq!(
            sym.char().unwrap_or(0 as char) as u32,
            xkb_keysym_to_utf32(sym.0),
            "{:?} = 0x{:x}",
            sym,
            sym.0,
        );
    }
}

#[test]
fn name() {
    let mut buf = [0; LONGEST_NAME + 1];
    for sym in Keysym::all() {
        let res = unsafe { xkb_keysym_get_name(sym.0, buf.as_mut_ptr(), buf.len()) };
        if res < 0 {
            panic!("xkb_keysym_get_name doesn't know about {:?}", sym);
        }
        let xkb = &buf[..res as usize];
        assert_eq!(
            sym.name().unwrap(),
            xkb.as_bstr(),
            "{:?} = 0x{:x}",
            sym,
            sym.0,
        );
    }
}

#[test]
fn from_name() {
    let mut buf = [0; LONGEST_NAME + 1];
    for sym in Keysym::all() {
        assert_eq!(
            Keysym::from_str(sym.name().unwrap()).unwrap(),
            sym,
            "{:?} = 0x{:x}",
            sym,
            sym.0,
        );
        let name = sym.name().unwrap();
        buf[..name.len()].copy_from_slice(name.as_bytes());
        buf[name.len()] = 0;
        let ks = unsafe { xkb_keysym_from_name(buf.as_ptr(), 0) };
        assert_eq!(ks, sym.0, "{:?} = 0x{:x}", sym, sym.0,);
    }
}

#[test]
fn from_name_xf86() {
    let mut buf = [0; LONGEST_NAME + 2];
    for sym in Keysym::all() {
        let Some(suffix) = sym.name().unwrap().strip_prefix("XF86") else {
            continue;
        };
        let name = format!("XF86_{suffix}");
        assert_eq!(
            Keysym::from_str(&name).unwrap(),
            sym,
            "{:?} = 0x{:x}",
            sym,
            sym.0,
        );
        buf[..name.len()].copy_from_slice(name.as_bytes());
        buf[name.len()] = 0;
        let ks = unsafe { xkb_keysym_from_name(buf.as_ptr(), 0) };
        assert_eq!(ks, sym.0, "{:?} = 0x{:x}", sym, sym.0,);
    }
}

#[test]
fn from_name_insensitive() {
    let mut buf = [0; LONGEST_NAME + 1];
    for sym in Keysym::all() {
        let name = sym.name().unwrap().to_ascii_uppercase();
        let l = Keysym::from_str_insensitive(&name).expect(&name);
        buf[..name.len()].copy_from_slice(name.as_bytes());
        buf[name.len()] = 0;
        let r = unsafe { xkb_keysym_from_name(buf.as_ptr(), 1) };
        assert_eq!(l.0, r, "{:?} = 0x{:x} ({name})", sym, sym.0,);
    }
}

#[test]
fn from_name_numbers() {
    let mut buf = [0; 10];
    for i in 0..0x20000 {
        let n1 = format!("U{:x}", i);
        let n2 = format!("u{:x}", i);
        let n3 = format!("0x{:x}", i);
        let n4 = format!("0X{:x}", i);
        for (n, case_insensitive) in [(n1, false), (n2, true), (n3, false), (n4, true)] {
            let res = match case_insensitive {
                true => Keysym::from_str_insensitive(&n),
                false => Keysym::from_str(&n),
            };
            let l = res.map(|v| v.0).unwrap_or_default();
            buf[..n.len()].copy_from_slice(n.as_bytes());
            buf[n.len()] = 0;
            let r = unsafe { xkb_keysym_from_name(buf.as_ptr(), case_insensitive as _) };
            assert_eq!(l, r, "0x{:x} ({n})", l);
        }
    }
}

#[test]
fn to_upper() {
    for sym in Keysym::all() {
        assert_eq!(
            sym.to_uppercase().0,
            xkb_keysym_to_upper(sym.0),
            "{:?} = 0x{:x}",
            sym,
            sym.0,
        );
    }
}

#[test]
fn to_lower() {
    for sym in Keysym::all() {
        assert_eq!(
            sym.to_lowercase().0,
            xkb_keysym_to_lower(sym.0),
            "{:?} = 0x{:x}",
            sym,
            sym.0,
        );
    }
}

#[test]
fn ssharp_is_lower() {
    assert!(syms::ssharp.is_lowercase());
}

#[test]
fn double_sided_iter() {
    let expected: Vec<_> = Keysym::all().collect();
    let mut actual: Vec<_> = Keysym::all().rev().collect();
    actual.reverse();
    assert_eq!(expected, actual);
}

#[test]
fn invalid_unicode_case_conv() {
    let sym = Keysym(0x01_ff_ff_ff);
    assert_eq!(sym.to_uppercase(), sym);
    assert_eq!(sym.to_lowercase(), sym);
}

#[test]
fn small_letter_dotless_i() {
    let upper = Keysym(0x01_00_01_31);
    let lower = Keysym(0x49);
    assert_eq!(upper.to_uppercase(), lower);
}

#[test]
fn is_in_unicode_range() {
    let sym = Keysym(0x01_00_01_31);
    assert!(sym.is_in_unicode_range());
    let sym = Keysym(0x31);
    assert!(sym.is_not_in_unicode_range());
}

#[test]
fn is_well_known() {
    assert!(syms::kana_A.is_well_known());
    assert!(Keysym(0x01_ff_ff_ff).is_not_well_known());
}

#[test]
fn is_valid() {
    assert!(syms::kana_A.is_valid());
    assert!(Keysym(!0).is_invalid());
    assert!(Keysym(0x01_ff_ff_ff).is_invalid());
}

#[test]
fn is_lowercase() {
    assert!(syms::a.is_lowercase());
    assert!(!syms::A.is_lowercase());
    assert!(!Keysym(!0).is_lowercase());
    assert!(!Keysym(0x01_ff_ff_ff).is_lowercase());
}

#[test]
fn is_uppercase() {
    assert!(syms::A.is_uppercase());
    assert!(!syms::a.is_uppercase());
    assert!(!Keysym(!0).is_uppercase());
    assert!(!Keysym(0x01_ff_ff_ff).is_uppercase());
}

#[test]
fn very_long_name() {
    let name = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
    assert!(Keysym::from_str(name).is_none());
    assert!(Keysym::from_str_insensitive(name).is_none());
}

#[test]
fn early_unicode() {
    let name = "U55";
    assert_eq!(Keysym::from_str(name), Some(Keysym(0x55)));
}

#[test]
fn invalid_unicode() {
    let name = "UAAX";
    assert_eq!(Keysym::from_str(name), None);
}

#[test]
fn xf86_case_insensitive() {
    let name = "xf86_10cHANNELSdOWN";
    assert_eq!(
        Keysym::from_str_insensitive(name),
        Some(syms::XF8610ChannelsDown)
    );
}

#[test]
fn parse() {
    let name = "XF8610ChannelsDown";
    let keysym = name.parse::<Keysym>();
    assert_eq!(keysym.ok(), Some(syms::XF8610ChannelsDown));
}

#[test]
fn debug_invalid() {
    let s = format!("{:?}", Keysym(0xff_ff_ff_ff));
    assert_eq!(s, "0xffffffff");
}

#[test]
fn display() {
    assert_eq!(syms::kana_A.to_string(), "kana_A");
    assert_eq!(Keysym::from_char('').to_string(), "U8a9e");
    assert_eq!(Keysym(0xff_ff_ff_ff).to_string(), "0xffffffff");
}