mirror of
https://github.com/digital-asset/daml.git
synced 2024-09-19 16:57:40 +03:00
Remove daml visualize / damlc visual (#16901)
* Remove daml visualize / damlc visual * fix redundant imports * remove visual-web subtest
This commit is contained in:
parent
3dea2d6bfc
commit
6058070637
@ -10,7 +10,6 @@ features:
|
||||
- Type-information on hover
|
||||
- Renaming of symbols
|
||||
- Daml snippet support
|
||||
- Command to generate visualization for Daml project via command palette ctrl + p.
|
||||
|
||||
Please note that this will only install the VSCode extension. Full use of the
|
||||
above features will also require that you have a working Daml SDK installed,
|
||||
@ -20,8 +19,6 @@ which you can get with:
|
||||
curl -s https://get.daml.com | sh
|
||||
```
|
||||
|
||||
To see graphs from `daml.visualize` command please install [Graphivz plugin](https://marketplace.visualstudio.com/items?itemName=EFanZh.graphviz-preview).
|
||||
|
||||
For more information on Daml please see [docs.daml.com](https://docs.daml.com).
|
||||
|
||||
## Troubleshooting
|
||||
|
@ -62,11 +62,6 @@
|
||||
"command": "daml.openDamlDocs",
|
||||
"title": "[Daml Documentation]"
|
||||
},
|
||||
{
|
||||
"command": "daml.visualize",
|
||||
"title": "Visualize daml project",
|
||||
"when": "editorLangId == daml"
|
||||
},
|
||||
{
|
||||
"command": "daml.resetTelemetryConsent",
|
||||
"title": "Ask me about Daml telemetry again on startup"
|
||||
|
@ -96,7 +96,6 @@ export async function activate(context: vscode.ExtensionContext) {
|
||||
);
|
||||
|
||||
let d2 = vscode.commands.registerCommand("daml.openDamlDocs", openDamlDocs);
|
||||
let d5 = vscode.commands.registerCommand("daml.visualize", visualize);
|
||||
|
||||
let highlight = vscode.window.createTextEditorDecorationType({
|
||||
backgroundColor: "rgba(200,200,200,.35)",
|
||||
@ -135,7 +134,7 @@ export async function activate(context: vscode.ExtensionContext) {
|
||||
resetTelemetryConsent(context),
|
||||
);
|
||||
|
||||
context.subscriptions.push(d1, d2, d3, d4, d5);
|
||||
context.subscriptions.push(d1, d2, d3, d4);
|
||||
}
|
||||
|
||||
// Compare the extension version with the one stored in the global state.
|
||||
@ -207,34 +206,6 @@ function getViewColumnForShowResource(): ViewColumn {
|
||||
}
|
||||
}
|
||||
|
||||
function visualize() {
|
||||
if (vscode.window.activeTextEditor) {
|
||||
let currentFile = vscode.window.activeTextEditor.document.fileName;
|
||||
if (vscode.window.activeTextEditor.document.languageId != "daml") {
|
||||
vscode.window.showInformationMessage("Open the daml file to visualize");
|
||||
} else {
|
||||
damlLanguageClient
|
||||
.sendRequest(ExecuteCommandRequest.type, {
|
||||
command: "daml/damlVisualize",
|
||||
arguments: [currentFile],
|
||||
})
|
||||
.then(dotFileContents => {
|
||||
vscode.workspace
|
||||
.openTextDocument({ content: dotFileContents, language: "dot" })
|
||||
.then(doc =>
|
||||
vscode.window
|
||||
.showTextDocument(doc, vscode.ViewColumn.One, true)
|
||||
.then(_ => loadPreviewIfAvailable()),
|
||||
);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
vscode.window.showInformationMessage(
|
||||
"Please open a Daml module to be visualized and then run the command",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function loadPreviewIfAvailable() {
|
||||
if (vscode.extensions.getExtension("EFanZh.graphviz-preview")) {
|
||||
vscode.commands.executeCommand("graphviz.showPreviewToSide");
|
||||
|
@ -222,7 +222,6 @@ da_haskell_library(
|
||||
"//compiler/damlc/daml-opts:daml-opts-types",
|
||||
"//compiler/damlc/daml-package-config",
|
||||
"//compiler/damlc/daml-rule-types",
|
||||
"//compiler/damlc/daml-visual",
|
||||
"//compiler/repl-service/client",
|
||||
"//compiler/scenario-service/client",
|
||||
"//compiler/scenario-service/protos:scenario_service_haskell_proto",
|
||||
|
@ -64,7 +64,6 @@ da_haskell_library(
|
||||
"//compiler/damlc/daml-opts",
|
||||
"//compiler/damlc/daml-opts:daml-opts-types",
|
||||
"//compiler/damlc/daml-rule-types",
|
||||
"//compiler/damlc/daml-visual",
|
||||
"//compiler/scenario-service/client",
|
||||
"//libs-haskell/bazel-runfiles",
|
||||
"//libs-haskell/da-hs-base",
|
||||
@ -105,7 +104,6 @@ da_haskell_library(
|
||||
"//compiler/damlc/daml-compiler",
|
||||
"//compiler/damlc/daml-opts",
|
||||
"//compiler/damlc/daml-opts:daml-opts-types",
|
||||
"//compiler/damlc/daml-visual",
|
||||
"//compiler/scenario-service/client",
|
||||
"//libs-haskell/bazel-runfiles",
|
||||
"//libs-haskell/da-hs-base",
|
||||
|
@ -15,9 +15,6 @@ module Development.IDE.Core.API.Testing
|
||||
, GoToDefinitionPattern (..)
|
||||
, HoverExpectation (..)
|
||||
, D.DiagnosticSeverity(..)
|
||||
, ExpectedGraph(..)
|
||||
, ExpectedSubGraph(..)
|
||||
, ExpectedChoiceDetails(..)
|
||||
, runShakeTest
|
||||
, runShakeTestOpts
|
||||
, makeFile
|
||||
@ -42,7 +39,6 @@ module Development.IDE.Core.API.Testing
|
||||
, expectNoVirtualResource
|
||||
, expectVirtualResourceNote
|
||||
, expectNoVirtualResourceNote
|
||||
, expectedGraph
|
||||
, timedSection
|
||||
, example
|
||||
) where
|
||||
@ -51,11 +47,9 @@ module Development.IDE.Core.API.Testing
|
||||
import qualified Development.IDE.Core.API as API
|
||||
import Development.IDE.Core.Debouncer
|
||||
import Development.IDE.Core.Shake (ShakeLspEnv(..), NotificationHandler(..))
|
||||
import qualified Development.IDE.Core.Rules.Daml as API
|
||||
import qualified Development.IDE.Types.Diagnostics as D
|
||||
import qualified Development.IDE.Types.Location as D
|
||||
import DA.Daml.LF.ScenarioServiceClient as SS
|
||||
import Development.IDE.Core.API.Testing.Visualize
|
||||
import Development.IDE.Core.Rules.Daml
|
||||
import Development.IDE.Types.Logger
|
||||
import DA.Daml.Options
|
||||
@ -69,7 +63,6 @@ import Language.LSP.Types hiding (SemanticTokenAbsolute (..), SemanticTokenRelat
|
||||
import Control.Concurrent.STM
|
||||
import Control.Exception.Extra
|
||||
import qualified Control.Monad.Reader as Reader
|
||||
import Data.Either.Combinators
|
||||
import Data.Map.Strict (Map)
|
||||
import qualified Data.Map.Strict as Map
|
||||
import qualified Data.Aeson as Aeson
|
||||
@ -106,7 +99,6 @@ data ShakeTestError
|
||||
| ExpectedVirtualResourceNote VirtualResource T.Text (Map VirtualResource T.Text)
|
||||
| ExpectedNoVirtualResourceNote VirtualResource (Map VirtualResource T.Text)
|
||||
| ExpectedNoErrors [D.FileDiagnostic]
|
||||
| ExpectedGraphProps FailedGraphExpectation
|
||||
| ExpectedDefinition Cursor GoToDefinitionPattern (Maybe D.Location)
|
||||
| ExpectedHoverText Cursor HoverExpectation [T.Text]
|
||||
| TimedSectionTookTooLong Clock.NominalDiffTime Clock.NominalDiffTime
|
||||
@ -534,14 +526,6 @@ timedSection targetDiffTime block = do
|
||||
throwError $ TimedSectionTookTooLong targetDiffTime actualDiffTime
|
||||
return value
|
||||
|
||||
-- Not using the ide call as we do not have a rule defined for visualization because of memory overhead
|
||||
expectedGraph :: D.NormalizedFilePath -> ExpectedGraph -> ShakeTest ()
|
||||
expectedGraph damlFilePath expectedGraph = do
|
||||
ideState <- ShakeTest $ Reader.asks steService
|
||||
wrld <- Reader.liftIO $ API.runActionSync ideState (API.worldForFile damlFilePath)
|
||||
expectNoErrors
|
||||
whenLeft (graphTest wrld expectedGraph) $ throwError . ExpectedGraphProps
|
||||
|
||||
-- | Example testing scenario.
|
||||
example :: ShakeTest ()
|
||||
example = do
|
||||
|
@ -1,64 +0,0 @@
|
||||
-- Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
||||
-- SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
module Development.IDE.Core.API.Testing.Visualize
|
||||
( ExpectedGraph(..)
|
||||
, ExpectedSubGraph(..)
|
||||
, ExpectedChoiceDetails(..)
|
||||
, FailedGraphExpectation(..)
|
||||
, graphTest
|
||||
)
|
||||
where
|
||||
|
||||
import Control.Monad
|
||||
import Data.Bifunctor
|
||||
import qualified Data.Text as T
|
||||
|
||||
import qualified DA.Daml.LF.Ast as LF
|
||||
import qualified DA.Daml.Visual as V
|
||||
|
||||
type TemplateName = String
|
||||
type ChoiceName = String
|
||||
|
||||
data ExpectedGraph = ExpectedGraph
|
||||
{ expectedSubgraphs :: [ExpectedSubGraph]
|
||||
, expectedEdges :: [(ExpectedChoiceDetails, ExpectedChoiceDetails)]
|
||||
} deriving (Eq, Ord, Show )
|
||||
|
||||
data ExpectedSubGraph = ExpectedSubGraph
|
||||
{ expectedNodes :: [ChoiceName]
|
||||
, expectedTplFields :: [String]
|
||||
, expectedTemplate :: TemplateName
|
||||
} deriving (Eq, Ord, Show )
|
||||
|
||||
data ExpectedChoiceDetails = ExpectedChoiceDetails
|
||||
{ expectedConsuming :: Bool
|
||||
, expectedName :: String
|
||||
} deriving (Eq, Ord, Show )
|
||||
|
||||
subgraphToExpectedSubgraph :: V.SubGraph -> ExpectedSubGraph
|
||||
subgraphToExpectedSubgraph vSubgraph = ExpectedSubGraph vNodes vFields vTplName
|
||||
where vNodes = map (T.unpack . LF.unChoiceName . V.displayChoiceName) (V.nodes vSubgraph)
|
||||
vFields = map T.unpack (V.templateFields vSubgraph)
|
||||
vTplName = T.unpack $ V.tplNameUnqual (V.clusterTemplate vSubgraph)
|
||||
|
||||
graphToExpectedGraph :: V.Graph -> ExpectedGraph
|
||||
graphToExpectedGraph vGraph = ExpectedGraph vSubgrpaghs vEdges
|
||||
where vSubgrpaghs = map subgraphToExpectedSubgraph (V.subgraphs vGraph)
|
||||
vEdges = map (bimap expectedChcDetails expectedChcDetails) (V.edges vGraph)
|
||||
expectedChcDetails chc = ExpectedChoiceDetails (V.consuming chc)
|
||||
((T.unpack . LF.unChoiceName . V.displayChoiceName) chc)
|
||||
|
||||
|
||||
data FailedGraphExpectation = FailedGraphExpectation
|
||||
{ expected :: ExpectedGraph
|
||||
, actual :: ExpectedGraph
|
||||
}
|
||||
deriving (Eq, Show)
|
||||
|
||||
graphTest :: LF.World -> ExpectedGraph -> Either FailedGraphExpectation ()
|
||||
graphTest wrld expectedGraph = do
|
||||
let actualGraph = V.graphFromWorld wrld
|
||||
let actual = graphToExpectedGraph actualGraph
|
||||
unless (expectedGraph == actual) $
|
||||
Left $ FailedGraphExpectation expectedGraph actual
|
@ -38,7 +38,6 @@ da_haskell_library(
|
||||
"//compiler/damlc/daml-ide-core",
|
||||
"//compiler/damlc/daml-lf-util",
|
||||
"//compiler/damlc/daml-rule-types",
|
||||
"//compiler/damlc/daml-visual",
|
||||
"//libs-haskell/da-hs-base",
|
||||
],
|
||||
)
|
||||
|
@ -27,7 +27,6 @@ import Development.IDE.Core.Service.Daml
|
||||
import Development.IDE.Plugin
|
||||
|
||||
import qualified DA.Daml.SessionTelemetry as SessionTelemetry
|
||||
import qualified DA.Daml.LanguageServer.Visualize as Visualize
|
||||
import qualified DA.Service.Logger as Lgr
|
||||
import qualified Network.URI as URI
|
||||
|
||||
@ -88,7 +87,7 @@ runLanguageServer
|
||||
-> (LSP.LanguageContextEnv c -> VFSHandle -> Maybe FilePath -> IO IdeState)
|
||||
-> IO ()
|
||||
runLanguageServer lgr plugins conf getIdeState = SessionTelemetry.withPlugin lgr $ \sessionHandlerPlugin -> do
|
||||
let allPlugins = plugins <> setHandlersKeepAlive <> setHandlersVirtualResource <> VirtualResource.plugin <> Visualize.plugin <> sessionHandlerPlugin
|
||||
let allPlugins = plugins <> setHandlersKeepAlive <> setHandlersVirtualResource <> VirtualResource.plugin <> sessionHandlerPlugin
|
||||
let onConfigurationChange c _ = Right c
|
||||
let options = def { LSP.executeCommandCommands = Just (commandIds allPlugins) }
|
||||
LS.runLanguageServer options conf onConfigurationChange allPlugins getIdeState
|
||||
|
@ -1,37 +0,0 @@
|
||||
-- Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
||||
-- SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
module DA.Daml.LanguageServer.Visualize
|
||||
( plugin
|
||||
) where
|
||||
|
||||
import Control.Monad.IO.Class
|
||||
import qualified Data.Aeson as Aeson
|
||||
import Language.LSP.Types
|
||||
import Development.IDE.Types.Logger
|
||||
import qualified Data.Text as T
|
||||
import Development.IDE.Plugin
|
||||
import Development.IDE.Core.Rules
|
||||
import Development.IDE.Core.Rules.Daml
|
||||
import Development.IDE.Core.Service.Daml
|
||||
import qualified Language.LSP.Server as LSP
|
||||
import Development.IDE.Types.Location
|
||||
import qualified DA.Daml.Visual as Visual
|
||||
|
||||
onCommand
|
||||
:: IdeState
|
||||
-> FilePath
|
||||
-> LSP.LspM c (Either ResponseError Aeson.Value)
|
||||
onCommand ide (toNormalizedFilePath' -> mod) = do
|
||||
liftIO $ logInfo (ideLogger ide) "Generating visualization for current daml project"
|
||||
world <- liftIO $ runAction ide (worldForFile mod)
|
||||
let dots = T.pack $ Visual.dotFileGen world
|
||||
return $ Right $ Aeson.String dots
|
||||
|
||||
plugin :: Plugin c
|
||||
plugin = Plugin
|
||||
{ pluginRules = mempty
|
||||
, pluginCommands = [PluginCommand "daml/damlVisualize" onCommand]
|
||||
, pluginNotificationHandlers = mempty
|
||||
, pluginHandlers = mempty
|
||||
}
|
@ -56,7 +56,6 @@ da_haskell_library(
|
||||
"//compiler/damlc/daml-doctest",
|
||||
"//compiler/damlc/daml-lf-conversion",
|
||||
"//compiler/damlc/daml-opts:daml-opts-types",
|
||||
"//compiler/damlc/daml-visual",
|
||||
"//compiler/scenario-service/client",
|
||||
"//libs-haskell/bazel-runfiles",
|
||||
"//libs-haskell/da-hs-base",
|
||||
|
@ -1,40 +0,0 @@
|
||||
# Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
load("//bazel_tools:haskell.bzl", "da_haskell_library")
|
||||
|
||||
da_haskell_library(
|
||||
name = "daml-visual",
|
||||
srcs = glob(["src/**/*.hs"]),
|
||||
data = [
|
||||
"@static_asset_d3plus//:js/d3.min.js",
|
||||
"@static_asset_d3plus//:js/d3plus.min.js",
|
||||
],
|
||||
hackage_deps = [
|
||||
"aeson",
|
||||
"base",
|
||||
"bytestring",
|
||||
"containers",
|
||||
"extra",
|
||||
"filepath",
|
||||
"mtl",
|
||||
"open-browser",
|
||||
"text",
|
||||
"safe",
|
||||
"stache",
|
||||
"unordered-containers",
|
||||
"utf8-string",
|
||||
"zip-archive",
|
||||
"uniplate",
|
||||
],
|
||||
src_strip_prefix = "src",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//compiler/daml-lf-ast",
|
||||
"//compiler/daml-lf-proto",
|
||||
"//compiler/daml-lf-reader",
|
||||
"//compiler/daml-lf-tools",
|
||||
"//libs-haskell/bazel-runfiles",
|
||||
"//libs-haskell/da-hs-base",
|
||||
],
|
||||
)
|
@ -1,438 +0,0 @@
|
||||
-- Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
||||
-- SPDX-License-Identifier: Apache-2.0
|
||||
{-# LANGUAGE PatternSynonyms #-}
|
||||
-- | Main entry-point of the Daml compiler
|
||||
module DA.Daml.Visual
|
||||
( execVisual
|
||||
, tplNameUnqual
|
||||
, TemplateChoices(..)
|
||||
, ChoiceAndAction(..)
|
||||
, Action(..)
|
||||
, Graph(..)
|
||||
, SubGraph(..)
|
||||
, ChoiceDetails(..)
|
||||
, dotFileGen
|
||||
, graphFromWorld
|
||||
, execVisualHtml
|
||||
) where
|
||||
|
||||
|
||||
import qualified DA.Daml.LF.Ast as LF
|
||||
import DA.Daml.LF.Ast.World as AST
|
||||
import DA.Daml.LF.Reader
|
||||
import qualified Data.NameMap as NM
|
||||
import qualified Data.Set as Set
|
||||
import qualified DA.Pretty as DAP
|
||||
import qualified DA.Daml.LF.Proto3.Archive as Archive
|
||||
import qualified "zip-archive" Codec.Archive.Zip as ZIPArchive
|
||||
import qualified Data.ByteString.Lazy as BSL
|
||||
import qualified Data.ByteString as B
|
||||
import Data.Generics.Uniplate.Data
|
||||
import qualified Data.Map.Strict as Map
|
||||
import qualified Data.Text as T
|
||||
import Data.Tuple.Extra (both)
|
||||
import GHC.Generics
|
||||
import Data.Aeson
|
||||
import Text.Mustache
|
||||
import qualified Data.Text.Lazy.IO as TIO
|
||||
import qualified Data.Text.Encoding as DT
|
||||
import Web.Browser
|
||||
import DA.Bazel.Runfiles
|
||||
import System.FilePath
|
||||
import Safe
|
||||
import Control.Monad
|
||||
import Control.Monad.State
|
||||
|
||||
type IsConsuming = Bool
|
||||
|
||||
data Action = ACreate (LF.Qualified LF.TypeConName)
|
||||
| AExercise (LF.Qualified LF.TypeConName) LF.ChoiceName deriving (Eq, Ord, Show )
|
||||
|
||||
data ChoiceAndAction = ChoiceAndAction
|
||||
{ choiceName :: LF.ChoiceName
|
||||
, choiceConsuming :: IsConsuming
|
||||
, actions :: Set.Set Action
|
||||
} deriving (Show)
|
||||
|
||||
|
||||
data TemplateChoices = TemplateChoices
|
||||
{ template :: LF.Qualified LF.Template
|
||||
, choiceAndActions :: [ChoiceAndAction]
|
||||
} deriving (Show)
|
||||
|
||||
templateId :: TemplateChoices -> LF.Qualified LF.TypeConName
|
||||
templateId TemplateChoices{..} =
|
||||
fmap LF.tplTypeCon template
|
||||
|
||||
data ChoiceDetails = ChoiceDetails
|
||||
{ nodeId :: Int
|
||||
, consuming :: Bool
|
||||
, displayChoiceName :: LF.ChoiceName
|
||||
} deriving (Show, Eq)
|
||||
|
||||
data SubGraph = SubGraph
|
||||
{ nodes :: [ChoiceDetails]
|
||||
, templateFields :: [T.Text]
|
||||
, clusterTemplate :: LF.Template
|
||||
} deriving (Show, Eq)
|
||||
|
||||
data Graph = Graph
|
||||
{ subgraphs :: [SubGraph]
|
||||
, edges :: [(ChoiceDetails, ChoiceDetails)]
|
||||
} deriving (Show, Eq)
|
||||
|
||||
data D3Link = D3Link
|
||||
{ source :: Int
|
||||
, target :: Int
|
||||
, weight :: Int
|
||||
} deriving (Generic, Show)
|
||||
|
||||
-- can add more information like signatories, observers
|
||||
data D3Node = D3Node
|
||||
{ fields :: T.Text
|
||||
, tplName :: T.Text
|
||||
, id :: Int
|
||||
, chcName :: T.Text
|
||||
} deriving (Generic, Show)
|
||||
|
||||
data D3Graph = D3Graph
|
||||
{ d3links :: [D3Link]
|
||||
, d3nodes :: [D3Node]
|
||||
} deriving (Generic, Show)
|
||||
|
||||
data WebPage = WebPage
|
||||
{ links :: T.Text
|
||||
, dnodes :: T.Text
|
||||
, d3Js :: String
|
||||
, d3PlusJs :: String
|
||||
} deriving (Generic, Show)
|
||||
|
||||
d3LinksFromGraphEdges :: Graph -> [D3Link]
|
||||
d3LinksFromGraphEdges g = map edgeToD3Link (edges g)
|
||||
where edgeToD3Link edge = D3Link (nodeId (fst edge)) (nodeId (snd edge)) 10
|
||||
|
||||
d3NodesFromGraph :: Graph -> [D3Node]
|
||||
d3NodesFromGraph g = concatMap subGraphToD3Nodes (subgraphs g)
|
||||
where subGraphToD3Nodes sg = map (\chcD ->
|
||||
D3Node (T.unlines $ templateFields sg)
|
||||
(tplNameUnqual $ clusterTemplate sg)
|
||||
(nodeId chcD)
|
||||
(DAP.renderPretty $ displayChoiceName chcD)
|
||||
)
|
||||
(nodes sg)
|
||||
|
||||
graphToD3Graph :: Graph -> D3Graph
|
||||
graphToD3Graph g = D3Graph (d3LinksFromGraphEdges g) (d3NodesFromGraph g)
|
||||
|
||||
instance ToJSON D3Link
|
||||
instance ToJSON D3Node
|
||||
instance ToJSON D3Graph
|
||||
instance ToJSON WebPage
|
||||
|
||||
startFromUpdate :: Set.Set (LF.Qualified LF.ExprValName) -> LF.World -> LF.Update -> Set.Set Action
|
||||
startFromUpdate seen world update = case update of
|
||||
LF.UPure _ e -> startFromExpr seen world e
|
||||
LF.UBind (LF.Binding _ e1) e2 -> startFromExpr seen world e1 `Set.union` startFromExpr seen world e2
|
||||
LF.UGetTime -> Set.empty
|
||||
LF.UEmbedExpr _ upEx -> startFromExpr seen world upEx
|
||||
LF.UCreate tpl _ -> Set.singleton (ACreate tpl)
|
||||
LF.UCreateInterface{} ->
|
||||
error "Interfaces are not supported"
|
||||
LF.UExercise tpl choice _ _ -> Set.singleton (AExercise tpl choice)
|
||||
LF.UDynamicExercise {} ->
|
||||
-- TODO https://github.com/digital-asset/daml/issues/16154 (dynamic-exercise)
|
||||
error "Dynamic exercise is not supported"
|
||||
LF.UExerciseInterface{} ->
|
||||
-- TODO https://github.com/digital-asset/daml/issues/12051
|
||||
error "Interfaces are not supported"
|
||||
LF.UExerciseByKey tpl choice _ _ -> Set.singleton (AExercise tpl choice)
|
||||
LF.UFetch{} -> Set.empty
|
||||
LF.USoftFetch {} -> Set.empty
|
||||
LF.UFetchInterface{} ->
|
||||
-- TODO https://github.com/digital-asset/daml/issues/12051
|
||||
error "Interfaces are not supported"
|
||||
LF.ULookupByKey{} -> Set.empty
|
||||
LF.UFetchByKey{} -> Set.empty
|
||||
LF.UTryCatch _ e1 _ e2 -> startFromExpr seen world e1 `Set.union` startFromExpr seen world e2
|
||||
|
||||
startFromExpr :: Set.Set (LF.Qualified LF.ExprValName) -> LF.World -> LF.Expr -> Set.Set Action
|
||||
startFromExpr seen world e = case e of
|
||||
LF.EVar _ -> Set.empty
|
||||
-- NOTE(MH/RJR): Do not explore the `$fChoice`/`$fTemplate` dictionaries because
|
||||
-- they contain all the ledger actions and therefore creates too many edges
|
||||
-- in the graph. We instead detect calls to the `create`, `archive` and
|
||||
-- `exercise` methods from `Template` and `Choice` instances.
|
||||
LF.EVal (LF.Qualified _ _ (LF.ExprValName ref))
|
||||
| any (`T.isPrefixOf` ref)
|
||||
[ "$fHasCreate"
|
||||
, "$fHasExercise"
|
||||
, "$fHasArchive"
|
||||
, "$fHasFetch" -- also filters out $fHasFetchByKey
|
||||
, "$fHasLookupByKey"
|
||||
] -> Set.empty
|
||||
LF.EVal ref -> case LF.lookupValue ref world of
|
||||
Right LF.DefValue{..}
|
||||
| ref `Set.member` seen -> Set.empty
|
||||
| otherwise -> startFromExpr (Set.insert ref seen) world dvalBody
|
||||
Left _ -> error "This should not happen"
|
||||
LF.EUpdate upd -> startFromUpdate seen world upd
|
||||
-- NOTE(RJR): Look for calls to `create` and `archive` methods from a
|
||||
-- `Template` instance and produce the corresponding edges in the graph.
|
||||
EInternalTemplateVal "create" `LF.ETyApp` LF.TCon tpl `LF.ETmApp` _dict
|
||||
-> Set.singleton (ACreate tpl)
|
||||
EInternalTemplateVal "archive" `LF.ETyApp` LF.TCon tpl `LF.ETmApp` _dict ->
|
||||
Set.singleton (AExercise tpl (LF.ChoiceName "Archive"))
|
||||
-- NOTE(RJR): Look for calls to the `exercise` method from a `Choice`
|
||||
-- instance and produce the corresponding edge in the graph.
|
||||
EInternalTemplateVal "exercise" `LF.ETyApp` LF.TCon tpl `LF.ETyApp` LF.TCon (LF.Qualified _ _ (LF.TypeConName [chc])) `LF.ETyApp` _ret `LF.ETmApp` _dict ->
|
||||
Set.singleton (AExercise tpl (LF.ChoiceName chc))
|
||||
EInternalTemplateVal "exerciseByKey" `LF.ETyApp` LF.TCon tpl `LF.ETyApp` _ `LF.ETyApp` LF.TCon (LF.Qualified _ _ (LF.TypeConName [chc])) `LF.ETyApp` _ret `LF.ETmApp` _dict ->
|
||||
Set.singleton (AExercise tpl (LF.ChoiceName chc))
|
||||
expr -> Set.unions $ map (startFromExpr seen world) $ children expr
|
||||
|
||||
pattern EInternalTemplateVal :: T.Text -> LF.Expr
|
||||
pattern EInternalTemplateVal val <-
|
||||
LF.EVal (LF.Qualified _pkg (LF.ModuleName ["DA", "Internal", "Template", "Functions"]) (LF.ExprValName val))
|
||||
|
||||
startFromChoice :: LF.World -> LF.TemplateChoice -> Set.Set Action
|
||||
startFromChoice world chc = startFromExpr Set.empty world (LF.chcUpdate chc)
|
||||
|
||||
templatePossibleUpdates :: LF.World -> LF.Template -> [ChoiceAndAction]
|
||||
templatePossibleUpdates world tpl = map toActions $ NM.toList $ LF.tplChoices tpl
|
||||
where toActions c = ChoiceAndAction {
|
||||
choiceName = LF.chcName c
|
||||
, choiceConsuming = LF.chcConsuming c
|
||||
, actions = startFromChoice world c
|
||||
}
|
||||
|
||||
moduleAndTemplates :: LF.World -> LF.PackageRef -> LF.Module -> [TemplateChoices]
|
||||
moduleAndTemplates world pkgRef mod =
|
||||
map (\t -> TemplateChoices (LF.Qualified pkgRef (LF.moduleName mod) t) (templatePossibleUpdates world t))
|
||||
(NM.toList $ LF.moduleTemplates mod)
|
||||
|
||||
dalfBytesToPakage :: BSL.ByteString -> ExternalPackage
|
||||
dalfBytesToPakage bytes = case Archive.decodeArchive Archive.DecodeAsDependency $ BSL.toStrict bytes of
|
||||
Right (pkgId, pkg) -> ExternalPackage pkgId pkg
|
||||
Left err -> error (show err)
|
||||
|
||||
darToWorld :: Dalfs -> LF.World
|
||||
darToWorld Dalfs{..} = case Archive.decodeArchive Archive.DecodeAsMain $ BSL.toStrict mainDalf of
|
||||
Right (_, mainPkg) -> AST.initWorldSelf pkgs mainPkg
|
||||
Left err -> error (show err)
|
||||
where
|
||||
pkgs = map dalfBytesToPakage dalfs
|
||||
|
||||
tplNameUnqual :: LF.Template -> T.Text
|
||||
tplNameUnqual LF.Template {..} = headNote "tplNameUnqual" (LF.unTypeConName tplTypeCon)
|
||||
|
||||
data ChoiceIdentifier = ChoiceIdentifier
|
||||
{ choiceIdTemplate :: !(LF.Qualified LF.TypeConName)
|
||||
, choiceIdName :: !LF.ChoiceName
|
||||
} deriving (Eq, Show, Ord)
|
||||
|
||||
choiceNameWithId :: [TemplateChoices] -> Map.Map ChoiceIdentifier ChoiceDetails
|
||||
choiceNameWithId tplChcActions = Map.unions (evalState (mapM f tplChcActions) 0)
|
||||
where
|
||||
f :: TemplateChoices -> State Int (Map.Map ChoiceIdentifier ChoiceDetails)
|
||||
f tpl@TemplateChoices{..} = do
|
||||
choices <- forM (createChoice : choiceAndActions) $ \ChoiceAndAction{..} -> do
|
||||
id <- get
|
||||
put (id + 1)
|
||||
let choiceId = ChoiceIdentifier (templateId tpl) choiceName
|
||||
pure (choiceId, ChoiceDetails id choiceConsuming choiceName)
|
||||
pure (Map.fromList choices)
|
||||
createChoice = ChoiceAndAction
|
||||
{ choiceName = LF.ChoiceName "Create"
|
||||
, choiceConsuming = False
|
||||
, actions = Set.empty
|
||||
}
|
||||
|
||||
nodeIdForChoice :: Map.Map ChoiceIdentifier ChoiceDetails -> ChoiceIdentifier -> ChoiceDetails
|
||||
nodeIdForChoice nodeLookUp chc = case Map.lookup chc nodeLookUp of
|
||||
Just node -> node
|
||||
Nothing -> error "Template node lookup failed"
|
||||
|
||||
addCreateChoice :: TemplateChoices -> Map.Map ChoiceIdentifier ChoiceDetails -> ChoiceDetails
|
||||
addCreateChoice tpl lookupData = nodeIdForChoice lookupData tplNameCreateChoice
|
||||
where
|
||||
tplNameCreateChoice =
|
||||
ChoiceIdentifier
|
||||
(templateId tpl)
|
||||
createChoiceName
|
||||
|
||||
labledField :: T.Text -> T.Text -> T.Text
|
||||
labledField fname "" = fname
|
||||
labledField fname label = fname <> "." <> label
|
||||
|
||||
typeConFieldsNames :: LF.World -> (LF.FieldName, LF.Type) -> [T.Text]
|
||||
typeConFieldsNames world (LF.FieldName fName, LF.TConApp tcn _) = map (labledField fName) (typeConFields tcn world)
|
||||
typeConFieldsNames _ (LF.FieldName fName, _) = [fName]
|
||||
|
||||
-- TODO: Anup This will fail if we were to recursively continue exploring the AST.
|
||||
typeConFields :: LF.Qualified LF.TypeConName -> LF.World -> [T.Text]
|
||||
typeConFields qName world = case LF.lookupDataType qName world of
|
||||
Right dataType -> case LF.dataCons dataType of
|
||||
LF.DataRecord re -> concatMap (typeConFieldsNames world) re
|
||||
LF.DataVariant _ -> [""]
|
||||
LF.DataEnum _ -> [""]
|
||||
-- TODO https://github.com/digital-asset/daml/issues/12051
|
||||
LF.DataInterface -> error "interfaces are not implemented"
|
||||
Left _ -> error "malformed template constructor"
|
||||
|
||||
constructSubgraphsWithLables :: LF.World -> Map.Map ChoiceIdentifier ChoiceDetails -> TemplateChoices -> SubGraph
|
||||
constructSubgraphsWithLables wrld lookupData tpla@TemplateChoices {..} =
|
||||
SubGraph (addCreateChoice tpla lookupData : choices) fieldsInTemplate (LF.qualObject template)
|
||||
where
|
||||
fieldsInTemplate = typeConFields (templateId tpla) wrld
|
||||
choicesInTemplate =
|
||||
map (\c -> ChoiceIdentifier (templateId tpla) (choiceName c))
|
||||
choiceAndActions
|
||||
choices = map (nodeIdForChoice lookupData) choicesInTemplate
|
||||
|
||||
createChoiceName :: LF.ChoiceName
|
||||
createChoiceName = LF.ChoiceName "Create"
|
||||
|
||||
actionToChoice :: Action -> ChoiceIdentifier
|
||||
actionToChoice (ACreate tpl) =
|
||||
ChoiceIdentifier tpl createChoiceName
|
||||
actionToChoice (AExercise tpl chcT) =
|
||||
ChoiceIdentifier tpl chcT
|
||||
|
||||
choiceActionToChoicePairs :: LF.Qualified LF.TypeConName -> ChoiceAndAction -> [(ChoiceIdentifier, ChoiceIdentifier)]
|
||||
choiceActionToChoicePairs tpl ChoiceAndAction{..} =
|
||||
map (\a -> (choiceId, actionToChoice a)) (Set.elems actions)
|
||||
where
|
||||
choiceId = ChoiceIdentifier tpl choiceName
|
||||
|
||||
graphEdges :: Map.Map ChoiceIdentifier ChoiceDetails -> [TemplateChoices] -> [(ChoiceDetails, ChoiceDetails)]
|
||||
graphEdges lookupData tplChcActions =
|
||||
map (both (nodeIdForChoice lookupData)) $
|
||||
concat $
|
||||
concatMap
|
||||
(\tpl -> map (choiceActionToChoicePairs (templateId tpl)) (choiceAndActions tpl))
|
||||
tplChcActions
|
||||
|
||||
subGraphHeader :: SubGraph -> String
|
||||
subGraphHeader sg = "subgraph cluster_" ++ (DAP.renderPretty $ head (LF.unTypeConName $ LF.tplTypeCon $ clusterTemplate sg)) ++ "{\n"
|
||||
|
||||
choiceDetailsColorCode :: IsConsuming -> String
|
||||
choiceDetailsColorCode True = "red"
|
||||
choiceDetailsColorCode False = "green"
|
||||
|
||||
subGraphBodyLine :: ChoiceDetails -> String
|
||||
subGraphBodyLine chc = "n" ++ show (nodeId chc)++ "[label=" ++ DAP.renderPretty (displayChoiceName chc) ++"][color=" ++ choiceDetailsColorCode (consuming chc) ++"]; "
|
||||
|
||||
subGraphEnd :: SubGraph -> String
|
||||
subGraphEnd sg = "label=<" ++ tHeader ++ tTitle ++ tBody ++ tclose ++ ">" ++ ";color=" ++ "blue" ++ "\n}"
|
||||
where tHeader = "<table align = \"left\" border=\"0\" cellborder=\"0\" cellspacing=\"1\">\n"
|
||||
tTitle = "<tr><td align=\"center\"><b>" ++ DAP.renderPretty (LF.tplTypeCon $ clusterTemplate sg) ++ "</b></td></tr>"
|
||||
tBody = concatMap fieldTableLine (templateFields sg)
|
||||
fieldTableLine field = "<tr><td align=\"left\">" ++ T.unpack field ++ "</td></tr> \n"
|
||||
tclose = "</table>"
|
||||
|
||||
subGraphCluster :: SubGraph -> String
|
||||
subGraphCluster sg@SubGraph {..} = subGraphHeader sg ++ unlines (map subGraphBodyLine nodes) ++ subGraphEnd sg
|
||||
|
||||
drawEdge :: ChoiceDetails -> ChoiceDetails -> String
|
||||
drawEdge n1 n2 = "n" ++ show (nodeId n1) ++ "->" ++ "n" ++ show (nodeId n2)
|
||||
|
||||
constructDotGraph :: Graph -> String
|
||||
constructDotGraph graph = "digraph G {\ncompound=true;\n" ++ "rankdir=LR;\n"++ graphLines ++ "\n}\n"
|
||||
where subgraphsLines = concatMap subGraphCluster (subgraphs graph)
|
||||
edgesLines = unlines $ map (uncurry drawEdge) (edges graph)
|
||||
graphLines = subgraphsLines ++ edgesLines
|
||||
|
||||
graphFromWorld :: LF.World -> Graph
|
||||
graphFromWorld world = Graph subGraphs edges
|
||||
where
|
||||
templatesAndModules = concat
|
||||
[ moduleAndTemplates world pkgRef mod
|
||||
| (pkgRef, pkg) <- pkgs
|
||||
, mod <- NM.toList $ LF.packageModules pkg
|
||||
]
|
||||
nodes = choiceNameWithId templatesAndModules
|
||||
subGraphs = map (constructSubgraphsWithLables world nodes) templatesAndModules
|
||||
edges = graphEdges nodes templatesAndModules
|
||||
pkgs =
|
||||
(LF.PRSelf, getWorldSelf world)
|
||||
: map (\ExternalPackage{..} -> (LF.PRImport extPackageId, extPackagePkg))
|
||||
(getWorldImported world)
|
||||
|
||||
dotFileGen :: LF.World -> String
|
||||
dotFileGen world = constructDotGraph $ graphFromWorld world
|
||||
|
||||
webPageTemplate :: T.Text
|
||||
webPageTemplate =
|
||||
T.unlines [ "<html>"
|
||||
, "<head><title>Daml Visualization</title><meta charset=\"utf-8\"></head>"
|
||||
, "<body>"
|
||||
, "<div id='viz'></div>"
|
||||
, "<script>"
|
||||
, "{{{d3Js}}}"
|
||||
, "</script>"
|
||||
, "<script>"
|
||||
, "{{{d3PlusJs}}}"
|
||||
, "</script>"
|
||||
, "<script>"
|
||||
, "var nodes = {{{dnodes}}}"
|
||||
, "var links = {{{links}}}"
|
||||
, "d3plus.viz()"
|
||||
, " .container('#viz')"
|
||||
, " .type('network')"
|
||||
, " .data(nodes)"
|
||||
, " .text('chcName')"
|
||||
, " .edges({ value: links, arrows: true })"
|
||||
, " .tooltip({"
|
||||
, " Template: function (d) { return d['tplName'] },"
|
||||
, " Fields: function (d) { return d['fields']; }"
|
||||
, " })"
|
||||
, " .draw();"
|
||||
, "</script>"
|
||||
, "</body>"
|
||||
, "</html>"
|
||||
]
|
||||
|
||||
type OpenBrowserFlag = Bool
|
||||
execVisualHtml :: FilePath -> FilePath -> OpenBrowserFlag -> IO ()
|
||||
execVisualHtml darFilePath webFilePath oBrowser = do
|
||||
darBytes <- B.readFile darFilePath
|
||||
dalfs <- either fail pure $
|
||||
readDalfs $ ZIPArchive.toArchive (BSL.fromStrict darBytes)
|
||||
d3js <- readFile =<< locateResource Resource
|
||||
-- @static_asset_d3plus//:js/d3.min.js
|
||||
{ resourcesPath = "d3.min.js"
|
||||
-- In a packaged application, this is stored directly underneath the
|
||||
-- resources directory because it's a single file.
|
||||
-- See @bazel_tools/packaging/packaging.bzl@.
|
||||
, runfilesPathPrefix = "static_asset_d3plus" </> "js"
|
||||
}
|
||||
d3plusjs <- readFile =<< locateResource Resource
|
||||
-- @static_asset_d3plus//:js/d3plus.min.js
|
||||
{ resourcesPath = "d3plus.min.js"
|
||||
-- as above
|
||||
, runfilesPathPrefix = "static_asset_d3plus" </> "js"
|
||||
}
|
||||
let world = darToWorld dalfs
|
||||
graph = graphFromWorld world
|
||||
d3G = graphToD3Graph graph
|
||||
linksJson = DT.decodeUtf8 $ BSL.toStrict $ encode $ d3links d3G
|
||||
nodesJson = DT.decodeUtf8 $ BSL.toStrict $ encode $ d3nodes d3G
|
||||
webPage = WebPage linksJson nodesJson d3js d3plusjs
|
||||
case compileMustacheText "Webpage" webPageTemplate of
|
||||
Left err -> error $ show err
|
||||
Right mTpl -> do
|
||||
TIO.writeFile webFilePath $ renderMustache mTpl $ toJSON webPage
|
||||
when oBrowser
|
||||
(do _ <- openBrowser webFilePath
|
||||
return ())
|
||||
|
||||
|
||||
execVisual :: FilePath -> Maybe FilePath -> IO ()
|
||||
execVisual darFilePath dotFilePath = do
|
||||
darBytes <- B.readFile darFilePath
|
||||
dalfs <- either fail pure $ readDalfs $ ZIPArchive.toArchive (BSL.fromStrict darBytes)
|
||||
let world = darToWorld dalfs
|
||||
result = dotFileGen world
|
||||
case dotFilePath of
|
||||
Just outDotFile -> writeFile outDotFile result
|
||||
Nothing -> putStrLn result
|
@ -1,29 +0,0 @@
|
||||
### Dev Setup
|
||||
The dev setup is not automated, to get a local dev env follow steps.
|
||||
|
||||
cd compiler/damlc/daml-visual/src/DA/d3-dev
|
||||
wget https://github.com/alexandersimoes/d3plus/releases/download/v1.9.8/d3plus.zip
|
||||
unzip d3plus.zip
|
||||
python -m http.server --directory .
|
||||
open http://0.0.0.0:8000/d3-dev.html
|
||||
|
||||
If there are any changes to be made to the JS, they will have to be made to the webpage template within `Visual.hs` module.
|
||||
|
||||
Note: We can automate this with using npm/yarn but is not done as of yet as we are not dealing with a lot of JS/HTML/CSS code. If we add more customization it might be worth the effort to setup a automated dev setup.
|
||||
|
||||
### About D3 and D3Plus and versions used in this project
|
||||
[D3JS](https://d3js.org/) is a popular Data visualization library and [D3Plus](https://github.com/alexandersimoes/d3plus) gives simple api with reasonable defaults to draw SVG.
|
||||
|
||||
The version currently we are using is `1.9.8` and the next version of `D3Plus` is `2.0.0` which takes compositional style i.e. lets us pick the libs (tooltips, legends etc.) that we need. The reason we are not using latest(`2.0.0`) is the composition of libs is not very well documented and lacks advanced examples and also it is currently labeled as beta.
|
||||
|
||||
### Why are JS files packed into one HTML and inlined?
|
||||
- This makes sharing the file a lot easy
|
||||
- We don't have to worry about webserver on client machine as we can get away with just opening a file in browser vs referencing local files on disk which is restricted by CQRS policy.
|
||||
- There might be performance issues with larger projects.
|
||||
|
||||
|
||||
### Helpful Links
|
||||
* [Getting Started](https://d3plus.org/blog/getting-started/2014/06/12/getting-started-1/)
|
||||
* [Live Examples](https://d3plus.org/examples/)
|
||||
* [Documentation](https://github.com/alexandersimoes/d3plus/wiki)
|
||||
* [Google Group Discussions](https://groups.google.com/forum/#!forum/d3plus)
|
@ -1,63 +0,0 @@
|
||||
<!-- Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved. -->
|
||||
<!-- SPDX-License-Identifier: Apache-2.0 -->
|
||||
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Daml Visualization</title>
|
||||
<meta charset="utf-8">
|
||||
<script src="/d3plus.v1.9.8/js/d3.js"></script>
|
||||
<script src="/d3plus.v1.9.8/js/d3plus.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id='viz'></div>
|
||||
|
||||
<script>
|
||||
var nodes = [
|
||||
{ "fields": "buyer\nseller\nbaseIouCid\nbaseIssuer\nbaseCurrency\nbaseAmount\nquoteIssuer\nquoteCurrency\nquoteAmount\n", "tplName": "IouTrade", "id": 0, "chcName": "Create" },
|
||||
{ "fields": "buyer\nseller\nbaseIouCid\nbaseIssuer\nbaseCurrency\nbaseAmount\nquoteIssuer\nquoteCurrency\nquoteAmount\n", "tplName": "IouTrade", "id": 1, "chcName": "Archive" },
|
||||
{ "fields": "buyer\nseller\nbaseIouCid\nbaseIssuer\nbaseCurrency\nbaseAmount\nquoteIssuer\nquoteCurrency\nquoteAmount\n", "tplName": "IouTrade", "id": 2, "chcName": "IouTrade_Accept" },
|
||||
{ "fields": "buyer\nseller\nbaseIouCid\nbaseIssuer\nbaseCurrency\nbaseAmount\nquoteIssuer\nquoteCurrency\nquoteAmount\n", "tplName": "IouTrade", "id": 3, "chcName": "TradeProposal_Reject" }, { "fields": "issuer\nowner\ncurrency\namount\nobservers\n", "tplName": "Iou", "id": 4, "chcName": "Create" },
|
||||
{ "fields": "issuer\nowner\ncurrency\namount\nobservers\n", "tplName": "Iou", "id": 5, "chcName": "Archive" },
|
||||
{ "fields": "issuer\nowner\ncurrency\namount\nobservers\n", "tplName": "Iou", "id": 6, "chcName": "Iou_Split" },
|
||||
{ "fields": "issuer\nowner\ncurrency\namount\nobservers\n", "tplName": "Iou", "id": 7, "chcName": "Iou_Merge" },
|
||||
{ "fields": "issuer\nowner\ncurrency\namount\nobservers\n", "tplName": "Iou", "id": 8, "chcName": "Iou_Transfer" },
|
||||
{ "fields": "issuer\nowner\ncurrency\namount\nobservers\n", "tplName": "Iou", "id": 9, "chcName": "Iou_AddObserver" },
|
||||
{ "fields": "issuer\nowner\ncurrency\namount\nobservers\n", "tplName": "Iou", "id": 10, "chcName": "Iou_RemoveObserver" },
|
||||
{ "fields": "iou.issuer\niou.owner\niou.currency\niou.amount\niou.observers\nnewOwner\n", "tplName": "IouTransfer", "id": 11, "chcName": "Create" },
|
||||
{ "fields": "iou.issuer\niou.owner\niou.currency\niou.amount\niou.observers\nnewOwner\n", "tplName": "IouTransfer", "id": 12, "chcName": "Archive" },
|
||||
{ "fields": "iou.issuer\niou.owner\niou.currency\niou.amount\niou.observers\nnewOwner\n", "tplName": "IouTransfer", "id": 13, "chcName": "IouTransfer_Cancel" },
|
||||
{ "fields": "iou.issuer\niou.owner\niou.currency\niou.amount\niou.observers\nnewOwner\n", "tplName": "IouTransfer", "id": 14, "chcName": "IouTransfer_Reject" },
|
||||
{ "fields": "iou.issuer\niou.owner\niou.currency\niou.amount\niou.observers\nnewOwner\n", "tplName": "IouTransfer", "id": 15, "chcName": "IouTransfer_Accept" }
|
||||
]
|
||||
|
||||
|
||||
var links = [
|
||||
{ "source": 2, "target": 8, "weight": 10 },
|
||||
{ "source": 2, "target": 15, "weight": 10 },
|
||||
{ "source": 6, "target": 4, "weight": 10 },
|
||||
{ "source": 7, "target": 4, "weight": 10 },
|
||||
{ "source": 7, "target": 5, "weight": 10 },
|
||||
{ "source": 8, "target": 11, "weight": 10 },
|
||||
{ "source": 9, "target": 4, "weight": 10 },
|
||||
{ "source": 10, "target": 4, "weight": 10 },
|
||||
{ "source": 13, "target": 4, "weight": 10 },
|
||||
{ "source": 14, "target": 4, "weight": 10 },
|
||||
{ "source": 15, "target": 4, "weight": 10 }
|
||||
]
|
||||
d3plus.viz()
|
||||
.container('#viz')
|
||||
.type('network')
|
||||
.data(nodes)
|
||||
.text('chcName')
|
||||
.edges({ value: links, arrows: true })
|
||||
.tooltip({
|
||||
Template: function (d) { return d['tplName'] },
|
||||
Fields: function (d) { return d['fields']; }
|
||||
})
|
||||
.draw();
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -41,7 +41,6 @@ import DA.Daml.Package.Config
|
||||
import DA.Daml.Project.Config
|
||||
import DA.Daml.Project.Consts
|
||||
import DA.Daml.Project.Types (ConfigError(..), ProjectPath(..))
|
||||
import DA.Daml.Visual
|
||||
import qualified DA.Pretty
|
||||
import qualified DA.Service.Logger as Logger
|
||||
import qualified DA.Service.Logger.Impl.GCP as Logger.GCP
|
||||
@ -121,7 +120,6 @@ data CommandName =
|
||||
| MergeDars
|
||||
| Package
|
||||
| Test
|
||||
| Visual
|
||||
| Repl
|
||||
deriving (Ord, Show, Eq)
|
||||
data Command = Command CommandName (Maybe ProjectOpts) (IO ())
|
||||
@ -294,20 +292,6 @@ cmdInspect =
|
||||
optional $ optionOnce auto $ long "detail" <> metavar "LEVEL" <> help "Detail level of the pretty printed output (default: 0)"
|
||||
cmd = execInspect <$> inputFileOptWithExt ".dalf or .dar" <*> outputFileOpt <*> jsonOpt <*> detailOpt
|
||||
|
||||
cmdVisual :: Mod CommandFields Command
|
||||
cmdVisual =
|
||||
command "visual" $ info (helper <*> cmd) $ progDesc "Early Access (Labs). Generate visual from dar" <> fullDesc
|
||||
where
|
||||
cmd = vis <$> inputDarOpt <*> dotFileOpt
|
||||
vis a b = Command Visual Nothing $ execVisual a b
|
||||
|
||||
cmdVisualWeb :: Mod CommandFields Command
|
||||
cmdVisualWeb =
|
||||
command "visual-web" $ info (helper <*> cmd) $ progDesc "Early Access (Labs). Generate D3-Web Visual from dar" <> fullDesc
|
||||
where
|
||||
cmd = vis <$> inputDarOpt <*> htmlOutFile <*> openBrowser
|
||||
vis a b browser = Command Visual Nothing $ execVisualHtml a b browser
|
||||
|
||||
cmdBuild :: Int -> Mod CommandFields Command
|
||||
cmdBuild numProcessors =
|
||||
command "build" $
|
||||
@ -1033,8 +1017,6 @@ options numProcessors =
|
||||
<> cmdBuild numProcessors
|
||||
<> cmdTest numProcessors
|
||||
<> Damldoc.cmd numProcessors (\cli -> Command DamlDoc Nothing $ Damldoc.exec cli)
|
||||
<> cmdVisual
|
||||
<> cmdVisualWeb
|
||||
<> cmdInspectDar
|
||||
<> cmdValidateDar
|
||||
<> cmdDocTest numProcessors
|
||||
@ -1044,8 +1026,6 @@ options numProcessors =
|
||||
<|> subparser
|
||||
(internal -- internal commands
|
||||
<> cmdInspect
|
||||
<> cmdVisual
|
||||
<> cmdVisualWeb
|
||||
<> cmdMergeDars
|
||||
<> cmdInit numProcessors
|
||||
<> cmdCompile numProcessors
|
||||
@ -1122,7 +1102,6 @@ cmdUseDamlYamlArgs = \case
|
||||
MergeDars -> False -- just reads the dars
|
||||
Package -> False -- deprecated
|
||||
Test -> True
|
||||
Visual -> False -- just reads the dar
|
||||
Repl -> True
|
||||
|
||||
withProjectRoot' :: ProjectOpts -> ((FilePath -> IO FilePath) -> IO a) -> IO a
|
||||
|
@ -45,38 +45,6 @@ da_haskell_test(
|
||||
],
|
||||
)
|
||||
|
||||
# Tests for visualization
|
||||
da_haskell_test(
|
||||
name = "visualization",
|
||||
srcs = ["src/DamlcVisualize.hs"],
|
||||
data = [
|
||||
"//compiler/damlc",
|
||||
],
|
||||
hackage_deps = [
|
||||
"base",
|
||||
"bytestring",
|
||||
"directory",
|
||||
"either",
|
||||
"extra",
|
||||
"filepath",
|
||||
"tasty",
|
||||
"tasty-expected-failure",
|
||||
"tasty-hunit",
|
||||
"zip-archive",
|
||||
],
|
||||
main_function = "DamlcVisualize.main",
|
||||
visibility = ["//visibility:private"],
|
||||
deps = [
|
||||
"//:sdk-version-hs-lib",
|
||||
"//compiler/daml-lf-ast",
|
||||
"//compiler/daml-lf-proto",
|
||||
"//compiler/daml-lf-reader",
|
||||
"//compiler/damlc/daml-ide-core:ide-testing",
|
||||
"//libs-haskell/bazel-runfiles",
|
||||
"//libs-haskell/test-utils",
|
||||
],
|
||||
)
|
||||
|
||||
# Tests for damlc test
|
||||
da_haskell_test(
|
||||
name = "damlc-test",
|
||||
@ -366,7 +334,6 @@ da_haskell_test(
|
||||
"//compiler/damlc/daml-ide-core",
|
||||
"//compiler/damlc/daml-ide-core:ide-testing",
|
||||
"//compiler/damlc/daml-opts:daml-opts-types",
|
||||
"//compiler/damlc/daml-visual",
|
||||
"//compiler/scenario-service/client",
|
||||
"//libs-haskell/da-hs-base",
|
||||
],
|
||||
|
@ -52,7 +52,6 @@ ideTests mbScenarioService scriptPackageData =
|
||||
, onHoverTests mbScenarioService scriptPackageData
|
||||
, dlintSmokeTests mbScenarioService
|
||||
, scriptTests mbScenarioService scriptPackageData
|
||||
, visualDamlTests
|
||||
]
|
||||
|
||||
addScriptOpts :: Maybe ScriptPackageData -> Daml.Options -> Daml.Options
|
||||
@ -1332,168 +1331,3 @@ scriptTests mbScenarioService scriptPackageData = Tasty.testGroup "Script tests"
|
||||
where
|
||||
testCase' = testCase mbScenarioService (Just scriptPackageData)
|
||||
testCaseFails' = testCaseFails mbScenarioService (Just scriptPackageData)
|
||||
|
||||
|
||||
visualDamlTests :: Tasty.TestTree
|
||||
visualDamlTests = Tasty.testGroup "Visual Tests"
|
||||
[ testCase' "Template with no actions (edges) from choices" $ do
|
||||
foo <- makeModule "F"
|
||||
[ "template Coin"
|
||||
, " with"
|
||||
, " owner : Party"
|
||||
, " where"
|
||||
, " signatory owner"
|
||||
, " choice Delete : ()"
|
||||
, " controller owner"
|
||||
, " do return ()"
|
||||
]
|
||||
setFilesOfInterest [foo]
|
||||
expectedGraph foo (
|
||||
ExpectedGraph {expectedSubgraphs =
|
||||
[ExpectedSubGraph {expectedNodes = ["Create","Archive","Delete"]
|
||||
, expectedTplFields = ["owner"]
|
||||
, expectedTemplate = "Coin"}
|
||||
]
|
||||
, expectedEdges = []})
|
||||
, testCase' "Fetch shoud not be an create action" $ do
|
||||
fetchTest <- makeModule "F"
|
||||
[ "template Coin"
|
||||
, " with"
|
||||
, " owner : Party"
|
||||
, " amount : Int"
|
||||
, " where"
|
||||
, " signatory owner"
|
||||
, " nonconsuming choice ReducedCoin : ()"
|
||||
, " with otherCoin : ContractId Coin"
|
||||
, " controller owner"
|
||||
, " do cn <- fetch otherCoin"
|
||||
, " return ()"
|
||||
]
|
||||
setFilesOfInterest [fetchTest]
|
||||
expectNoErrors
|
||||
expectedGraph fetchTest ( ExpectedGraph {expectedSubgraphs =
|
||||
[ExpectedSubGraph {expectedNodes = ["Create","Archive","ReducedCoin"]
|
||||
, expectedTplFields = ["owner","amount"]
|
||||
, expectedTemplate = "Coin"}]
|
||||
, expectedEdges = []})
|
||||
, testCase' "Exercise should add an edge" $ do
|
||||
exerciseTest <- makeModule "F"
|
||||
[ "template TT"
|
||||
, " with"
|
||||
, " owner : Party"
|
||||
, " where"
|
||||
, " signatory owner"
|
||||
, " choice Consume : ()"
|
||||
, " with coinId : ContractId Coin"
|
||||
, " controller owner"
|
||||
, " do exercise coinId Delete"
|
||||
, "template Coin"
|
||||
, " with"
|
||||
, " owner : Party"
|
||||
, " where"
|
||||
, " signatory owner"
|
||||
, " choice Delete : ()"
|
||||
, " controller owner"
|
||||
, " do return ()"
|
||||
]
|
||||
setFilesOfInterest [exerciseTest]
|
||||
expectNoErrors
|
||||
expectedGraph exerciseTest (ExpectedGraph
|
||||
[ ExpectedSubGraph { expectedNodes = ["Create", "Archive", "Delete"]
|
||||
, expectedTplFields = ["owner"]
|
||||
, expectedTemplate = "Coin"
|
||||
}
|
||||
, ExpectedSubGraph { expectedNodes = ["Create", "Consume", "Archive"]
|
||||
, expectedTplFields = ["owner"]
|
||||
, expectedTemplate = "TT"}]
|
||||
|
||||
[(ExpectedChoiceDetails {expectedConsuming = True
|
||||
, expectedName = "Consume"},
|
||||
ExpectedChoiceDetails {expectedConsuming = True
|
||||
, expectedName = "Delete"})
|
||||
])
|
||||
-- test case taken from #5726
|
||||
, testCase' "ExerciseByKey should add an edge" $ do
|
||||
exerciseByKeyTest <- makeModule "F"
|
||||
[ "template Ping"
|
||||
, " with"
|
||||
, " party : Party"
|
||||
, " where"
|
||||
, " signatory party"
|
||||
, " key party: Party"
|
||||
, " maintainer key"
|
||||
, ""
|
||||
, " nonconsuming choice ArchivePong : ()"
|
||||
, " with"
|
||||
, " pong : ContractId Pong"
|
||||
, " controller party"
|
||||
, " do exercise pong Archive"
|
||||
, ""
|
||||
, "template Pong"
|
||||
, " with"
|
||||
, " party : Party"
|
||||
, " where"
|
||||
, " signatory party"
|
||||
, ""
|
||||
, " nonconsuming choice ArchivePing : ()"
|
||||
, " with"
|
||||
, " pingParty : Party"
|
||||
, " controller party"
|
||||
, " do exerciseByKey @Ping pingParty Archive"
|
||||
]
|
||||
setFilesOfInterest [exerciseByKeyTest]
|
||||
expectNoErrors
|
||||
expectedGraph exerciseByKeyTest (ExpectedGraph
|
||||
[ ExpectedSubGraph { expectedNodes = ["Create", "ArchivePong", "Archive"]
|
||||
, expectedTplFields = ["party"]
|
||||
, expectedTemplate = "Ping"
|
||||
}
|
||||
, ExpectedSubGraph { expectedNodes = ["Create", "Archive", "ArchivePing"]
|
||||
, expectedTplFields = ["party"]
|
||||
, expectedTemplate = "Pong"}
|
||||
]
|
||||
[ (ExpectedChoiceDetails {expectedConsuming = False
|
||||
, expectedName = "ArchivePong"},
|
||||
ExpectedChoiceDetails {expectedConsuming = True
|
||||
, expectedName = "Archive"})
|
||||
, (ExpectedChoiceDetails {expectedConsuming = False
|
||||
, expectedName = "ArchivePing"},
|
||||
ExpectedChoiceDetails {expectedConsuming = True
|
||||
, expectedName = "Archive"})
|
||||
])
|
||||
, testCase' "Create on other template should be edge" $ do
|
||||
createTest <- makeModule "F"
|
||||
[ "template TT"
|
||||
, " with"
|
||||
, " owner : Party"
|
||||
, " where"
|
||||
, " signatory owner"
|
||||
, " choice CreateCoin : ContractId Coin"
|
||||
, " controller owner"
|
||||
, " do create Coin with owner"
|
||||
, "template Coin"
|
||||
, " with"
|
||||
, " owner : Party"
|
||||
, " where"
|
||||
, " signatory owner"
|
||||
]
|
||||
setFilesOfInterest [createTest]
|
||||
expectNoErrors
|
||||
expectedGraph createTest (ExpectedGraph
|
||||
{expectedSubgraphs = [ExpectedSubGraph { expectedNodes = ["Create","Archive"]
|
||||
, expectedTplFields = ["owner"]
|
||||
, expectedTemplate = "Coin"}
|
||||
,ExpectedSubGraph { expectedNodes = ["Create","Archive","CreateCoin"]
|
||||
, expectedTplFields = ["owner"]
|
||||
, expectedTemplate = "TT"
|
||||
}
|
||||
]
|
||||
, expectedEdges = [(ExpectedChoiceDetails {expectedConsuming = True, expectedName = "CreateCoin"}
|
||||
,ExpectedChoiceDetails {expectedConsuming = False, expectedName = "Create"})]})
|
||||
|
||||
]
|
||||
where
|
||||
testCase' = testCase Nothing Nothing
|
||||
-- | Suppress unused binding warning in case we run out of tests for open issues.
|
||||
_suppressUnusedWarning :: ()
|
||||
_suppressUnusedWarning = testCaseFails `seq` ()
|
||||
|
@ -1,248 +0,0 @@
|
||||
-- Copyright (c) 2023 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
||||
-- SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
-- cache-reset: 1
|
||||
|
||||
module DamlcVisualize (main) where
|
||||
|
||||
{- HLINT ignore "locateRunfiles/package_app" -}
|
||||
|
||||
import qualified "zip-archive" Codec.Archive.Zip as Zip
|
||||
import qualified Data.ByteString as BS
|
||||
import qualified Data.ByteString.Lazy as BSL
|
||||
import Data.Either.Combinators
|
||||
import DA.Bazel.Runfiles
|
||||
import qualified DA.Daml.LF.Proto3.Archive as Archive
|
||||
import Development.IDE.Core.API.Testing.Visualize
|
||||
import DA.Daml.LF.Ast
|
||||
( ExternalPackage(..)
|
||||
, World
|
||||
, initWorldSelf
|
||||
)
|
||||
import DA.Daml.LF.Reader (Dalfs(..), readDalfs)
|
||||
import DA.Test.Process
|
||||
import SdkVersion
|
||||
import System.Directory
|
||||
import System.Environment.Blank
|
||||
import System.FilePath
|
||||
import System.IO.Extra
|
||||
import Test.Tasty
|
||||
import Test.Tasty.HUnit
|
||||
|
||||
main :: IO ()
|
||||
main = do
|
||||
setEnv "TASTY_NUM_THREADS" "1" True
|
||||
damlc <- locateRunfiles (mainWorkspace </> "compiler" </> "damlc" </> exe "damlc")
|
||||
defaultMain $ testGroup "visualize"
|
||||
[ testCase "single package" $ withTempDir $ \dir -> do
|
||||
writeFileUTF8 (dir </> "daml.yaml") $ unlines
|
||||
[ "sdk-version: " <> sdkVersion
|
||||
, "name: foobar"
|
||||
, "source: ."
|
||||
, "version: 0.0.1"
|
||||
, "dependencies: [daml-stdlib, daml-prim]"
|
||||
]
|
||||
writeFileUTF8 (dir </> "A.daml") $ unlines
|
||||
[ "module A where"
|
||||
, "import B"
|
||||
, "template A with"
|
||||
, " p : Party"
|
||||
, " where"
|
||||
, " signatory p"
|
||||
, " choice CreateB : ContractId B"
|
||||
, " controller p"
|
||||
, " do create B with p"
|
||||
]
|
||||
writeFileUTF8 (dir </> "B.daml") $ unlines
|
||||
[ "module B where"
|
||||
, "template B with"
|
||||
, " p : Party"
|
||||
, " where"
|
||||
, " signatory p"
|
||||
]
|
||||
withCurrentDirectory dir $ callProcessSilent damlc ["build", "-o", "foobar.dar"]
|
||||
testFile (dir </> "foobar.dar") ExpectedGraph
|
||||
{ expectedSubgraphs =
|
||||
[ ExpectedSubGraph
|
||||
{ expectedNodes = ["Create", "Archive"]
|
||||
, expectedTplFields = ["p"]
|
||||
, expectedTemplate = "B"
|
||||
}
|
||||
, ExpectedSubGraph
|
||||
{ expectedNodes = ["Create", "Archive", "CreateB"]
|
||||
, expectedTplFields = ["p"]
|
||||
, expectedTemplate = "A"
|
||||
}
|
||||
]
|
||||
, expectedEdges =
|
||||
[ ( ExpectedChoiceDetails
|
||||
{ expectedConsuming = True
|
||||
, expectedName = "CreateB"
|
||||
}
|
||||
, ExpectedChoiceDetails
|
||||
{ expectedConsuming = False
|
||||
, expectedName = "Create"
|
||||
}
|
||||
)
|
||||
]
|
||||
}
|
||||
, multiPackageTests damlc
|
||||
]
|
||||
|
||||
multiPackageTests :: FilePath -> TestTree
|
||||
multiPackageTests damlc = testGroup "multiple packages"
|
||||
[ testCase "different module names" $ withTempDir $ \dir -> do
|
||||
createDirectory (dir </> "foobar-a")
|
||||
createDirectory (dir </> "foobar-b")
|
||||
writeFileUTF8 (dir </> "foobar-b" </> "daml.yaml") $ unlines
|
||||
[ "sdk-version: " <> sdkVersion
|
||||
, "name: foobar-b"
|
||||
, "source: ."
|
||||
, "version: 0.0.1"
|
||||
, "dependencies: [daml-stdlib, daml-prim]"
|
||||
]
|
||||
writeFileUTF8 (dir </> "foobar-b" </> "B.daml") $ unlines
|
||||
[ "module B where"
|
||||
, "template B with"
|
||||
, " p : Party"
|
||||
, " where"
|
||||
, " signatory p"
|
||||
]
|
||||
withCurrentDirectory (dir </> "foobar-b") $ callProcessSilent damlc ["build", "-o", "foobar-b.dar"]
|
||||
writeFileUTF8 (dir </> "foobar-a" </> "daml.yaml") $ unlines
|
||||
[ "sdk-version: " <> sdkVersion
|
||||
, "name: foobar-a"
|
||||
, "source: ."
|
||||
, "version: 0.0.1"
|
||||
, "dependencies: [daml-stdlib, daml-prim, " <>
|
||||
show (dir </> "foobar-b" </> "foobar-b.dar") <> "]"
|
||||
]
|
||||
writeFileUTF8 (dir </> "foobar-a" </> "A.daml") $ unlines
|
||||
[ "module A where"
|
||||
, "import B"
|
||||
, "template A with"
|
||||
, " p : Party"
|
||||
, " where"
|
||||
, " signatory p"
|
||||
, " choice CreateB : ContractId B"
|
||||
, " controller p"
|
||||
, " do create B with p"
|
||||
]
|
||||
withCurrentDirectory (dir </> "foobar-a") $ callProcessSilent damlc ["build", "-o", "foobar-a.dar"]
|
||||
testFile (dir </> "foobar-a" </> "foobar-a.dar") ExpectedGraph
|
||||
{ expectedSubgraphs =
|
||||
[ ExpectedSubGraph
|
||||
{ expectedNodes = ["Create", "Archive", "CreateB"]
|
||||
, expectedTplFields = ["p"]
|
||||
, expectedTemplate = "A"
|
||||
}
|
||||
, ExpectedSubGraph
|
||||
{ expectedNodes = ["Create", "Archive"]
|
||||
, expectedTplFields = ["p"]
|
||||
, expectedTemplate = "B"
|
||||
}
|
||||
]
|
||||
, expectedEdges =
|
||||
[ ( ExpectedChoiceDetails
|
||||
{ expectedConsuming = True
|
||||
, expectedName = "CreateB"
|
||||
}
|
||||
, ExpectedChoiceDetails
|
||||
{ expectedConsuming = False
|
||||
, expectedName = "Create"
|
||||
}
|
||||
)
|
||||
]
|
||||
}
|
||||
, testCase "same module names" $ withTempDir $ \dir -> do
|
||||
createDirectory (dir </> "foobar-a")
|
||||
createDirectory (dir </> "foobar-b")
|
||||
writeFileUTF8 (dir </> "foobar-b" </> "daml.yaml") $ unlines
|
||||
[ "sdk-version: " <> sdkVersion
|
||||
, "name: foobar-b"
|
||||
, "source: ."
|
||||
, "version: 0.0.1"
|
||||
, "dependencies: [daml-stdlib, daml-prim]"
|
||||
]
|
||||
writeFileUTF8 (dir </> "foobar-b" </> "A.daml") $ unlines
|
||||
[ "module A where"
|
||||
, "template T with"
|
||||
, " p : Party"
|
||||
, " where"
|
||||
, " signatory p"
|
||||
]
|
||||
withCurrentDirectory (dir </> "foobar-b") $ callProcessSilent damlc ["build", "-o", "foobar-b.dar"]
|
||||
writeFileUTF8 (dir </> "foobar-a" </> "daml.yaml") $ unlines
|
||||
[ "sdk-version: " <> sdkVersion
|
||||
, "name: foobar-a"
|
||||
, "source: ."
|
||||
, "version: 0.0.1"
|
||||
, "dependencies: [daml-stdlib, daml-prim, " <>
|
||||
show (dir </> "foobar-b" </> "foobar-b.dar") <> "]"
|
||||
]
|
||||
writeFileUTF8 (dir </> "foobar-a" </> "A.daml") $ unlines
|
||||
[ "module A where"
|
||||
, "import qualified \"foobar-b\" A as AA"
|
||||
, "template T with"
|
||||
, " p : Party"
|
||||
, " where"
|
||||
, " signatory p"
|
||||
, " choice CreateB : ContractId AA.T"
|
||||
, " controller p"
|
||||
, " do create AA.T with p"
|
||||
]
|
||||
withCurrentDirectory (dir </> "foobar-a") $ callProcessSilent damlc ["build", "-o", "foobar-a.dar"]
|
||||
testFile (dir </> "foobar-a" </> "foobar-a.dar") ExpectedGraph
|
||||
{ expectedSubgraphs =
|
||||
[ ExpectedSubGraph
|
||||
{ expectedNodes = ["Create", "Archive", "CreateB"]
|
||||
, expectedTplFields = ["p"]
|
||||
, expectedTemplate = "T"
|
||||
}
|
||||
, ExpectedSubGraph
|
||||
{ expectedNodes = ["Create", "Archive"]
|
||||
, expectedTplFields = ["p"]
|
||||
, expectedTemplate = "T"
|
||||
}
|
||||
]
|
||||
, expectedEdges =
|
||||
[ ( ExpectedChoiceDetails
|
||||
{ expectedConsuming = True
|
||||
, expectedName = "CreateB"
|
||||
}
|
||||
, ExpectedChoiceDetails
|
||||
{ expectedConsuming = False
|
||||
, expectedName = "Create"
|
||||
}
|
||||
)
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
testFile :: FilePath -> ExpectedGraph -> Assertion
|
||||
testFile dar expected = do
|
||||
darBytes <- BS.readFile dar
|
||||
dalfs <- either fail pure $ readDalfs $ Zip.toArchive (BSL.fromStrict darBytes)
|
||||
!world <- pure $ darToWorld dalfs
|
||||
whenLeft (graphTest world expected) $
|
||||
\(FailedGraphExpectation expected actual) ->
|
||||
assertFailure $ unlines
|
||||
[ "Failed graph expectation:"
|
||||
, "Expected:"
|
||||
, show expected
|
||||
, "Actual:"
|
||||
, show actual
|
||||
]
|
||||
|
||||
dalfBytesToPakage :: BSL.ByteString -> ExternalPackage
|
||||
dalfBytesToPakage bytes = case Archive.decodeArchive Archive.DecodeAsDependency $ BSL.toStrict bytes of
|
||||
Right (pkgId, pkg) -> ExternalPackage pkgId pkg
|
||||
Left err -> error (show err)
|
||||
|
||||
darToWorld :: Dalfs -> World
|
||||
darToWorld Dalfs{..} = case Archive.decodeArchive Archive.DecodeAsMain $ BSL.toStrict mainDalf of
|
||||
Right (_, mainPkg) -> initWorldSelf pkgs mainPkg
|
||||
Left err -> error (show err)
|
||||
where
|
||||
pkgs = map dalfBytesToPakage dalfs
|
||||
|
@ -742,25 +742,7 @@ executeCommandTests
|
||||
:: (Session () -> IO ())
|
||||
-> TestTree
|
||||
executeCommandTests run = testGroup "execute command"
|
||||
[ testCase "execute commands" $ run $ do
|
||||
main' <- openDoc' "Coin.daml" damlId $ T.unlines
|
||||
[ "module Coin where"
|
||||
, "template Coin"
|
||||
, " with"
|
||||
, " owner : Party"
|
||||
, " where"
|
||||
, " signatory owner"
|
||||
, " choice Delete : ()"
|
||||
, " controller owner"
|
||||
, " do return ()"
|
||||
]
|
||||
Just escapedFp <- pure $ uriToFilePath (main' ^. uri)
|
||||
actualDotString <- LSP.request SWorkspaceExecuteCommand $ ExecuteCommandParams
|
||||
Nothing "daml/damlVisualize" (Just (List [Aeson.String $ T.pack escapedFp]))
|
||||
let expectedDotString = "digraph G {\ncompound=true;\nrankdir=LR;\nsubgraph cluster_Coin{\nn0[label=Create][color=green]; \nn1[label=Archive][color=red]; \nn2[label=Delete][color=red]; \nlabel=<<table align = \"left\" border=\"0\" cellborder=\"0\" cellspacing=\"1\">\n<tr><td align=\"center\"><b>Coin</b></td></tr><tr><td align=\"left\">owner</td></tr> \n</table>>;color=blue\n}\n}\n"
|
||||
liftIO $ assertEqual "Visulization command" (Right expectedDotString) (_result actualDotString)
|
||||
closeDoc main'
|
||||
, testCase "Invalid commands result in error" $ run $ do
|
||||
[ testCase "Invalid commands result in error" $ run $ do
|
||||
main' <- openDoc' "Empty.daml" damlId $ T.unlines
|
||||
[ "module Empty where"
|
||||
]
|
||||
@ -769,10 +751,6 @@ executeCommandTests run = testGroup "execute command"
|
||||
Nothing "daml/NoCommand" (Just (List [Aeson.String $ T.pack escapedFp]))
|
||||
liftIO $ assertBool "Expected response error but got success" (isLeft $ _result actualDotString)
|
||||
closeDoc main'
|
||||
, testCase "Visualization command with no arguments" $ run $ do
|
||||
actualDotString <- LSP.request SWorkspaceExecuteCommand $ ExecuteCommandParams
|
||||
Nothing "daml/damlVisualize" Nothing
|
||||
liftIO $ assertBool "Expected response error but got Nothing" (isLeft $ _result actualDotString)
|
||||
]
|
||||
|
||||
-- | Do extreme things to the compiler service.
|
||||
|
@ -116,9 +116,6 @@ quickstartTests quickstartDir mvnDir getSandbox =
|
||||
-- Testing `daml new` and `daml build` is done when the QuickSandboxResource is build.
|
||||
subtest "daml damlc test --files" $
|
||||
callCommandSilentIn quickstartDir "daml damlc test --files daml/Main.daml"
|
||||
subtest "daml damlc visual-web" $
|
||||
callCommandSilentIn quickstartDir
|
||||
"daml damlc visual-web .daml/dist/quickstart-0.0.1.dar -o visual.html -b"
|
||||
subtest "mvn compile" $ do
|
||||
mvnDbTarball <-
|
||||
locateRunfiles
|
||||
|
Loading…
Reference in New Issue
Block a user