From 2947b54c40b21df8bb11e3c7fc331a7f0835c29d Mon Sep 17 00:00:00 2001
From: dgelessus <dgelessus@users.noreply.github.com>
Date: Wed, 4 Dec 2019 17:41:14 +0100
Subject: [PATCH] Add test notebook and models for :modelcheck

---
 notebooks/models/CarlaTravelAgencyErr.mch | 297 ++++++++++++++++++++++
 notebooks/models/scheduler_large.mch      |  79 ++++++
 notebooks/tests/modelcheck.ipynb          | 206 +++++++++++++++
 3 files changed, 582 insertions(+)
 create mode 100644 notebooks/models/CarlaTravelAgencyErr.mch
 create mode 100644 notebooks/models/scheduler_large.mch
 create mode 100644 notebooks/tests/modelcheck.ipynb

diff --git a/notebooks/models/CarlaTravelAgencyErr.mch b/notebooks/models/CarlaTravelAgencyErr.mch
new file mode 100644
index 0000000..4e8a39f
--- /dev/null
+++ b/notebooks/models/CarlaTravelAgencyErr.mch
@@ -0,0 +1,297 @@
+MACHINE  CarlaTravelAgencyErr
+/* contains an error that can be found by model checking */
+/* machine developed by Carla Ferreira (the final machine contains no errors) */
+SETS
+    USER = {user1,user2};
+    SESSION = {sess1,sess2, sess3};
+    SESSION_REQUEST = {bc, br, uc, ur, none};
+    SESSION_STATE = {s0, s1, s2, s3, s4, s5, s6, s7};
+    HOTEL = {h1, h2, noHotel};
+    CAR_RENT = {c1, c2, noCarRent};
+    CARD = {valid, wrong, unknown};
+    ROOM = {h1a, h1b, h2a, h2b};
+    CAR = {c1a, c1b, c2a, c2b};
+    RESP = {done, impossible, undef}
+
+CONSTANTS
+    AGENCY_USER
+
+PROPERTIES
+    AGENCY_USER <: USER & AGENCY_USER /= {}
+
+VARIABLES
+    session,
+    session_response,
+    session_card,
+    session_state,
+    session_request,
+    user_hotel_bookings,
+    user_rental_bookings,
+    rooms_hotel,
+    cars_rental,
+    global_room_bookings,
+    global_car_bookings
+
+DEFINITIONS
+    rooms_available(h) == (dom(global_room_bookings)/\rooms_hotel~[{h}]) /= rooms_hotel~[{h}];
+    cars_available(r) == (dom(global_car_bookings)/\cars_rental~[{r}]) /= cars_rental~[{r}]
+
+INVARIANT
+
+    session: SESSION +-> USER & /*AGENCY_USER & */
+    session_response: SESSION +-> RESP &
+    session_card: SESSION +->  CARD &
+    session_state: SESSION +->  SESSION_STATE &
+    session_request: SESSION +->  SESSION_REQUEST &
+    user_hotel_bookings: USER +-> HOTEL & /* AGENCY_USER +->  HOTEL & */
+    user_rental_bookings: USER +->  CAR_RENT & /* AGENCY_USER +->  CAR_RENT & */
+    rooms_hotel: ROOM --> HOTEL &
+    cars_rental: CAR --> CAR_RENT &
+    global_room_bookings: ROOM >+> USER  &
+    global_car_bookings: CAR >+> USER &
+
+
+    dom(session)=dom(session_response) & dom(session)=dom(session_card) &
+    dom(session)=dom(session_state) & dom(session)=dom(session_request) &
+
+    dom(user_hotel_bookings)=dom(user_rental_bookings) &
+
+    ran(session) <: dom(user_hotel_bookings)
+    
+    &
+
+         
+         ( sess1:dom(session) & session_card(sess1) /= valid =>
+            session_response(sess1) = undef )
+    
+
+
+INITIALISATION
+
+    session := {} ||
+    session_response := {} ||
+    session_card := {} ||
+    session_state := {} ||
+    session_request := {} ||
+    user_hotel_bookings := {} ||
+    user_rental_bookings := {} ||
+    rooms_hotel := {h1a |-> h1, h1b |-> h1, h2a |-> h2, h2b |-> h2} ||
+    cars_rental := {c1a |-> c1, c1b |-> c1, c2a |-> c2, c2b |-> c2} ||
+    /* global_room_bookings : ( global_room_bookings : ROOM >+>USER ) ||
+    global_car_bookings : ( global_car_bookings : CAR >+> USER ) */
+    global_room_bookings := {} ||
+    global_car_bookings := {}
+
+
+OPERATIONS
+
+    sid_ <-- login(uu) =
+    PRE uu : USER /* AGENCY_USER */
+    THEN
+      SELECT uu : dom(user_hotel_bookings) /* New session for a known user */
+      THEN
+        skip
+      WHEN uu /: dom(user_hotel_bookings)  /* A new user - information needs to be created */
+      THEN
+        user_hotel_bookings(uu) := noHotel ||
+        user_rental_bookings(uu) := noCarRent
+      END
+      ||
+      ANY sid WHERE sid : SESSION & sid /: dom(session)  /* Creating a new session for the user */
+      THEN
+        session(sid) := uu ||
+        session_response(sid) := undef ||
+        session_card(sid) := unknown ||
+        session_state(sid) := s1 ||
+        session_request(sid) := none ||
+    sid_ := sid
+      END
+    END;
+
+   bookRoom(sid)=
+    PRE sid : SESSION
+        THEN
+          SELECT sid:dom(session) & session_state(sid) = s1 & session_request(sid) = none
+          THEN
+            session_state(sid) := s3 ||
+            session_request(sid) := br
+          END
+        END;
+
+    bookCar(sid)=
+    PRE sid : SESSION
+        THEN
+          SELECT sid:dom(session) & session_state(sid) = s1 & session_request(sid) = none
+          THEN
+            session_state(sid) := s5 ||
+            session_request(sid) := bc
+          END
+        END;
+
+    unbookRoom(sid)=
+    PRE sid : SESSION
+        THEN
+          SELECT sid:dom(session) & session_state(sid) = s1 & session_request(sid) = none
+          THEN
+            session_state(sid) := s2 ||
+            session_request(sid) := ur
+          END
+        END;
+
+    unbookCar(sid)=
+    PRE sid : SESSION
+        THEN
+          SELECT sid:dom(session) & session_state(sid) = s1  & session_request(sid) = none
+          THEN
+            session_state(sid) := s4 ||
+            session_request(sid) := uc
+          END
+        END;
+
+    enterCard(sid)=
+        PRE sid : SESSION
+        THEN
+          SELECT sid:dom(session) & session_state(sid) : {s2,s3,s4,s5}
+          THEN
+            session_state(sid) := s6 ||
+            ANY cc WHERE cc: (CARD-{unknown})
+            THEN
+              session_card(sid) := cc
+            END
+          END
+        END;
+
+    retryCard(sid)=
+        PRE  sid : SESSION
+        THEN
+          SELECT sid:dom(session) & session_state(sid) = s6 & session_card(sid) = wrong
+          THEN
+            ANY cc WHERE cc: (CARD-{unknown})
+            THEN
+              session_card(sid) := cc
+            END
+          END
+        END;
+
+            
+    response(sid)=
+        PRE sid : SESSION
+        THEN
+          SELECT sid:dom(session) & session_state(sid)  = s6
+          THEN
+            session_state(sid) := s7
+          END
+          ||
+          SELECT session_request(sid) = br & user_hotel_bookings(session(sid)) = noHotel
+          THEN
+            IF dom(global_room_bookings) <<: ROOM
+            THEN
+              ANY rr, hh WHERE rr:ROOM-dom(global_room_bookings) & hh:HOTEL & rooms_hotel(rr) = hh
+              THEN
+                global_room_bookings(rr) := session(sid) ||
+                user_hotel_bookings(session(sid)) := hh  ||
+                session_response(sid) := done
+              END
+            ELSE session_response(sid) := impossible
+            END
+      WHEN session_request(sid) = br & user_hotel_bookings(session(sid)) /= noHotel
+          THEN
+            IF (dom(global_room_bookings)/\
+                 rooms_hotel~[{user_hotel_bookings(session(sid))}]) /=
+                   rooms_hotel~[{user_hotel_bookings(session(sid))}]
+            THEN
+              ANY rr WHERE rr:ROOM-dom(global_room_bookings) & rooms_hotel(rr) = user_hotel_bookings(session(sid))
+              THEN
+                global_room_bookings := global_room_bookings \/ { rr |-> session(sid)} ||
+                session_response(sid) := done
+              END
+            ELSE
+              session_response(sid) := impossible
+            END
+
+      WHEN session_request(sid) = bc & user_rental_bookings(session(sid)) = noCarRent
+          THEN
+            IF dom(global_car_bookings) <<: CAR
+            THEN
+              ANY cc, re WHERE cc:CAR-dom(global_car_bookings) & re:CAR_RENT & cars_rental(cc) = re
+              THEN
+                global_car_bookings(cc) := session(sid) ||
+                user_rental_bookings(session(sid)) := re  ||
+                session_response(sid) := done
+              END
+            ELSE session_response(sid) := impossible
+            END
+
+          WHEN session_request(sid) = bc & user_rental_bookings(session(sid)) /= noCarRent
+          THEN
+            IF (dom(global_car_bookings) /\ cars_rental~[{user_rental_bookings(session(sid))}]) 
+              /= cars_rental~[{user_rental_bookings(session(sid))}]
+            THEN
+              ANY cc WHERE cc:CAR-dom(global_car_bookings) & cars_rental(cc) = user_rental_bookings(session(sid))
+              THEN
+                global_car_bookings(cc) := session(sid) ||
+                session_response(sid) := done
+              END
+            ELSE
+              session_response(sid) := impossible
+            END
+
+          WHEN session_request(sid) = ur
+          THEN
+            IF session(sid) : ran(global_room_bookings)
+            THEN
+              ANY rr WHERE rr:dom(global_room_bookings) & global_room_bookings(rr) = session(sid)
+              THEN
+                global_room_bookings := {rr} <<| global_room_bookings ||
+                session_response(sid) := done ||
+                IF {xx | xx:dom(global_room_bookings) & global_room_bookings(xx) = session(sid)} = {rr}
+                THEN user_hotel_bookings(session(sid)) := noHotel END
+              END
+            ELSE
+              session_response(sid) := impossible
+            END
+
+          WHEN session_request(sid) = uc
+          THEN
+            IF session(sid) : ran(global_car_bookings)
+            THEN
+              ANY cc WHERE cc:dom(global_car_bookings) & global_car_bookings(cc) = session(sid)
+              THEN
+                global_car_bookings := {cc} <<| global_car_bookings ||
+                session_response(sid) := done ||
+                IF {xx | xx:dom(global_car_bookings) & global_car_bookings(xx) = session(sid)} = {cc}
+                THEN user_rental_bookings(session(sid)) := noCarRent END
+              END
+            ELSE
+              session_response(sid) := impossible
+            END
+      END
+        END;
+
+   again(sid)=
+        PRE sid : SESSION
+        THEN
+          SELECT sid:dom(session) & session_state(sid)  = s7
+          THEN
+            session_state(sid) := s1 ||
+            session_request(sid) := none ||
+            session_response(sid) := undef ||
+            session_card(sid) := unknown
+          END
+        END;
+
+    logout(sid)=
+        PRE sid : SESSION
+        THEN
+          SELECT sid:dom(session) & session_state(sid) : {s6,s7}
+          THEN
+            session := {sid} <<| session ||
+            session_response := {sid} <<| session_response ||
+            session_card := {sid} <<| session_card ||
+            session_state := {sid} <<| session_state ||
+            session_request := {sid} <<| session_request
+          END
+        END
+
+END
+
diff --git a/notebooks/models/scheduler_large.mch b/notebooks/models/scheduler_large.mch
new file mode 100644
index 0000000..9595425
--- /dev/null
+++ b/notebooks/models/scheduler_large.mch
@@ -0,0 +1,79 @@
+MACHINE scheduler
+
+SETS
+	PID 
+
+/* LTL Formulas:
+  G (e(del) -> {card(ready \\/ waiting)>0})  TRUE
+  G ({card(ready \\/ waiting)>0} -> e(del))    FALSE
+  G ({card(active)>0} -> (e(swap) U {card(active)=0}))  FALSE
+  G ({card(active)>0} -> ((G e(swap)) | (e(swap) U {card(active)=0})))  TRUE
+*/
+
+VARIABLES active, ready, waiting
+
+DEFINITIONS
+    scope_PID == 1..8
+
+INVARIANT  active : POW(PID) & ready : POW(PID) & waiting: POW(PID) & /* the types */
+           /* and now the rest of the invariant */
+           active <: PID &
+           ready <: PID   &
+           waiting <: PID &
+           (ready /\ waiting) = {} &
+           active /\ (ready \/ waiting) = {} &
+           card(active) <= 1 &
+           ((active = {})  => (ready = {}))
+              
+INITIALISATION  
+	active := {} || ready := {} || waiting := {}
+	
+OPERATIONS
+
+rr <-- nr_ready = rr:= card(ready);
+
+new(pp) =
+	SELECT
+		pp : PID  &
+		pp /: active &
+		pp /: (ready \/ waiting) 
+	THEN
+		waiting := (waiting \/ { pp })
+	END;
+
+del(pp) =
+	SELECT
+		pp : waiting 
+	THEN
+		waiting := waiting - { pp }
+	END;
+	
+ready(rr) =
+        SELECT
+                rr : waiting
+        THEN
+                waiting := (waiting - {rr}) ||
+                IF (active = {}) THEN
+                   active := {rr}
+                ELSE
+                   ready := ready \/ {rr} 
+                END
+        END; 
+        	
+swap =
+        SELECT
+                active /= {}
+        THEN
+                waiting := (waiting \/ active) ||
+                IF (ready = {}) THEN
+                   active := {}
+                ELSE
+                   ANY pp WHERE pp : ready
+                   THEN
+                       active := {pp} ||
+                       ready := ready - {pp} 
+                   END
+                END
+        END       
+END
+
diff --git a/notebooks/tests/modelcheck.ipynb b/notebooks/tests/modelcheck.ipynb
new file mode 100644
index 0000000..ebfd01f
--- /dev/null
+++ b/notebooks/tests/modelcheck.ipynb
@@ -0,0 +1,206 @@
+{
+ "cells": [
+  {
+   "cell_type": "code",
+   "execution_count": 1,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/markdown": [
+       "```\n",
+       ":modelcheck\n",
+       "```\n",
+       "\n",
+       "Run the ProB model checker on the current model.\n",
+       "\n",
+       "If an error state is found, it is made the current state, so that it can be inspected using `:trace`, `:check`, etc."
+      ],
+      "text/plain": [
+       ":modelcheck\n",
+       "Run the ProB model checker on the current model.\n",
+       "\n",
+       "If an error state is found, it is made the current state, so that it can be inspected using `:trace`, `:check`, etc."
+      ]
+     },
+     "execution_count": 1,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    ":help :modelcheck"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "Loaded machine: scheduler"
+      ]
+     },
+     "execution_count": 2,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    ":load ../models/scheduler.mch"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 3,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "0.265 sec, 36 of 36 states processed, 156 transitions"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/plain": [
+       "Model Checking complete. No error nodes found."
+      ]
+     },
+     "execution_count": 3,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    ":modelcheck"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 4,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "Loaded machine: CarlaTravelAgencyErr"
+      ]
+     },
+     "execution_count": 4,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    ":load ../models/CarlaTravelAgencyErr.mch"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 5,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "0.519 sec, 84 of 455 states processed, 625 transitions"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/plain": [
+       "Model check uncovered an error: Invariant violation found.\n",
+       "Use :trace to view the trace to the error state."
+      ]
+     },
+     "execution_count": 5,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    ":modelcheck"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 6,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "Loaded machine: scheduler"
+      ]
+     },
+     "execution_count": 6,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    ":load ../models/scheduler_large.mch"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Checking this machine can take a short while."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 7,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "25.925 sec, 17753 of 17753 states processed, 185145 transitions"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/plain": [
+       "Model Checking complete. No error nodes found."
+      ]
+     },
+     "execution_count": 7,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    ":modelcheck"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "ProB 2",
+   "language": "prob",
+   "name": "prob2"
+  },
+  "language_info": {
+   "codemirror_mode": "prob2_jupyter_repl",
+   "file_extension": ".prob",
+   "mimetype": "text/x-prob2-jupyter-repl",
+   "name": "prob"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
-- 
GitLab