1
1
mirror of https://github.com/github/semantic.git synced 2024-12-23 14:54:16 +03:00
semantic/test/AlignmentSpec.hs
2016-03-11 18:35:31 -05:00

125 lines
5.6 KiB
Haskell
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

module AlignmentSpec where
import Test.Hspec
import Test.Hspec.QuickCheck
import Test.QuickCheck hiding (Fixed)
import Data.Text.Arbitrary ()
import Alignment
import ArbitraryTerm ()
import Control.Arrow
import Control.Comonad.Cofree
import Control.Monad.Free hiding (unfold)
import Data.Copointed
import Data.Functor.Both as Both
import Diff
import qualified Data.Maybe as Maybe
import Data.Functor.Identity
import Line
import Patch
import Prelude hiding (fst, snd)
import qualified Prelude
import Range
import Source hiding ((++))
import qualified Source
import SplitDiff
import Syntax
instance Arbitrary a => Arbitrary (Both a) where
arbitrary = pure (curry Both) <*> arbitrary <*> arbitrary
instance Arbitrary a => Arbitrary (Line a) where
arbitrary = oneof [ Line <$> arbitrary, Closed <$> arbitrary ]
instance Arbitrary a => Arbitrary (Patch a) where
arbitrary = oneof [
Insert <$> arbitrary,
Delete <$> arbitrary,
Replace <$> arbitrary <*> arbitrary ]
instance Arbitrary a => Arbitrary (Source a) where
arbitrary = fromList <$> arbitrary
arbitraryLeaf :: Gen (Source Char, Info, Syntax (Source Char) f)
arbitraryLeaf = toTuple <$> arbitrary
where toTuple string = (string, Info (Range 0 $ length string) mempty, Leaf string)
spec :: Spec
spec = parallel $ do
describe "splitDiffByLines" $ do
prop "preserves line counts in equal sources" $
\ source ->
length (splitDiffByLines (pure source) (Free $ Annotated (pure $ Info (totalRange source) mempty) (Indexed . Prelude.fst $ foldl combineIntoLeaves ([], 0) source))) `shouldBe` length (filter (== '\n') $ toString source) + 1
prop "produces the maximum line count in inequal sources" $
\ sources ->
length (splitDiffByLines sources (Free $ Annotated ((`Info` mempty) . totalRange <$> sources) (Indexed $ leafWithRangesInSources sources <$> runBothWith (zipWith both) (actualLineRanges <$> (totalRange <$> sources) <*> sources)))) `shouldBe` runBothWith max ((+ 1) . length . filter (== '\n') . toString <$> sources)
describe "adjoinRows" $ do
prop "is identity on top of no rows" $ forAll (arbitrary `suchThat` (not . isEmptyRow)) $
\ a -> adjoinRows alignRows a [] `shouldBe` [ a :: Row Char ]
prop "prunes empty rows" $
\ a -> adjoinRows alignRows mempty [ a ] `shouldBe` [ a :: Row Char ]
prop "merges open rows" $
\ a b -> adjoinRows alignRows (pure (Line [a])) [ b ] `shouldBe` [ pure (Line [a]) `mappend` b :: Row Char ]
prop "prepends closed rows" $
\ a b -> adjoinRows alignRows (pure (Closed [a])) [ b ] `shouldBe` [ pure (Closed [a]), b :: Row Char ]
-- it "aligns closed lines" $
-- foldr (adjoinRows alignRows) [] (zipWith both (pureBy (/= '\n') <$> "[ bar ]\nquux") (pureBy (/= '\n') <$> "[\nbar\n]\nquux")) `shouldBe`
-- [ both (Closed "[ bar ]\n") (Closed "[\n")
-- , both (Closed "") (Closed "bar\n")
-- , both (Closed "") (Closed "]\n")
-- , both (Line "quux") (Line "quux")
-- ]
it "preserves childrens alignment" $
let rows = concat
[ [ both (Closed "[ bar ]\n") (Closed "[\n")
, both (Closed "") (Closed "bar\n")
, both (Line "") (Line "]")
]
, [ both (Closed "") (Closed "\n") ]
, [ both (Line "quux") (Line "quux") ]
] :: [Row Char] in
foldr (adjoinRows alignRows) [] rows `shouldBe`
[ both (Closed "[ bar ]\n") (Closed "[\n")
, both (Closed "") (Closed "bar\n")
, both (Closed "") (Closed "]\n")
, both (Line "quux") (Line "quux")
]
describe "splitAbstractedTerm" $ do
prop "preserves line count" $
\ source -> let range = totalRange source in
splitAbstractedTerm sequenceA (:<) (Identity source) (Identity (Info range mempty)) (Leaf source) `shouldBe` (Identity . lineMap (fmap (((:< Leaf source) . (`Info` mempty) &&& id))) <$> linesInRangeOfSource range source)
let makeTerm = ((Free .) . Annotated) :: Info -> Syntax (Source Char) (SplitDiff (Source Char) Info) -> SplitDiff (Source Char) Info
prop "outputs one row for single-line unchanged leaves" $
forAll (arbitraryLeaf `suchThat` isOnSingleLine) $
\ (source, info@(Info range categories), syntax) -> splitAbstractedTerm alignRows makeTerm (pure source) (pure $ Info range categories) syntax `shouldBe` [
both (pure (makeTerm info $ Leaf source, Range 0 (length source))) (pure (makeTerm info $ Leaf source, Range 0 (length source))) ]
prop "outputs one row for single-line empty unchanged indexed nodes" $
forAll (arbitrary `suchThat` (\ a -> filter (/= '\n') (toString a) == toString a)) $
\ source -> splitAbstractedTerm alignRows makeTerm (pure source) (pure $ Info (totalRange source) mempty) (Indexed []) `shouldBe` [
both (pure (makeTerm (Info (totalRange source) mempty) $ Indexed [], Range 0 (length source))) (pure (makeTerm (Info (totalRange source) mempty) $ Indexed [], Range 0 (length source))) ]
where
isEmptyRow = and . fmap isEmpty
isOnSingleLine (a, _, _) = filter (/= '\n') (toString a) == toString a
combineIntoLeaves (leaves, start) char = (leaves ++ [ Free $ Annotated (Info <$> pure (Range start $ start + 1) <*> mempty) (Leaf [ char ]) ], start + 1)
leafWithRangesInSources sources ranges = Free $ Annotated (Info <$> ranges <*> pure mempty) (Leaf $ runBothWith (++) (toString <$> sources))
leafWithRangeInSource source range = Info range mempty :< Leaf source
patchWithBoth (Insert ()) = Insert . snd
patchWithBoth (Delete ()) = Delete . fst
patchWithBoth (Replace () ()) = runBothWith Replace