Example

advertisement
Lecture 6
Model-based Testing
and Automated Test Case Generation
Automated Test-Case Generation
(TCG)
• By now we have seen that manual construction
of test cases is a difficult, time-consuming and
error-prone activity that requires expertise.
• Automated Test-Case Generation (TCG) has been
the “holy grail” of testing for some time.
• Is this even possible? If so, how?
• Black/white-box testing
• Want algorithms which generate test cases
(preferably with a known level of coverage)
Constraint Solving
• A constraint is a logical formula with one or more
free variables
• Examples: x >= 0, x+1 > y, x2 = 2.0, etc.
• A constraint solver is an algorithm which finds
values for the free variables, which make a
formula true, aka. solutions
• Examples:
– x=1 I= x >= 0 ( I= is the satisfaction symbol )
– x=1.41421356 I= x2 = 2.0
• Exist many different kinds of constraint solvers
Specification-based Black-box Testing
1. System requirement (Sys-Req)
2. System under Test (SUT )
3. Test verdict pass/fail (Oracle step)
Sys-Req
Test
case
pass/fail
Output
TCG
SUT
Oracle
Constraint solver
Language runtime
Constraint
checker
Example: ATCG for numerical code
Newton’s Square root algorithm
Postcondition
Ι y*y – x Ι ≤ ε
precondition x ≥ 0.0
x=4.0
TCG
Input
Constraint solver
x=4.0 satisfies x ≥ 0.0
y=2.0
SUT
Newton Code
Output
Oracle
Constraint checker
Verdict
x=4.0, y=2.0 satisfies
Ι y*y – x Ι ≤ ε
TCG for MBT of Embedded Systems
• We can make a simple model of an embedded
system as a deterministic Kripke structure (or
Moore machine)
• M = ( Q, Σ, q0, δ, λ )
• Where
– q0 Q, - initial state
– δ : Q x Σ  Q - state transition function
– λ: Q  Bn - n-bit output function
Example: 2-bit shift register
• Q = A, B, C, D, Σ = 0,1, q0=A
1
A,
λ(A) =00
0
1
0
0
1
B,
λ(B) =10
C,
λ(C) =01
0
D,
λ(D) =11
1
Another Representation
• Q = A, B, C, D, Σ = 0,1, q0=A
1
!Bit1 &
!Bit2
0
1
0
0
1
Bit1 &
!Bit2
!Bit1 &
Bit2
0
Bit1 &
Bit2
1
2-Bit Shift Reg in nuSMV format
MODULE main
VAR
Bit1 : boolean; -- Boolean var
Bit2 : boolean;
input : boolean;
state : {A, B, C, D}; -- scalar var
variable
ASSIGN
init(state) := A;
init(Bit1) := 0;
init(Bit2) := 0;
-- var “input” is uninitialised!
next(state) := case
state = A & input = 1
state = B & input = 0
state = B & input = 1
state = C & input = 0
state = C & input = 1
state = D & input = 0
TRUE : state; -- why?
esac;
next(Bit1) := case ??
next(Bit2) := case ??
:
:
:
:
:
:
B;
C;
D;
A;
B;
C;
esac;
esac;
Propositional Linear Temporal Logic
PLTL in nuSMV syntax
•
•
•
•
•
•
•
•
•
•
•
•
Simple temporal logic to express automaton requirements
Requirements are statements about all possible simulations
Boolean variables
A, B, …, X, Y, .. MyVar, etc.
Boolean operators
!ϕ, (ϕ & φ), (ϕ  ϕ) , (ϕ -> ϕ) ,…
LTL operators
Fϕ (sometime in the future ϕ)
Gϕ (always in the future ϕ)
ϕ U φ (φ holds until ϕ holds)
Xϕ (next step ϕ holds)
Write Xnϕ for X X … Xϕ (n times)
Examples
Today is Wednesday
Wednesday
Tomorrow is Wednesday
X Wednesday
(A) Thursday (always) immediately follows (a) Wednesday
G( Wednesday -> Thursday )
(A) Saturday (always) follows (a) Wednesday
G( Wednesday -> F( Saturday ) )
Yesterday was (a) Wednesday
G( Wednesday -> X Thursday) & Thursday
• Exercise: define the sequence of days precisely, i.e. just one solution
• Question: are there any English statements you can’t make in LTL?
• Question: what use cases can you express in LTL?
Useful Logical Identities for LTL
• Boolean identities
!!φ  φ,
!(ϕ  φ) (!ϕ & !φ) ,
(ϕ -> φ) (!ϕ  φ) etc.
• LTL identities
!G(!ϕ )  F(ϕ)
!X(ϕ)  X(!ϕ)
G(φ & ϕ)  G(φ)& G(ϕ)
G(φ)  φ & X G(φ)
• Exercise: using these identities, prove:
!F(!φ)  G(φ)
F(φ  ϕ)  F(φ)  F(ϕ)
F(φ)  φ  X F(φ)
• Remark TCG usually involves negating formulas, so its useful to understand
what a negation means
Specifications in nuSMV files
LTLSPEC
G( Bit1 <-> X Bit2 )
–- the value of Bit1 now always equals
the
-- value of Bit2 in the next time step
-- This is obviously TRUE!
G(
–the
---
Bit1 <-> X Bit1 )
the value of Bit1 now always equals
value of Bit2 in the next time step
This is obviously FALSE!
Model Checking
• nuSMV is a temporal logic model checker
• A model checker is a tool which takes as input
•
•
•
•
– An automaton model M
– A temporal logic formula φ
If φ is a true statement about all possible behaviors of M
then the model checker confirms it (proof)
If φ is a false statement about M the model checker
constructs a counterexample (a simulation sequence)
A counterexample to φ satisfies !φ
A simulation sequence can be executed as a test case
nuSMV Output Example
-- specification G( Bit1 <-> X Bit2 )
-- is true
-- specification G( Bit1 <-> X Bit1 )
-- is false
-- as demonstrated by the following execution
sequence
Trace Description: LTL Counterexample
Trace Type: Counterexample
-> State: 1.1 <state = A Bit1 = false Bit2 = false
-> Input: 1.2 <input = 1
-> State 1.2 <state = A Bit1 = true Bit2 = false
Automated White-box TCG
• We can use a model checker to generate
counterexamples to formulas (i.e. test cases)
with specific structural properties.
• This is done by inspecting the graph structure
of the automaton
• i.e. white/glass box testing
• “Most” use of model checkers concerns this.
Test Requirements/ Trap Properties
• Recall a test requirement is a requirement that
can be satisfied by one or more test cases
• Basic idea is to capture each test requirement as
an LTL formula known as a “trap property”
Example Suppose the test requirement is “cover
state D”
Trap property is G!( Bit1 & Bit2 )
This formula is False and any counterexample
must be a path that goes through state D.
Case Study: Car Controller
!accelerate
accelerate
Velocity
= stop
Velocity
= slow
brake
accelerate
brake
Basic idea (incomplete)
Velocity
= fast
!accelerate
Module main
Var
accelerate: boolean;
brake: boolean;
velocity: {stop, slow, fast}
ASSIGN
init(velocity) := stop
next(velocity := case
accelerate & !brake & velocity = stop : slow;
accelerate & !brake & velocity = slow : fast;
!accelerate & !brake & velocity = fast : slow;
!accelerate & !brake & velocity = slow: stop;
Brake: stop;
TRUE: velocity;
esac;
Trap properties for Structural Coverage
Models
• Let us use nuSMV to automatically construct
test suites that satisfy the different structural
coverage models introduced in Lecture 2.
• Examples:
– Node coverage NC
– Edge coverage EC
– Condition coverage PC
• How to interpret these concepts?
Node Coverage for CC
•
•
•
•
•
•
•
Want a path that visits each node
Simple approach: 1 trap property per node
General form:
G(!“unique_state_property”)
Counterexamples satisfy:
F(“unique_state_property” )
Examples:
G(!velocity = stop)
G(!velocity = slow)
G(!velocity = fast)
• Clearly this will give redundant test cases, but method is
still linear in state-space size.
Edge coverage for CC
• Want to be sure we traverse each edge between any pair of
nodes
α
φ
β
• General form G( α & ϕ -> X !β )
• Counterexample satisfies F(α & ϕ & X β )
• Example (5 more cases):
G(velocity=stop & accelerate ->
X velocity=slow)
• Exercise: define the remaining 5 trap formulas
Condition Coverage for CC
• For each transition, and each Boolean guard v
there exist two test cases for the transition:
(1) guard is true. (2) guard is false
α
G!(α & v) -> X(!β)
G!(α & !v) -> X(!β)
β
Boolean v;
Requirements-based TCG
• Can we also perform requirements-based test
case generation?
• Want to test the requirements are fulfilled
rather than explore structure of the code.
• Can look at negation of a requirement
Car Controller LTL requirements
1. Whenever the brake is activated, car has to stop
G(brake -> X velocity=stop)
2. When accelerating and not braking, velocity has to increase
gradually
G(!brake& accelerate&velocity=stop ->
X velocity=slow )
G(!brake& accelerate&velocity=slow ->
X velocity=fast )
3. When not accelerating and not braking, velocity has to decrease
gradually
G(!brake& !accelerate&velocity=fast -> X
velocity=slow)
G(!brake& accelerate&velocity=slow -> X
velocity=stop)
Safety Requirements
• A safety requirement describes a behavior
that may not occur on any path.
• “Something bad may not happen”
• To verify, all execution paths must be checked
exhaustively
• Safety properties usually have the form G!φ
where φ is the “bad thing”
• Counterexamples (test cases) are finite
Liveness Requirements
• A liveness requirement describes a behavior that
must hold on all execution paths
• “Something good eventually happens”
• Safety does not imply liveness
• Liveness does not imply safety
• Liveness properties may have the form
F(φ) or G(ϕ -> Xnφ ) or
G(ϕ -> Fφ )
where φ describes the “good” thing and ϕ is
some precondition needed for it to occur.
• Counterexamples may be finite or infinite (why?)
Infinite Counterexamples
• nuSMV describes infinite counterexamples as a “loop
and handle”
• a,b,c,d,(x,y,z)ω or handle(loop) ω
• a, …, d, x, …, z are the individual states
• We can generate these sequences, but can we execute
them?
• Are they test cases at all?
• We call a liveness requirement finite if all
counterexamples are finite
• Use cases are examples of finite liveness requirements
TCG for Finite Liveness Requirements
• A common form of liveness requirement is
G( φ -> Xn ϕ ) (1)
• Negating (1) and rewriting gives
F( φ & Xn (!ϕ ) ) (2)
• Every counterexample to (2) is a solution to (1) i.e. an
intended behavior.
• We can ask nuSMV for these intended behavior, and
compare them against the actual behavior.
• Edit the sequence to delete all internal and output
variables
• Agreement = pass, disagreement = fail
Car Controller Examples
(1)F(brake & X !velocity=stop)
(2) F(!brake & accelerate &
velocity=stop -> X
velocity=slow)
Conclusions
• TCG is certainly possible using various existing
algorithms.
• Generally requirements MBT is trickier than
structural MBT using this approach.
• There are other approaches to requirements
testing. See next lecture.
Download