Skip to content

Commit

Permalink
add cli test cases
Browse files Browse the repository at this point in the history
This commit add some unit/integration tests
and a workflow to run tests in pull requests.

Signed-off-by: bin liu <[email protected]>
  • Loading branch information
liubin committed Dec 3, 2022
1 parent b29fe5f commit 0868c62
Show file tree
Hide file tree
Showing 6 changed files with 341 additions and 27 deletions.
22 changes: 22 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: CI
on:
pull_request:
types:
- opened
- edited
- reopened
- synchronize

env:
CARGO_TERM_COLOR: always
RUST_TARGET: x86_64-unknown-linux-musl

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Build and test
uses: gmiam/rust-musl-action@master
with:
args: make all
51 changes: 51 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,6 @@ serde_json = "1.0"
structopt = "0.3"
toml_edit = "0.15.0"
chrono = "0.4"

[dev-dependencies]
tempfile = "3.3.0"
31 changes: 31 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
default: build

CARGO ?= $(shell which cargo)
RUST_TARGET ?= x86_64-unknown-linux-musl
INSTALL_DIR_PREFIX ?= "/usr/local/bin"

.format:
${CARGO} fmt -- --check

build: .format
${CARGO} build --target ${RUST_TARGET} --release
# Cargo will skip checking if it is already checked
${CARGO} clippy --bins --tests -- -Dwarnings

install: .format build
@sudo mkdir -m 755 -p $(INSTALL_DIR_PREFIX)
@sudo install -m 755 target/${RUST_TARGET}/release/toml $(INSTALL_DIR_PREFIX)/toml

clean:
${CARGO} clean

ut:
RUST_BACKTRACE=1 ${CARGO} test --workspace -- --skip integration --nocapture

integration:
# run tests under `test` directory
RUST_BACKTRACE=1 ${CARGO} test --workspace -- integration --nocapture

test: ut integration

all: build install test
76 changes: 63 additions & 13 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,14 @@ enum Args {
// TODO: append/add (name TBD)
}

#[derive(StructOpt)]
#[derive(Clone, Copy, Default, StructOpt)]
struct GetOpts {
/// Print as a TOML fragment (default: print as JSON)
#[structopt(long)]
output_toml: bool,
}

#[derive(StructOpt)]
#[derive(Clone, Copy, Default, StructOpt)]
struct SetOpts {
/// Overwrite the TOML file (default: print to stdout)
#[structopt(long)]
Expand Down Expand Up @@ -137,20 +137,31 @@ fn check(path: PathBuf, query: &str) {
}

fn get(path: PathBuf, query: &str, opts: GetOpts) -> Result<(), Error> {
let value = get_value(path, query, opts)?;
if opts.output_toml {
print!("{}", value);
} else {
println!("{}", value);
}
Ok(())
}

fn get_value(path: PathBuf, query: &str, opts: GetOpts) -> Result<String, Error> {
let tpath = parse_query_cli(query)?.0;
let doc = read_parse(path)?;

if opts.output_toml {
print_toml_fragment(&doc, &tpath);
let value = if opts.output_toml {
format_toml_fragment(&doc, &tpath)
} else {
let item = walk_tpath(doc.as_item(), &tpath);
// TODO: support shell-friendly output like `jq -r`
println!("{}", serde_json::to_string(&JsonItem(item))?);
}
Ok(())
serde_json::to_string(&JsonItem(item))?
};

Ok(value)
}

fn print_toml_fragment(doc: &Document, tpath: &[TpathSegment]) {
fn format_toml_fragment(doc: &Document, tpath: &[TpathSegment]) -> String {
let mut item = doc.as_item();
let mut breadcrumbs = vec![];
for seg in tpath {
Expand Down Expand Up @@ -193,10 +204,23 @@ fn print_toml_fragment(doc: &Document, tpath: &[TpathSegment]) {

let root = item.into_table().unwrap();
let doc: Document = root.into();
print!("{}", doc);
format!("{}", doc)
}

fn set(path: PathBuf, query: &str, value_str: &str, opts: SetOpts) -> Result<(), Error> {
let result = set_value(path, query, value_str, opts)?;
if let Some(doc) = result {
print!("{}", doc);
}
Ok(())
}

fn set_value(
path: PathBuf,
query: &str,
value_str: &str,
opts: SetOpts,
) -> Result<Option<String>, Error> {
let tpath = parse_query_cli(query)?.0;
let mut doc = read_parse(path.clone())?;

Expand Down Expand Up @@ -241,7 +265,7 @@ fn set(path: PathBuf, query: &str, value_str: &str, opts: SetOpts) -> Result<(),
}
*item = detect_value(value_str);

if opts.overwrite {
let result = if opts.overwrite {
// write content to path
if opts.backup {
let now: DateTime<Utc> = Utc::now();
Expand All @@ -251,10 +275,12 @@ fn set(path: PathBuf, query: &str, value_str: &str, opts: SetOpts) -> Result<(),
}
let mut output = OpenOptions::new().write(true).truncate(true).open(path)?;
write!(output, "{}", doc)?;
None
} else {
print!("{}", doc);
}
Ok(())
Some(format!("{}", doc))
};

Ok(result)
}

fn detect_value(value_str: &str) -> Item {
Expand Down Expand Up @@ -355,3 +381,27 @@ impl Serialize for JsonValue<'_> {
}
}
}

#[cfg(test)]
mod tests {
// functions to test
use super::detect_value;

#[test]
fn test_detect_value() {
let i = detect_value("abc");
assert_eq!("string", i.type_name());
assert!(i.is_str());
assert_eq!(Some("abc"), i.as_str());

let i = detect_value("123");
assert_eq!("integer", i.type_name());
assert!(i.is_integer());
assert_eq!(Some(123), i.as_integer());

let i = detect_value("true");
assert_eq!("boolean", i.type_name());
assert!(i.is_bool());
assert_eq!(Some(true), i.as_bool());
}
}
Loading

0 comments on commit 0868c62

Please sign in to comment.