Add unit tests for linter (#9)

This commit is contained in:
Charlie Marsh 2022-08-13 17:32:40 -04:00 committed by GitHub
parent 52afc02023
commit 4a67c8d44b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 168 additions and 18 deletions

View file

@ -73,6 +73,28 @@ jobs:
${{ runner.os }}-
- run: cargo clippy -- -D warnings
cargo_test:
name: "cargo test"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
- uses: actions/cache@v3
env:
cache-name: cache-cargo
with:
path: |
~/.cargo/registry
~/.cargo/git
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-
- run: cargo test
maturin_build:
name: "maturin build"
runs-on: ubuntu-latest

View file

@ -0,0 +1,10 @@
def foo(x: int, y: int, x: int) -> None:
pass
def bar(x: int, y: int, *, x: int) -> None:
pass
def baz(x: int, y: int, **x: int) -> None:
pass

View file

@ -1,5 +0,0 @@
from bar import *
def baz(x: int, x: int) -> None:
pass

View file

@ -4,5 +4,5 @@ if (1, 2):
for _ in range(5):
if True:
pass
elif (1, 2):
elif (3, 4):
pass

View file

@ -0,0 +1 @@
from if_tuple import *

View file

@ -2,19 +2,14 @@ use std::collections::HashSet;
use rustpython_parser::ast::{Arg, Arguments, ExprKind, Stmt, StmtKind, Suite};
use crate::check::{Check, CheckKind};
use crate::checks::{Check, CheckKind};
use crate::visitor::{walk_arguments, walk_stmt, Visitor};
#[derive(Default)]
struct Checker {
checks: Vec<Check>,
}
impl Checker {
fn new() -> Self {
Checker { checks: vec![] }
}
}
impl Visitor for Checker {
fn visit_stmt(&mut self, stmt: &Stmt) {
match &stmt.node {
@ -79,9 +74,46 @@ pub fn check_ast(python_ast: &Suite) -> Vec<Check> {
python_ast
.iter()
.flat_map(|stmt| {
let mut checker = Checker::new();
let mut checker: Checker = Default::default();
checker.visit_stmt(stmt);
checker.checks
})
.collect()
}
#[cfg(test)]
mod tests {
use rustpython_parser::ast::{Alias, Location, Stmt, StmtKind};
use crate::checker::Checker;
use crate::checks::Check;
use crate::checks::CheckKind::ImportStarUsage;
use crate::visitor::Visitor;
#[test]
fn import_star_usage() {
let mut checker: Checker = Default::default();
checker.visit_stmt(&Stmt {
location: Location::new(1, 1),
custom: (),
node: StmtKind::ImportFrom {
module: Some("bar".to_string()),
names: vec![Alias {
name: "*".to_string(),
asname: None,
}],
level: 0,
},
});
let actual = checker.checks;
let expected = vec![Check {
kind: ImportStarUsage,
location: Location::new(1, 1),
}];
assert_eq!(actual.len(), expected.len());
for i in 1..actual.len() {
assert_eq!(actual[i], expected[i]);
}
}
}

View file

@ -1,7 +1,7 @@
use rustpython_parser::ast::Location;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum CheckKind {
DuplicateArgumentName,
ImportStarUsage,
@ -28,6 +28,7 @@ impl CheckKind {
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct Check {
pub kind: CheckKind,
pub location: Location,

View file

@ -1,6 +1,6 @@
mod cache;
mod check;
pub mod checker;
mod checks;
pub mod fs;
pub mod linter;
pub mod logging;

View file

@ -28,3 +28,92 @@ pub fn check_path(path: &Path, mode: &cache::Mode) -> Result<Vec<Message>> {
Ok(messages)
}
#[cfg(test)]
mod tests {
use std::path::Path;
use anyhow::Result;
use rustpython_parser::ast::Location;
use crate::cache;
use crate::checks::CheckKind::{DuplicateArgumentName, IfTuple, ImportStarUsage};
use crate::linter::check_path;
use crate::message::Message;
#[test]
fn duplicate_argument_name() -> Result<()> {
let actual = check_path(
&Path::new("./resources/test/src/duplicate_argument_name.py"),
&cache::Mode::None,
)?;
let expected = vec![
Message {
kind: DuplicateArgumentName,
location: Location::new(1, 25),
filename: "./resources/test/src/duplicate_argument_name.py".to_string(),
},
Message {
kind: DuplicateArgumentName,
location: Location::new(5, 9),
filename: "./resources/test/src/duplicate_argument_name.py".to_string(),
},
Message {
kind: DuplicateArgumentName,
location: Location::new(9, 27),
filename: "./resources/test/src/duplicate_argument_name.py".to_string(),
},
];
assert_eq!(actual.len(), expected.len());
for i in 1..actual.len() {
assert_eq!(actual[i], expected[i]);
}
Ok(())
}
#[test]
fn if_tuple() -> Result<()> {
let actual = check_path(
&Path::new("./resources/test/src/if_tuple.py"),
&cache::Mode::None,
)?;
let expected = vec![
Message {
kind: IfTuple,
location: Location::new(1, 1),
filename: "./resources/test/src/if_tuple.py".to_string(),
},
Message {
kind: IfTuple,
location: Location::new(7, 5),
filename: "./resources/test/src/if_tuple.py".to_string(),
},
];
assert_eq!(actual.len(), expected.len());
for i in 1..actual.len() {
assert_eq!(actual[i], expected[i]);
}
Ok(())
}
#[test]
fn import_star_usage() -> Result<()> {
let actual = check_path(
&Path::new("./resources/test/src/import_star_usage.py"),
&cache::Mode::None,
)?;
let expected = vec![Message {
kind: ImportStarUsage,
location: Location::new(1, 1),
filename: "./resources/test/src/import_star_usage.py".to_string(),
}];
assert_eq!(actual.len(), expected.len());
for i in 1..actual.len() {
assert_eq!(actual[i], expected[i]);
}
Ok(())
}
}

View file

@ -4,7 +4,7 @@ use colored::Colorize;
use rustpython_parser::ast::Location;
use serde::{Deserialize, Serialize};
use crate::check::CheckKind;
use crate::checks::CheckKind;
#[derive(Serialize, Deserialize)]
#[serde(remote = "Location")]
@ -21,7 +21,7 @@ impl From<LocationDef> for Location {
}
}
#[derive(Serialize, Deserialize)]
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct Message {
pub kind: CheckKind,
#[serde(with = "LocationDef")]