1 % (c) 2009-2023 Lehrstuhl fuer Softwaretechnik und Programmiersprachen,
2 % Heinrich Heine Universitaet Duesseldorf
3 % This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html)
4
5 :- module(sap,[%reset_sap/0,
6 explore_and_generate_testcases/7,
7 cbc_gen_test_cases/5, cbc_gen_test_cases_from_string/5,
8 cbc_get_uncovered_events/1, cbc_get_nr_uncovered_events/1,
9 test_generation_by_xml_description/1,
10 tcl_create_local_testsuite/5,
11 tcl_get_events_preselected/2, tcl_convert_event_numbers_to_names/2,
12 tcl_generate_cb_testcases/5, % not used?
13 tcl_generate_testcases/7,
14 tcl_get_stored_test_cases/1, tcl_execute_stored_test_case/1,
15 stored_test_case_op_trace/2,
16 cb_timeout_path/2, cb_path/3, cb_path_testcase_only/3, % provide access to cbc-tests tree
17 write_all_deadlocking_paths_to_xml/1,
18 cbc_gen_test_cases_task_step/7, cbc_gen_test_cases_finish_tasks/0,
19 get_counter_info/4
20 ]).
21
22 :- use_module(library(lists)).
23 :- use_module(probsrc(xml_prob),[xml_parse/2]).
24 :- use_module(library(timeout)).
25
26 :- use_module(probsrc(error_manager)).
27 :- use_module(probsrc(state_space)).
28 :- use_module(probsrc(bmachine)).
29 :- use_module(probsrc(b_state_model_check),[set_up_transition/7]).
30 :- use_module(probsrc(preferences),[get_preference/2]).
31 :- use_module(probsrc(specfile),[expand_const_and_vars_to_full_store/2,expand_to_constants_and_variables/3]).
32 :- use_module(probsrc(translate),[translate_bvalue/2]).
33 :- use_module(probsrc(prob2_interface),[set_eclipse_preference/2]).
34 :- use_module(probsrc(kernel_objects),[not_equal_object/2]).
35
36 :- use_module(probsrc(bsyntaxtree)).
37 :- use_module(probsrc(tools)).
38 :- use_module(probsrc(debug),[debug_mode/1,debug_println/2, printsilent/1,nls/0,silent_mode/1]).
39 :- use_module(probsrc(store),[empty_state/1]).
40
41
42 :- use_module(probsrc(module_information),[module_info/2]).
43 :- module_info(group,test_generation).
44 :- module_info(description,'This module contains code to generate test cases based on Event-B or Classical B models').
45
46
47 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
48 % some code for information in the GUI
49
50 % get_events_preselected(-Events,-Preselect):
51 % Events: a list of all events
52 % Preselect: a list of numbers, each an index (starting with 0) of which event in Events should
53 % be preselected because it is already covered in the state space
54 % Both lists are wrapped by list(...) to use it from Tcl
55 tcl_get_events_preselected(list(Events),list(Preselect)) :-
56 get_all_events(Events),
57 select_uncovered_operations(Events,0,Preselect).
58 select_uncovered_operations([],_,[]).
59 select_uncovered_operations([Event|Erest],N,Preselect) :-
60 ( operation_already_covered_or_infeasible(Event) ->
61 Preselect = PSrest
62 ;
63 Preselect = [N|PSrest]),
64 N2 is N+1,
65 select_uncovered_operations(Erest,N2,PSrest).
66
67 :- use_module(cbcsrc(enabling_analysis),[infeasible_operation_cache/1]).
68 operation_already_covered_or_infeasible(Event) :-
69 \+ operation_name_not_yet_covered(Event).
70 operation_already_covered_or_infeasible(Event) :-
71 infeasible_operation_cache(Event). % check if determined to be infeasible; but do not trigger computation
72
73 % returns a list of all events (their namese) in the model
74 get_all_events(Events) :-
75 findall(E,b_top_level_operation(E),Events).
76
77 tcl_convert_event_numbers_to_names(list(EventNumbers),list(EventNames)) :-
78 get_all_events(Events),
79 maplist(get_event_name(Events),EventNumbers,EventNames).
80 get_event_name(List,Number,Res) :-
81 ((number(Number),nth0(Number,List,Res)) -> true
82 ; add_error(get_event_name,'Illegal event number: ',Number),fail).
83
84 % tcl_generate_testcases(+EventSelection,+TargetString,+MaxDepth,+MaxNewNodes,+Testfile,-NumberOfTestcases,-NrCovEvents):
85 % EventSelection: a list of numbers containing all indeces of events that should be covered
86 % TargetString: the predicate that identifies target nodes as string (atom)
87 % MaxDepth: the maximum depth of the search
88 % MaxNewNodes: the maximum number of newly explored nodes
89 % Testfile: the file where we store the found test cases
90 % NumberOfTestcases: The number of test cases that where found
91 tcl_generate_testcases(EventSelection,TargetString,MaxDepth,MaxNewNodes,Testfile,NumberOfTestcases,NrCoveredEvents) :-
92 % convert the list of event indeces into a list of events
93 get_selected_events(EventSelection,AllEvents,Events),
94 b_parse_optional_machine_predicate(TargetString,Target),
95 set_max_nr_of_new_impl_trans_nodes(MaxNewNodes),
96 safe_get_inits(Inits),
97 ( explore_covered_statespace(Inits, Events, Target, MaxDepth, _ExploredDepth) ->
98 true
99 ;
100 show_uncovered_events_as_errors),
101 generate_test_cases(Inits,Testcases),
102 length(Testcases,NumberOfTestcases),
103 maplist(get_coverage_count,Testcases,Nrs),
104 sumlist(Nrs,NrCoveredEvents),
105 format('Generated ~w testcases covering ~w events~n',[NumberOfTestcases,NrCoveredEvents]),
106 testcase_output(Testfile,AllEvents,Events,Testcases).
107
108
109 get_coverage_count(global_test_case_for_events(Events,_),Len) :- !, length(Events,Len).
110 get_coverage_count(global_test_case(_,_),Len) :- !, Len=1.
111 get_coverage_count(_,0).
112
113
114 % get_selected_events(+TkNumbers,-ListOfAllEvents,-SelectedEvents)
115 % Input: TkNumbers: numbers starting at 0; comes from Tk Selection Box
116 % Output: ListOfAllEvents
117 % Output: SelectedEvents: Name of Selected Events
118 get_selected_events(Selection,AllEvents,Events) :-
119 get_all_events(AllEvents),
120 (Selection==all -> Events=AllEvents
121 ; get_selected_events2(Selection,AllEvents,Events)).
122 get_selected_events2([],_AllEvents,[]).
123 get_selected_events2([Selection|Srest],AllEvents,[Event|Erest]) :-
124 nth0(Selection,AllEvents,Event),!,
125 get_selected_events2(Srest,AllEvents,Erest).
126
127
128 :- use_module(probsrc(state_space),[find_initialised_states/1]).
129
130 safe_get_inits(Inits) :-
131 find_initialised_states(Inits),
132 (Inits = [] -> add_error(sap,'no initial states found'),fail ; true).
133
134 % this predicate is called if the breadth-first search fails
135 % it shows an error for every event, for which we did not find
136 % an appropiate path
137 show_uncovered_events_as_errors :-
138 target_needed(Event),
139 (infeasible_operation_cache(Event)
140 -> ajoin(['No test path exists for infeasible (!) event ',Event],Msg)
141 ; ajoin(['No test path found for event ',Event],Msg)
142 ),
143 add_error(sap,Msg),
144 fail.
145 show_uncovered_events_as_errors.
146
147 get_uncovered_events(Uncovered) :-
148 findall(Event, target_needed(Event), Uncovered).
149
150 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
151
152 % model checking based test-case generation mcm
153
154 explore_and_generate_testcases(InEvents,Target,MaxDepth,MaxNewNodes,
155 Testfile,NumberOfTestcases,UncoveredEvents) :-
156 % If no event is specified to be covered, we assume that all events
157 % should be covered
158 ( InEvents = [] ->
159 printsilent('No events to cover specified, assuming all'),nls,
160 get_all_events(Events)
161 ; InEvents = [_|_] -> InEvents = Events
162 ; add_internal_error('Illegal events: ',InEvents),fail
163 ),
164 statistics(walltime, [Start|_]),
165 set_max_nr_of_new_impl_trans_nodes(MaxNewNodes),
166 safe_get_inits(Inits),
167 ( explore_covered_statespace(Inits, Events, Target, MaxDepth, _ExploredDepth) ->
168 UncoveredEvents = []
169 ;
170 get_uncovered_events(UncoveredEvents)),
171 generate_test_cases(Inits, Testcases),
172 length(Testcases, NumberOfTestcases),
173 get_all_events(AllEvents),
174 testcase_output(Testfile,AllEvents,Events,Testcases),
175 statistics(walltime, [End|_]),
176 Runtime is End - Start,
177 format_msg('Runtime for test case generation: ~w ms~n',[Runtime]).
178
179
180 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
181 % generate test cases from an explored state-space
182
183
184 % generate_test_cases(+Inits,-TestCases):
185 % Inits: List of initial states
186 % TestCases: list of test cases, each of the form global_test_case_for_event(Event,Trace),
187 % where Trace is a list of events
188 generate_test_cases(Inits,TestCases) :-
189 init_wanted_event_to_cover,
190 findall(T, generate_test_case(Inits,T), Ts),
191 sort(Ts,TestCases).
192 % TO DO: if generate_minimal_nr_of_testcases is true we could still have redundant tests; eliminate them.
193
194 :- dynamic wanted_event_to_cover/1.
195 init_wanted_event_to_cover :- retractall(wanted_event_to_cover(_)),
196 wanted_event(E), assertz(wanted_event_to_cover(E)),fail.
197 init_wanted_event_to_cover.
198 assert_covered(E) :- retractall(wanted_event_to_cover(E)).
199 % find a single test case by first choosing one event and one initial state
200 generate_test_case(Inits,global_test_case_for_events(WantedEventsCovered,Trace)) :-
201 %fail, % SAP wants all test cases, not just for each event the shortest,
202 !,
203 % generate a single test_case per event
204 wanted_event_to_cover(Event),
205 (minimum_path_length(Event,MaxSteps) -> true ; debug_println(20,cannot_cover(Event)),fail),
206 % this code could be improved: maybe a previous test-case has already covered this event !
207 debug_println(20,generating_testcase(Event,MaxSteps)),
208 % now find a path that can serve as a test case
209 (find_test_case_with_init(Event,Inits,MaxSteps,Trace)
210 -> trace_covers_wanted_events(Trace,WantedEventsCovered),
211 debug_println(44,found_trace(WantedEventsCovered,Trace)),
212 (get_preference(generate_minimal_nr_of_testcases,true)
213 -> maplist(assert_covered,WantedEventsCovered)
214 ; true)
215 ).
216 generate_test_case(Inits,global_test_case_for_events(WantedEventsCovered,Trace)) :-
217 % generate all paths up to certain depth; was requested by SAP, but can blow up
218 find_global_maximum(MaxSteps),
219 member(Init,Inits),
220 find_way_to_target(Init,MaxSteps,Trace),
221 trace_covers_wanted_events(Trace,WantedEventsCovered).
222
223 trace_covers_wanted_events(Trace,WantedEventsCovered) :-
224 findall(TestEvent,contains_an_event_to_test(TestEvent,Trace),Covered),
225 Covered\=[],
226 sort(Covered,WantedEventsCovered).
227 contains_an_event_to_test(Event,Trace) :-
228 member('$full_event'(Event,_Args),Trace),wanted_event_to_cover(Event).
229
230
231 % find variables or constants which are non-deterministically assigned
232 % TO DO: check max_reached of predecessor ?!
233 non_deterministic_values(_,[],NonDetState) :- !, NonDetState=[].
234 non_deterministic_values(Init,OtherInits,NonDetState) :-
235 findall(bind(ID,Value),non_deterministic_value(Init,OtherInits,ID,Value),NonDetState).
236
237 non_deterministic_value(Init,OtherInits,ID,Value) :-
238 expand_initalised_state(Init,State0),
239 maplist(expand_initalised_state,OtherInits,OtherStates),
240 member(bind(ID,Value),State0),
241 (other_value_exists(ID,Value,OtherStates) -> true).
242
243 other_value_exists(ID,Value,OtherStates) :-
244 member(OtherState,OtherStates),
245 member(bind(ID,OtherValue),OtherState),
246 kernel_objects:not_equal_object(Value,OtherValue).
247
248 % find the maximum of the minimum mapth length
249 find_global_maximum(MaxSteps) :-
250 findall(M, minimum_path_length(_Event,M), [First|Maxis]),
251 find_global_maximum2(Maxis,First,MaxSteps).
252 find_global_maximum2([],Max,Max).
253 find_global_maximum2([H|T],A,Max) :- B is max(H,A), find_global_maximum2(T,B,Max).
254
255
256 % find a single test case for a single event and a set of initial states
257 find_test_case_with_init(Event,Inits,MaxSteps,ResTrace) :-
258 select(Init,Inits,OtherInits),
259 find_test_case(Event,Init,MaxSteps,TraceWithoutInit),
260 non_deterministic_values(Init,OtherInits,NonDetState),
261 (NonDetState=[] -> ResTrace = TraceWithoutInit
262 ; ResTrace = ['$non_det_init'(NonDetState)|TraceWithoutInit]).
263
264 % find a single test case for a single event and an initial state
265 find_test_case(Event,Init,MaxSteps,Trace) :-
266 % first find a path where Event occurs
267 find_way_to_event(Init,MaxSteps,Event,Trace,TraceB,State,RestSteps),
268 % then find a path from that state to a target state
269 find_way_to_target(State,RestSteps,TraceB).
270
271 % find_way_to_event(+A,+Max,+Event,-Trace,TraceR,-B,-RestSteps):
272 % A: node where we start the search
273 % Max: maximum number of steps in the path
274 % Event: the event we are searching for
275 % Trace: a trace to the state, the last event is Event, the list
276 % has the variable TraceR as tail
277 % B: the state after Event occurs
278 % RestSteps: the number of remaining steps (i.e. RestSteps=Max-length(Trace))
279 find_way_to_event(A,Max,Event,['$full_event'(Operation,Args)|Trace],TraceR,B,RestSteps) :-
280 dec(Max,Max2),
281 do_full_event(A,Operation,Args,C), % TO DO: extract operation arguments
282 ( Event = Operation ->
283 Trace = TraceR,
284 B = C,
285 RestSteps = Max2
286 ;
287 find_way_to_event(C,Max2,Event,Trace,TraceR,B,RestSteps)).
288
289 find_way_to_target(A,_Max,[]) :-
290 target_node(A). % removed the cut means that if target found straight away, no paths were generated : ,!.
291 find_way_to_target(A,Max,['$full_event'(Operation,Args)|Trace]) :-
292 dec(Max,Max2),
293 do_full_event(A,Operation,Args,B),
294 find_way_to_target(B,Max2,Trace).
295
296 % decrease N, but never become negative
297 dec(N,N2) :- N>0, N2 is N-1.
298
299 :- use_module(probsrc(specfile),[get_operation_arguments/2, get_operation_name/2]).
300 do_event(A,Event,B) :-
301 transition(A,FullOperation,B),
302 get_operation_name(FullOperation,Event).
303 do_full_event(A,Event,Args,B) :-
304 transition(A,FullOperation,B),
305 get_operation_name(FullOperation,Event),
306 get_operation_arguments(FullOperation,Args).
307
308 :- use_module(library(ordsets)).
309 % :- use_module(probsrc(sap)), sap:path_to_deadlock_or_loop(root,[],P), print(P),nl,fail.
310 % compute all paths until a deadlock or non-interesting node is reached
311 % assumes no loop !! otherwise an infinite number of solutions will be returned
312 path_to_deadlock_or_loop(A,History,Path) :- ord_member(A,History),
313 !, % loop
314 Path = [loop(A)].
315 path_to_deadlock_or_loop(A,History,Path) :- ord_add_element(History,A,NH),
316 if(%do_event(A,Event,B),
317 %(transition(A,FullOperation,B), translate:translate_event(FullOperation,Event)),
318 my_event(A,Event,B),
319 (Path = [Event|P2], path_to_deadlock_or_loop(B,NH,P2)),
320 Path = [] %[deadlock(A)]
321 ).
322
323 my_event(Src,(TransId,Op,Src,Dst),Dst) :-
324 transition(Src,Op,TransId,Dst).
325
326 % :- use_module(probsrc(sap)), sap:write_all_deadlocking_paths_to_xml('~/Desktop/all.xml').
327 write_all_deadlocking_paths_to_xml(Filename) :-
328 retractall(path_nr(_)), assertz(path_nr(0)),
329 open_xml_testcase(Filename),
330 call_cleanup(write_path_to_deadlock_or_loop, close_xml_testcase),
331 get_path_nr(Nr),
332 format_msg('Wrote ~w tests to ~w.~n',[Nr,Filename]).
333
334 write_path_to_deadlock_or_loop :- path_to_deadlock_or_loop(root,[],Trace),
335 get_path_nr(Id),
336 (debug_mode(on) -> format_msg('Trace ~w = ~w~n',[Id,Trace]) ; true),
337 %write_testcase(global_test_case_for_event(unknown,Trace),Id),
338 get_initialisation(Trace,RestTrace,State),
339 cb_write_test_case(RestTrace,State),
340 (exceeded_limit(Id) -> format_warn('Exceeded limit of number of traces: ~w~n',[Id])
341 ; Id>0, Id mod 500 =:= 0, format_msg('Wrote ~w tests ...~n',[Id]),
342 fail). % continue backtracking
343 write_path_to_deadlock_or_loop.
344
345 exceeded_limit(100000). % do not write more than 100,000 states to avoid filling up disk
346 % TO DO: add loop detection and/or user settable upper bound
347
348 :- use_module(probsrc(specfile),[state_corresponds_to_initialised_b_machine/2]).
349 :- use_module(probsrc(state_space),[visited_expression/2]).
350 get_initialisation([(_,_,_,Dst)|T],Trace,InitBState) :-
351 expand_initalised_state(Dst,InitBState),!, Trace=T.
352 get_initialisation([_|T],Trace,State) :- get_initialisation(T,Trace,State).
353
354 expand_initalised_state(ID,InitBState) :-
355 state_space:visited_expression(ID,State),
356 state_corresponds_to_initialised_b_machine(State,InitBState).
357
358 :- dynamic path_nr/1.
359 path_nr(0).
360 get_path_nr(N) :- retract(path_nr(N)), N1 is N+1, assertz(path_nr(N1)).
361
362 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
363 % explore the state-space so that it is usable for test-case generation
364
365 % bfs_node(NodeId,Depth): open nodes in the state space, for breadth first search,
366 % the order is relevant, Depth is the distance from the initialisation nodes
367 % (later we call this the breadt-first FIFO queue)
368 % predecessor(NodeId,PreNodeId): store predecessors of a node
369 % target_reachable(NodeId,TargetDist): A target state is reachable from this state
370 % event_dest(NodeId,Event): One of the events we want to cover leads to this node
371 % node_covered(NodeId,Depth): This node was encountered before by the search at depth Depth.
372 % target_needed(Event): We need to find a final from a state that is the destination of the event,
373 % in the beginning, each given event is stored here
374 % wanted_event(Event): Event if one of the events we are interested in
375 % in the beginning, each given event is stored here
376 % target_node(NodeId): The node is a target node
377
378 :- volatile bfs_node/2, predecessor/2, target_reachable/2, event_dest/2.
379 :- volatile node_covered/2, target_needed/1, wanted_event/1.
380 :- volatile target_node/1, minimum_path_length/2.
381 :- volatile maximum_depth_reached/1.
382 :- dynamic bfs_node/2, predecessor/2, target_reachable/2, event_dest/2.
383 :- dynamic node_covered/2, target_needed/1, wanted_event/1.
384 :- dynamic target_node/1, minimum_path_length/2.
385 :- dynamic maximum_depth_reached/1.
386
387 % explore_covered_statespace(+Inits,+Events,+Target,-ExploredDepth):
388 % Inits: a list of initialisation states
389 % Events: the list of events that should be covered
390 % Target: the predicate that is used to identify a target state
391 % MaxDepth: the maximum depth that will be explored
392 % ExploredDepth: the depth until the state-space was explored
393 % This predicate explores a part of the state-space by breadt-first search
394 % that contains for each event a path where the event occurs and that ends
395 % in a target state.
396 % Additionally, the state-space is explored to a fixed depth.
397 % (i.e. if we needed a state that is reachable by 4 steps, than all states
398 % that are reachable by 4 steps are explored with their transitions and
399 % successor states)
400 explore_covered_statespace(Inits,Events,Target,MaxDepth,ExploredDepth) :-
401 % initialise the data structures
402 reset_bfs(Events),
403
404 % put initial states into the breadt-first FIFO queue
405 assert_bfs_nodes(Inits,0,_,_),
406 % check if the initialised states are target states
407 mark_target_nodes(Inits,Target),
408
409 % do the breadt-first search
410 explore(Target,MaxDepth,-1,ExploredDepth).
411
412 % explore(+Target,+PrevExploredDepth,-ExploredDepth):
413 % Target: the predicate that is used to identify target nodes
414 % MaxDepth: the maximum depth that will be explored
415 % PrevExploredDepth: the deepest node that is explored before calling this predicate
416 % ExploredDepth: the deepest node that was explored by the search
417 explore(Target,_MaxDepth,ExploredDepth,ExploredDepth) :-
418 % we do not have to find any more targets, we're finished
419 \+ target_needed(_),!,
420 % we did a breadth-first search until a certain depth, now we
421 % complete the exploration of the state space to that depth
422 complete_depth(Target,ExploredDepth).
423 explore(Target,MaxDepth,_,ExploredDepth) :-
424 % get a node from the breadth-first FIFO queue
425 retract( bfs_node(Node,Depth) ),!,
426 % and explore it
427 explore_node(Node, Target, Depth),
428 !, % just to be safe that no back-tracking occurs
429 Depth =< MaxDepth,
430 update_depth(Depth,MaxDepth),
431 explore(Target,MaxDepth,Depth,ExploredDepth).
432
433 :- use_module(probsrc(state_space),[get_state_space_stats/3]).
434 update_depth(Depth,MaxDepth) :- maximum_depth_reached(MD), Depth>MD,
435 !,
436 get_state_space_stats(NrNodes,NrTransitions,ProcessedNodes),
437 format_msg('Depth reached: ~w (max.: ~w), States: ~w (~w processed), Transitions: ~w~n',[Depth,MaxDepth,NrNodes,ProcessedNodes,NrTransitions]),
438 retractall(maximum_depth_reached(_)),
439 assertz(maximum_depth_reached(Depth)).
440 update_depth(_,_).
441
442 % explore_node(+Node,+Target,+Depth):
443 % Node: the node that should be explored
444 % Target: the predicate that is used to identify target nodes
445 % Depth: depth of the given node
446 explore_node(Node,Target,Depth) :-
447 % newly encountered nodes are one level deeper
448 NewDepth is Depth+1,
449 % get all transitions from this node to successor nodes
450 impl_trans_term_all(Node,Transitions),
451 % check if there are events we are interested in and
452 % get the successor nodes
453 analyse_transitions(Transitions,Successors),
454 % find out wich of the successor nodes are new and
455 % store those in the breadt-first FIFO queue
456 assert_bfs_nodes(Successors,NewDepth,NewNodes,OldNodes),
457 % store that this node is a predecessor node of
458 % all the successor nodes encountered
459 assert_predecessors(Successors, Node),
460 % find out which of the new nodes are target nodes
461 % and in such a case propagate the information that a target
462 % note is reachable back into the network.
463 mark_target_nodes(NewNodes,Target),
464 mark_target_nodes_by_old(OldNodes,Node).
465
466 mark_target_nodes_by_old([],_Node).
467 mark_target_nodes_by_old([PotentialTargetNode|Rest],Node) :-
468 ( target_reachable(PotentialTargetNode,DistToTarget) ->
469 NewDist is DistToTarget + 1,
470 update_minimal_path_for_events(Node,PotentialTargetNode,DistToTarget),
471 assert_target_reachable(Node,NewDist)
472 ; true),
473 mark_target_nodes_by_old(Rest,Node).
474
475
476 % mark_target_nodes(+Nodes,+Target).
477 % Nodes: a list of node IDs
478 % Target: the predicate to check whether the node is a target node
479 % PreDepth: the depth of the node that led to this node,
480 % checks for each node in the given list if it is a target
481 % node, and if yes then propagate the information that the
482 % target is reachable to all predecessor nodes.
483 :- use_module(probsrc(eclipse_interface),[test_boolean_expression_in_node/3]).
484 mark_target_nodes([],_Target).
485 mark_target_nodes([Node|NRest],Target) :-
486 ( test_boolean_expression_in_node(Node,Target,'testcase target') ->
487 assertz( target_node(Node) ),
488 assert_target_reachable(Node,1)
489 ; true),
490 mark_target_nodes(NRest,Target).
491
492 % for all transitions, check if the resp. event is interesting
493 % and if yes, mark the successor node.
494 % and return the list of successor nodes.
495 analyse_transitions([],[]).
496 analyse_transitions([op(_Id,Operation,Succ)|Orest],[Succ|Srest]) :-
497 % the arguments of the event are not interesting to us
498 specfile:get_operation_name(Operation,Event), %functor(Operation,Event,_),
499 % if Event is one of the events we monitor, we mark
500 % the destination node as a node from where we look for
501 % a path to a target node.
502 (wanted_event(Event) -> assert_event_dest(Succ,Event) ; true),
503 analyse_transitions(Orest,Srest).
504
505 % store the information, that the Node is reached by Event.
506 % We need this, because we need such a node fro
507 assert_event_dest(Node,Event) :-
508 ( event_dest(Node,Event) -> true % information was already present, ignore
509 ;
510 assertz( event_dest(Node,Event) ),
511 % if a target node can be reached from Node, we have a path in the explored
512 % state-space that includes the Event and ends in a target node.
513 ( target_reachable(Node,_) ->
514 ( retract( target_needed(Event) ) -> true; true)
515 ; true)).
516
517 % reset all data structures needed for the breadth-first search
518 reset_bfs(Events) :-
519 retractall(bfs_node(_,_)),
520 retractall(predecessor(_,_)),
521 retractall(target_reachable(_,_)),
522 retractall(node_covered(_,_)),
523 retractall(event_dest(_,_)),
524 retractall(target_needed(_)),
525 retractall(wanted_event(_)),
526 retractall(target_node(_)),
527 retractall(minimum_path_length(_,_)),
528 retractall(maximum_depth_reached(_)),
529 assertz( maximum_depth_reached(0)),
530 reset_bfs2(Events).
531 reset_bfs2([]).
532 reset_bfs2([Event|Rest]) :-
533 (b_top_level_operation(Event) -> true
534 ; add_warning(sap,'Unknown event: ',Event)),
535 assertz( target_needed(Event) ),
536 assertz( wanted_event(Event) ),
537 reset_bfs2(Rest).
538
539 % assert_bfs_nodes(+Nodes,+Depth,-NewNodes):
540 % Nodes: a list of nodes
541 % Depth: the current depth of the search, this is stored for finding
542 % out later until which depth the search has been done
543 % NewNodes: a sublist of Nodes, that includes all nodes that have not been
544 % encountered before
545 % This predicate checks if the given nodes are already encountered and if
546 % not, it stores them into the breadt-first FIFO queue
547 assert_bfs_nodes([],_,[],[]).
548 assert_bfs_nodes([Node|Rest],Depth,NewNodes,OldNodes) :-
549 ( node_covered(Node,_NodeDepth) -> % we already encountered Node, ignore
550 NewNodes = NewRest,
551 OldNodes = [Node|OldRest]
552 ; % this is a new node
553 NewNodes = [Node|NewRest],
554 OldNodes = OldRest,
555 assertz( node_covered(Node,Depth) ), % mark as covered
556 assertz( bfs_node(Node,Depth) )), % put it into the FIFO queue for the breadth-first search
557 assert_bfs_nodes(Rest,Depth,NewRest,OldRest).
558
559 % assert_predecessors(+Nodes,+Predecessor):
560 % Nodes: a list of nodes
561 % Predecessor: the predecessor node, a node from where a transition
562 % leads to each of the nodes in Nodes
563 % This predicate stores for each node in Nodes that Predecessor is a
564 % predecessor of the node.
565 % This information is needed to be able to propagate information of
566 % a reachable target state back into the graph.
567 assert_predecessors([],_).
568 assert_predecessors([Node|Rest],Pre) :-
569 ( predecessor(Node,Pre) -> true % information already there, ignore
570 ; assertz(predecessor(Node,Pre))),
571 assert_predecessors(Rest,Pre).
572
573 % store the information that a target node is reachable from a node
574 % propagate this information to predecessor nodes if necessary
575 assert_target_reachable(Node,DistToTarget) :-
576 ( target_reachable(Node,OldDistToTarget) ->
577 ( OldDistToTarget =< DistToTarget ->
578 true % information already there, ignore
579 ;
580 retract( target_reachable(Node,_) ),
581 assert_target_reachable2(Node,DistToTarget)
582 )
583 ;
584 assert_target_reachable2(Node,DistToTarget)
585 ).
586 assert_target_reachable2(Node,DistToTarget) :-
587 assertz( target_reachable(Node,DistToTarget) ), fail.
588 assert_target_reachable2(Node,_DistToTarget) :-
589 % the node can be reached by Event, so we know
590 % that we have a path in the explored state-space that contains
591 % Event a ends in a target node.
592 event_dest(Node,Event),
593 retract( target_needed(Event) ),
594 % (this is a fail-loop)
595 fail.
596 assert_target_reachable2(Node,DistToTarget) :-
597 % propagate the information to the predecessors
598 % (if we can reach a target node from this node, we also can
599 % reach the target node from each predecessor node.)
600 predecessor(Node,Pre),
601 update_minimal_path_for_events(Pre,Node,DistToTarget),
602 NewDistToTarget is DistToTarget + 1,
603 assert_target_reachable(Pre,NewDistToTarget),
604 % (this is a fail-loop)
605 fail.
606 assert_target_reachable2(_,_).
607
608 % Pre: node where the event starts
609 % Post: node where the events leads to
610 % DistToTarget: number of steps to reach the target when starting in the
611 % pre node.
612 % For each event that should be covered, we store the minimal length of the
613 % path needed to cover that event.
614 % We store and compute that information while going back from the target node
615 % to set the target_reachable flag for states.
616 % To do that, we add the distance to the target to the depth of the node. The
617 % encountered minimum for each event is stored in minimum_path_length/2.
618 update_minimal_path_for_events(Pre,Post,DistToTarget) :-
619 do_event(Pre,Event,Post),
620 wanted_event(Event),
621 node_covered(Pre,NodeDepth),
622 MinLength is DistToTarget + NodeDepth,
623 ( minimum_path_length(Event,Old) ->
624 Old > MinLength,
625 retractall( minimum_path_length(Event,_) ),
626 assertz( minimum_path_length(Event,MinLength) )
627 ;
628 format_ok('Covered: ~w (length: ~w)~n',[Event,MinLength]),
629 assertz( minimum_path_length(Event,MinLength) )),
630 fail.
631 update_minimal_path_for_events(_Pre,_Post,_DistToTarget).
632
633 % explore the state-space to the given depth
634 complete_depth(Target,Depth) :-
635 retract( bfs_node(Node,NodeDepth) ),!,
636 ( NodeDepth =< Depth ->
637 explore_node(Node,Target,Depth),
638 complete_depth(Target,Depth)
639 ;
640 % the next nodes are deeper, just revert the previous retract
641 % (this might not be necessary, because the data may not be used
642 % anymore after this, but just to keep it cleaner)
643 asserta( bfs_node(Node,NodeDepth) )).
644 complete_depth(_Target,_Depth).
645
646
647 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
648 % XML output
649
650 testcase_output(Filename,_AllEvents,_SelectedEvents,Testcases) :-
651 open_xml_testcase(Filename),
652 % write_selected_events(AllEvents,SelectedEvents),
653 call_cleanup((write_testcases(Testcases,1) -> true ; add_internal_error('Generating testcases failed: ',Filename)),
654 close_xml_testcase).
655
656 :- use_module(probsrc(tools_io)).
657 :- dynamic no_xml_file/0.
658
659 open_xml_testcase('') :- assertz(no_xml_file),!.
660 open_xml_testcase(Filename) :-
661 safe_open_file(Filename,write,_Stream,[alias(xml_testcase),encoding(utf8)]),
662 absolute_file_name(Filename,AF), format_msg('% Writing testcases to ~w~n',[AF]),
663 xml_write('<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<extended_test_suite>\n').
664
665 close_xml_testcase :- retract(no_xml_file),!.
666 close_xml_testcase :-
667 xml_write('</extended_test_suite>\n'),
668 close(xml_testcase).
669
670 xml_write(Atom) :- no_xml_file,!, (debug_mode(on) -> write(Atom) ; true).
671 xml_write(Atom) :- write(xml_testcase,Atom).
672
673 :- use_module(probsrc(tools_files),[put_codes/2]).
674 put_codes_xml(Codes) :- no_xml_file,!, (debug_mode(on) -> put_codes(Codes,user_output) ; true).
675 put_codes_xml(Codes) :- put_codes(Codes,xml_testcase).
676
677 flush_output_xml :- no_xml_file,!.
678 flush_output_xml :- flush_output(xml_testcase).
679
680 /*
681 write_selected_events(AllEvents,Events) :-
682 xml_write(' <global_events>\n'),
683 write_selected_events2(AllEvents,Events),
684 xml_write(' </global_events>\n').
685 write_selected_events2([],_Selected).
686 write_selected_events2([Event|Rest],Selected) :-
687 (member(Event,Selected) -> Sel = true ; Sel = false),
688 xml_write(' <event name=\"'),xml_write(Event),
689 xml_write('\" covered=\"'),xml_write(Sel),
690 xml_write('\"/>\n'),
691 write_selected_events2(Rest,Selected).
692 */
693
694
695 write_testcases([],_).
696 write_testcases([Testcase|TCrest],N) :-
697 write_testcase(Testcase,N),
698 N2 is N+1,
699 write_testcases(TCrest,N2).
700
701 write_testcase(global_test_case_for_events(Events,Trace),N) :-
702 number_codes(N,Id),
703 write_testcase2(Trace,[],Id,Events).
704 write_testcase(global_test_case(Id,Trace),_N) :-
705 write_testcase2(Trace,[],Id,unknown).
706 write_testcase(local_test_case(Id,Global,Local),_N) :-
707 write_testcase2(Global,[Local],Id,unknown).
708
709 write_testcase2(GlobalTrace,LocalTraces,Id,Events) :-
710 xml_write(' <test_case id=\"'),put_codes_xml(Id),
711 (Events=unknown -> true
712 ; xml_write('\" targets=\"'), xml_write(Events)),
713 xml_write('\">\n'),
714 write_trace(global,GlobalTrace),
715 write_traces(LocalTraces,local),
716 xml_write(' </test_case>\n').
717
718 write_traces([],_Type).
719 write_traces([Trace|Trest],Type) :-
720 write_trace(Type,Trace),
721 write_traces(Trest,Type).
722 write_trace(Type,Trace) :-
723 xml_write(' <'),xml_write(Type),xml_write('>\n'),
724 write_trace2(Trace,1),
725 xml_write(' </'),xml_write(Type),xml_write('>\n').
726 write_trace2([],_).
727 write_trace2([Step|Trace],N) :-
728 write_step(Step,N),
729 N2 is N+1,
730 write_trace2(Trace,N2).
731
732 write_step('$non_det_init'(NonDetState),N) :- !,
733 write_step_tag(N,'INITIALISATION'),
734 maplist(xml_write_binding(value),NonDetState),
735 xml_write(' </step>\n').
736 write_step('$full_event'(Event,[]),N) :- !,write_step(Event,N).
737 write_step('$full_event'(Event,Args),N) :-
738 write_step_tag(N,Event),
739 b_get_machine_operation(Event,_,Parameters,_),
740 xml_write_parameters(Event,Parameters,Args),
741 xml_write(' </step>\n').
742 write_step(Event,N) :-
743 %xml_write(' <step id=\"'), xml_write(N), xml_write('\">'), xml_write(Step), xml_write(' </step>\n').
744 xml_write(' <step id=\"'),
745 xml_write(N),
746 xml_write('\" name=\"'),
747 xml_write(Event),
748 xml_write('\" />\n').
749
750 write_step_tag(N,Event) :-
751 xml_write(' <step id=\"'),
752 xml_write(N),
753 xml_write('\" name=\"'),
754 xml_write(Event),
755 xml_write('\">\n').
756
757
758 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
759 % local test case generation
760
761 :- volatile is_local_event/1, is_global_event/2.
762 :- dynamic is_local_event/1, is_global_event/2.
763
764 tcl_create_local_testsuite(GlobalFile,LocalFile,MaxNewNodes,NumSat,NumUnsat) :-
765 read_xml_testcases(GlobalFile,Global),
766 set_max_nr_of_new_impl_trans_nodes(MaxNewNodes),
767 process_testsuite(Global,Local,Unsatisfied),
768 length(Local,NumSat),length(Unsatisfied,NumUnsat),
769 append(Local,Unsatisfied,Testcases),
770 testcase_output(LocalFile,[],[],Testcases).
771
772 process_testsuite(GlobalTestsuite,LocalTestsuite,Unsatisfied) :-
773 reset_local_search,
774 safe_get_inits(Inits),
775 process_testsuite2(GlobalTestsuite,Inits,LocalTestsuite,Unsatisfied).
776 process_testsuite2([],_Inits,[],[]).
777 process_testsuite2([Global|Grest],Inits,LocalTestsuite,Unsatisfied) :-
778 get_testcase_id(Global,Id),
779 ( find_local_testcase(Global,Inits,Local) ->
780 write('++ found local testcase: '),print(Id),nl,
781 LocalTestsuite = [Local|Lnext],
782 Unsatisfied = Unext
783 ;
784 write('-- no local testcase: '),print(Id),nl,
785 LocalTestsuite = Lnext,
786 Unsatisfied = [Global|Unext]),
787 process_testsuite2(Grest,Inits,Lnext,Unext).
788
789 get_testcase_id(global_test_case(Id,_),I) :- atom_codes(I,Id).
790
791 % find_local_testcase(+Global,+Inits,-Local):
792 % Global: a global test case
793 % Inits: list of initial states
794 % Local: a local test case
795 % search for a local trace of a global test case in the current model.
796 % A local trace consists of refined events of events of the global model,
797 % interspered by local events (i.e. events that refine skip)
798 find_local_testcase(global_test_case(Id,Global),Inits,local_test_case(Id,Global,Local)) :-
799 member(Init,Inits),
800 find_local_trace(Global,Init,Local).
801
802 find_local_trace([],_State,[]).
803 find_local_trace([GlobalEvent|Grest],State,LocalTrace) :-
804 % do a number of local steps (without loop) until the global event
805 find_local_path_to_global_event(State,GlobalEvent,[State],LocalTrace,RTrace,Dest),
806 % continue with the next global event
807 find_local_trace(Grest,Dest,RTrace).
808
809 % find_local_path_to_global_event(+State,+GlobalEvent,+History,-Trace,?RTrace,-End):
810 % State: the state where the search starts
811 % GlobalEvent: the global event that we are searching for, until reaching it, only
812 % local events are taken
813 % History: the states that we encountered before in this search, to prevent loops
814 % Trace: the resulting trace, the last element is the global event, the tail of the
815 % list is uninstantiated
816 % RTrace: the tail of Trace
817 % End: the last state in the search, the global event leads into this state
818 :- use_module(probsrc(state_space),[compute_transitions_if_necessary/1]).
819 find_local_path_to_global_event(State,GlobalEvent,History,[Event|Trace],RTrace,End) :-
820 % make sure this node is explored
821 compute_transitions_if_necessary(State), % this may use a ltllimit (max_nr_of_new_nodes)
822 % do an (non-deterministic) step
823 do_event(State,Event,Dest),
824 ( is_local_event(Event) ->
825 % local events can be taken as long we do not loop
826 \+ member(Dest,History),
827 % continue the search with the new state
828 find_local_path_to_global_event(Dest,GlobalEvent,[Dest|History],Trace,RTrace,End)
829 ; is_global_event(Event,GlobalEvent) ->
830 % if we encounter the global event we search for, we can stop
831 Trace = RTrace,
832 Dest = End).
833
834 % initial the data structures for the search
835 reset_local_search :-
836 get_all_events(Events),
837 retractall(is_local_event(_)),
838 retractall(is_global_event(_,_)),
839 assert_local_events(Events).
840
841 % for each event, we store whether it's local or global,
842 % and if it's global, we store the refined event
843 assert_local_events([]).
844 assert_local_events([Event|Erest]) :-
845 get_operation_info(Event,Info),
846 ( member(refines(Ref),Info) ->
847 assert_abstract_events(Event,Ref)
848 ; assertz(is_local_event(Event))),
849 assert_local_events(Erest).
850 assert_abstract_events(Event,Refines) :-
851 member(Abstract,Refines), assertz( is_global_event(Event,Abstract) ),
852 % (fail-loop)
853 fail.
854 assert_abstract_events(_,_).
855
856
857 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
858 % read XML testcases
859 read_xml_testcases(Filename,TestCases) :-
860 safe_read_string_from_file(Filename,utf8,Codes),
861 xml_parse(Codes,xml(_Atts,Content)),
862 member(element(extended_test_suite,_,XmlTestSuite),Content),
863 extract_testcases(XmlTestSuite,TestCases).
864 extract_testcases(Content,TestCases) :-
865 findall(TC, extract_testcase(Content,TC), TestCases).
866 extract_testcase(Content,global_test_case(Id,GlobalTrace)) :-
867 member(element(test_case,Atts,EContent),Content),
868 member(id=Id, Atts),
869 member(element(global,_,GContent),EContent),
870 findall(Step,
871 ( member(element(step,_,[pcdata(P)]),GContent),atom_codes(Step,P) ),
872 GlobalTrace).
873
874
875 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
876 % dot debugger
877
878 :- use_module(library(process),[process_create/3,process_wait/2]).
879 :- public sapdebug/0.
880 sapdebug :-
881 process_create(path(dot),
882 ['-T','pdf','-o','sap_debug.pdf'],
883 [stdin(pipe(S)),process(P)]),
884 write(S,'digraph sap_debug {\n'),
885 sapwrite_nodes(S),
886 write(S,'\n'),
887 sapwrite_edges(S),
888 write(S,'}\n'),
889 close(S),
890 process_wait(P,_).
891
892 sapwrite_nodes(S) :-
893 node_covered(Node,_),
894 ( bfs_node(Node,_) -> Shape = triangle; Shape = box ),
895 ( target_reachable(Node,_) -> Style = ',filled'; Style = ''),
896 write_l(S,[' ',Node,' [shape=',Shape,', style=\"rounded',Style,'\"];\n']),
897 fail.
898 sapwrite_nodes(_).
899
900 sapwrite_edges(S) :-
901 predecessor(Post,Pre),
902 write_l(S,[' ',Pre,' -> ',Post,';']),
903 fail.
904 sapwrite_edges(_).
905
906 write_l(_S,[]) :- !.
907 write_l(S,[H|T]) :-
908 write(S,H),write_l(S,T).
909
910
911
912
913 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
914 % constraint based test case generation
915
916 tcl_generate_cb_testcases(EventSelection,TargetString,MaxDepth,Filename,list(Uncovered)) :-
917 % first translate Tk EventSelection Number list into Events
918 get_selected_events(EventSelection,_AllEvents,Events),
919 cbc_gen_test_cases_from_string(Events,TargetString,MaxDepth,Filename,Uncovered).
920
921 % TargetString can be #not_invariant for BMC - bounded model checking
922 cbc_gen_test_cases_from_string(Events,TargetString,MaxDepth,Filename,Uncovered) :-
923 % parse target predicate for test-case sequences
924 parse_cbc_target_predicate(TargetString,Target),
925 cbc_gen_test_cases(Events,Target,MaxDepth,Filename,Uncovered).
926
927 % cb_uncovered_event/1: event is not covered in any test-case; but could by covered in a path
928 :- volatile cb_path/3, cb_path_testcase_only/3, cb_uncovered_event/1, cb_target_event/1, cb_final_event/1.
929 :- volatile cb_try_state_caching/0.
930 :- dynamic cb_path/3, cb_path_testcase_only/3, cb_uncovered_event/1, cb_target_event/1, cb_final_event/1.
931 :- dynamic cb_try_state_caching/0.
932
933 print_cb_uncovered_events :- %print_prefs
934 findall(E,cb_uncovered_event_in_path(E),L1),
935 findall(E,(cb_uncovered_event(E), \+cb_uncovered_event_in_path(E)),L2),
936 % cb_feasible_uncovered_event \+ infeasible_operation_cache(E)
937 length(L1,Len1), length(L2,Len2),
938 counter(total_events,Tot),
939 test_case_name(TCN),
940 ((debug_mode(on); Len1+Len2<160)
941 -> format_silent_msg('Completely uncovered events: ~w/~w : ~w~n',[Len1,Tot,L1]),
942 format_silent_msg('Covered but no ~w yet: ~w/~w : ~w~n',[TCN,Len2,Tot,L2])
943 ;
944 format_silent_msg('Completely uncovered events: ~w/~w, Covered but no ~w: ~w~n',[Len1,Tot,TCN,Len2])
945 ).
946
947 test_case_name('counter-example') :- bounded_model_checking,!.
948 test_case_name('test-case').
949
950 %print_prefs :-
951 % get_preference(use_smt_mode,SMT), get_preference(use_chr_solver,CHR),
952 % get_preference(use_clpfd_solver,CLPFD), get_preference(use_po,PO),
953 % format('Preferences: SMT = ~w, CHR=~w, CLPFD=~w, PROOF_INFO=~w~n',[SMT,CHR,CLPFD,PO]).
954 % constraint-based test case generation: one step predicate
955 :- use_module(probsrc(solver_interface), [call_with_smt_mode_enabled/1]).
956 cbc_gen_test_cases(EventsToCover,TargetPredicate,MaxLength,Filename,UncoveredEvents) :-
957 cbc_reset_gen_test_cases(EventsToCover),
958 open_xml_testcase(Filename),
959 get_preference(time_out,Timeout),
960 call_with_smt_mode_enabled(
961 call_cleanup(cb_timed_testcases(TargetPredicate, MaxLength, Timeout),
962 close_xml_testcase)),
963 cbc_get_uncovered_events(UncoveredEvents).
964
965 cbc_reset_gen_test_cases(EventsToCover) :-
966 reset_test_cases,
967 cb_test_cases_init_search(EventsToCover).
968
969 cbc_get_uncovered_events(UncoveredEvents) :-
970 findall(E,cb_uncovered_event(E),UncoveredEvents).
971 cbc_get_nr_uncovered_events(Len) :-
972 cbc_get_uncovered_events(UncoveredEvents),
973 length(UncoveredEvents,Len).
974
975 % constraint-based test case generation: multi step predicate; for use by prologTasks module
976 :- use_module(library(between),[between/3]).
977 cbc_gen_test_cases_task_step(EventSelection,TargetString,MaxLength,Filename, Final, Len, Task) :-
978 % first translate Tk EventSelection Number list into Events
979 get_selected_events(EventSelection,_AllEvents,EventsToCover),
980 % parse target predicate for test-case sequences
981 parse_cbc_target_predicate(TargetString,TargetPredicate),
982
983 (Final=1 -> cbc_reset_gen_test_cases(final(EventsToCover))
984 ; cbc_reset_gen_test_cases(EventsToCover)),
985 get_preference(time_out,Timeout),
986
987 open_xml_testcase(Filename),
988 Task = sap: cb_test_cases_step(Len,TargetPredicate,MaxLength,Timeout,_),
989 between(1,MaxLength,Len).
990
991 cbc_gen_test_cases_finish_tasks :-
992 close_xml_testcase.
993
994 cb_timed_testcases(TargetPred,MaxLengthOfTestCase,Timeout) :-
995 statistics(walltime, [Start|_]),
996 InitialLength = 1,
997 cb_test_cases_loop(InitialLength,TargetPred,MaxLengthOfTestCase,Timeout),
998 statistics(walltime, [End|_]),
999 Runtime is End - Start,
1000 write_results(Runtime).
1001
1002 cb_test_cases_loop(CurLength,TargetPred,MaxLengthOfTestCase,Timeout) :-
1003 cb_test_cases_step(CurLength,TargetPred,MaxLengthOfTestCase,Timeout,Result),
1004 (Result=paths_found
1005 -> L1 is CurLength+1,
1006 cb_test_cases_loop(L1,TargetPred,MaxLengthOfTestCase,Timeout)
1007 ; Result=limit_reached -> check_progress_possible
1008 ; Result=no_test_found -> check_progress_possible
1009 ; true
1010 ).
1011
1012 :- use_module(probsrc(solver_interface), [call_with_smt_mode_enabled/1]).
1013 :- use_module(probsrc(tools_printing),[format_with_colour/4]).
1014
1015 :- use_module(cbcsrc(cbc_path_solver),[create_testcase_path/5]).
1016
1017 % try and find test-cases of a particular depth: CurLengthOfTestCase
1018 cb_test_cases_step(1,TargetPred,_MaxLengthOfTestCase,Timeout,Res) :- bounded_model_checking,
1019 % check if INITIALISATION can violate INVARIANT or Deadlock
1020 Events = [], Length=0,
1021 count_path(Length),
1022 call_with_smt_mode_enabled(create_testcase_path(init,Events,TargetPred,Timeout,PathResult)),
1023 debug_println(19,init_bmc_check(PathResult)),
1024 PathResult = [_|_], % Result contains e.g., (0,$initialise_machine(int(-1)),root,0) and possibly set_up_constants
1025 !,
1026 Res =paths_found,
1027 format_err('~nBMC counterexample found, Length=~w~n',[Length]),
1028 assertz(bounded_model_checking_complete), count_found(0,testcase),
1029 store_test_case(['$initialise_machine'],PathResult).
1030 cb_test_cases_step(CurLengthOfTestCase,_TargetPred,_MaxLengthOfTestCase,_Timeout,success) :-
1031 cb_test_case_algorithm_finished,
1032 !,
1033 L1 is CurLengthOfTestCase-1,
1034 format_msg('~nCBC Algorithm finished (~w)~n',[L1]),
1035 (debug_mode(on), specfile:currently_opened_file(F) -> format_msg(' File: ~w~n',[F]) ; true).
1036 cb_test_cases_step(CurLengthOfTestCase,_TargetPred,MaxLengthOfTestCase,_Timeout,limit_reached) :-
1037 CurLengthOfTestCase > MaxLengthOfTestCase,!,
1038 format_warn('~nLimit Reached: ~w~n',[MaxLengthOfTestCase]).
1039 cb_test_cases_step(CurLengthOfTestCase,TargetPred,MaxLengthOfTestCase,Timeout,paths_found) :-
1040 % now try to find a path of length CurLengthOfTestCase
1041 call_with_smt_mode_enabled(cb_find_paths(CurLengthOfTestCase,TargetPred,MaxLengthOfTestCase,Timeout)),
1042 !,
1043 (bounded_model_checking_complete -> format_err('~nBMC counterexample found, Length=~w~n',[CurLengthOfTestCase])
1044 ; \+ cb_has_uncovered_events -> format_ok('~nEverything covered, Length=~w~n',[CurLengthOfTestCase])
1045 ; \+ cb_has_uncovered_feasible_events -> format_ok('~nEverything feasible covered, Length=~w~n',[CurLengthOfTestCase])
1046 ; true).
1047 cb_test_cases_step(CurLengthOfTestCase,_TargetPred,MaxLengthOfTestCase,_Timeout,Res) :-
1048 % either everything covered or MaxLength reached or no way to extend current paths
1049 (CurLengthOfTestCase = MaxLengthOfTestCase
1050 -> format_warn('~nCould not generate a test case of length ~w~n',[CurLengthOfTestCase]),
1051 Res = no_test_found
1052 ; format_warn('~nCould not generate a path of length ~w~n',[CurLengthOfTestCase]),
1053 Res = no_path_found
1054 ).
1055
1056 % utilities to print colored messages
1057 format_msg(FormatStr,Args) :- format_with_colour(user_output,[blue],FormatStr,Args).
1058 format_err(FormatStr,Args) :- format_with_colour(user_output,[red,bold],FormatStr,Args).
1059 format_warn(FormatStr,Args) :- format_with_colour(user_output,[cyan,bold],FormatStr,Args).
1060 format_ok(FormatStr,Args) :- format_with_colour(user_output,[green,bold],FormatStr,Args).
1061
1062 format_silent_msg(_,_) :- silent_mode(on),!.
1063 format_silent_msg(FormatStr,Args) :- format_msg(FormatStr,Args).
1064
1065 format_silent_ok(_,_) :- silent_mode(on),!.
1066 format_silent_ok(FormatStr,Args) :- format_with_colour(user_output,[green],FormatStr,Args).
1067
1068
1069 cb_test_case_algorithm_finished :-
1070 (cb_has_uncovered_feasible_events -> bounded_model_checking_complete ; true).
1071
1072 :- dynamic cb_uncovered_event_in_path/1. % uncovered in any path (and thus also test-case)
1073 % reset datastructures for CBC test case generation
1074 cb_test_cases_init_search(Events) :-
1075 reset_counters,
1076 retractall(cb_path(_,_,_)), % retract all found paths
1077 retractall(cb_path_testcase_only(_,_,_)),
1078 retractall(cb_timeout_path(_,_)),
1079 assertz(cb_path(0,[],no_state)), % generate an intial path; Note: we assume the initialisation is feasible
1080 % set up all coverage targets:
1081 retractall(cb_uncovered_event(_)),
1082 retractall(cb_uncovered_event_in_path(_)),
1083 retractall(cb_target_event(_)),
1084 retractall(cb_final_event(_)),
1085 cb_test_cases_init_uncovered_in_path,
1086 add_target_events(Events).
1087 cb_test_cases_init_uncovered_in_path :-
1088 ? b_top_level_operation(Event),
1089 assertz(cb_uncovered_event_in_path(Event)),
1090 count_total_events,
1091 fail.
1092 cb_test_cases_init_uncovered_in_path.
1093
1094 ?add_target_events(Desc) :- add_events_aux(Desc),fail.
1095 add_target_events(_).
1096 ?add_events_aux(all) :- b_top_level_operation(Event), assert_cb_uncovered_event(Event).
1097 add_events_aux([H|T]) :- member(Event,[H|T]),
1098 match_event(Event,MatchedEvent),
1099 assert_cb_uncovered_event(MatchedEvent).
1100 add_events_aux(final(List)) :- member(Event,List),
1101 match_event(Event,MatchedEvent),
1102 assert_cb_uncovered_event(MatchedEvent), assertz(cb_final_event(MatchedEvent)).
1103
1104 match_event(match_event(E),Match) :- !, % try and find operation/event with E occuring inside string
1105 atom_codes(E,EC), append(EC,_,EC2),
1106 if((b_top_level_operation(Match),
1107 atom_codes(Match,MC),
1108 append(_,EC2,MC)),
1109 debug_println(19,match_event(E,Match)),
1110 (add_error(cbc_cover_match,'No matching event for: ',E),fail)).
1111 match_event(E,E).
1112
1113 assert_cb_uncovered_event(E) :- \+ b_top_level_operation(E),!,
1114 add_error(cbc_cover,'Unknown event: ',E).
1115 assert_cb_uncovered_event(E) :- assertz(cb_uncovered_event(E)), assertz(cb_target_event(E)).
1116
1117 get_feasible_event_name(Event) :-
1118 ? b_top_level_feasible_operation(Event),
1119 \+ infeasible_operation_cache(Event).
1120 ?cb_feasible_uncovered_event(E) :- cb_uncovered_event(E), \+ infeasible_operation_cache(E).
1121
1122 useless_prefix(PrefixPath) :-
1123 last(PrefixPath,LastPrefix),
1124 \+ cb_uncovered_event(LastPrefix), % we still need to try and navigate into a valid end state
1125 bmachine:b_operation_cannot_modify_state(LastPrefix). % no use in trying such prefixes
1126
1127
1128 :- dynamic stat_last_print_time/1.
1129 print_cb_stats(Level,StartWTime) :-
1130 statistics(walltime,[CurWTime,_]),
1131 ToTime is CurWTime-StartWTime,
1132 (stat_last_print_time(Last)
1133 -> (CurWTime - Last > 60000
1134 -> retract(stat_last_print_time(_)),
1135 assertz(stat_last_print_time(CurWTime)),
1136 format_msg('~nTotal runtime for level ~w so far: ~w ms~n',[Level,ToTime]),
1137 print_cb_uncovered_events,
1138 write_results,nls
1139 ; true)
1140 ; assertz(stat_last_print_time(CurWTime))).
1141
1142 cb_find_paths(Length,TargetPredicate,MaxLength,Timeout) :-
1143 (all_uncovered_events_are_final
1144 -> (CoveredStatus = uncovered ; CoveredStatus = covered), % force using uncovered operations first in b_ordered_get_operation
1145 % this only works if once an uncovered operation becomes covered it no longer needs to be enumerated; otherwise we will miss paths
1146 format_silent_msg('~nCBC test search: starting length ~w (~w events)~n',[Length,CoveredStatus])
1147 ; format_silent_msg('~nCBC test search: starting length ~w~n',[Length])),
1148 print_cb_uncovered_events,
1149 statistics(walltime,[StartWTime,_]),
1150 % set up template for Path = PrefixPath ^ [Event]
1151 cb_prefix_trace(Length,PreLength,PrefixPath,Event,Path),
1152 % choose any existing prefix of length PreLength for which a CBC solution was found:
1153 ? cb_path(PreLength,PrefixPath,LastStateId),
1154 % TO DO: maybe also try cb_timeout_path(PreLength,PrefixPath), LastStateId = no_state
1155 \+ useless_prefix(PrefixPath), % avoid using operations that do not change the state
1156 statistics(walltime,[WT,_]), Delta is WT-StartWTime,
1157 (Delta>1000 -> nls ; true), % too long lines on terminal slow things down
1158 (debug_mode(on) -> format_msg('~nTrying to extend path of length ~w: ~w~n',[Length,PrefixPath])
1159 ; printsilent('*'),printsilent(Length)),flush_output,
1160 % print(prefix(PrefixPath)),nl,
1161 % check if full coverage not yet achieved:
1162 \+ cb_test_case_algorithm_finished,
1163 % now choose any event; uncovered ones first:
1164 ? b_ordered_get_operation(Event,CoveredStatus),
1165 \+ bounded_model_checking_complete, % check again ; we may have found a solution with the previous event
1166 possible_trace(PrefixPath,Event,Length), % check if trace is in principle possible
1167 printsilent('.'),flush_output,
1168 % statistics information: store that we attempt another path of length Length:
1169 count_path(Length),
1170 (Length>=MaxLength -> Result=testcase, % no need to find partial solutions now
1171 cb_feasible_uncovered_event(Event) % only set up paths for feasible Events we want to cover
1172 ; true
1173 ),
1174 print_cb_stats(Length,StartWTime),
1175 % and try to set up that path PrefixPath ^ [Event]
1176 debug_println(9,setting_up_path(Path,Event)),
1177 cb_set_up_path(Path,Event,TargetPredicate,LastStateId,Timeout,Result),
1178 % now store info that we have found a solution for length Length with Result
1179 debug_println(19,found_solution(Length,Result,Path)),
1180 count_found(Length,Result),
1181 % fail-loop
1182 fail.
1183 cb_find_paths(Length,_TargetPredicate,_MaxLengthOfTestCase,_Timeout) :-
1184 % success if a path or testcase of this length was generated, fail otherwise
1185 ? (cb_path(Length,_,_) -> true ; counter(testcase_count(Length),TC), TC>0).
1186
1187 % enumerate events with uncovered first:
1188 b_ordered_get_operation(Event,CoveredStatus) :-
1189 findall(Event,get_feasible_event_name(Event),AllEvents),
1190 include(cb_uncovered_event,AllEvents,Uncovered),
1191 exclude(cb_uncovered_event,AllEvents,Covered),
1192 % Note: we do a findall and exclude/include first to avoid side-effects from modifying cb_uncovered_event
1193 % and possibly forgetting an event
1194 %print(uncovered(Uncovered)),nl,
1195 ( CoveredStatus=uncovered,
1196 ? member(Event,Uncovered)
1197 ;
1198 CoveredStatus=covered,
1199 member(Event,Covered),
1200 \+ bmachine:b_operation_cannot_modify_state(Event),
1201 \+ cb_final_event(Event) % it is already covered; no need to cover it again
1202 ). % no use in using a query/skip operation at the end
1203
1204 ?all_uncovered_events_are_final :- cb_uncovered_event(Ev), \+ cb_final_event(Ev),!,fail.
1205 all_uncovered_events_are_final.
1206
1207 cb_prefix_trace(Length,PreLength,PrefixPath,Event,Path) :-
1208 PreLength is Length-1,
1209 length(PrefixPath,PreLength),append(PrefixPath,[Event],Path).
1210
1211 cb_has_uncovered_events :-
1212 ? cb_uncovered_event(_),!. %%
1213 cb_has_uncovered_feasible_events :-
1214 ? cb_feasible_uncovered_event(_),!. %%
1215
1216 :- use_module(cbcsrc(cbc_path_solver),[testcase_set_up_events/9]).
1217 cb_set_up_events(Trace,InitialState,ConstantsState,FinalState,DetailedTrace,WF,WF2) :-
1218 DetailedTrace = dt1(States,Operations,OpTransInfos),
1219 testcase_set_up_events(Trace,InitialState,ConstantsState,Operations,States,FinalState,OpTransInfos,WF,WF2).
1220
1221 combine_detail_infos(ConstantsState,InitialState,Initialisation,InitInfo,DT1,DT) :-
1222 DT1 = dt1(States,Operations,OpTransInfos),
1223 DT = dt(ConstantsState,
1224 [InitialState|States],
1225 [Initialisation|Operations],
1226 [InitInfo|OpTransInfos]).
1227
1228
1229
1230 % check if trace : Trace ^ [Event] is in principle possible using static/cbc enabling analysis results
1231 possible_trace(Trace,Event,Length) :- get_preference(use_po,true),
1232 last(Trace,PreviousEvent),
1233 !,
1234 %format('Try "~w" after "~w"~n',[Event,PreviousEvent]),nl,
1235 check_operation_sequence_possible(PreviousEvent,Event,Length),
1236 % TO DO: we could delay computing this until after cb_set_up_path has failed; if we managed to find a solution from the intialisation no need to do this check which starts from Invariant
1237 true.
1238 possible_trace(_,_,_).
1239
1240
1241 :- use_module(probsrc(eventhandling),[register_event_listener/3,announce_event/1]).
1242 :- register_event_listener(specification_initialised,reset_sap,
1243 'Initialise module sap.').
1244
1245 :- use_module(cbcsrc(enabling_analysis),[operation_sequence_possible/3]).
1246 :- dynamic operation_sequence_possible_cache/3.
1247 :- dynamic operation_can_be_enabled_by_cache/3.
1248 reset_sap :-
1249 retractall(operation_sequence_possible_cache(_,_,_)),
1250 retractall(operation_can_be_enabled_by_cache(_,_,_)),
1251 announce_event(end_solving).
1252
1253 check_operation_sequence_possible(PreviousEvent,Event,Length) :-
1254 (operation_sequence_possible_cache(PreviousEvent,Event,R)
1255 -> chk_possible(R,PreviousEvent,Event,Length)
1256 ; operation_sequence_possible(PreviousEvent,Event,R) ->
1257 assertz(operation_sequence_possible_cache(PreviousEvent,Event,R)),
1258 chk_possible(R,PreviousEvent,Event,Length)
1259 ; assertz(operation_sequence_possible_cache(PreviousEvent,Event,failure))
1260 % we assume it is possible to be on the safe side
1261 ).
1262
1263 %chk_possible(R,PreviousEvent,Event) :- print(chk(R,PreviousEvent,Event)),nl,fail.
1264 chk_possible(impossible,PrevEv,Event,_) :- !,
1265 (debug_mode(on) -> format_warn('Impossible event: ~w after ~w~n',[Event,PrevEv]) ; printsilent('-')), fail.
1266 chk_possible(activation_independent,Prev,Event,_) :- cb_uncovered_event_in_path(Event), !,
1267 % PreviousEvent cannot influence the variables of the guards of Event;
1268 % as Event was previously not covered it cannot possible be enabled after PreviousEvent
1269 (debug_mode(on) -> format_msg('Activation independent: ~w (previous ~w)~n',[Event,Prev]) ; printsilent('||')),
1270 fail.
1271 % this is probably not very often useful ; to do: enable only if we repeatedly fail to satisfy this combination?
1272 % or do this only at deeper levels ?
1273 chk_possible(_,PreviousEvent,Event,Length) :-
1274 Length>3, % only start doing this check later, when hopefully many events are already covered
1275 cb_uncovered_event_in_path(Event),!,
1276 % we know Event must be disabled before PreviousEvent
1277 (check_operation_can_be_enabled_by(PreviousEvent,Event) -> true
1278 ; (debug_mode(on) -> format_msg('Event ~w cannot be enabled by ~w~n',[Event,PreviousEvent]) ; printsilent('|^|')),
1279 fail
1280 ).
1281 chk_possible(_,_,_,_).
1282
1283 :- use_module(cbcsrc(enabling_analysis),[operation_can_be_enabled_by/4]).
1284
1285 check_operation_can_be_enabled_by(PreviousEvent,Event) :-
1286 TIMEOUT = 500, % TO DO : pass timeout from cb_find_paths and divide by 5? (default in cb_find_paths is 2500)
1287 (operation_can_be_enabled_by_cache(PreviousEvent,Event,R) -> R \= failure
1288 ; operation_can_be_enabled_by(PreviousEvent,Event,TIMEOUT,R) ->
1289 assertz(operation_can_be_enabled_by_cache(PreviousEvent,Event,R)),
1290 R \= failure
1291 ; assertz(operation_can_be_enabled_by_cache(PreviousEvent,Event,failure)),
1292 fail
1293 ).
1294
1295 % TO DO: use this check at regular intervals ?!? and use it to abort cbc_tests if no progress possible and or prune the events that can be reached
1296 % check if there exists a covered event that can enable one of the uncovered ones:
1297 check_progress_possible :-
1298 bounded_model_checking,!. % progress always possible
1299 check_progress_possible :-
1300 % to do: check only valid if target pred = truth
1301 (progress_possible(PreviousEvent,Event)
1302 -> format_msg('Progress possible, e.g., covered ~w could enable uncovered ~w~nTry increasing depth bound for cbc_tests.~n',[PreviousEvent,Event])
1303 ; cbc_get_uncovered_events(Uncov),
1304 format_warn('Impossible to activate any of the uncovered events ~w.~nNo further progress possible!~n',[Uncov])).
1305
1306 progress_possible(PreviousEvent,Event) :- cb_feasible_uncovered_event(Event),
1307 cb_covered_event(PreviousEvent),
1308 check_operation_can_be_enabled_by(PreviousEvent,Event).
1309
1310 cb_covered_event(Event) :- % TO DO: this may give false positives; if an event was not marked to be covered initially and has not yet occured
1311 b_top_level_operation(Event), \+ cb_uncovered_event(Event).
1312
1313 :- use_module(probsrc(kernel_waitflags),[init_wait_flags/2]).
1314 :- use_module(cbcsrc(cbc_path_solver),[testcase_initialise/5]).
1315
1316 %cb_set_up_path([Event],Event,TargetPredicate,no_state,Timeout,Result) :-
1317 % cb_find_constants_and_initial_vars_in_statespace(State,InitID),
1318 % do_event(InitID,Event,SuccId),
1319 % visited_expression(SuccId,OutState), expand ...
1320 % cb_finalize_path_with_timeout(TargetPredicate,OutState,Trace,Event,WF,LResult,Timeout,TO_Success).
1321 %cb_set_up_path(Trace,Event,TargetPredicate,StateId,Timeout,Result) :-
1322 % print(cb_set_up_path(Trace,Event,TargetPredicate,StateId,Timeout,Result)),nl,fail.
1323 cb_set_up_path(Trace,Event,TargetPredicate,StateId,Timeout,Result) :-
1324 % StateID is the last state of the previously found path Trace (without Event)
1325 cb_try_state_caching, % if true: try to use model checking paths found; currently disabled
1326 StateId \== no_state,
1327 lookup_state(StateId,ConstantState,InState),
1328 init_wait_flags(WF,[cb_set_up_path]),
1329 set_up_transition(Event,Operation,ConstantState,InState,OutState,TransInfo,WF),
1330 cb_finalize_path_with_timeout(TargetPredicate,OutState,Trace,Event,WF,LResult,Timeout,TO_Success),
1331 ( TO_Success = time_out ->
1332 cb_write_timeout(Timeout,Trace),fail
1333 ;
1334 cb_store_single_step_in_statespace(ConstantState,OutState,StateId,Operation,TransInfo,LastStateId,Sequence),
1335 cb_save_new_step(Trace,Event,LResult,LastStateId,Sequence),
1336 % if we did not find a test case, just a normal step, we need to fall back
1337 % to constraint solving on the full path
1338 !,
1339 ( LResult==testcase -> Result=testcase
1340 ; cb_set_up_path(Trace,Event,TargetPredicate,no_state,Timeout,testcase) -> Result=testcase
1341 ; Result = path)).
1342 %cb_set_up_path(Trace,Event,TargetPredicate,StateId,Timeout,Result) :- %print(try(Trace,Event,TargetPredicate,StateId)),nl, % see if an outgoing transition event exists for StateId already explored by MC
1343 % is_truth(TargetPredicate), do_event(StateId,Event,LastStateId),!, print('MC'),Sequence=[], % TO DO: compute sequence
1344 % Result=testcase,cb_save_new_step(Trace,Event,Result,LastStateId,Sequence).
1345 cb_set_up_path(Trace,Event,TargetPredicate,_StateId,Timeout,Result) :-
1346 init_wait_flags(WF,[cb_set_up_path]),
1347 % set up constants and initialisation:
1348 testcase_initialise(ConstantsState,InitialState,Initialisation,TransInfo,WF),
1349 combine_detail_infos(ConstantsState,InitialState,Initialisation,TransInfo,DT1,DT),
1350 cb_set_up_events(Trace,InitialState,ConstantsState,FinalState,DT1,WF,WF2),
1351 % either find a real testcase where TargetPredicate holds at end
1352 % or find a path where TargetPredicate does not hold at end:
1353 cb_finalize_path_with_timeout(TargetPredicate,FinalState,Trace,Event,WF2,Result,Timeout,TO_Success),
1354 !,
1355 ( TO_Success = time_out ->
1356 cb_write_timeout(Timeout,Trace),
1357 fail
1358 ; (Result=testcase, bounded_model_checking -> format_err('Found BMC counter example (~w)~n',[Trace])
1359 ; debug_mode(on) -> format_ok('Found ~w path for ~w (~w)~n',[Result,Event,Trace])
1360 ; Result=testcase -> printsilent('!') %,print(Trace),nl
1361 ; printsilent('+')
1362 ),
1363 cb_store_path_in_statespace(DT,LastStateId,Sequence), % only do if path required or new testcase generated
1364 cb_save_new_step(Trace,Event,Result,LastStateId,Sequence)
1365 ).
1366 cb_save_new_step(Trace,Event,Result,LastStateId,Sequence) :-
1367 (retract(cb_uncovered_event_in_path(Event))
1368 -> (debug_mode(on) -> format_ok('Covered event ~w~n',[Event]) ; printsilent('#')) ; true),
1369 ( Result == testcase
1370 -> (cb_final_event(Event) -> cb_save_path_testcase_only(Trace,LastStateId) ; cb_save_path(Trace,LastStateId)),
1371 cb_save_found_test_case(Trace,Sequence)
1372 ; cb_save_path(Trace,LastStateId)).
1373
1374 lookup_state(StateId,ConstStore,VarStore) :-
1375 visited_expression(StateId,Store),
1376 lookup_state2(Store,ConstStore,VarStore).
1377 lookup_state2(const_and_vars(ConstId,VarStore),ConstStore,VarStore) :-
1378 !,visited_expression(ConstId,concrete_constants(ConstStore)).
1379 lookup_state2(Store,Empty,Store) :- empty_state(Empty).
1380
1381
1382
1383 :- use_module(probsrc(tools_meta),[call_residue/2]).
1384
1385 cb_finalize_path_with_timeout(TargetPredicate,FinalState,Trace,Event,WF,Result,Timeout,TO_Success) :-
1386 call_residue(
1387 cb_catch_finalize(TargetPredicate,FinalState,Trace,Event,WF,Result,Timeout,TO_Success2),Residue),
1388 (TO_Success2=time_out -> TO_Success=TO_Success2 % when time-out occurs there can be residues
1389 ; Residue = [] -> TO_Success=TO_Success2
1390 ; add_internal_error('Call had residue: ',trace_residue(Trace,Residue)/cb_finalize_path_with_timeout),
1391 %flush_output, print('GOAL:'),nl,tools_printing:print_goal(Residue),nl,
1392 TO_Success=time_out % as we have pending co-routines: assume time-out
1393 ).
1394
1395 :- use_module(probsrc(clpfd_interface),[catch_clpfd_overflow_call3/3]).
1396 cb_catch_finalize(TargetPredicate,FinalState,Trace,Event,WF,Result,Timeout,TO_Success) :-
1397 catch_clpfd_overflow_call3(
1398 cb_finalize_path_aux(TargetPredicate,FinalState,Trace,Event,WF,Result,Timeout,TO_Success),
1399 warning,
1400 (printsilent('@OVERFLOW@'), Result=path, TO_Success = time_out)).
1401
1402
1403 % finalize the path by checking whether TargetPredicate holds in final state; if so: we have a testcase otherwise just a path
1404 cb_finalize_path_aux(TargetPredicate,_FinalState,Trace,Event,WF,Result,Timeout,TO_Success) :-
1405 % if the target predicate is just true, we can store every found path
1406 % directly as a possible test trace, too.
1407 is_truth(TargetPredicate),!, % avoid ground_wait_flags twice if path infeasible
1408 % now check if the path makes sense as either testcase or path; if not: no use in enumerating
1409 (is_potential_new_test_case(Trace) -> Result = testcase % replace by is_potential_test_case if you want more tests in the test xml files
1410 ; is_potential_path_to_be_stored(Trace,Event) -> Result = path), % paths are not written to xml file
1411 ground_wait_flags_with_timeout(WF,Timeout,TO_Success),
1412 !.
1413 cb_finalize_path_aux(TargetPredicate,FinalState,Trace,Event,WF,RESULT,Timeout,TO_Success) :-
1414 is_potential_new_test_case(Trace),
1415 % Note: if the target predicate is fulfilled in the last state, we
1416 % can store the solution as a test trace.
1417 (bounded_model_checking_invariant,
1418 b_specialized_invariant_for_op(Event,SpecINV) % only computed if get_preference(use_po,true)
1419 -> bsyntaxtree:create_negation(SpecINV,NegSpecINV),
1420 ? eval_predicate(NegSpecINV,FinalState,WF),
1421 (debug_mode(on) -> print('Specialized Invariant Target: '),translate:print_bexpr(SpecINV),nl ; true)
1422 ; eval_predicate(TargetPredicate,FinalState,WF),
1423 (debug_mode(on) -> print('Target: '),translate:print_bexpr(TargetPredicate),nl ; true)
1424 ),
1425 % print(grounding_with_predicate(Event,Trace)),nl,
1426 ground_wait_flags_with_timeout(WF,Timeout,TO_Success),
1427 (RESULT == testcase -> true % we are only looking for testcases not paths where we do not reach the target predicate
1428 ; TO_Success = time_out -> printsilent('@Timeout@'), %nl,print(Trace),nl,
1429 fail % try and find a solution without target predicate below
1430 ; true),
1431 !,
1432 RESULT=testcase.
1433 cb_finalize_path_aux(_TargetPredicate,_FinalState,Trace,Event,WF,path,Timeout,TO_Success) :-
1434 % if the path is not feasible with the target predicate applied to the
1435 % last state, it still might be feasible as a starting point for other traces.
1436 is_potential_path_to_be_stored(Trace,Event),
1437 % print(grounding_without_predicate(_Trace)),nl,
1438 ? ground_wait_flags_with_timeout(WF,Timeout,TO_Success),!.
1439
1440 :- use_module(probsrc(b_interpreter), [b_test_boolean_expression/4]).
1441 eval_predicate(Predicate,State,WF) :-
1442 empty_state(EmptyState),
1443 ? b_test_boolean_expression(Predicate,EmptyState,State,WF).
1444
1445 % check if a sequence of events is a potential new test case
1446 is_potential_new_test_case(_Trace) :- bounded_model_checking,
1447 !. % any trace can potentially lead to an invariant violation/deadlock
1448 % TO DO: check if last event is not a proven event
1449 is_potential_new_test_case(Trace) :-
1450 ((member(Ev,Trace), cb_uncovered_event(Ev)) -> true). %print(uncov(Ev)),nl ; fail)
1451 % we could add a requirement that only certain events are admissible as last events (a bit like cb_final_event + requiring that if there is one cb_final_event then one must be used)
1452
1453 :- public is_potential_test_case/1.
1454 % check if a sequence of events is a potential test case
1455 is_potential_test_case(_Trace) :- bounded_model_checking,
1456 !. % any trace can potentially lead to an invariant violation/deadlock
1457 % TO DO: check if last event is not a proven event
1458 is_potential_test_case(Trace) :- ((member(Ev,Trace), cb_target_event(Ev)) -> true). %print(uncov(Ev)),nl ; fail)
1459
1460 % check if it is worth storing this trace, i.e., could it later be extended to a test-case
1461 is_potential_path_to_be_stored(_Trace,Event) :-
1462 (cb_final_event(Event) -> printsilent(f),fail ; true). % this event is only allowed at the end: we cannot extend the trace
1463
1464 :- use_module(probsrc(kernel_waitflags),[ground_wait_flags/1]).
1465 ground_wait_flags_with_timeout(WF,Timeout,TO_Success) :-
1466 ? catch(
1467 time_out(ground_wait_flags(WF),Timeout,TO_Success),
1468 enumeration_warning(_,_,_,_,_),
1469 TO_Success = time_out).
1470
1471 % cb_save_found_test_case(TraceOfOperationNames,Sequence of 4-tuples (Id,Op,Src,Dest))
1472 cb_save_found_test_case(Trace,Sequence) :- % print(found(Trace)),nl,
1473 % mark all events in Trace as covered
1474 cb_mark_all_covered(Trace,NewCover),
1475 (bounded_model_checking -> assertz(bounded_model_checking_complete) ; true),
1476 cb_get_initialised_state(Sequence,InitState),
1477 % Sequence is a list of (_Id,Op,Src,Dst)
1478 (NewCover=[] -> true
1479 ; store_test_case(NewCover,Sequence)
1480 ),
1481 cb_write_test_case(Sequence,InitState).
1482
1483 % store main test cases for later replaying in GUI / Tcl/Tk
1484 :- dynamic stored_test_case/3, test_case_nr/1.
1485 test_case_nr(0).
1486
1487 reset_test_cases :- retractall(stored_test_case(_,_,_)), retract(test_case_nr(_)),
1488 assertz(test_case_nr(0)).
1489 print_op_name((_Id,Op,_Src,_Dst)) :- specfile:get_operation_name(Op,Name), !,write(Name),write(' ').
1490 print_op_names(Sequence) :- maplist(print_op_name,Sequence),!,nl.
1491 print_op_names(L) :- add_internal_error('Illegal BMC counterexample:',L).
1492
1493 store_test_case(NewCover,Sequence) :- retract(test_case_nr(Nr)), N1 is Nr+1,
1494 assertz(test_case_nr(N1)),
1495 %print(stored_test_case(N1,NewCover,Sequence)),nl,
1496 assertz(stored_test_case(N1,NewCover,Sequence)),
1497 (bounded_model_checking
1498 -> write('Replaying BMC counterexample: '), print_op_names(Sequence),
1499 tcl_execute_stored_test_case(N1) ; true).
1500
1501 % a function to get a textual description of a stored test case
1502 stored_test_case_description(N1,Descr) :-
1503 stored_test_case(N1,NewCover,Sequence), get_op(Sequence,Ops),
1504 add_sep(NewCover,NCS),ajoin(NCS,NCA),
1505 ajoin(['Test ', N1, '. Covering ',NCA,'. Trace = '|Ops],Descr).
1506 % function to get the list of operation names of a stored test case
1507 stored_test_case_op_trace(N1,OpTrace) :-
1508 stored_test_case(N1,_NewCover,Sequence),
1509 maplist(get_op_name,Sequence,OpTrace).
1510
1511 add_sep([],[]).
1512 add_sep([A],R) :- !, R=[A].
1513 add_sep([A|T],[A,' '|AT]) :- add_sep(T,AT).
1514 get_op([],[]).
1515 get_op([Tuple|Rest],[Name,' '|T]) :-
1516 get_op_name(Tuple,Name),
1517 get_op(Rest,T).
1518 get_op_name((_Id,Op,_Src,_Dst),Name) :-
1519 specfile:get_operation_name(Op,Name). % deal with '-->'
1520
1521 % a function to execute a stored test case in the animator; should probably move to tcltk_interface.pl
1522 tcl_execute_stored_test_case(Nr) :-
1523 stored_test_case(Nr,NewCover,Sequence),
1524 debug_println(19,executing_stored_test_case(Nr,NewCover,Sequence)),
1525 Sequence = [(_,_,Src,_)|_],
1526 tcltk_interface:tcltk_execute_trace_to_node(Src), % first find a trace to the starting initialisation
1527 last(Sequence,(_,_,_,Dest)),
1528 maplist(get_op_id,Sequence,OpIds),
1529 maplist(get_dest,Sequence,Ids),
1530 execute_id_trace_from_current(Dest,OpIds,Ids).
1531
1532 get_op_id((OpID,_,_,_),OpID).
1533 get_dest((_,_,_,Dest),Dest).
1534
1535 tcl_get_stored_test_cases(list(L)) :-
1536 findall(Descr,stored_test_case_description(_,Descr),L).
1537
1538 cb_get_initialised_state([(_Id,_Op,Src,_Dst)|_],InitState) :-
1539 visited_expression(Src,Store),
1540 expand_const_and_vars_to_full_store(Store,InitState).
1541
1542 cb_mark_all_covered([],[]).
1543 cb_mark_all_covered([Event|Rest],NewCover) :-
1544 ( retract(cb_uncovered_event(Event)) -> NewCover=[Event|RestCover],
1545 format_silent_ok('~nCBC test search: covered event ~w~n',[Event])
1546 ; NewCover=RestCover),
1547 cb_mark_all_covered(Rest,RestCover).
1548
1549 :- use_module(probsrc(store),[lookup_value/3]).
1550
1551 cb_write_test_case(Sequence,InitState) :-
1552 xml_write(' <test_case>\n'),
1553 if(cb_xml_initialisation(InitState),true,add_internal_error('Call failed: ',cb_xml_initialisation(InitState))),
1554 if(cb_xml_sequence(Sequence),true,add_internal_error('Call failed: ',cb_xml_sequence(Sequence))),
1555 xml_write(' </test_case>\n'),
1556 flush_output_xml.
1557 cb_xml_initialisation(InitState) :-
1558 xml_write(' <initialisation>\n'),
1559 b_get_machine_constants(Constants),cb_xml_init2(InitState,Constants,constant),
1560 b_get_machine_variables(Variables),cb_xml_init2(InitState,Variables,variable),
1561 xml_write(' </initialisation>\n').
1562 cb_xml_init2(Store,Identifiers,Type) :-
1563 get_texpr_id(TId,Id),
1564 member(TId,Identifiers),
1565 lookup_value(Id,Store,BValue),
1566 cb_xml_value(Id,Type,BValue),
1567 fail.
1568 cb_xml_init2(_Store,_Identifiers,_Type).
1569 cb_xml_value(Id,Type,BValue) :-
1570 translate_bvalue(BValue,SValue),
1571 xml_write(' <value type=\"'),
1572 xml_write(Type),
1573 xml_write('\" name=\"'),xml_write(Id),xml_write('\">'),
1574 xml_write(SValue),xml_write('</value>\n').
1575 cb_xml_sequence([]).
1576 cb_xml_sequence([(_Id,Op,Src,Dst)|Rest]) :- !,
1577 specfile:get_operation_name(Op,Name),
1578 cb_xml_step(Name,Op,Src,Dst), % TO DO: also provide operation parameters
1579 cb_xml_sequence(Rest).
1580 cb_xml_sequence([loop(_ID)]).
1581 cb_xml_step(Event,Op,SrcId,DstId) :-
1582 b_get_machine_operation(Event,_,Parameters,_),
1583 Parameters = [_|_],!,
1584 xml_write(' <step name=\"'),xml_write(Event),xml_write('\">\n'),
1585 specfile:get_operation_arguments(Op,Args), % TO DO: maybe also write out return arguments ?!
1586 xml_write_parameters(Event,Parameters,Args),
1587 expand_state(SrcId,Src),
1588 expand_state(DstId,Dst),
1589 maplist(cb_xml_modified(Src),Dst),
1590 xml_write(' </step>\n').
1591 cb_xml_step(Event,_,_Src,_Dst) :-
1592 xml_write(' <step name=\"'),xml_write(Event),xml_write('\" />\n').
1593
1594 xml_write_parameters(Event,Parameters,Args) :-
1595 get_preference(prefix_internal_operation_arguments,Prefix),
1596 maplist(cb_xml_parameter(Event,Prefix),Parameters,Args).
1597
1598 % write operation parameters
1599 cb_xml_parameter(_Event,Prefix,Parameter,BValue) :-
1600 get_texpr_id(Parameter,Id),!,
1601 (is_internal_parameter(Prefix,Id) -> true % do not print it
1602 ; cb_xml_value_element(value,Id,BValue)).
1603 cb_xml_parameter(Event,Prefix,Parameter,BValue) :-
1604 add_internal_error('Call failed: ',cb_xml_parameter(Event,Prefix,Parameter,BValue)).
1605
1606 is_internal_parameter(Prefix,Parameter) :- atom_concat(Prefix,_,Parameter).
1607
1608 % write modified variable values
1609 cb_xml_modified(Src,bind(Name,NewValue)) :-
1610 cb_var_is_modified(Name,NewValue,Src),!,
1611 cb_xml_value_element(modified,Name,NewValue).
1612 cb_xml_modified(_,_).
1613
1614 cb_var_is_modified(Name,NewValue,Src) :-
1615 memberchk(bind(Name,OldValue),Src),!,
1616 not_equal_object(OldValue,NewValue).
1617 cb_var_is_modified(Name,_NewValue,Src) :-
1618 nonmember(bind(Name,_OldValue),Src).
1619
1620 xml_write_binding(Element,bind(Name,Value)) :- cb_xml_value_element(Element,Name,Value).
1621
1622 cb_xml_value_element(Element,Name,Value) :-
1623 xml_write(' <'),xml_write(Element),
1624 xml_write(' name=\"'),xml_write(Name),xml_write('\">'),
1625 xml_write_b_value(Value),
1626 !,
1627 xml_write('</'),xml_write(Element),xml_write('>\n').
1628 cb_xml_value_element(Element,Name,Value) :-
1629 add_internal_error('Call failed: ',cb_xml_value_element(Element,Name,Value)).
1630
1631 xml_write_b_value((Fst,Snd)) :- !,
1632 xml_write('<pair><fst>'),
1633 xml_write_b_value(Fst),
1634 xml_write('</fst><snd>'),
1635 xml_write_b_value(Snd),
1636 xml_write('</snd></pair> ').
1637 xml_write_b_value(avl_set(A)) :- !, custom_explicit_sets:expand_custom_set_to_list(avl_set(A),Elements),
1638 xml_write('<set>'),
1639 maplist(xml_write_b_value,Elements),
1640 xml_write('</set> ').
1641 xml_write_b_value([H|T]) :- !,
1642 xml_write('<set>'),
1643 maplist(xml_write_b_value,[H|T]),
1644 xml_write('</set> ').
1645 xml_write_b_value(rec(Fields)) :- !,
1646 xml_write('<record>'),
1647 maplist(xml_write_b_field_value,Fields),
1648 xml_write('</record> ').
1649 xml_write_b_value(string(S)) :- !,
1650 xml_write('<string>'),
1651 xml_write_b_value(S), % TO DO: encode S !
1652 xml_write('</string> ').
1653 xml_write_b_value(Value) :- translate_bvalue(Value,SValue), xml_write(SValue).
1654 % TO DO: support other types: other set representations, booleans pred_false, pred_true
1655 % [] -> empty_set ?
1656 % interval closures ? freeval(ID,Case,Value)
1657
1658 xml_write_b_field_value(field(Name,Val)) :-
1659 xml_write('<field name=\"'), xml_write(Name), xml_write('\">'),
1660 xml_write_b_value(Val), xml_write('</field>').
1661
1662 expand_state(Id,VariableBindings) :-
1663 visited_expression(Id,State),
1664 expand_to_constants_and_variables(State,_ConstantBindings,VariableBindings),
1665 !.
1666 expand_state(Id,VariableBindings) :-
1667 add_internal_error('Call failed: ',expand_state(Id,VariableBindings)),
1668 VariableBindings=[].
1669
1670 :- volatile cb_timeout_path/2.
1671 :- dynamic cb_timeout_path/2.
1672 cb_write_timeout(Timeout,Events) :-
1673 length(Events,Length),
1674 count_timeout(Length),
1675 printsilent(timeout),flush_output,
1676 assertz(cb_timeout_path(Length,Events)),
1677 xml_write(' <!-- timeout ('),xml_write(Timeout),xml_write(') ms) for events '),
1678 cb_write_timeouts2(Events),
1679 xml_write(' --!>\n'),flush_output_xml.
1680 cb_write_timeouts2([Event]) :-
1681 !,xml_write(Event).
1682 cb_write_timeouts2([Event|Erest]) :-
1683 xml_write(Event),xml_write(','),cb_write_timeouts2(Erest).
1684
1685 cb_save_path(Trace,LastStateId) :-
1686 length(Trace,CurDepth),
1687 assertz(cb_path(CurDepth,Trace,LastStateId)).
1688 cb_save_path_testcase_only(Trace,LastStateId) :- % save mainly for Tcl/Tk tree viewer
1689 length(Trace,CurDepth),
1690 assertz(cb_path_testcase_only(CurDepth,Trace,LastStateId)).
1691
1692 :- use_module(probsrc(store),[normalise_state/2, normalise_states/2]).
1693 :- use_module(cbcsrc(cbc_path_solver),[add_constants_to_state_space/5,
1694 add_operations_to_state_space/5, remove_constants_from_state/4]).
1695
1696 cb_store_path_in_statespace(dt(ConstantsState1,States1,Operations,TransInfos),LastStateId,RealOperations) :-
1697 normalise_states([ConstantsState1|States1],[ConstantsState|States]),
1698 add_constants_to_state_space(ConstantsState,States,ConstStates,_SetupSequence,StartStateId),
1699 add_operations_to_state_space(Operations,StartStateId,ConstStates,TransInfos,OpSequence),
1700 OpSequence = [_InitOp|RealOperations],
1701 last(OpSequence,(_,_,_,LastStateId)).
1702
1703 cb_store_single_step_in_statespace(ConstantsState,State1,PrevStateId,Operation,TransInfo,NewStateId,[Tuple]) :-
1704 normalise_state(State1,State2),
1705 ( cb_find_constants_state_in_statespace(ConstantsState,ConstId) ->
1706 remove_constants_from_state([State2],ConstantsState,ConstId,[State])
1707 ;
1708 State = State2),
1709 add_operations_to_state_space([Operation],PrevStateId,[State],[TransInfo],[Tuple]),
1710 Tuple = (_,_,_,NewStateId).
1711
1712 % find a state where the constants have been valued
1713 cb_find_constants_state_in_statespace(ConstantsState,_ConstId) :-
1714 empty_state(ConstantsState),!,fail.
1715 cb_find_constants_state_in_statespace(ConstantsState,ConstId) :-
1716 transition(root,_,ConstId),
1717 visited_expression(ConstId,concrete_constants(ConstantsState)),!.
1718
1719 % %try and find an initial state in the state space
1720 %cb_find_constants_and_initial_vars_in_statespace(InitialState,InitId) :-
1721 % transition(root,_,Id1),
1722 % (visited_expression(Id1,concrete_constants(_))
1723 % -> transition(Id1,_,InitId)
1724 % ; InitId = Id1),
1725 % visited_expression(InitId,State),
1726 % expand_const_and_vars_to_full_store(State,InitialState).
1727
1728 test_generation_by_xml_description(Filename) :-
1729 read_test_generation_description(Filename,Description),
1730 execute_test_generation(Description).
1731 execute_test_generation(cb_test_description(Events,TargetString,MaxDepth,Preferences,Filename,StateCaching)) :-
1732 parse_cbc_target_predicate(TargetString,Target),
1733 maplist(set_ec_preference,Preferences),
1734 retractall(cb_try_state_caching),
1735 ( StateCaching = true
1736 -> assertz(cb_try_state_caching), printsilent('State caching (states from model checker) enabled'),nls
1737 ; true),
1738 cbc_gen_test_cases(Events,Target,MaxDepth,Filename,_Uncovered).
1739 set_ec_preference(pref(Name,Value)) :-
1740 set_eclipse_preference(Name,Value).
1741
1742 read_test_generation_description(Filename,Description) :-
1743 safe_read_string_from_file(Filename,utf8,Codes),
1744 xml_parse(Codes,xml(_Atts,Content)),
1745 ( read_test_generation_description2(Content,Description) ->
1746 true
1747 ;
1748 add_error(sap,'error while reading test description file'),fail).
1749 read_test_generation_description2(Xml,cb_test_description(Events,Target,MaxDepth,Preferences,File,StateCaching)) :-
1750 expect_xml_element('test-generation-description',Xml,Content),!,
1751 expect_xml_element('output-file',Content,[pcdata(FileCodes)]),atom_codes(File,FileCodes),
1752 get_xml_events(Content,Events),
1753 expect_xml_element(parameters,Content,XmlParameters),
1754 % maximum depth in parameters section
1755 expect_xml_element('maximum-depth',XmlParameters,[pcdata(MaxDepthCodes)]),number_codes(MaxDepth,MaxDepthCodes),
1756 % optional target predicate
1757 (memberchk(element(target,_,[pcdata(Tcodes)]),Content) -> atom_codes(Target,Tcodes) ; Target = ''),
1758 % set preference in parameters section
1759 findall(PL,member(element(preference,PL,_),XmlParameters),PrefAttLists),
1760 maplist(convert_xml_preference,PrefAttLists,Preferences),
1761 % check if state caching should be used
1762 (memberchk(element('state-caching',_,_),Content) -> StateCaching = true ; StateCaching = false).
1763 convert_xml_preference(PAL,pref(Name,Value)) :-
1764 (memberchk(name=CName,PAL) -> atom_codes(Name,CName) ; add_error(sap,'Missing preference name'),fail),
1765 (memberchk(value=CValue,PAL) -> atom_codes(Value,CValue) ; add_error(sap,'Missing preference value'),fail).
1766 get_xml_events(Content,Events) :-
1767 memberchk(element('event-coverage',_,EventElements),Content),!,
1768 findall(E,(member(element(event,_,[pcdata(Ecodes)]),EventElements),atom_codes(E,Ecodes)),Events).
1769 get_xml_events(_Content,Events) :-
1770 get_all_events(Events).
1771
1772
1773 expect_xml_element(ElemName,Xml,Elements) :-
1774 ( memberchk(element(ElemName,_,Es),Xml) -> Elements=Es
1775 ;
1776 atom_concat('did not find xml element ',ElemName,Msg),
1777 add_error(sap,Msg),fail).
1778
1779 :- volatile counter/2.
1780 :- dynamic counter/2.
1781
1782 reset_counters :-
1783 retractall(counter(_,_)).
1784
1785 count(Counter) :-
1786 (retract(counter(Counter,Count)) -> true ; Count = 0),
1787 NCount is Count+1,
1788 assertz(counter(Counter,NCount)).
1789
1790 count_total_events :- count(total_events).
1791 count_timeout(Length) :- count(timeouts(Length)).
1792 count_path(Length) :- count(path_count(Length)).
1793 count_found(Length,Result) :-
1794 (Result==testcase -> count(testcase_count(Length)) ; true),
1795 count(solution_count(Length)).
1796
1797 write_result_item(Desc,Db) :-
1798 format('~w: ',[Desc]),
1799 C =..[Db,Length],
1800 findall( count(Length,Num), counter(C,Num), UResults),
1801 sort(UResults,Results),
1802 maplist(write_result_item2,Results),
1803 maplist(extract_num,Results,Nums),sumlist(Nums,Total),
1804 format('total: ~w~n',[Total]).
1805 write_result_item2(count(Length,Num)) :-
1806 format('~w:~w, ',[Length,Num]).
1807 extract_num(count(_,Num),Num).
1808
1809 %total_cbc_testcases(T) :- get_totals(testcase_count,T).
1810 %get_totals(CounterName,Total) :-
1811 % C =..[CounterName,_Len],
1812 % findall( Num, counter(C,Num), Ns),
1813 % sumlist(Ns,Total).
1814
1815 write_results(_) :- silent_mode(on),!.
1816 write_results(Runtime) :-
1817 write_results,
1818 format('Runtime (wall): ~w ms~n',[Runtime]).
1819 write_results :-
1820 write_result_item('Timeouts',timeouts),
1821 write_result_item('Checked paths (Depth:Paths)',path_count),
1822 write_result_item('Found paths',solution_count),
1823 write_result_item('Found test cases', testcase_count).
1824
1825 get_counter_info(CBC_Depth,Paths,Tests,Timeouts) :-
1826 get_counter(solution_count(CBC_Depth),Paths),
1827 get_counter(testcase_count(CBC_Depth),Tests),
1828 get_counter(timeouts(CBC_Depth),Timeouts).
1829
1830 get_counter(C,Num) :- counter(C,R) -> Num=R ; Num=0.
1831
1832 :- use_module(probsrc(b_state_model_check),[get_unsorted_all_guards_false_pred/1]).
1833 :- dynamic bounded_model_checking/0, bounded_model_checking_invariant/0, bounded_model_checking_complete/0.
1834 parse_cbc_target_predicate(_,_) :-
1835 retractall(bounded_model_checking),
1836 retractall(bounded_model_checking_invariant),
1837 retractall(bounded_model_checking_complete),
1838 fail.
1839 parse_cbc_target_predicate('#invariant',Invariant) :- !,
1840 b_get_invariant_from_machine(Invariant).
1841 parse_cbc_target_predicate('#deadlock',DeadlockPO) :- !,
1842 assertz(bounded_model_checking), % stop at first counter example found
1843 get_unsorted_all_guards_false_pred(DeadlockPO).
1844 parse_cbc_target_predicate('#not_invariant',TargetPredicate) :- !,
1845 debug_println(19,bounded_model_checking),
1846 b_get_invariant_from_machine(Invariant),
1847 assertz(bounded_model_checking), % stop at first counter example found
1848 assertz(bounded_model_checking_invariant), % for bounded model check we will try to use proven invariant for op !
1849 bsyntaxtree:create_negation(Invariant,TargetPredicate).
1850 parse_cbc_target_predicate(TargetString,TargetPredicate) :- debug_println(15,parse_target_string(TargetString)),
1851 b_parse_optional_machine_predicate(TargetString,TargetPredicate).