From 530271319ec7aba1976db2cc0abd7df4aa8be768 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Loi=CC=88c=20Correnson?= <loic.correnson@cea.fr>
Date: Thu, 30 Nov 2023 20:32:41 +0100
Subject: [PATCH] [ivette/wp] fix goal status details

---
 ivette/src/frama-c/plugins/wp/goals.tsx | 27 +++++++++++---------
 ivette/src/frama-c/plugins/wp/index.tsx | 16 +++++++-----
 ivette/src/frama-c/plugins/wp/tip.tsx   | 34 ++++++++++++++++++++++---
 src/plugins/wp/wpApi.ml                 | 21 +++++++--------
 4 files changed, 66 insertions(+), 32 deletions(-)

diff --git a/ivette/src/frama-c/plugins/wp/goals.tsx b/ivette/src/frama-c/plugins/wp/goals.tsx
index 1155150d55d..535c7713344 100644
--- a/ivette/src/frama-c/plugins/wp/goals.tsx
+++ b/ivette/src/frama-c/plugins/wp/goals.tsx
@@ -45,6 +45,7 @@ function getScope(g : WP.goalsData): string {
 
 interface IconStatus {
   icon: string;
+  label: string;
   kind: IconKind;
   title: string;
 }
@@ -52,22 +53,24 @@ interface IconStatus {
 interface Status extends IconStatus { label: string }
 
 const noResult : IconStatus =
-  { icon: 'MINUS', kind: 'disabled', title: 'No Result' };
+  { icon: 'MINUS', label: 'No Result', kind: 'disabled', title: 'No Result' };
 
+/* eslint-disable max-len */
 const baseStatus : { [key:string]: IconStatus } = {
-  'VALID': { icon: 'CHECK', kind: 'positive', title: 'Valid Goal' },
-  'PASSED': { icon: 'CHECK', kind: 'positive', title: 'Passed Test' },
-  'DOOMED': { icon: 'CROSS', kind: 'negative', title: 'Doomed Test' },
-  'FAILED': { icon: 'WARNING', kind: 'negative', title: 'Prover Failure' },
-  'UNKNOWN': { icon: 'ATTENTION', kind: 'warning', title: 'Prover Stucked' },
-  'TIMEOUT': { icon: 'HELP', kind: 'warning', title: 'Prover Timeout' },
-  'STEPOUT': { icon: 'HELP', kind: 'warning', title: 'Prover Stepout' },
-  'COMPUTING': { icon: 'EXECUTE', kind: 'default', title: 'Computing…' },
+  'VALID': { icon: 'CHECK', label: 'Valid', kind: 'positive', title: 'Valid Goal' },
+  'PASSED': { icon: 'CHECK', label: 'Passed', kind: 'positive', title: 'Passed Test' },
+  'DOOMED': { icon: 'CROSS', label: 'Doomed', kind: 'negative', title: 'Doomed Test' },
+  'FAILED': { icon: 'WARNING', label: 'Failed', kind: 'negative', title: 'Prover Failure' },
+  'UNKNOWN': { icon: 'ATTENTION', label: 'Unknown', kind: 'warning', title: 'Prover Stucked' },
+  'TIMEOUT': { icon: 'HELP', label: 'Timeout', kind: 'warning', title: 'Prover Timeout' },
+  'STEPOUT': { icon: 'HELP', label: 'Stepout', kind: 'warning', title: 'Prover Stepout' },
+  'COMPUTING': { icon: 'EXECUTE', label: 'Running', kind: 'default', title: 'Prover is running' },
 };
+/* eslint-enable max-len */
 
-function getStatus(g : WP.goalsData): Status {
-  const base = baseStatus[g.status] ?? noResult;
-  return { ...base, label: g.stats.summary };
+export function getStatus(g : WP.goalsData): Status {
+  const { label, ...base } = baseStatus[g.status] ?? noResult;
+  return { ...base, label: label + g.stats.summary };
 }
 
 function renderStatus(s : Status): JSX.Element {
diff --git a/ivette/src/frama-c/plugins/wp/index.tsx b/ivette/src/frama-c/plugins/wp/index.tsx
index ac06fc2a954..872aa980024 100644
--- a/ivette/src/frama-c/plugins/wp/index.tsx
+++ b/ivette/src/frama-c/plugins/wp/index.tsx
@@ -34,7 +34,7 @@ import * as Ivette from 'ivette';
 import * as Server from 'frama-c/server';
 import * as States from 'frama-c/states';
 import { GoalTable } from './goals';
-import { TIP } from './tip';
+import { TIPView } from './tip';
 import * as WP from 'frama-c/plugins/wp/api';
 import './style.css';
 
@@ -45,15 +45,17 @@ import './style.css';
 function WPGoals(): JSX.Element {
   const [scoped, flipScoped] = Dome.useFlipSettings('frama-c.wp.goals.scoped');
   const [failed, flipFailed] = Dome.useFlipSettings('frama-c.wp.goals.failed');
-  const [tip, flipTip] = Dome.useFlipSettings('frama-c.wp.goals.tip', false);
+  const [displayTip, setTip] = React.useState(false);
   const [current, setCurrent] = React.useState(WP.goalDefault);
-  Server.useShutdown(() => setCurrent(WP.goalDefault));
+  Server.useShutdown(() => {
+    setTip(false);
+    setCurrent(WP.goalDefault);
+  });
   const scope = States.useCurrentScope();
   const [goals, setGoals] = React.useState(0);
   const [total, setTotal] = React.useState(0);
   const hasGoals = total > 0;
   const hasSelection = current !== WP.goalDefault;
-  const displayTip = tip && hasSelection;
   return (
     <>
       <Ivette.TitleBar>
@@ -76,7 +78,7 @@ function WPGoals(): JSX.Element {
           kind={displayTip ? 'warning' : 'positive'}
           title='Goal Resolution'
           enabled={hasSelection}
-          onClick={flipTip} />
+          onClick={() => setTip(!displayTip)} />
       </Ivette.TitleBar>
       <GoalTable
         display={!displayTip}
@@ -84,11 +86,11 @@ function WPGoals(): JSX.Element {
         failed={failed}
         current={current}
         setCurrent={setCurrent}
-        setTIP={flipTip}
+        setTIP={() => setTip(true)}
         setGoals={setGoals}
         setTotal={setTotal}
       />
-      <TIP
+      <TIPView
         display={displayTip}
         goal={current} />
     </>
diff --git a/ivette/src/frama-c/plugins/wp/tip.tsx b/ivette/src/frama-c/plugins/wp/tip.tsx
index af1dafa4fe8..1ed009f3313 100644
--- a/ivette/src/frama-c/plugins/wp/tip.tsx
+++ b/ivette/src/frama-c/plugins/wp/tip.tsx
@@ -21,18 +21,46 @@
 /* ************************************************************************ */
 
 import React from 'react';
-import { Vfill } from 'dome/layout/boxes';
+import { Cell } from 'dome/controls/labels';
+import { Vfill, Hbox } from 'dome/layout/boxes';
+import * as States from 'frama-c/states';
 import * as WP from 'frama-c/plugins/wp/api';
+import * as TIP from 'frama-c/plugins/wp/api/tip';
+import { getStatus } from './goals';
+
+/* -------------------------------------------------------------------------- */
+/* --- TIP View                                                           --- */
+/* -------------------------------------------------------------------------- */
+
 export interface TIPProps {
   display: boolean;
   goal: WP.goal;
 }
 
-export function TIP(props: TIPProps): JSX.Element {
+function useTarget(target: WP.goal | undefined) : WP.goalsData {
+  const data = States.useSyncArrayElt( WP.goals, target );
+  return data ?? WP.goalsDataDefault;
+}
+
+export function TIPView(props: TIPProps): JSX.Element {
   const { display, goal } = props;
+  const target = goal !== WP.goalDefault ? goal : undefined;
+  const infos: WP.goalsData = useTarget(goal);
+  const status = getStatus(infos);
+  const { index=0, pending=0 } =
+    States.useRequest( TIP.getProofState, target ) ?? {};
   return (
     <Vfill display={display}>
-      <div>{goal}</div>
+      <Hbox>
+        <Cell
+          icon='HOME'
+          label={infos.wpo} title='Goal identifier' />
+        <Cell
+          icon='CODE'
+          display={pending > 0}
+          label={`${index+1}/${pending}`} title='Pending proof nodes'/>
+        <Cell {...status}/>
+      </Hbox>
     </Vfill>
   );
 }
diff --git a/src/plugins/wp/wpApi.ml b/src/plugins/wp/wpApi.ml
index 41b8b9415c0..26910b42448 100644
--- a/src/plugins/wp/wpApi.ml
+++ b/src/plugins/wp/wpApi.ml
@@ -52,17 +52,18 @@ module INDEX = State_builder.Ref
       let default () = Hashtbl.create 0
     end)
 
+let indexGoal g =
+  let id = g.Wpo.po_gid in
+  let index = INDEX.get () in
+  if not (Hashtbl.mem index id) then Hashtbl.add index id g ; id
+
 module Goal : D.S with type t = Wpo.t =
 struct
   type t = Wpo.t
   let jtype = D.declare ~package ~name:"goal"
       ~descr:(Md.plain "Proof Obligations") (Jkey "wpo")
   let of_json js = Hashtbl.find (INDEX.get ()) (Json.string js)
-  let to_json g =
-    let id = g.Wpo.po_gid in
-    let index = INDEX.get () in
-    if not (Hashtbl.mem index id) then Hashtbl.add index id g ;
-    `String id
+  let to_json g = `String (indexGoal g)
 end
 
 (* -------------------------------------------------------------------------- *)
@@ -123,13 +124,13 @@ struct
   let to_json { smoke ; verdict } =
     `String begin
       match verdict with
-      | VCS.Valid -> if smoke then "DOOMED" else "VALID"
-      | VCS.Unknown -> if smoke then "PASSED" else "UNKNOWN"
+      | Valid -> if smoke then "DOOMED" else "VALID"
+      | Unknown -> if smoke then "PASSED" else "UNKNOWN"
+      | Timeout -> if smoke then "PASSED" else "TIMEOUT"
+      | Stepout -> if smoke then "PASSED" else "STEPOUT"
       | Failed -> "FAILED"
       | NoResult -> "NORESULT"
       | Computing _ -> "COMPUTING"
-      | Timeout -> "TIMEOUT"
-      | Stepout -> "STEPOUT"
     end
 end
 
@@ -263,7 +264,7 @@ let () = S.column gmodel ~name:"saved"
 
 let _ = S.register_array ~package ~name:"goals"
     ~descr:(Md.plain "Generated Goals")
-    ~key:(fun g -> g.Wpo.po_gid)
+    ~key:indexGoal
     ~keyName:"wpo"
     ~keyType:Goal.jtype
     ~iter:Wpo.iter_on_goals
-- 
GitLab