Skip to content

Commit a1eb073

Browse files
committed
Add documentation and extra tests for querying for non-existent properties
1 parent e3a7dcf commit a1eb073

File tree

2 files changed

+140
-8
lines changed

2 files changed

+140
-8
lines changed

src/libcprover-rust/include/c_api.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#pragma once
44

55
#include <memory>
6+
#include <stdexcept>
67
#include <string>
78

89
// NOLINTNEXTLINE(build/include)
@@ -56,6 +57,16 @@ static void trycatch(Try &&func, Fail &&fail) noexcept
5657
{
5758
fail(i.what());
5859
}
60+
catch(const std::out_of_range &our)
61+
{
62+
fail(our.what());
63+
}
64+
catch(const std::exception &exc)
65+
{
66+
// collective catch-all for all exceptions that derive
67+
// from standard exception class.
68+
fail(exc.what());
69+
}
5970
}
6071

6172
} // namespace behavior

src/libcprover-rust/src/lib.rs

Lines changed: 129 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,23 +12,42 @@ pub mod cprover_api {
1212
// the definitions on the C++ side (i.e. it will treat C++ as the
1313
// source of truth rather than Rust).
1414

15+
/// This type reflects the success of the whole verification run.
16+
/// E.g. If all of the assertions have been passing, then the result is
17+
/// going to be `PASS` - if there's just one failing, the verification
18+
/// result will be `FAIL`. You can get `UNKNOWN` only in the case where
19+
/// you request verification results before running the verification
20+
/// engine (not possible through the Rust interface) and `ERROR` if there
21+
/// is something that causes the SAT solver to fail.
1522
#[derive(Debug)]
1623
#[repr(i32)]
1724
enum verifier_resultt {
25+
/// The verification engine run hasn't been run yet.
1826
UNKNOWN,
27+
/// No properties were violated.
1928
PASS,
29+
/// Some properties were violated.
2030
FAIL,
31+
/// An error occured during the running of the solver.
2132
ERROR,
2233
}
2334

35+
/// This type is similar to [verifier_resultt] above, but reflects the
36+
/// status of a single property checked.
2437
#[derive(Debug)]
2538
#[repr(i32)]
2639
enum prop_statust {
40+
/// The property was not checked.
2741
NOT_CHECKED,
42+
/// The checker was unable to determine the status of the property.
2843
UNKNOWN,
44+
/// The property was proven to be unreachable.
2945
NOT_REACHABLE,
46+
/// The property was not violated.
3047
PASS,
48+
/// The property was violated.
3149
FAIL,
50+
/// An error occured in the solver during checking the property's status.
3251
ERROR,
3352
}
3453

@@ -44,6 +63,14 @@ pub mod cprover_api {
4463

4564
type verifier_resultt;
4665
type prop_statust;
66+
67+
/// This type acts as an opaque handle to the verification results object.
68+
/// This will be given back to us through a UniquePtr, which we pass into
69+
/// the functions that will give us back the results in a more granular level:
70+
/// * [get_verification_result] will give the full verification engine run result,
71+
/// * [get_property_ids] will give the list of property identifiers of the model,
72+
/// * [get_property_description] will give a string description for a property identifier
73+
/// * [get_property_status] will give the status of a property for a given identifier.
4774
type verification_resultt;
4875

4976
/// Provide a unique pointer to the API handle. This will be required to interact
@@ -69,16 +96,31 @@ pub mod cprover_api {
6996
/// the CProver CLI option `--drop-unused-functions`
7097
fn drop_unused_functions(self: &api_sessiont) -> Result<()>;
7198

99+
/// Gets a pointer to the opaque type describing the aggregate verification results and
100+
/// returns an enum value of type [verifier_resultt] representing the whole of the verification
101+
/// engine run.
72102
fn get_verification_result(result: &UniquePtr<verification_resultt>) -> verifier_resultt;
103+
/// Gets a pointer to the opaque type describing the aggregate verification results
104+
/// and returns a C++ Vector of C++ Strings containing the identifiers of the
105+
/// properties present in the model.
73106
fn get_property_ids(result: &UniquePtr<verification_resultt>) -> &CxxVector<CxxString>;
107+
/// Given a pointer to the opaque type representing the aggregate verification results
108+
/// and a property identifier using a C++ string (you can use `cxx:let_cxx_string` to
109+
/// declare), returns a C++ string that contains the property description.
110+
/// If a bad identifier is given, this returns an `Result::Err`.
74111
fn get_property_description<'a>(
75112
result: &'a UniquePtr<verification_resultt>,
76113
property_id: &CxxString,
77-
) -> &'a CxxString;
114+
) -> Result<&'a CxxString>;
115+
/// Given a pointer to the opaque type representing the aggregate verification results
116+
/// and a property identifier using a C++ string (you can use `cxx:let_cxx_string` to
117+
/// declare), returns a value of type [prop_statust] that contains the individual
118+
/// property's status.
119+
/// If a bad identifier is given, this returns an `Result::Err`.
78120
fn get_property_status(
79121
result: &UniquePtr<verification_resultt>,
80122
property_id: &CxxString,
81-
) -> prop_statust;
123+
) -> Result<prop_statust>;
82124

