10
Cargo.lock
generated
10
Cargo.lock
generated
@@ -1316,6 +1316,7 @@ dependencies = [
|
||||
"tempfile",
|
||||
"textwrap",
|
||||
"uu_blockdev",
|
||||
"uu_cal",
|
||||
"uu_chcpu",
|
||||
"uu_ctrlaltdel",
|
||||
"uu_dmesg",
|
||||
@@ -1351,6 +1352,15 @@ dependencies = [
|
||||
"uucore",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uu_cal"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"clap",
|
||||
"uucore",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uu_chcpu"
|
||||
version = "0.0.1"
|
||||
|
||||
@@ -27,6 +27,7 @@ uudoc = []
|
||||
|
||||
feat_common_core = [
|
||||
"blockdev",
|
||||
"cal",
|
||||
"chcpu",
|
||||
"ctrlaltdel",
|
||||
"dmesg",
|
||||
@@ -48,6 +49,7 @@ feat_common_core = [
|
||||
]
|
||||
|
||||
[workspace.dependencies]
|
||||
chrono = "0.4"
|
||||
clap = { version = "4.4", features = ["wrap_help", "cargo"] }
|
||||
clap_complete = "4.4"
|
||||
clap_mangen = "0.2"
|
||||
@@ -92,6 +94,7 @@ uucore = { workspace = true }
|
||||
|
||||
#
|
||||
blockdev = { optional = true, version = "0.0.1", package = "uu_blockdev", path = "src/uu/blockdev" }
|
||||
cal = { optional = true, version = "0.0.1", package = "uu_cal", path = "src/uu/cal" }
|
||||
chcpu = { optional = true, version = "0.0.1", package = "uu_chcpu", path = "src/uu/chcpu" }
|
||||
ctrlaltdel = { optional = true, version = "0.0.1", package = "uu_ctrlaltdel", path = "src/uu/ctrlaltdel" }
|
||||
dmesg = { optional = true, version = "0.0.1", package = "uu_dmesg", path = "src/uu/dmesg" }
|
||||
|
||||
18
src/uu/cal/Cargo.toml
Normal file
18
src/uu/cal/Cargo.toml
Normal file
@@ -0,0 +1,18 @@
|
||||
[package]
|
||||
name = "uu_cal"
|
||||
version = "0.0.1"
|
||||
description = "cal ~ display a calendar"
|
||||
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
path = "src/cal.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "cal"
|
||||
path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
chrono = { workspace = true }
|
||||
clap = { workspace = true }
|
||||
uucore = { workspace = true }
|
||||
7
src/uu/cal/cal.md
Normal file
7
src/uu/cal/cal.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# cal
|
||||
|
||||
```
|
||||
cal [options] [[[day] month] year]
|
||||
```
|
||||
|
||||
Display a calendar
|
||||
354
src/uu/cal/src/cal.rs
Normal file
354
src/uu/cal/src/cal.rs
Normal file
@@ -0,0 +1,354 @@
|
||||
// This file is part of the uutils util-linux package.
|
||||
//
|
||||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
|
||||
use chrono::{Datelike, Local, NaiveDate, Weekday};
|
||||
use clap::{crate_version, value_parser, Arg, ArgAction, ArgMatches, Command};
|
||||
use std::io::IsTerminal;
|
||||
use uucore::{error::UResult, format_usage, help_about, help_usage};
|
||||
|
||||
const ABOUT: &str = help_about!("cal.md");
|
||||
const USAGE: &str = help_usage!("cal.md");
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
enum DisplayMode {
|
||||
ThreeMonths,
|
||||
Year,
|
||||
NMonths(u32),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct CalOptions {
|
||||
date: NaiveDate,
|
||||
highlight_date: NaiveDate,
|
||||
display_mode: DisplayMode,
|
||||
monday_first: bool,
|
||||
julian: bool,
|
||||
week_numbers: bool,
|
||||
color: bool,
|
||||
}
|
||||
|
||||
const NUM_CALENDAR_LINES: usize = 8;
|
||||
const NUM_SPACES_BETWEEN_CALENDARS: usize = 3;
|
||||
const MAX_CALENDARS_SIDE_BY_SIDE: usize = 3;
|
||||
|
||||
fn calculate_field_widths(options: &CalOptions) -> (usize, usize) {
|
||||
let day_width = if options.julian { 3 } else { 2 };
|
||||
let mut line_width = 7 * (day_width + 1) - 1;
|
||||
if options.week_numbers {
|
||||
line_width += 3;
|
||||
}
|
||||
(day_width, line_width)
|
||||
}
|
||||
|
||||
#[uucore::main]
|
||||
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||
let matches = uu_app().try_get_matches_from(args)?;
|
||||
let options = parse_options(&matches)?;
|
||||
|
||||
let date = NaiveDate::from_ymd_opt(options.date.year(), options.date.month(), 1).unwrap();
|
||||
let months: Vec<NaiveDate> = match options.display_mode {
|
||||
DisplayMode::Year => {
|
||||
let (_, line_width) = calculate_field_widths(&options);
|
||||
let total_width = MAX_CALENDARS_SIDE_BY_SIDE * line_width
|
||||
+ (MAX_CALENDARS_SIDE_BY_SIDE - 1) * NUM_SPACES_BETWEEN_CALENDARS;
|
||||
println!("{:^width$}", options.date.year(), width = total_width);
|
||||
println!();
|
||||
|
||||
(1..=12)
|
||||
.map(|month| NaiveDate::from_ymd_opt(options.date.year(), month, 1).unwrap())
|
||||
.collect()
|
||||
}
|
||||
DisplayMode::ThreeMonths => {
|
||||
vec![
|
||||
date - chrono::Months::new(1),
|
||||
date,
|
||||
date + chrono::Months::new(1),
|
||||
]
|
||||
}
|
||||
DisplayMode::NMonths(count) => (0..count).map(|x| date + chrono::Months::new(x)).collect(),
|
||||
};
|
||||
print_months_side_by_side(&months, &options);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_options(matches: &ArgMatches) -> UResult<CalOptions> {
|
||||
let now = Local::now();
|
||||
|
||||
let args: Vec<&str> = matches
|
||||
.get_many::<String>("args")
|
||||
.unwrap_or_default()
|
||||
.map(|s| s.as_str())
|
||||
.collect();
|
||||
|
||||
let mut year_mode = false;
|
||||
let mut full_date_provided = false;
|
||||
|
||||
let date = match args.len() {
|
||||
0 => now.date_naive(),
|
||||
1 => {
|
||||
// One argument - if numeric, a year, else a month
|
||||
if args[0].parse::<i32>().is_ok() {
|
||||
year_mode = true;
|
||||
try_parse_date(args[0], "1", "1")?
|
||||
} else {
|
||||
try_parse_date(&now.year().to_string(), args[0], "1")?
|
||||
}
|
||||
}
|
||||
2 => {
|
||||
// month year
|
||||
try_parse_date(args[1], args[0], "1")?
|
||||
}
|
||||
3 => {
|
||||
// day month year
|
||||
full_date_provided = true;
|
||||
try_parse_date(args[2], args[1], args[0])?
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let highlight_date = if full_date_provided {
|
||||
date
|
||||
} else {
|
||||
now.date_naive()
|
||||
};
|
||||
|
||||
let display_mode = if year_mode || matches.get_flag("year") {
|
||||
DisplayMode::Year
|
||||
} else if matches.get_flag("twelve") {
|
||||
DisplayMode::NMonths(12)
|
||||
} else if matches.get_flag("three") {
|
||||
DisplayMode::ThreeMonths
|
||||
} else if let Some(count) = matches.get_one::<u32>("months").cloned() {
|
||||
DisplayMode::NMonths(count.max(1))
|
||||
} else {
|
||||
DisplayMode::NMonths(1)
|
||||
};
|
||||
|
||||
Ok(CalOptions {
|
||||
date,
|
||||
highlight_date,
|
||||
display_mode,
|
||||
monday_first: matches.get_flag("monday"),
|
||||
julian: matches.get_flag("julian"),
|
||||
week_numbers: matches.get_flag("week"),
|
||||
color: match matches.get_one::<String>("color").unwrap().as_str() {
|
||||
"always" => true,
|
||||
"never" => false,
|
||||
"auto" => std::io::stdout().is_terminal(),
|
||||
_ => unreachable!(),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
fn try_parse_date(year: &str, month: &str, day: &str) -> UResult<NaiveDate> {
|
||||
let date_str = format!("{}-{}-{}", year, month, day);
|
||||
let formats = [
|
||||
"%Y-%m-%d", // "1992-8-25"
|
||||
"%Y-%B-%d", // "1992-august-25"
|
||||
];
|
||||
|
||||
for format in &formats {
|
||||
if let Ok(date) = NaiveDate::parse_from_str(&date_str, format) {
|
||||
return Ok(date);
|
||||
}
|
||||
}
|
||||
|
||||
Err(uucore::error::USimpleError::new(1, "invalid date"))
|
||||
}
|
||||
|
||||
fn print_months_side_by_side(months: &[NaiveDate], options: &CalOptions) {
|
||||
for chunk in months.chunks(MAX_CALENDARS_SIDE_BY_SIDE) {
|
||||
let all_calendars: Vec<_> = chunk
|
||||
.iter()
|
||||
.map(|&date| generate_month_lines(date, options))
|
||||
.collect();
|
||||
|
||||
for line_idx in 0..NUM_CALENDAR_LINES {
|
||||
let output_line = all_calendars
|
||||
.iter()
|
||||
.map(|calendar| calendar[line_idx].as_str())
|
||||
.collect::<Vec<_>>()
|
||||
.join(&" ".repeat(NUM_SPACES_BETWEEN_CALENDARS));
|
||||
println!("{}", output_line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn us_week_number(date: NaiveDate) -> i64 {
|
||||
let jan1 = NaiveDate::from_ymd_opt(date.year(), 1, 1).unwrap();
|
||||
|
||||
let days_before = jan1.weekday().num_days_from_sunday();
|
||||
let first_sunday = jan1 - chrono::Duration::days(days_before as i64);
|
||||
|
||||
(date - first_sunday).num_days() / 7 + 1
|
||||
}
|
||||
|
||||
fn get_weekday_abbreviations(start_weekday: Weekday, length: usize) -> Vec<String> {
|
||||
let mut weekday = start_weekday;
|
||||
let mut ret = vec![];
|
||||
for _ in 0..7 {
|
||||
ret.push(weekday.to_string()[..length].to_string());
|
||||
weekday = weekday.succ();
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
fn generate_month_lines(date: NaiveDate, options: &CalOptions) -> Vec<String> {
|
||||
let (day_width, line_width) = calculate_field_widths(options);
|
||||
|
||||
// Year mode shows year number once at the very top, not per each month
|
||||
let fmt = if options.display_mode == DisplayMode::Year {
|
||||
"%B"
|
||||
} else {
|
||||
"%B %Y"
|
||||
};
|
||||
let mut lines = vec![format!("{:^width$}", date.format(fmt), width = line_width)];
|
||||
|
||||
let week_start = if options.monday_first {
|
||||
Weekday::Mon
|
||||
} else {
|
||||
Weekday::Sun
|
||||
};
|
||||
|
||||
lines.push(format!(
|
||||
"{}{}",
|
||||
if options.week_numbers { " " } else { "" },
|
||||
get_weekday_abbreviations(week_start, day_width).join(" ")
|
||||
));
|
||||
|
||||
let mut d = NaiveDate::from_ymd_opt(date.year(), date.month(), 1).unwrap();
|
||||
let mut current_line = String::new();
|
||||
while d.month() == date.month() {
|
||||
if options.week_numbers && current_line.is_empty() {
|
||||
if options.monday_first {
|
||||
current_line.push_str(&format!("{:2} ", d.iso_week().week()));
|
||||
} else {
|
||||
current_line.push_str(&format!("{:2} ", us_week_number(d)));
|
||||
}
|
||||
}
|
||||
|
||||
// Space pad the days that belong to the same week but in previous month
|
||||
if d.day() == 1 {
|
||||
let num_padding_days = (d - d.week(week_start).first_day()).num_days() as usize;
|
||||
current_line.push_str(&" ".repeat(num_padding_days * (day_width + 1)));
|
||||
}
|
||||
|
||||
let day_str = if options.julian {
|
||||
format!("{:width$}", d.ordinal(), width = day_width)
|
||||
} else {
|
||||
format!("{:width$}", d.day(), width = day_width)
|
||||
};
|
||||
|
||||
// Apply reverse video attribute to the highlighted day
|
||||
let formatted_day = if options.color && options.highlight_date == d {
|
||||
format!("\x1b[7m{}\x1b[0m", day_str)
|
||||
} else {
|
||||
day_str
|
||||
};
|
||||
|
||||
current_line.push_str(&format!("{} ", formatted_day));
|
||||
|
||||
d += chrono::Duration::days(1);
|
||||
|
||||
if d.weekday() == week_start {
|
||||
lines.push(current_line.trim_end().to_string());
|
||||
current_line.clear();
|
||||
}
|
||||
}
|
||||
|
||||
if !current_line.is_empty() {
|
||||
// Original cal pads all lines to fixed length
|
||||
// (also print_months_side_by_side relies on this).
|
||||
lines.push(format!(
|
||||
"{:<width$}",
|
||||
current_line.trim_end(),
|
||||
width = line_width
|
||||
));
|
||||
}
|
||||
while lines.len() < NUM_CALENDAR_LINES {
|
||||
lines.push(" ".repeat(line_width));
|
||||
}
|
||||
|
||||
lines
|
||||
}
|
||||
|
||||
pub fn uu_app() -> Command {
|
||||
Command::new(uucore::util_name())
|
||||
.version(crate_version!())
|
||||
.about(ABOUT)
|
||||
.override_usage(format_usage(USAGE))
|
||||
.infer_long_args(true)
|
||||
.arg(
|
||||
Arg::new("year")
|
||||
.short('y')
|
||||
.long("year")
|
||||
.help("show whole year")
|
||||
.action(ArgAction::SetTrue)
|
||||
.conflicts_with_all(["twelve", "months"]),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("three")
|
||||
.short('3')
|
||||
.long("three")
|
||||
.help("show previous, current and next month")
|
||||
.action(ArgAction::SetTrue),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("twelve")
|
||||
.short('Y')
|
||||
.long("twelve")
|
||||
.help("show the next twelve months")
|
||||
.action(ArgAction::SetTrue)
|
||||
.conflicts_with_all(["year", "months"]),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("months")
|
||||
.short('n')
|
||||
.long("months")
|
||||
.help("show this many months")
|
||||
.value_parser(value_parser!(u32))
|
||||
.action(ArgAction::Set)
|
||||
.conflicts_with_all(["year", "twelve"]),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("monday")
|
||||
.short('m')
|
||||
.long("monday")
|
||||
.help("Monday as first day of week")
|
||||
.action(ArgAction::SetTrue),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("julian")
|
||||
.short('j')
|
||||
.long("julian")
|
||||
.help("use day-of-year numbering")
|
||||
.action(ArgAction::SetTrue),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("week")
|
||||
.short('w')
|
||||
.long("week")
|
||||
.help("show week numbers")
|
||||
.action(ArgAction::SetTrue),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("color")
|
||||
.long("color")
|
||||
.help("colorize the output")
|
||||
.value_name("when")
|
||||
.value_parser(["always", "auto", "never"])
|
||||
.default_missing_value("auto")
|
||||
.default_value("auto")
|
||||
.require_equals(true)
|
||||
.num_args(0..=1)
|
||||
.action(ArgAction::Set),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("args")
|
||||
.help("date arguments")
|
||||
.action(ArgAction::Append)
|
||||
.num_args(0..=3),
|
||||
)
|
||||
}
|
||||
1
src/uu/cal/src/main.rs
Normal file
1
src/uu/cal/src/main.rs
Normal file
@@ -0,0 +1 @@
|
||||
uucore::bin!(uu_cal);
|
||||
194
tests/by-util/test_cal.rs
Normal file
194
tests/by-util/test_cal.rs
Normal file
@@ -0,0 +1,194 @@
|
||||
// This file is part of the uutils util-linux package.
|
||||
//
|
||||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
|
||||
use regex::Regex;
|
||||
use uutests::new_ucmd;
|
||||
|
||||
#[test]
|
||||
fn test_invalid_arg() {
|
||||
new_ucmd!().arg("--definitely-invalid").fails().code_is(1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_invalid_dates() {
|
||||
new_ucmd!().args(&["31", "2", "2000"]).fails().code_is(1);
|
||||
new_ucmd!().args(&["13", "2000"]).fails().code_is(1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_iso_week_numbers() {
|
||||
let expected = vec![
|
||||
" January 2021 \n",
|
||||
" Mo Tu We Th Fr Sa Su\n",
|
||||
"53 1 2 3\n",
|
||||
" 1 4 5 6 7 8 9 10\n",
|
||||
" 2 11 12 13 14 15 16 17\n",
|
||||
" 3 18 19 20 21 22 23 24\n",
|
||||
" 4 25 26 27 28 29 30 31\n",
|
||||
" \n",
|
||||
];
|
||||
new_ucmd!()
|
||||
.args(&["-w", "-m", "1", "2021"])
|
||||
.succeeds()
|
||||
.stdout_is(expected.join(""));
|
||||
|
||||
let expected = vec![
|
||||
" January 2015 \n",
|
||||
" Mo Tu We Th Fr Sa Su\n",
|
||||
" 1 1 2 3 4\n",
|
||||
" 2 5 6 7 8 9 10 11\n",
|
||||
" 3 12 13 14 15 16 17 18\n",
|
||||
" 4 19 20 21 22 23 24 25\n",
|
||||
" 5 26 27 28 29 30 31 \n",
|
||||
" \n",
|
||||
];
|
||||
new_ucmd!()
|
||||
.args(&["-w", "-m", "1", "2015"])
|
||||
.succeeds()
|
||||
.stdout_is(expected.join(""));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_us_week_numbers() {
|
||||
let expected = vec![
|
||||
" January 2021 \n",
|
||||
" Su Mo Tu We Th Fr Sa\n",
|
||||
" 1 1 2\n",
|
||||
" 2 3 4 5 6 7 8 9\n",
|
||||
" 3 10 11 12 13 14 15 16\n",
|
||||
" 4 17 18 19 20 21 22 23\n",
|
||||
" 5 24 25 26 27 28 29 30\n",
|
||||
" 6 31 \n",
|
||||
];
|
||||
new_ucmd!()
|
||||
.args(&["-w", "1", "2021"])
|
||||
.succeeds()
|
||||
.stdout_is(expected.join(""));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_julian() {
|
||||
let expected = vec![
|
||||
" December 2000 \n",
|
||||
"Sun Mon Tue Wed Thu Fri Sat\n",
|
||||
" 336 337\n",
|
||||
"338 339 340 341 342 343 344\n",
|
||||
"345 346 347 348 349 350 351\n",
|
||||
"352 353 354 355 356 357 358\n",
|
||||
"359 360 361 362 363 364 365\n",
|
||||
"366 \n",
|
||||
];
|
||||
new_ucmd!()
|
||||
.args(&["-j", "12", "2000"])
|
||||
.succeeds()
|
||||
.stdout_is(expected.join(""));
|
||||
|
||||
let expected = vec![
|
||||
" February 2024 \n",
|
||||
" Mon Tue Wed Thu Fri Sat Sun\n",
|
||||
" 5 32 33 34 35\n",
|
||||
" 6 36 37 38 39 40 41 42\n",
|
||||
" 7 43 44 45 46 47 48 49\n",
|
||||
" 8 50 51 52 53 54 55 56\n",
|
||||
" 9 57 58 59 60 \n",
|
||||
" \n",
|
||||
];
|
||||
new_ucmd!()
|
||||
.args(&["-j", "-w", "-m", "2", "2024"])
|
||||
.succeeds()
|
||||
.stdout_is(expected.join(""));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_single_month_param() {
|
||||
new_ucmd!()
|
||||
.args(&["aug"])
|
||||
.succeeds()
|
||||
.stdout_contains("August");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_single_year_param() {
|
||||
new_ucmd!()
|
||||
.args(&["2024"])
|
||||
.succeeds()
|
||||
.stdout_contains(" 2024 ")
|
||||
.stdout_matches(&Regex::new("January +February +March").unwrap())
|
||||
.stdout_matches(&Regex::new("October +November +December").unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_year_option() {
|
||||
new_ucmd!()
|
||||
.args(&["-y", "3", "2024"])
|
||||
.succeeds()
|
||||
.stdout_matches(&Regex::new("January +February +March").unwrap())
|
||||
.stdout_matches(&Regex::new("October +November +December").unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_three_option() {
|
||||
let re = Regex::new("December 2023 +January 2024 +February 2024").unwrap();
|
||||
new_ucmd!()
|
||||
.args(&["-3", "1", "2024"])
|
||||
.succeeds()
|
||||
.stdout_matches(&re);
|
||||
|
||||
let re = Regex::new("November 2023 +December 2023 +January 2024").unwrap();
|
||||
new_ucmd!()
|
||||
.args(&["-3", "12", "2023"])
|
||||
.succeeds()
|
||||
.stdout_matches(&re);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_twelve_option() {
|
||||
new_ucmd!()
|
||||
.args(&["-Y", "15", "3", "2024"])
|
||||
.succeeds()
|
||||
.stdout_contains("March 2024")
|
||||
.stdout_contains("February 2025");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_zero_months_displays_one() {
|
||||
new_ucmd!()
|
||||
.args(&["-n", "0", "12", "2023"])
|
||||
.succeeds()
|
||||
.stdout_contains("Su Mo Tu We Th Fr Sa");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_color() {
|
||||
let expected = vec![
|
||||
" March 2024 \n",
|
||||
"Su Mo Tu We Th Fr Sa\n",
|
||||
" 1 2\n",
|
||||
" 3 4 5 6 7 8 9\n",
|
||||
"10 11 12 13 14 15 16\n",
|
||||
"17 18 19 20 21 22 23\n",
|
||||
"24 25 26 27 28 29 30\n",
|
||||
"31 \n",
|
||||
];
|
||||
new_ucmd!()
|
||||
.args(&["--color=never", "15", "3", "2024"])
|
||||
.succeeds()
|
||||
.stdout_is(expected.join(""));
|
||||
|
||||
let expected = vec![
|
||||
" March 2024 \n",
|
||||
"Su Mo Tu We Th Fr Sa\n",
|
||||
" 1 2\n",
|
||||
" 3 4 5 6 7 8 9\n",
|
||||
"10 11 12 13 14 \x1b[7m15\x1b[0m 16\n",
|
||||
"17 18 19 20 21 22 23\n",
|
||||
"24 25 26 27 28 29 30\n",
|
||||
"31 \n",
|
||||
];
|
||||
new_ucmd!()
|
||||
.args(&["--color=always", "15", "3", "2024"])
|
||||
.succeeds()
|
||||
.stdout_is(expected.join(""));
|
||||
}
|
||||
@@ -43,6 +43,10 @@ mod test_nologin;
|
||||
#[path = "by-util/test_blockdev.rs"]
|
||||
mod test_blockdev;
|
||||
|
||||
#[cfg(feature = "cal")]
|
||||
#[path = "by-util/test_cal.rs"]
|
||||
mod test_cal;
|
||||
|
||||
#[cfg(feature = "ctrlaltdel")]
|
||||
#[path = "by-util/test_ctrlaltdel.rs"]
|
||||
mod test_ctrlaltdel;
|
||||
|
||||
Reference in New Issue
Block a user