Skip to content

Commit 4895c70

Browse files
committed
Code coverage improvements
1 parent c11ee19 commit 4895c70

9 files changed

Lines changed: 518 additions & 57 deletions

File tree

coverage.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
#!/usr/bin/env bash
22

3-
cargo llvm-cov --html --open --ignore-filename-regex 'main|cliutil'
3+
cargo llvm-cov nextest --lcov --output-path target/llvm-cov/lcov.info --ignore-filename-regex 'main|cliutil'
4+
cargo llvm-cov report --html --open --output-dir target/llvm-cov

src/ast.rs

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,17 +25,14 @@ impl Expr {
2525
)))
2626
}
2727

28-
pub fn identifier_name(&self) -> Option<&str> {
29-
match self {
30-
Expr::Identifier(expr_identifier) => Some(expr_identifier.lookup_name()),
31-
_ => None,
32-
}
33-
}
34-
3528
pub fn string(string: &str) -> Self {
3629
Self::String(ExprString::new(string).into())
3730
}
3831

32+
pub fn number(value: f64) -> Self {
33+
Self::Number(ExprNumber::new(value).into())
34+
}
35+
3936
pub fn call(callee: ExprS, args: Vec<ExprS>) -> Self {
4037
Self::Call(Box::new(ExprCall {
4138
callee: callee.into(),
@@ -47,10 +44,6 @@ impl Expr {
4744
Self::Bool(Box::new(ExprBool::new(value)))
4845
}
4946

50-
pub fn is_bool(&self) -> bool {
51-
self.get_type() == Type::Bool
52-
}
53-
5447
pub fn get_type(&self) -> Type {
5548
match self {
5649
Expr::Bool(_) => Type::Bool,

src/cliutil.rs

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,6 @@ pub fn unzip_key_values(keys_values: Vec<(String, String)>) -> (Vec<String>, Vec
7272
/// let args = Args::parse_from(["test", "--vars", "key=value", "another_key=another_value"]);
7373
/// assert_eq!(args.vars, vec![("key".to_string(), "value".to_string()), ("another_key".to_string(), "another_value".to_string())]);
7474
/// ```
75-
///
7675
pub fn parse_key_val<T, U>(value: &str) -> Result<(T, U), Box<dyn Error + Send + Sync + 'static>>
7776
where
7877
T: std::str::FromStr,
@@ -134,3 +133,52 @@ pub fn read_in_source(path: Option<String>) -> String {
134133
}
135134
}
136135
}
136+
137+
#[cfg(test)]
138+
mod cliutil_tests {
139+
use clap::Parser;
140+
141+
use crate::cliutil::{parse_key_val, read_in_source};
142+
143+
#[test]
144+
fn read_in_source_from_file() {
145+
let result = read_in_source(Some("./spec/valid/call_id.expr".to_string()));
146+
147+
assert_eq!("(id (noop))", result);
148+
}
149+
150+
#[test]
151+
fn parse_key_val_valid_keyvalue_pair() {
152+
#[derive(Parser, Debug, PartialEq)]
153+
struct Args {
154+
#[arg(long, value_delimiter = ' ', num_args = 1.., value_parser = parse_key_val::<String, String>)]
155+
vars: Vec<(String, String)>,
156+
}
157+
158+
assert_eq!(
159+
Args {
160+
vars: vec![(String::from("key"), String::from("value"))]
161+
},
162+
Args::try_parse_from(["test", "--vars", "key=value"])
163+
.ok()
164+
.unwrap()
165+
);
166+
}
167+
168+
#[test]
169+
fn parse_key_val_invalid_keyvalue_pair() {
170+
#[derive(Parser, Debug, PartialEq)]
171+
struct Args {
172+
#[arg(long, value_delimiter = ' ', num_args = 1.., value_parser = parse_key_val::<String, String>)]
173+
vars: Vec<(String, String)>,
174+
}
175+
176+
assert_eq!(
177+
"error: invalid value 'key_without_value' for '--vars <VARS>...': should be formatted as key=value pair: `key_without_value`\n\nFor more information, try '--help'.\n",
178+
Args::try_parse_from(["test", "--vars", "key_without_value"])
179+
.err()
180+
.unwrap()
181+
.to_string()
182+
);
183+
}
184+
}

src/compiler.rs

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -171,10 +171,6 @@ impl CompileTimeEnv {
171171
}
172172
}
173173

174-
pub fn add_keys_to_client_context(&mut self, keys: Vec<String>) {
175-
self.client_context.extend(keys);
176-
}
177-
178174
pub fn get_client_context_index(&self, name: &str) -> Option<(&String, u8)> {
179175
let index = self
180176
.client_context
@@ -223,10 +219,6 @@ impl ExprByteCode {
223219
&self.codes
224220
}
225221

226-
pub fn get_code(&self, index: usize) -> Option<&u8> {
227-
self.codes.get(index)
228-
}
229-
230222
pub fn constants(&self) -> &[Value] {
231223
&self.constants
232224
}

src/errors.rs

Lines changed: 249 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -60,12 +60,6 @@ impl diagnostics::AsDiagnostic for LexicalError {
6060
}
6161
}
6262

63-
impl From<ParseFloatError> for LexicalError {
64-
fn from(value: ParseFloatError) -> Self {
65-
LexicalError::InvalidNumber(value)
66-
}
67-
}
68-
6963
#[derive(Debug, Clone, Error, PartialEq)]
7064
pub enum SyntaxError {
7165
#[error("extraneous input: {token:?}")]
@@ -486,7 +480,10 @@ pub mod diagnostics {
486480

487481
#[cfg(test)]
488482
mod error_to_diagnostics_tests {
489-
use crate::errors::{CompileError, ExprError, LexicalError};
483+
use crate::{
484+
errors::{CompileError, ExprError, LexicalError, RuntimeError, SyntaxError},
485+
types::Type,
486+
};
490487

491488
use super::*;
492489
use std::ops::Range;
@@ -552,5 +549,250 @@ pub mod diagnostics {
552549
assert_eq!(diagnostic.labels.len(), 1);
553550
assert_eq!(diagnostic.labels[0], Label::primary((), range));
554551
}
552+
553+
#[test]
554+
fn it_converts_compileerror_no_callee_to_diagnostic() {
555+
let source = dummy_source();
556+
let range = dummy_range();
557+
let error = ExprError::CompileError(CompileError::NoCallee);
558+
let diagnostics = get_diagnostics(&[(error, range.clone())], source);
559+
560+
assert_eq!(diagnostics.len(), 1);
561+
let diagnostic = &diagnostics[0];
562+
assert_eq!(diagnostic.code, Some("compiler".to_string()));
563+
assert_eq!(
564+
diagnostic.message,
565+
"call expression without a callee".to_string()
566+
);
567+
assert_eq!(diagnostic.severity, Severity::Error);
568+
assert_eq!(diagnostic.labels.len(), 1);
569+
assert_eq!(diagnostic.labels[0], Label::primary((), range));
570+
}
571+
572+
#[test]
573+
fn it_converts_compileerror_type_mismatch_to_diagnostic() {
574+
let source = dummy_source();
575+
let range = dummy_range();
576+
let error = ExprError::CompileError(CompileError::TypeMismatch {
577+
expected: Type::String,
578+
actual: Type::Bool,
579+
});
580+
let diagnostics = get_diagnostics(&[(error, range.clone())], source);
581+
582+
assert_eq!(diagnostics.len(), 1);
583+
let diagnostic = &diagnostics[0];
584+
assert_eq!(diagnostic.code, Some("compiler".to_string()));
585+
assert_eq!(
586+
diagnostic.message,
587+
"expected type String but received Bool".to_string()
588+
);
589+
assert_eq!(diagnostic.severity, Severity::Error);
590+
assert_eq!(diagnostic.labels.len(), 1);
591+
assert_eq!(diagnostic.labels[0], Label::primary((), range));
592+
}
593+
594+
#[test]
595+
fn it_converts_compileerror_invalid_lookup_type_to_diagnostic() {
596+
let source = dummy_source();
597+
let range = dummy_range();
598+
let error = ExprError::CompileError(CompileError::InvalidLookupType(99));
599+
let diagnostics = get_diagnostics(&[(error, range.clone())], source);
600+
601+
assert_eq!(diagnostics.len(), 1);
602+
let diagnostic = &diagnostics[0];
603+
assert_eq!(diagnostic.code, Some("compiler".to_string()));
604+
assert_eq!(diagnostic.message, "invalid lookup type: 99".to_string());
605+
assert_eq!(diagnostic.severity, Severity::Error);
606+
assert_eq!(diagnostic.labels.len(), 1);
607+
assert_eq!(diagnostic.labels[0], Label::primary((), range));
608+
}
609+
610+
#[test]
611+
fn it_converts_runtimeerror_undefined_to_diagnostic() {
612+
let source = dummy_source();
613+
let range = dummy_range();
614+
let error = ExprError::RuntimeError(RuntimeError::TypeMismatch {
615+
expected: Type::Bool,
616+
actual: Type::String,
617+
});
618+
let diagnostics = get_diagnostics(&[(error, range.clone())], source);
619+
620+
assert_eq!(diagnostics.len(), 1);
621+
let diagnostic = &diagnostics[0];
622+
assert_eq!(diagnostic.code, Some("runtime".to_string()));
623+
assert_eq!(
624+
diagnostic.message,
625+
"expected type Bool but received String".to_string()
626+
);
627+
assert_eq!(diagnostic.severity, Severity::Error);
628+
assert_eq!(diagnostic.labels.len(), 1);
629+
assert_eq!(diagnostic.labels[0], Label::primary((), range));
630+
}
631+
632+
#[test]
633+
fn it_converts_runtimeerror_empty_stack_to_diagnostic() {
634+
let source = dummy_source();
635+
let range = dummy_range();
636+
let error = ExprError::RuntimeError(RuntimeError::EmptyStack);
637+
let diagnostics = get_diagnostics(&[(error, range.clone())], source);
638+
639+
assert_eq!(diagnostics.len(), 1);
640+
let diagnostic = &diagnostics[0];
641+
assert_eq!(diagnostic.code, Some("runtime".to_string()));
642+
assert_eq!(
643+
diagnostic.message,
644+
"attempting to pop from an empty stack".to_string()
645+
);
646+
assert_eq!(diagnostic.severity, Severity::Error);
647+
assert_eq!(diagnostic.labels.len(), 1);
648+
assert_eq!(diagnostic.labels[0], Label::primary((), range));
649+
}
650+
651+
#[test]
652+
fn it_converts_syntaxerror_unrecognized_eof_to_diagnostic() {
653+
let source = dummy_source();
654+
let range = dummy_range();
655+
let error = ExprError::SyntaxError(SyntaxError::UnrecognizedEOF {
656+
expected: vec!["string".to_string()],
657+
});
658+
let diagnostics = get_diagnostics(&[(error, range.clone())], source);
659+
660+
assert_eq!(diagnostics.len(), 1);
661+
let diagnostic = &diagnostics[0];
662+
assert_eq!(diagnostic.code, Some("syntax".to_string()));
663+
assert_eq!(
664+
diagnostic.message,
665+
"unexpected end of file; expected: [\"string\"]".to_string()
666+
);
667+
assert_eq!(diagnostic.severity, Severity::Error);
668+
assert_eq!(diagnostic.labels.len(), 1);
669+
assert_eq!(diagnostic.labels[0], Label::primary((), range));
670+
}
671+
672+
#[test]
673+
fn it_converts_syntaxerror_invalid_token_to_diagnostic() {
674+
let source = dummy_source();
675+
let range = dummy_range();
676+
let error = ExprError::SyntaxError(SyntaxError::InvalidToken);
677+
let diagnostics = get_diagnostics(&[(error, range.clone())], source);
678+
679+
assert_eq!(diagnostics.len(), 1);
680+
let diagnostic = &diagnostics[0];
681+
assert_eq!(diagnostic.code, Some("syntax".to_string()));
682+
assert_eq!(diagnostic.message, "invalid input".to_string());
683+
assert_eq!(diagnostic.severity, Severity::Error);
684+
assert_eq!(diagnostic.labels.len(), 1);
685+
assert_eq!(diagnostic.labels[0], Label::primary((), range));
686+
}
687+
688+
#[test]
689+
fn it_converts_syntaxerror_unexpected_input_to_diagnostic() {
690+
let source = dummy_source();
691+
let range = dummy_range();
692+
let error = ExprError::SyntaxError(SyntaxError::UnexpectedInput {
693+
token: "number".to_string(),
694+
});
695+
let diagnostics = get_diagnostics(&[(error, range.clone())], source);
696+
697+
assert_eq!(diagnostics.len(), 1);
698+
let diagnostic = &diagnostics[0];
699+
assert_eq!(diagnostic.code, Some("syntax".to_string()));
700+
assert_eq!(
701+
diagnostic.message,
702+
"unexpected input: \"number\"".to_string()
703+
);
704+
assert_eq!(diagnostic.severity, Severity::Error);
705+
assert_eq!(diagnostic.labels.len(), 1);
706+
assert_eq!(diagnostic.labels[0], Label::primary((), range));
707+
}
708+
709+
#[test]
710+
fn it_converts_syntaxerror_unrecognized_token_to_diagnostic() {
711+
let source = dummy_source();
712+
let range = dummy_range();
713+
let error = ExprError::SyntaxError(SyntaxError::UnrecognizedToken {
714+
token: "number".to_string(),
715+
expected: vec![",".to_string(), "number".to_string(), "]".to_string()],
716+
});
717+
let diagnostics = get_diagnostics(&[(error, range.clone())], source);
718+
719+
assert_eq!(diagnostics.len(), 1);
720+
let diagnostic = &diagnostics[0];
721+
assert_eq!(diagnostic.code, Some("syntax".to_string()));
722+
assert_eq!(
723+
diagnostic.message,
724+
"unexpected \"number\"; expected: [\",\", \"number\", \"]\"]".to_string()
725+
);
726+
assert_eq!(diagnostic.severity, Severity::Error);
727+
assert_eq!(diagnostic.labels.len(), 1);
728+
assert_eq!(diagnostic.labels[0], Label::primary((), range));
729+
}
730+
731+
#[test]
732+
fn it_converts_syntaxerror_unterminated_string_to_diagnostic() {
733+
let source = dummy_source();
734+
let range = dummy_range();
735+
let error = ExprError::SyntaxError(SyntaxError::UnterminatedString);
736+
let diagnostics = get_diagnostics(&[(error, range.clone())], source);
737+
738+
assert_eq!(diagnostics.len(), 1);
739+
let diagnostic = &diagnostics[0];
740+
assert_eq!(diagnostic.code, Some("syntax".to_string()));
741+
assert_eq!(diagnostic.message, "unterminated string".to_string());
742+
assert_eq!(diagnostic.severity, Severity::Error);
743+
assert_eq!(diagnostic.labels.len(), 1);
744+
assert_eq!(diagnostic.labels[0], Label::primary((), range));
745+
}
746+
747+
#[test]
748+
fn it_converts_syntaxerror_extra_token_to_diagnostic() {
749+
let source = dummy_source();
750+
let range = dummy_range();
751+
let error = ExprError::SyntaxError(SyntaxError::ExtraToken {
752+
token: ",".to_string(),
753+
});
754+
let diagnostics = get_diagnostics(&[(error, range.clone())], source);
755+
756+
assert_eq!(diagnostics.len(), 1);
757+
let diagnostic = &diagnostics[0];
758+
assert_eq!(diagnostic.code, Some("syntax".to_string()));
759+
assert_eq!(diagnostic.message, "extraneous input: \",\"".to_string());
760+
assert_eq!(diagnostic.severity, Severity::Error);
761+
assert_eq!(diagnostic.labels.len(), 1);
762+
assert_eq!(diagnostic.labels[0], Label::primary((), range));
763+
}
764+
}
765+
#[cfg(test)]
766+
mod to_severity_tests {
767+
use codespan_reporting::diagnostic::Severity;
768+
769+
use crate::errors::diagnostics::ExprDiagnosisSeverity;
770+
771+
#[test]
772+
fn from_hint() {
773+
assert_eq!(Severity::Help, ExprDiagnosisSeverity::HINT.to_severity());
774+
}
775+
776+
#[test]
777+
fn from_information() {
778+
assert_eq!(
779+
Severity::Note,
780+
ExprDiagnosisSeverity::INFORMATION.to_severity()
781+
);
782+
}
783+
784+
#[test]
785+
fn from_warning() {
786+
assert_eq!(
787+
Severity::Warning,
788+
ExprDiagnosisSeverity::WARNING.to_severity()
789+
);
790+
}
791+
792+
#[test]
793+
#[should_panic(expected = "Invalid diagnosis severity: 99")]
794+
fn from_invalid() {
795+
ExprDiagnosisSeverity(99).to_severity();
796+
}
555797
}
556798
}

0 commit comments

Comments
 (0)