1{-# LANGUAGE RecordWildCards #-}
2{-# LANGUAGE ViewPatterns #-}
3{-# LANGUAGE LambdaCase #-}
4{-# LANGUAGE DataKinds #-}
5{-# LANGUAGE OverloadedStrings #-}
12import Control.Arrow (first)
13import Control.Monad.IO.Class (liftIO)
16import Data.List ((\\))
17import Data.Maybe (fromMaybe)
19import Data.Text (Text)
20import qualified Data.Text as T
21import Data.Time.Clock (getCurrentTime)
22import Data.Version (Version(..))
25import qualified Language.PureScript as P
26import qualified Language.PureScript.Docs as Docs
27import Language.PureScript.Docs.AsMarkdown (codeToString)
28import qualified Language.PureScript.Publish as Publish
29import qualified Language.PureScript.Publish.ErrorsWarnings as Publish
31import Web.Bower.PackageMeta (parsePackageName)
35publishOpts :: Publish.PublishOptions
36publishOpts = Publish.defaultPublishOptions
37 { Publish.publishGetVersion = return testVersion
38 , Publish.publishGetTagTime = const (liftIO getCurrentTime)
39 , Publish.publishWorkingTreeDirty = return ()
41 where testVersion = ("v999.0.0", Version [999,0,0] [])
44main = pushd "examples/docs" $ do
45 res <- Publish.preparePackage publishOpts
47 Left e -> Publish.printErrorToStdout e >> exitFailure
48 Right pkg@Docs.Package{..} ->
49 forM_ testCases $ \(P.moduleNameFromString -> mn, pragmas) ->
50 let mdl = takeJust ("module not found in docs: " ++ T.unpack (P.runModuleName mn))
51 (find ((==) mn . Docs.modName) pkgModules)
52 linksCtx = Docs.getLinksContext pkg
53 in forM_ pragmas (\a -> runAssertionIO a linksCtx mdl)
56takeJust :: String -> Maybe a -> a
57takeJust msg = fromMaybe (error msg)
60 -- | Assert that a particular declaration is documented with the given
62 = ShouldBeDocumented P.ModuleName Text [Text]
63 -- | Assert that a particular declaration is not documented
64 | ShouldNotBeDocumented P.ModuleName Text
65 -- | Assert that a particular declaration exists, but without a particular
67 | ChildShouldNotBeDocumented P.ModuleName Text Text
68 -- | Assert that a particular declaration has a particular type class
70 | ShouldBeConstrained P.ModuleName Text Text
71 -- | Assert that a particular typeclass declaration has a functional
73 | ShouldHaveFunDeps P.ModuleName Text [([Text],[Text])]
74 -- | Assert that a particular value declaration exists, and its type
75 -- satisfies the given predicate.
76 | ValueShouldHaveTypeSignature P.ModuleName Text (ShowFn (P.Type -> Bool))
77 -- | Assert that a particular type alias exists, and its corresponding
78 -- type, when rendered, matches a given string exactly
79 -- fields: module, type synonym name, expected type
80 | TypeSynonymShouldRenderAs P.ModuleName Text Text
81 -- | Assert that a documented declaration includes a documentation comment
82 -- containing a particular string
83 | ShouldHaveDocComment P.ModuleName Text Text
84 -- | Assert that there should be some declarations re-exported from a
85 -- particular module in a particular package.
86 | ShouldHaveReExport (Docs.InPackage P.ModuleName)
87 -- | Assert that a link to some specific declaration exists within the
88 -- rendered code for a declaration. Fields are: local module, local
89 -- declaration title, title of linked declaration, namespace of linked
90 -- declaration, destination of link.
91 | ShouldHaveLink P.ModuleName Text Text Docs.Namespace Docs.LinkLocation
94newtype ShowFn a = ShowFn a
96instance Show (ShowFn a) where
100 -- | A declaration was not documented, but should have been
101 = NotDocumented P.ModuleName Text
102 -- | A child declaration was not documented, but should have been
103 | ChildrenNotDocumented P.ModuleName Text [Text]
104 -- | A declaration was documented, but should not have been
105 | Documented P.ModuleName Text
106 -- | A child declaration was documented, but should not have been
107 | ChildDocumented P.ModuleName Text Text
108 -- | A constraint was missing.
109 | ConstraintMissing P.ModuleName Text Text
110 -- | A functional dependency was missing.
111 | FunDepMissing P.ModuleName Text [([Text], [Text])]
112 -- | A declaration had the wrong "type" (ie, value, type, type class)
113 -- Fields: declaration title, expected "type", actual "type".
114 | WrongDeclarationType P.ModuleName Text Text Text
115 -- | A value declaration had the wrong type (in the sense of "type
116 -- checking"), eg, because the inferred type was used when the explicit type
118 -- Fields: module name, declaration name, actual type.
119 | ValueDeclarationWrongType P.ModuleName Text P.Type
120 -- | A Type synonym has been rendered in an unexpected format
121 -- Fields: module name, declaration name, expected rendering, actual rendering
122 | TypeSynonymMismatch P.ModuleName Text Text Text
123 -- | A doc comment was not found or did not match what was expected
124 -- Fields: module name, expected substring, actual comments
125 | DocCommentMissing P.ModuleName Text (Maybe Text)
126 -- | A module was missing re-exports from a particular module.
127 -- Fields: module name, expected re-export, actual re-exports.
128 | ReExportMissing P.ModuleName (Docs.InPackage P.ModuleName) [Docs.InPackage P.ModuleName]
129 -- | Expected to find some other declaration mentioned in this declaration's
130 -- rendered code, but did not find anything.
131 -- Fields: module name, declaration title, title of declaration which was
132 -- expected but not found in.
133 | LinkedDeclarationMissing P.ModuleName Text Text
134 -- | Expected one link location for a declaration mentioned in some other
135 -- declaration's rendered code, but found a different one. Fields: module
136 -- name, title of the local declaration which links to some other
137 -- declaration, title of the linked declaration, expected location, actual
139 | BadLinkLocation P.ModuleName Text Text Docs.LinkLocation Docs.LinkLocation
144 | Fail AssertionFailure
147runAssertion :: Assertion -> Docs.LinksContext -> Docs.Module -> AssertionResult
148runAssertion assertion linksCtx Docs.Module{..} =
150 ShouldBeDocumented mn decl children ->
151 case findChildren decl (declarationsFor mn) of
153 Fail (NotDocumented mn decl)
154 Just actualChildren ->
155 case children \\ actualChildren of
157 cs -> Fail (ChildrenNotDocumented mn decl cs)
159 ShouldNotBeDocumented mn decl ->
160 case findChildren decl (declarationsFor mn) of
162 Fail (Documented mn decl)
166 ChildShouldNotBeDocumented mn decl child ->
167 case findChildren decl (declarationsFor mn) of
169 if child `elem` children
170 then Fail (ChildDocumented mn decl child)
173 Fail (NotDocumented mn decl)
175 ShouldBeConstrained mn decl tyClass ->
176 findDecl mn decl $ \Docs.Declaration{..} ->
178 Docs.ValueDeclaration ty ->
179 if checkConstrained ty tyClass
181 else Fail (ConstraintMissing mn decl tyClass)
183 Fail (WrongDeclarationType mn decl "value"
184 (Docs.declInfoToString declInfo))
186 ShouldHaveFunDeps mn decl fds ->
187 findDecl mn decl $ \Docs.Declaration{..} ->
189 Docs.TypeClassDeclaration _ _ fundeps ->
192 else Fail (FunDepMissing mn decl fds)
194 Fail (WrongDeclarationType mn decl "value"
195 (Docs.declInfoToString declInfo))
197 ValueShouldHaveTypeSignature mn decl (ShowFn tyPredicate) ->
198 findDecl mn decl $ \Docs.Declaration{..} ->
200 Docs.ValueDeclaration ty ->
204 (ValueDeclarationWrongType mn decl ty)
206 Fail (WrongDeclarationType mn decl "value"
207 (Docs.declInfoToString declInfo))
209 TypeSynonymShouldRenderAs mn decl expected ->
210 findDecl mn decl $ \Docs.Declaration{..} ->
212 Docs.TypeSynonymDeclaration [] ty ->
213 let actual = codeToString (Docs.renderType ty) in
214 if actual == expected
216 else Fail (TypeSynonymMismatch mn decl expected actual)
218 Fail (WrongDeclarationType mn decl "synonym"
219 (Docs.declInfoToString declInfo))
221 ShouldHaveDocComment mn decl expected ->
222 findDecl mn decl $ \Docs.Declaration{..} ->
223 if maybe False (expected `T.isInfixOf`) declComments
225 else Fail (DocCommentMissing mn decl declComments)
227 ShouldHaveReExport reExp ->
229 reExps = map fst modReExports
231 if reExp `elem` reExps
233 else Fail (ReExportMissing modName reExp reExps)
235 ShouldHaveLink mn decl destTitle destNs expectedLoc ->
236 findDecl mn decl $ \decl' ->
238 rendered = Docs.renderDeclaration decl'
240 case extract rendered destNs destTitle of
241 Just (Docs.linkLocation -> actualLoc) ->
242 if expectedLoc == actualLoc
244 else Fail (BadLinkLocation mn decl destTitle expectedLoc actualLoc)
246 Fail (LinkedDeclarationMissing mn decl destTitle)
252 else fromMaybe [] (lookup mn (map (first Docs.ignorePackage) modReExports))
255 fmap childrenTitles . find ((==) title . Docs.declTitle)
257 findDecl mn title f =
258 case find ((==) title . Docs.declTitle) (declarationsFor mn) of
260 Fail (NotDocumented mn title)
264 childrenTitles = map Docs.cdeclTitle . Docs.declChildren
266 extract :: Docs.RenderedCode -> Docs.Namespace -> Text -> Maybe Docs.DocLink
267 extract rc ns title = getFirst (Docs.outputWith (First . go) rc) >>= getLink
270 Docs.getLink linksCtx (P.moduleNameFromString "$DocsTest") ns title
272 Docs.Symbol ns' title' (Docs.Link containingMod)
273 | ns' == ns && title' == title -> Just containingMod
277checkConstrained :: P.Type -> Text -> Bool
278checkConstrained ty tyClass =
279 -- Note that we don't recurse on ConstrainedType if none of the constraints
280 -- match; this is by design, as constraints should be moved to the front
283 P.ConstrainedType cs _ | any (matches tyClass) cs ->
286 checkConstrained ty' tyClass
291 (==) className . P.runProperName . P.disqualify . P.constraintClass
293runAssertionIO :: Assertion -> Docs.LinksContext -> Docs.Module -> IO ()
294runAssertionIO assertion linksCtx mdl = do
295 putStrLn ("In " ++ T.unpack (P.runModuleName (Docs.modName mdl)) ++ ": " ++ show assertion)
296 case runAssertion assertion linksCtx mdl of
299 putStrLn ("Failed: " <> show reason)
302testCases :: [(Text, [Assertion])]
305 [ -- From dependencies
306 ShouldBeDocumented (n "Prelude") "Unit" []
307 , ShouldNotBeDocumented (n "Prelude") "unit"
310 , ShouldBeDocumented (n "Example2") "one" []
311 , ShouldNotBeDocumented (n "Example2") "two"
314 , ShouldHaveReExport (Docs.FromDep (pkg "purescript-prelude") (n "Prelude"))
315 , ShouldHaveReExport (Docs.Local (n "Example2"))
319 [ ShouldBeDocumented (n "Example2") "one" []
320 , ShouldBeDocumented (n "Example2") "two" []
322 , ShouldHaveLink (n "Example2") "one" "Int" Docs.TypeLevel (Docs.BuiltinModule (n "Prim"))
326 [ ShouldBeDocumented (n "UTF8") "thing" []
330 [ ShouldBeDocumented (n "Transitive2") "transitive3" []
334 [ ShouldBeDocumented (n "Prelude") "Boolean2" ["True"]
335 , ChildShouldNotBeDocumented (n "Prelude") "Boolean2" "False"
339 [ ShouldBeDocumented (n "Prelude") "Unit" []
340 , ShouldBeDocumented (n "DuplicateNames") "unit" []
341 , ShouldNotBeDocumented (n "Prelude") "unit"
345 [ ShouldBeDocumented (n "MultiVirtual1") "foo" []
346 , ShouldBeDocumented (n "MultiVirtual2") "bar" []
347 , ShouldBeDocumented (n "MultiVirtual2") "baz" []
351 [ ShouldBeDocumented (n "Clash1") "value" []
352 , ShouldBeDocumented (n "Clash1") "Type" []
353 , ShouldBeDocumented (n "Clash1") "TypeClass" ["typeClassMember"]
356 , ("SolitaryTypeClassMember",
357 [ ShouldBeDocumented (n "SomeTypeClass") "member" []
358 , ShouldNotBeDocumented (n "SomeTypeClass") "SomeClass"
359 , ShouldBeConstrained (n "SomeTypeClass") "member" "SomeClass"
362 , ("ReExportedTypeClass",
363 [ ShouldBeDocumented (n "SomeTypeClass") "SomeClass" ["member"]
366 , ("TypeClassWithoutMembers",
367 [ ShouldBeDocumented (n "TypeClassWithoutMembersIntermediate") "SomeClass" []
368 , ChildShouldNotBeDocumented (n "TypeClassWithoutMembersIntermediate") "SomeClass" "member"
371 , ("TypeClassWithFunDeps",
372 [ ShouldHaveFunDeps (n "TypeClassWithFunDeps") "TypeClassWithFunDeps" [(["a","b"], ["c"]), (["c"], ["d","e"])]
376 [ ShouldBeDocumented (n "NewOperators2") "(>>>)" []
379 , ("ExplicitTypeSignatures",
380 [ ValueShouldHaveTypeSignature (n "ExplicitTypeSignatures") "explicit" (ShowFn (hasTypeVar "something"))
381 , ValueShouldHaveTypeSignature (n "ExplicitTypeSignatures") "anInt" (ShowFn (P.tyInt ==))
382 , ValueShouldHaveTypeSignature (n "ExplicitTypeSignatures") "aNumber" (ShowFn (P.tyNumber ==))
385 , ("ConstrainedArgument",
386 [ TypeSynonymShouldRenderAs (n "ConstrainedArgument") "WithoutArgs" "forall a. (Partial => a) -> a"
387 , TypeSynonymShouldRenderAs (n "ConstrainedArgument") "WithArgs" "forall a. (Foo a => a) -> a"
388 , TypeSynonymShouldRenderAs (n "ConstrainedArgument") "MultiWithoutArgs" "forall a. ((Partial, Partial) => a) -> a"
389 , TypeSynonymShouldRenderAs (n "ConstrainedArgument") "MultiWithArgs" "forall a b. ((Foo a, Foo b) => a) -> a"
393 [ ValueShouldHaveTypeSignature (n "TypeOpAliases") "test1" (renderedType "forall a b. a ~> b")
394 , ValueShouldHaveTypeSignature (n "TypeOpAliases") "test2" (renderedType "forall a b c. a ~> b ~> c")
395 , ValueShouldHaveTypeSignature (n "TypeOpAliases") "test3" (renderedType "forall a b c d. a ~> (b ~> c) ~> d")
396 , ValueShouldHaveTypeSignature (n "TypeOpAliases") "test4" (renderedType "forall a b c d. ((a ~> b) ~> c) ~> d")
397 , ValueShouldHaveTypeSignature (n "TypeOpAliases") "third" (renderedType "forall a b c. a × b × c -> c")
401 [ ShouldHaveDocComment (n "DocComments") "example" " example == 0"
406 n = P.moduleNameFromString
407 pkg str = let Right p = parsePackageName str in p
410 getAny . P.everythingOnTypes (<>) (Any . isVar varName)
412 isVar varName (P.TypeVar name) | varName == T.unpack name = True
415 renderedType expected =
416 ShowFn $ \ty -> codeToString (Docs.renderType ty) == expected
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list