Add tests for Color::parse

This commit is contained in:
Alexandre Bury 2020-01-08 17:20:00 -08:00
parent 45a9d54801
commit 29f85a4398

View File

@ -155,14 +155,14 @@ impl Color {
/// * `"#123456"` becomes `Color::Rgb(0x12, 0x34, 0x56)`
pub fn parse(value: &str) -> Option<Self> {
Some(match value {
"black" => Color::Dark(BaseColor::Black),
"red" => Color::Dark(BaseColor::Red),
"green" => Color::Dark(BaseColor::Green),
"yellow" => Color::Dark(BaseColor::Yellow),
"blue" => Color::Dark(BaseColor::Blue),
"magenta" => Color::Dark(BaseColor::Magenta),
"cyan" => Color::Dark(BaseColor::Cyan),
"white" => Color::Dark(BaseColor::White),
"dark black" | "black" => Color::Dark(BaseColor::Black),
"dark red" | "red" => Color::Dark(BaseColor::Red),
"dark green" | "green" => Color::Dark(BaseColor::Green),
"dark yellow" | "yellow" => Color::Dark(BaseColor::Yellow),
"dark blue" | "blue" => Color::Dark(BaseColor::Blue),
"dark magenta" | "magenta" => Color::Dark(BaseColor::Magenta),
"dark cyan" | "cyan" => Color::Dark(BaseColor::Cyan),
"dark white" | "white" => Color::Dark(BaseColor::White),
"light black" => Color::Light(BaseColor::Black),
"light red" => Color::Light(BaseColor::Red),
"light green" => Color::Light(BaseColor::Green),
@ -172,24 +172,23 @@ impl Color {
"light cyan" => Color::Light(BaseColor::Cyan),
"light white" => Color::Light(BaseColor::White),
"default" => Color::TerminalDefault,
value => return Color::parse_special(value),
value => {
return parse_special(value).or_else(|| {
log::warn!("Could not parse color `{}`.", value);
None
})
}
})
}
}
fn parse_special(value: &str) -> Option<Color> {
fn parse_special(value: &str) -> Option<Color> {
if value.starts_with('#') {
let value = &value[1..];
// Compute per-color length, and amplitude
let (l, multiplier) = match value.len() {
6 => (2, 1),
3 => (1, 17),
_ => panic!("Cannot parse color: {}", value),
};
let r = load_hex(&value[0..l]) * multiplier;
let g = load_hex(&value[l..2 * l]) * multiplier;
let b = load_hex(&value[2 * l..3 * l]) * multiplier;
Some(Color::Rgb(r as u8, g as u8, b as u8))
parse_hex(&value[1..])
} else if value.starts_with("0x") {
parse_hex(&value[2..])
} else if value.len() == 6 {
parse_hex(value)
} else if value.len() == 3 {
// RGB values between 0 and 5 maybe?
// Like 050 for green
@ -198,47 +197,111 @@ impl Color {
assert_eq!(rgb.len(), 3);
if rgb.iter().all(|&i| i >= 0 && i < 6) {
Some(Color::RgbLowRes(
rgb[0] as u8,
rgb[1] as u8,
rgb[2] as u8,
))
Some(Color::RgbLowRes(rgb[0] as u8, rgb[1] as u8, rgb[2] as u8))
} else {
None
}
} else {
None
}
}
}
fn parse_hex(value: &str) -> Option<Color> {
// Compute per-color length, and amplitude
let (l, multiplier) = match value.len() {
6 => (2, 1),
3 => (1, 17),
_ => return None,
};
let r = load_hex(&value[0..l]) * multiplier;
let g = load_hex(&value[l..2 * l]) * multiplier;
let b = load_hex(&value[2 * l..3 * l]) * multiplier;
Some(Color::Rgb(r as u8, g as u8, b as u8))
}
/// Loads a hexadecimal code
fn load_hex(s: &str) -> u16 {
let mut sum = 0;
for c in s.chars() {
sum *= 16;
sum += match c {
n @ '0'..='9' => n as i16 - '0' as i16,
n @ 'a'..='f' => n as i16 - 'a' as i16 + 10,
n @ 'A'..='F' => n as i16 - 'A' as i16 + 10,
_ => 0,
};
s.chars()
.rev()
.filter_map(|c| {
Some(match c {
'0'..='9' => c as u16 - '0' as u16,
'a'..='f' => c as u16 - 'a' as u16 + 10,
'A'..='F' => c as u16 - 'A' as u16 + 10,
other => {
log::warn!(
"Invalid character `{}` in hexadecimal value `{}`.",
other,
s
);
return None;
}
sum as u16
})
})
.enumerate()
.map(|(i, c)| c * 16u16.pow(i as u32))
.sum()
}
#[cfg(test)]
mod tests {
use super::Color;
#[test]
fn test_256_colors() {
// Make sure Color::from_256colors never panics
use super::Color;
// TODO: use inclusive range when it gets stable
for i in 0..256u16 {
Color::from_256colors(i as u8);
for i in 0..=255u8 {
Color::from_256colors(i);
}
}
#[test]
fn test_parse() {
assert_eq!(Color::parse("#fff"), Some(Color::Rgb(255, 255, 255)));
assert_eq!(
Color::parse("#abcdef"),
Some(Color::Rgb(0xab, 0xcd, 0xef))
);
assert_eq!(
Color::parse("0xFEDCBA"),
Some(Color::Rgb(0xfe, 0xdc, 0xba))
);
}
#[test]
fn test_low_res() {
// Make sure Color::low_res always works with valid ranges.
for r in 0..=5 {
for g in 0..=5 {
for b in 0..=5 {
assert!(
Color::low_res(r, g, b).is_some(),
"Could not create lowres color {}:{}:{}",
r,
g,
b,
);
}
}
}
for r in 6..=10 {
for g in 6..=10 {
for b in 6..=10 {
assert_eq!(
Color::low_res(r, g, b),
None,
"Created invalid lowres color {}:{}:{}",
r,
g,
b,
);
}
}
}
}
}