1
1
mirror of https://github.com/github/semantic.git synced 2024-12-01 09:15:01 +03:00
semantic/test/Examples.hs

312 lines
12 KiB
Haskell
Raw Normal View History

2020-01-24 23:41:49 +03:00
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-}
2020-01-24 23:41:49 +03:00
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TypeApplications #-}
{-# OPTIONS_GHC -O1 #-}
{-# OPTIONS_GHC -Wno-unused-top-binds -Wno-unused-imports #-}
2019-12-19 22:30:10 +03:00
module Main (main) where
2020-01-26 19:21:59 +03:00
import qualified Analysis.File as File
2019-10-01 18:17:57 +03:00
import Control.Carrier.Parse.Measured
2019-12-11 23:50:15 +03:00
import Control.Carrier.Reader
import Control.Concurrent.Async (forConcurrently)
import Control.Exception (displayException)
import Control.Lens
2018-09-18 23:00:31 +03:00
import Control.Monad
import Data.Blob
import Data.Foldable
2020-01-24 23:41:49 +03:00
import Data.Int
import Data.Language (LanguageMode (..), PerLanguageModes (..))
import Data.List
2019-12-17 02:11:37 +03:00
import qualified Data.Text as Text
2019-12-17 00:58:50 +03:00
import Data.Traversable
import System.FilePath.Glob
2019-09-20 18:34:09 +03:00
import System.Path ((</>))
import qualified System.Path as Path
import qualified System.Process as Process
2019-12-17 00:58:50 +03:00
import qualified Test.Tasty as Tasty
import qualified Test.Tasty.HUnit as HUnit
import Data.Flag
import Proto.Semantic as P hiding (Blob, BlobPair)
import Proto.Semantic_Fields as P
2019-11-12 21:50:26 +03:00
import Semantic.Api.Symbols (parseSymbols)
import Semantic.Config as Config
import Semantic.Task
import Semantic.Task.Files
2019-12-19 22:15:48 +03:00
data LanguageExample =
LanguageExample
{ languageName :: String
, languageExtension :: String
, languageSkips :: [Path.RelFile]
, languageDirSkips :: [Path.RelDir]
}
deriving (Eq, Show)
le :: String -> String -> [Path.RelFile] -> [Path.RelDir] -> LanguageExample
le = LanguageExample
examples :: [LanguageExample]
examples =
2020-01-16 00:44:29 +03:00
[ le "go" "**/*.go" goFileSkips goDirSkips
, le "python" "**/*.py" mempty mempty
, le "ruby" "**/*.rb" rubySkips mempty
, le "typescript" "**/*.[jt]s" typescriptSkips mempty
, le "typescript" "**/*.[jt]sx" tsxSkips mempty
2019-12-19 22:15:48 +03:00
]
goFileSkips :: [Path.RelFile]
2019-12-19 22:30:10 +03:00
goFileSkips = Path.relPath <$>
2019-12-18 19:31:53 +03:00
[
2019-12-19 00:52:56 +03:00
-- Super slow
"go/src/vendor/golang_org/x/text/unicode/norm/tables.go"
, "go/src/vendor/golang_org/x/text/unicode/bidi/tables.go"
2019-12-19 01:29:01 +03:00
, "go/src/vendor/golang_org/x/net/idna/tables.go"
2019-12-19 00:52:56 +03:00
, "go/src/cmd/vendor/golang.org/x/arch/x86/x86asm/tables.go"
, "moby/vendor/golang.org/x/text/unicode/norm/tables9.0.0.go"
, "moby/vendor/golang.org/x/text/unicode/norm/tables10.0.0.go"
-- Assignment timeouts
, "go/src/cmd/compile/internal/gc/constFold_test.go"
2019-12-19 22:15:48 +03:00
, "go/src/cmd/compile/internal/gc/testdata/arithConst.go"
, "moby/vendor/github.com/docker/swarmkit/api/types.pb.go"
, "moby/vendor/github.com/docker/swarmkit/api/control.pb.go"
-- Parser timeouts
, "moby/vendor/github.com/ugorji/go/codec/fast-path.generated.go"
2019-12-19 01:29:01 +03:00
-- Parse errors
, "go/src/math/big/arith.go" -- Unhandled identifier character: 'ŝ'
, "go/src/cmd/go/testdata/src/badpkg/x.go"
, "go/src/cmd/go/testdata/src/notest/hello.go"
2019-12-19 22:15:48 +03:00
, "go/src/cmd/vet/testdata/deadcode.go"
2020-01-14 23:37:39 +03:00
, "go/src/cmd/vet/testdata/testingpkg/tests_test.go"
2019-12-19 01:29:01 +03:00
, "moby/vendor/github.com/beorn7/perks/quantile/stream.go" -- Unhandled identifier character: 'ƒ'
2019-12-19 22:15:48 +03:00
2020-01-14 23:37:39 +03:00
-- A la carte struggles on these
, "src/cmd/go/testdata/src/notest/hello.go" -- a la carte chokes on ParseError
, "go/src/cmd/asm/internal/asm/parse.go" -- a la carte spans are off on line 1124
2019-12-18 03:00:46 +03:00
]
2019-12-19 22:15:48 +03:00
goDirSkips :: [Path.RelDir]
2019-12-19 22:30:10 +03:00
goDirSkips = Path.relDir <$>
2019-12-19 22:15:48 +03:00
[ "go/src/cmd/compile/internal/ssa"
, "go/test/fixedbugs"
, "go/test/syntax"
, "go/test/method4.dir"
, "go/test"
]
2019-12-19 22:30:10 +03:00
rubySkips :: [Path.RelFile]
rubySkips = Path.relFile <$>
[
-- Doesn't parse b/c of issue with r<<i
"ruby_spec/core/enumerable/shared/inject.rb"
2019-12-19 22:30:10 +03:00
-- Doesn't parse
, "ruby_spec/language/string_spec.rb"
2019-12-20 20:40:00 +03:00
, "ruby_spec/language/fixtures/freeze_magic_comment_required_diff_enc.rb"
2020-01-15 20:26:39 +03:00
, "ruby_spec/command_line/fixtures/freeze_flag_required_diff_enc.rb"
, "ruby_spec/command_line/fixtures/bad_syntax.rb"
2019-12-19 22:30:10 +03:00
-- Can't detect method calls inside heredoc bodies with precise ASTs
, "ruby_spec/core/argf/readpartial_spec.rb"
, "ruby_spec/core/process/exec_spec.rb"
-- These are known differences between precise and a la carte (usually precise is producing better data) that we aren't going to fix.
, "ruby_spec/language/def_spec.rb"
, "ruby_spec/language/block_spec.rb"
, "ruby_spec/language/method_spec.rb"
, "ruby_spec/language/lambda_spec.rb"
2019-12-19 22:30:10 +03:00
]
2019-12-19 00:52:56 +03:00
2019-12-20 20:40:00 +03:00
tsxSkips :: [Path.RelFile]
tsxSkips = Path.relFile <$>
[
]
typescriptSkips :: [Path.RelFile]
typescriptSkips = Path.relFile <$>
[
-- Assignment timeouts
"npm/node_modules/request/node_modules/http-signature/node_modules/sshpk/node_modules/tweetnacl/nacl-fast.js"
, "npm/node_modules/cli-table2/test/cell-test.js"
, "npm/node_modules/request/node_modules/har-validator/node_modules/ajv/dist/regenerator.min.js"
, "npm/node_modules/request/node_modules/har-validator/node_modules/ajv/dist/ajv.bundle.js"
, "npm/node_modules/request/node_modules/har-validator/node_modules/ajv/dist/ajv.min.js"
, "npm/node_modules/request/node_modules/har-validator/node_modules/ajv/dist/nodent.min.js"
, "npm/node_modules/bluebird/js/browser/bluebird.js"
, "npm/node_modules/bluebird/js/browser/bluebird.min.js"
, "npm/node_modules/bluebird/js/browser/bluebird.core.js"
, "npm/node_modules/cli-table2/node_modules/lodash/index.js"
, "npm/node_modules/cli-table2/node_modules/lodash/index.js"
2020-01-14 23:37:39 +03:00
-- Parse errors
, "npm/node_modules/slide/lib/async-map-ordered.js"
2019-12-20 20:40:00 +03:00
]
buildExamples :: TaskSession -> LanguageExample -> Path.RelDir -> IO Tasty.TestTree
buildExamples session lang tsDir = do
2019-12-19 22:15:48 +03:00
let fileSkips = fmap (tsDir </>) (languageSkips lang)
dirSkips = fmap (tsDir </>) (languageDirSkips lang)
files <- globDir1 (compile (languageExtension lang)) (Path.toString tsDir)
2019-12-19 22:15:48 +03:00
let paths = filter (\x -> Path.takeDirectory x `notElem` dirSkips) . filter (`notElem` fileSkips) $ Path.relFile <$> files
2019-12-14 00:44:49 +03:00
trees <- for paths $ \file -> do
pure . HUnit.testCaseSteps (Path.toString file) $ \step -> do
2019-12-06 21:05:09 +03:00
-- Use alacarte language mode
step "a la carte"
alacarte <- runTask session (runParse (parseSymbolsFilePath aLaCarteLanguageModes file))
2019-12-06 21:05:09 +03:00
assertOK "a la carte" alacarte
2019-12-06 21:05:09 +03:00
-- Test out precise language mode
step "precise"
precise <- runTask session (runParse (parseSymbolsFilePath preciseLanguageModes file))
2019-12-06 21:05:09 +03:00
assertOK "precise" precise
2019-12-06 21:05:09 +03:00
-- Compare the two
step "compare"
2019-12-18 03:00:46 +03:00
assertMatch alacarte precise
2019-12-09 23:47:19 +03:00
pure (Tasty.testGroup (languageName lang) trees)
where
2019-12-06 21:05:09 +03:00
assertOK msg = either (\e -> HUnit.assertFailure (msg <> " failed to parse" <> show e)) (refuteErrors msg)
refuteErrors msg a = case toList (a^.files) of
[x] | (e:_) <- toList (x^.errors) -> HUnit.assertFailure (msg <> " parse errors " <> show e)
2020-01-24 23:41:49 +03:00
_ -> pure ()
2019-12-06 21:05:09 +03:00
2019-12-18 03:00:46 +03:00
assertMatch a b = case (a, b) of
(Right a, Right b) -> case (toList (a^.files), toList (b^.files)) of
([x], [y]) | e1:_ <- toList (x^.errors)
, e2:_ <- toList (y^.errors)
2019-12-18 03:00:46 +03:00
-> HUnit.assertFailure ("Parse errors (both) " <> show e1 <> show e2)
(_, [y]) | e:_ <- toList (y^.errors)
2019-12-06 21:05:09 +03:00
-> HUnit.assertFailure ("Parse errors (precise) " <> show e)
([x], _) | e:_ <- toList (x^.errors)
-> HUnit.assertFailure ("Parse errors (a la carte) " <> show e)
([x], [y]) -> do
-- Check paths
HUnit.assertEqual "Expected paths to be equal" (x^.path) (y^.path)
-- Check symbols
2019-12-17 02:11:37 +03:00
let aLaCarteSymbols = sort . filterALaCarteSymbols (languageName lang) $ toListOf (symbols . traverse . symbol) x
preciseSymbols = sort . filterALaCarteSymbols (languageName lang) $ toListOf (symbols . traverse . symbol) y
2019-12-17 02:11:37 +03:00
delta = aLaCarteSymbols \\ preciseSymbols
invDelta = preciseSymbols \\ aLaCarteSymbols
2019-12-06 21:05:09 +03:00
msg = "Found in a la carte, but not precise: "
<> show delta
<> "\n"
<> "Found in precise but not a la carte: "
<> show invDelta
2019-12-06 21:05:09 +03:00
<> "\n"
2019-12-17 02:11:37 +03:00
<> "Expected: " <> show aLaCarteSymbols <> "\n"
<> "But got:" <> show preciseSymbols
HUnit.assertBool ("Expected symbols to be equal.\n" <> msg) (null delta)
HUnit.assertBool ("Expected symbols to be equal.\n" <> msg) (null invDelta)
-- Check details
2020-01-11 02:50:01 +03:00
let aLaCarteSymbols = sortOn sSym . filter (okALaCarteSymbol (languageName lang) . view symbol) $ toList (x^.symbols)
preciseSymbols = sortOn sSym . filter (okALaCarteSymbol (languageName lang) . view symbol) $ toList (y^.symbols)
for_ (zip aLaCarteSymbols preciseSymbols) $ \ (left, right) -> do
let lineNo = ":" <> show (left^.P.span^.start^.line)
2020-01-11 02:50:01 +03:00
-- lSpan = " [" <> show (startRow left) <> ", " <> show (left^.P.span^.start^.column) <> "]"
-- rSpan = " [" <> show (startRow right) <> ", " <> show (right^.P.span^.start^.column) <> "]"
HUnit.assertEqual (Text.unpack (x^.path) <> lineNo) (left^.symbol) (right^.symbol)
2020-01-14 02:19:00 +03:00
HUnit.assertEqual (Text.unpack (x^.path) <> lineNo) (Text.unpack (left^.symbol) <> span left) (Text.unpack (right^.symbol) <> span right)
2020-01-14 23:37:39 +03:00
-- HUnit.assertEqual (Text.unpack (x^.path) <> lineNo) (left^.line) (right^.line)
2020-01-15 01:11:16 +03:00
-- HUnit.assertBool (Text.unpack (x^.path) <> lineNo) (Text.isPrefixOf (left^.line) (right^.line))
2020-01-15 20:26:39 +03:00
-- if left^.kind == "Method"
-- then HUnit.assertEqual (Text.unpack (x^.path) <> lineNo) (left^.line) (right^.line)
-- -- -- then HUnit.assertBool (Text.unpack (x^.path) <> lineNo) (Text.isPrefixOf (left^.line) (right^.line))
-- else pure ()
2019-12-06 21:05:09 +03:00
_ -> HUnit.assertFailure "Expected 1 file in each response"
(Left e1, Left e2) -> HUnit.assertFailure ("Unable to parse (both)" <> show (displayException e1) <> show (displayException e2))
(_, Left e) -> HUnit.assertFailure ("Unable to parse (precise)" <> show (displayException e))
(Left e, _) -> HUnit.assertFailure ("Unable to parse (a la carte)" <> show (displayException e))
2020-01-14 02:19:00 +03:00
sSym x = SortableSymbol (x^.symbol) (x^.P.span^.start^.line) (x^.P.span^.start^.column) (x^.P.span^.end^.line) (x^.P.span^.end^.column)
span x = " [" <> show (x^.P.span^.start^.line) <> ", " <> show (x^.P.span^.start^.column) <>
" - " <> show (x^.P.span^.end^.line) <> ", " <> show (x^.P.span^.end^.column) <> "]"
2020-01-11 02:50:01 +03:00
2020-01-14 02:19:00 +03:00
data SortableSymbol = SortableSymbol Text.Text Int32 Int32 Int32 Int32
2020-01-11 02:50:01 +03:00
deriving (Eq, Show, Ord)
okALaCarteSymbol :: String -> Text.Text -> Bool
2020-01-14 23:37:39 +03:00
okALaCarteSymbol "typescript" symbol = symbol `notElem` blacklist
where
blacklist = ["require"]
okALaCarteSymbol "ruby" symbol = not (instanceVariable symbol || builtInMethod symbol)
2019-12-17 02:11:37 +03:00
where
instanceVariable = Text.isPrefixOf "@"
builtInMethod x = x `elem` blacklist
2019-12-17 02:11:37 +03:00
blacklist =
[ "alias"
, "load"
, "require_relative"
, "require"
, "super"
, "undef"
, "defined?"
2019-12-18 01:12:24 +03:00
, "lambda"
2019-12-17 02:11:37 +03:00
]
okALaCarteSymbol _ _ = True
filterALaCarteSymbols :: String -> [Text.Text] -> [Text.Text]
filterALaCarteSymbols lang = filter (okALaCarteSymbol lang)
2019-12-17 02:11:37 +03:00
aLaCarteLanguageModes :: PerLanguageModes
aLaCarteLanguageModes = PerLanguageModes
{ pythonMode = ALaCarte
, rubyMode = ALaCarte
2019-12-19 00:52:56 +03:00
, goMode = ALaCarte
2019-12-20 20:40:00 +03:00
, typescriptMode = ALaCarte
, tsxMode = ALaCarte
, javascriptMode = ALaCarte
, jsxMode = ALaCarte
}
preciseLanguageModes :: PerLanguageModes
preciseLanguageModes = PerLanguageModes
{ pythonMode = Precise
, rubyMode = Precise
2019-12-19 00:52:56 +03:00
, goMode = Precise
2019-12-20 20:40:00 +03:00
, typescriptMode = Precise
, tsxMode = Precise
, javascriptMode = Precise
, jsxMode = Precise
}
testOptions :: Config.Options
testOptions = defaultOptions
{ optionsFailOnWarning = flag FailOnWarning True
, optionsLogLevel = Nothing
}
main :: IO ()
main = withOptions testOptions $ \ config logger statter -> do
void $ Process.system "script/clone-example-repos"
let session = TaskSession config "-" False logger statter
allTests <- forConcurrently examples $ \lang@LanguageExample{..} -> do
let tsDir = Path.relDir "tmp" </> Path.relDir (languageName <> "-examples")
buildExamples session lang tsDir
Tasty.defaultMain $ Tasty.testGroup "parse-examples" allTests
parseSymbolsFilePath ::
( Has (Error SomeException) sig m
, Has Distribute sig m
, Has Parse sig m
, Has Files sig m
)
=> PerLanguageModes
-> Path.RelFile
-> m ParseTreeSymbolResponse
2020-01-24 23:41:49 +03:00
parseSymbolsFilePath languageModes path = readBlob (File.fromPath path) >>= runReader languageModes . parseSymbols . pure @[]