83125
// WARNING: Please don't use this function - use its public interface in [ffi_util::translate_rust_vector_to_cpp].
84126
// The reason this is here is that it's implemented on the C++ shim, and to link this function against
@@ -364,17 +406,25 @@ mod tests {
364406
let results = client.verify_model();
365407
if let Ok(el) = results {
366408
let_cxx_string!(existing_property_id = "main.assertion.1");
367-
let description =
368-
cprover_api::get_property_description(&el, &existing_property_id).to_string();
369-
assert_eq!(description, "expected failure: arr[3] == 3");
370-
Ok(())
409+
let description = cprover_api::get_property_description(&el, &existing_property_id);
410+
if let Ok(description_text) = description {
411+
assert_eq!(description_text, "expected failure: arr[3] == 3");
412+
Ok(())
413+
} else {
414+
let error_msg = format!(
415+
"Unable to get description for property {:?}",
416+
&existing_property_id
417+
);
418+
Err(error_msg)
419+
}
371420
} else {
372421
Err("Unable to produce results from the verification engine".to_string())
373422
}
374423
}
375424

376425
#[test]
377-
fn it_can_get_the_property_status_for_existing_property() -> Result<(), String> {
426+
fn it_raises_an_exception_when_getting_the_property_description_for_nonexisting_property(
427+
) -> Result<(), String> {
378428
let binding = cprover_api::new_api_session();
379429
let client = match binding.as_ref() {
380430
Some(api_ref) => api_ref,
@@ -391,12 +441,47 @@ mod tests {
391441
return Err(error_msg);
392442
}
393443

444+
let results = client.verify_model();
445+
if let Ok(el) = results {
446+
let_cxx_string!(non_existing_property = "main.the.jabberwocky");
447+
if let Err(_) = cprover_api::get_property_description(&el, &non_existing_property) {
448+
Ok(())
449+
} else {
450+
let error_msg = format!(
451+
"Got a description for non-existent property {:?}",
452+
&non_existing_property
453+
);
454+
Err(error_msg)
455+
}
456+
} else {
457+
Err("Unable to produce results from the verification engine".to_string())
458+
}
459+
}
460+
461+
#[test]
462+
fn it_can_get_the_property_status_for_existing_property() -> Result<(), String> {
463+
let binding = cprover_api::new_api_session();
464+
let client = match binding.as_ref() {
465+
Some(api_ref) => api_ref,
466+
None => panic!("Failed to acquire API session handle"),
467+
};
468+
469+
let vec: Vec<String> = vec!["other/example.c".to_owned()];
470+
471+
let vect = ffi_util::translate_rust_vector_to_cpp(vec);
472+
assert_eq!(vect.len(), 1);
473+
474+
if let Err(_) = client.load_model_from_files(vect) {
475+
let error_msg = format!("Failed to load GOTO model from files: {:?}", vect);
476+
return Err(error_msg);
477+
}
478+
394479
let results = client.verify_model();
395480
if let Ok(el) = results {
396481
let_cxx_string!(existing_property_id = "main.assertion.1");
397482
let prop_status = cprover_api::get_property_status(&el, &existing_property_id);
398483
match prop_status {
399-
prop_statust::FAIL => Ok(()),
484+
Ok(prop_statust::FAIL) => Ok(()),
400485
_ => {
401486
let error_msg = format!(
402487
"Property status for property {:?} was {:?} - expected FAIL",
@@ -409,4 +494,40 @@ mod tests {
409494
Err("Unable to produce results from the verification engine".to_string())
410495
}
411496
}
497+
498+
#[test]
499+
fn it_raises_an_exception_when_getting_status_of_non_existing_property() -> Result<(), String> {
500+
let binding = cprover_api::new_api_session();
501+
let client = match binding.as_ref() {
502+
Some(api_ref) => api_ref,
503+
None => panic!("Failed to acquire API session handle"),
504+
};
505+
506+
let vec: Vec<String> = vec!["other/example.c".to_owned()];
507+
508+
let vect = ffi_util::translate_rust_vector_to_cpp(vec);
509+
assert_eq!(vect.len(), 1);
510+
511+
if let Err(_) = client.load_model_from_files(vect) {
512+
let error_msg = format!("Failed to load GOTO model from files: {:?}", vect);
513+
return Err(error_msg);
514+
}
515+
516+
let results = client.verify_model();
517+
if let Ok(el) = results {
518+
let_cxx_string!(non_existing_property = "main.the.jabberwocky");
519+
let prop_status = cprover_api::get_property_status(&el, &non_existing_property);
520+
if let Err(status) = prop_status {
521+
Ok(())
522+
} else {
523+
let error_msg = format!(
524+
"Produced verification status for non-existing property: {:?}",
525+
non_existing_property
526+
);
527+
Err(error_msg)
528+
}
529+
} else {
530+
Err("Unable to produce results from the verification engine".to_string())
531+
}
532+
}
412533
}

0 commit comments

Comments
 (0)