1{-# LANGUAGE RecordWildCards #-}
2{-# LANGUAGE ViewPatterns #-}
3{-# LANGUAGE DataKinds #-}
4{-# LANGUAGE OverloadedStrings #-}
11import Control.Arrow (first)
12import Control.Monad.IO.Class (liftIO)
15import Data.List ((\\))
16import Data.Maybe (fromMaybe)
18import Data.Text (Text)
19import qualified Data.Text as T
20import Data.Time.Clock (getCurrentTime)
21import Data.Version (Version(..))
24import qualified Language.PureScript as P
25import qualified Language.PureScript.Docs as Docs
26import Language.PureScript.Docs.AsMarkdown (codeToString)
27import qualified Language.PureScript.Publish as Publish
28import qualified Language.PureScript.Publish.ErrorsWarnings as Publish
30import Web.Bower.PackageMeta (parsePackageName)
34publishOpts :: Publish.PublishOptions
35publishOpts = Publish.defaultPublishOptions
36 { Publish.publishGetVersion = return testVersion
37 , Publish.publishGetTagTime = const (liftIO getCurrentTime)
38 , Publish.publishWorkingTreeDirty = return ()
40 where testVersion = ("v999.0.0", Version [999,0,0] [])
43main = pushd "examples/docs" $ do
44 res <- Publish.preparePackage publishOpts
46 Left e -> Publish.printErrorToStdout e >> exitFailure
47 Right Docs.Package{..} ->
48 forM_ testCases $ \(P.moduleNameFromString -> mn, pragmas) ->
49 let mdl = takeJust ("module not found in docs: " ++ T.unpack (P.runModuleName mn))
50 (find ((==) mn . Docs.modName) pkgModules)
51 in forM_ pragmas (`runAssertionIO` mdl)
54takeJust :: String -> Maybe a -> a
55takeJust msg = fromMaybe (error msg)
58 -- | Assert that a particular declaration is documented with the given
60 = ShouldBeDocumented P.ModuleName Text [Text]
61 -- | Assert that a particular declaration is not documented
62 | ShouldNotBeDocumented P.ModuleName Text
63 -- | Assert that a particular declaration exists, but without a particular
65 | ChildShouldNotBeDocumented P.ModuleName Text Text
66 -- | Assert that a particular declaration has a particular type class
68 | ShouldBeConstrained P.ModuleName Text Text
69 -- | Assert that a particular typeclass declaration has a functional
71 | ShouldHaveFunDeps P.ModuleName Text [([Text],[Text])]
72 -- | Assert that a particular value declaration exists, and its type
73 -- satisfies the given predicate.
74 | ValueShouldHaveTypeSignature P.ModuleName Text (ShowFn (P.Type -> Bool))
75 -- | Assert that a particular type alias exists, and its corresponding
76 -- type, when rendered, matches a given string exactly
77 -- fields: module, type synonym name, expected type
78 | TypeSynonymShouldRenderAs P.ModuleName Text Text
79 -- | Assert that a documented declaration includes a documentation comment
80 -- containing a particular string
81 | ShouldHaveDocComment P.ModuleName Text Text
82 -- | Assert that there should be some declarations re-exported from a
83 -- particular module in a particular package.
84 | ShouldHaveReExport (Docs.InPackage P.ModuleName)
87newtype ShowFn a = ShowFn a
89instance Show (ShowFn a) where
93 -- | A declaration was not documented, but should have been
94 = NotDocumented P.ModuleName Text
95 -- | A child declaration was not documented, but should have been
96 | ChildrenNotDocumented P.ModuleName Text [Text]
97 -- | A declaration was documented, but should not have been
98 | Documented P.ModuleName Text
99 -- | A child declaration was documented, but should not have been
100 | ChildDocumented P.ModuleName Text Text
101 -- | A constraint was missing.
102 | ConstraintMissing P.ModuleName Text Text
103 -- | A functional dependency was missing.
104 | FunDepMissing P.ModuleName Text [([Text], [Text])]
105 -- | A declaration had the wrong "type" (ie, value, type, type class)
106 -- Fields: declaration title, expected "type", actual "type".
107 | WrongDeclarationType P.ModuleName Text Text Text
108 -- | A value declaration had the wrong type (in the sense of "type
109 -- checking"), eg, because the inferred type was used when the explicit type
111 -- Fields: module name, declaration name, actual type.
112 | ValueDeclarationWrongType P.ModuleName Text P.Type
113 -- | A Type synonym has been rendered in an unexpected format
114 -- Fields: module name, declaration name, expected rendering, actual rendering
115 | TypeSynonymMismatch P.ModuleName Text Text Text
116 -- | A doc comment was not found or did not match what was expected
117 -- Fields: module name, expected substring, actual comments
118 | DocCommentMissing P.ModuleName Text (Maybe Text)
119 -- | A module was missing re-exports from a particular module.
120 -- Fields: module name, expected re-export, actual re-exports.
121 | ReExportMissing P.ModuleName (Docs.InPackage P.ModuleName) [Docs.InPackage P.ModuleName]
126 | Fail AssertionFailure
129runAssertion :: Assertion -> Docs.Module -> AssertionResult
130runAssertion assertion Docs.Module{..} =
132 ShouldBeDocumented mn decl children ->
133 case findChildren decl (declarationsFor mn) of
135 Fail (NotDocumented mn decl)
136 Just actualChildren ->
137 case children \\ actualChildren of
139 cs -> Fail (ChildrenNotDocumented mn decl cs)
141 ShouldNotBeDocumented mn decl ->
142 case findChildren decl (declarationsFor mn) of
144 Fail (Documented mn decl)
148 ChildShouldNotBeDocumented mn decl child ->
149 case findChildren decl (declarationsFor mn) of
151 if child `elem` children
152 then Fail (ChildDocumented mn decl child)
155 Fail (NotDocumented mn decl)
157 ShouldBeConstrained mn decl tyClass ->
158 findDecl mn decl $ \Docs.Declaration{..} ->
160 Docs.ValueDeclaration ty ->
161 if checkConstrained ty tyClass
163 else Fail (ConstraintMissing mn decl tyClass)
165 Fail (WrongDeclarationType mn decl "value"
166 (Docs.declInfoToString declInfo))
168 ShouldHaveFunDeps mn decl fds ->
169 findDecl mn decl $ \Docs.Declaration{..} ->
171 Docs.TypeClassDeclaration _ _ fundeps ->
174 else Fail (FunDepMissing mn decl fds)
176 Fail (WrongDeclarationType mn decl "value"
177 (Docs.declInfoToString declInfo))
179 ValueShouldHaveTypeSignature mn decl (ShowFn tyPredicate) ->
180 findDecl mn decl $ \Docs.Declaration{..} ->
182 Docs.ValueDeclaration ty ->
186 (ValueDeclarationWrongType mn decl ty)
188 Fail (WrongDeclarationType mn decl "value"
189 (Docs.declInfoToString declInfo))
191 TypeSynonymShouldRenderAs mn decl expected ->
192 findDecl mn decl $ \Docs.Declaration{..} ->
194 Docs.TypeSynonymDeclaration [] ty ->
195 let actual = codeToString (Docs.renderType ty) in
196 if actual == expected
198 else Fail (TypeSynonymMismatch mn decl expected actual)
200 Fail (WrongDeclarationType mn decl "synonym"
201 (Docs.declInfoToString declInfo))
203 ShouldHaveDocComment mn decl expected ->
204 findDecl mn decl $ \Docs.Declaration{..} ->
205 if maybe False (expected `T.isInfixOf`) declComments
207 else Fail (DocCommentMissing mn decl declComments)
209 ShouldHaveReExport reExp ->
211 reExps = map fst modReExports
213 if reExp `elem` reExps
215 else Fail (ReExportMissing modName reExp reExps)
221 else fromMaybe [] (lookup mn (map (first Docs.ignorePackage) modReExports))
224 fmap childrenTitles . find ((==) title . Docs.declTitle)
226 findDecl mn title f =
227 case find ((==) title . Docs.declTitle) (declarationsFor mn) of
229 Fail (NotDocumented mn title)
233 childrenTitles = map Docs.cdeclTitle . Docs.declChildren
235checkConstrained :: P.Type -> Text -> Bool
236checkConstrained ty tyClass =
237 -- Note that we don't recurse on ConstrainedType if none of the constraints
238 -- match; this is by design, as constraints should be moved to the front
241 P.ConstrainedType cs _ | any (matches tyClass) cs ->
244 checkConstrained ty' tyClass
249 (==) className . P.runProperName . P.disqualify . P.constraintClass
251runAssertionIO :: Assertion -> Docs.Module -> IO ()
252runAssertionIO assertion mdl = do
253 putStrLn ("In " ++ T.unpack (P.runModuleName (Docs.modName mdl)) ++ ": " ++ show assertion)
254 case runAssertion assertion mdl of
257 putStrLn ("Failed: " <> show reason)
260testCases :: [(Text, [Assertion])]
263 [ -- From dependencies
264 ShouldBeDocumented (n "Prelude") "Unit" []
265 , ShouldNotBeDocumented (n "Prelude") "unit"
268 , ShouldBeDocumented (n "Example2") "one" []
269 , ShouldNotBeDocumented (n "Example2") "two"
272 , ShouldHaveReExport (Docs.FromDep (pkg "purescript-prelude") (n "Prelude"))
273 , ShouldHaveReExport (Docs.Local (n "Example2"))
277 [ ShouldBeDocumented (n "Example2") "one" []
278 , ShouldBeDocumented (n "Example2") "two" []
282 [ ShouldBeDocumented (n "UTF8") "thing" []
286 [ ShouldBeDocumented (n "Transitive2") "transitive3" []
290 [ ShouldBeDocumented (n "Prelude") "Boolean2" ["True"]
291 , ChildShouldNotBeDocumented (n "Prelude") "Boolean2" "False"
295 [ ShouldBeDocumented (n "Prelude") "Unit" []
296 , ShouldBeDocumented (n "DuplicateNames") "unit" []
297 , ShouldNotBeDocumented (n "Prelude") "unit"
301 [ ShouldBeDocumented (n "MultiVirtual1") "foo" []
302 , ShouldBeDocumented (n "MultiVirtual2") "bar" []
303 , ShouldBeDocumented (n "MultiVirtual2") "baz" []
307 [ ShouldBeDocumented (n "Clash1") "value" []
308 , ShouldBeDocumented (n "Clash1") "Type" []
309 , ShouldBeDocumented (n "Clash1") "TypeClass" ["typeClassMember"]
312 , ("SolitaryTypeClassMember",
313 [ ShouldBeDocumented (n "SomeTypeClass") "member" []
314 , ShouldNotBeDocumented (n "SomeTypeClass") "SomeClass"
315 , ShouldBeConstrained (n "SomeTypeClass") "member" "SomeClass"
318 , ("ReExportedTypeClass",
319 [ ShouldBeDocumented (n "SomeTypeClass") "SomeClass" ["member"]
322 , ("TypeClassWithoutMembers",
323 [ ShouldBeDocumented (n "TypeClassWithoutMembersIntermediate") "SomeClass" []
324 , ChildShouldNotBeDocumented (n "TypeClassWithoutMembersIntermediate") "SomeClass" "member"
327 , ("TypeClassWithFunDeps",
328 [ ShouldHaveFunDeps (n "TypeClassWithFunDeps") "TypeClassWithFunDeps" [(["a","b"], ["c"]), (["c"], ["d","e"])]
332 [ ShouldBeDocumented (n "NewOperators2") "(>>>)" []
335 , ("ExplicitTypeSignatures",
336 [ ValueShouldHaveTypeSignature (n "ExplicitTypeSignatures") "explicit" (ShowFn (hasTypeVar "something"))
337 , ValueShouldHaveTypeSignature (n "ExplicitTypeSignatures") "anInt" (ShowFn (P.tyInt ==))
338 , ValueShouldHaveTypeSignature (n "ExplicitTypeSignatures") "aNumber" (ShowFn (P.tyNumber ==))
339 , ValueShouldHaveTypeSignature (n "ExplicitTypeSignatures") "nestedForAll" (renderedType "forall c. (forall a b. c)")
342 , ("ConstrainedArgument",
343 [ TypeSynonymShouldRenderAs (n "ConstrainedArgument") "WithoutArgs" "forall a. (Partial => a) -> a"
344 , TypeSynonymShouldRenderAs (n "ConstrainedArgument") "WithArgs" "forall a. (Foo a => a) -> a"
345 , TypeSynonymShouldRenderAs (n "ConstrainedArgument") "MultiWithoutArgs" "forall a. ((Partial, Partial) => a) -> a"
346 , TypeSynonymShouldRenderAs (n "ConstrainedArgument") "MultiWithArgs" "forall a b. ((Foo a, Foo b) => a) -> a"
350 [ ValueShouldHaveTypeSignature (n "TypeOpAliases") "test1" (renderedType "forall a b. a ~> b")
351 , ValueShouldHaveTypeSignature (n "TypeOpAliases") "test2" (renderedType "forall a b c. a ~> b ~> c")
352 , ValueShouldHaveTypeSignature (n "TypeOpAliases") "test3" (renderedType "forall a b c d. a ~> (b ~> c) ~> d")
353 , ValueShouldHaveTypeSignature (n "TypeOpAliases") "test4" (renderedType "forall a b c d. ((a ~> b) ~> c) ~> d")
354 , ValueShouldHaveTypeSignature (n "TypeOpAliases") "third" (renderedType "forall a b c. a × b × c -> c")
358 [ ShouldHaveDocComment (n "DocComments") "example" " example == 0"
363 n = P.moduleNameFromString . T.pack
364 pkg str = let Right p = parsePackageName str in p
367 getAny . P.everythingOnTypes (<>) (Any . isVar varName)
369 isVar varName (P.TypeVar name) | varName == T.unpack name = True
372 renderedType expected =
373 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