2015-12-17 21:08:47 +03:00
module SplitSpec where
import Test.Hspec
import Split
import qualified Data.Set as Set
import Diff
import Range
import Test.Hspec.QuickCheck
import Test.QuickCheck hiding ( Fixed )
import Control.Comonad.Cofree
import Control.Monad.Free hiding ( unfold )
2015-12-21 21:00:46 +03:00
import qualified Data.Maybe as Maybe
2015-12-17 21:08:47 +03:00
import Patch
import Syntax
2015-12-22 21:04:07 +03:00
import ArbitraryTerm
2015-12-17 21:08:47 +03:00
2015-12-21 17:58:20 +03:00
instance Arbitrary a => Arbitrary ( Row a ) where
2015-12-17 21:08:47 +03:00
arbitrary = oneof [
Row <$> arbitrary <*> arbitrary ]
instance Arbitrary HTML where
arbitrary = oneof [
Text <$> arbitrary ,
Span <$> arbitrary <*> arbitrary ,
const Break <$> ( arbitrary :: Gen () ) ]
2015-12-21 17:58:20 +03:00
instance Arbitrary a => Arbitrary ( Line a ) where
2015-12-17 21:08:47 +03:00
arbitrary = oneof [
2015-12-22 20:47:12 +03:00
Line <$> arbitrary ,
2015-12-17 21:08:47 +03:00
const EmptyLine <$> ( arbitrary :: Gen () ) ]
2015-12-22 21:38:44 +03:00
arbitraryLeaf :: Gen ( String , Info , Syntax String f )
arbitraryLeaf = toTuple <$> arbitrary
where toTuple string = ( string , Info ( Range 0 $ length string ) mempty , Leaf string )
2015-12-22 21:16:03 +03:00
2015-12-17 21:08:47 +03:00
spec :: Spec
spec = do
2015-12-22 20:39:54 +03:00
describe " splitAnnotatedByLines " $ do
2015-12-22 21:04:07 +03:00
prop " outputs one row for single-line unchanged leaves " $
2015-12-22 22:44:57 +03:00
forAll ( arbitraryLeaf ` suchThat ` isOnSingleLine ) $
2015-12-22 21:29:36 +03:00
\ ( source , info @ ( Info range categories ) , syntax ) -> splitAnnotatedByLines ( source , source ) ( range , range ) ( categories , categories ) syntax ` shouldBe ` [
Row ( Line [ Free $ Annotated info $ Leaf source ] ) ( Line [ Free $ Annotated info $ Leaf source ] ) ]
2015-12-22 21:04:07 +03:00
2015-12-17 21:08:47 +03:00
describe " annotatedToRows " $ do
it " outputs one row for single-line empty unchanged indexed nodes " $
2015-12-22 20:47:12 +03:00
annotatedToRows ( unchanged " [] " " branch " ( Indexed [] ) ) " [] " " [] " ` shouldBe ` [ Row ( Line [ Ul ( Just " category-branch " ) [ Text " [] " ] ] ) ( Line [ Ul ( Just " category-branch " ) [ Text " [] " ] ] ) ]
2015-12-17 21:08:47 +03:00
it " outputs one row for single-line non-empty unchanged indexed nodes " $
annotatedToRows ( unchanged " [ a, b ] " " branch " ( Indexed [
Free . offsetAnnotated 2 2 $ unchanged " a " " leaf " ( Leaf " " ) ,
Free . offsetAnnotated 5 5 $ unchanged " b " " leaf " ( Leaf " " )
2015-12-22 20:47:12 +03:00
] ) ) " [ a, b ] " " [ a, b ] " ` shouldBe ` [ Row ( Line [ Ul ( Just " category-branch " ) [ Text " [ " , span " a " , Text " , " , span " b " , Text " ] " ] ] ) ( Line [ Ul ( Just " category-branch " ) [ Text " [ " , span " a " , Text " , " , span " b " , Text " ] " ] ] ) ]
2015-12-17 21:08:47 +03:00
it " outputs one row for single-line non-empty formatted indexed nodes " $
annotatedToRows ( formatted " [ a, b ] " " [ a, b ] " " branch " ( Indexed [
Free . offsetAnnotated 2 2 $ unchanged " a " " leaf " ( Leaf " " ) ,
Free . offsetAnnotated 5 6 $ unchanged " b " " leaf " ( Leaf " " )
2015-12-22 20:47:12 +03:00
] ) ) " [ a, b ] " " [ a, b ] " ` shouldBe ` [ Row ( Line [ Ul ( Just " category-branch " ) [ Text " [ " , span " a " , Text " , " , span " b " , Text " ] " ] ] ) ( Line [ Ul ( Just " category-branch " ) [ Text " [ " , span " a " , Text " , " , span " b " , Text " ] " ] ] ) ]
2015-12-17 21:08:47 +03:00
it " outputs two rows for two-line non-empty unchanged indexed nodes " $
annotatedToRows ( unchanged " [ a, \ n b ] " " branch " ( Indexed [
Free . offsetAnnotated 2 2 $ unchanged " a " " leaf " ( Leaf " " ) ,
Free . offsetAnnotated 5 5 $ unchanged " b " " leaf " ( Leaf " " )
] ) ) " [ a, \ n b ] " " [ a, \ n b ] " ` shouldBe `
2015-12-22 03:44:07 +03:00
[
2015-12-22 20:47:12 +03:00
Row ( Line [ Ul ( Just " category-branch " ) [ Text " [ " , span " a " , Text " , " , Break ] ] )
( Line [ Ul ( Just " category-branch " ) [ Text " [ " , span " a " , Text " , " , Break ] ] ) ,
Row ( Line [ Ul ( Just " category-branch " ) [ span " b " , Text " ] " ] ] )
( Line [ Ul ( Just " category-branch " ) [ span " b " , Text " ] " ] ] )
2015-12-22 03:44:07 +03:00
]
2015-12-17 21:08:47 +03:00
it " outputs two rows for two-line non-empty formatted indexed nodes " $
annotatedToRows ( formatted " [ a, \ n b ] " " [ \ n a, \ n b ] " " branch " ( Indexed [
Free . offsetAnnotated 2 2 $ unchanged " a " " leaf " ( Leaf " " ) ,
Free . offsetAnnotated 5 5 $ unchanged " b " " leaf " ( Leaf " " )
] ) ) " [ a, \ n b ] " " [ \ n a, \ n b ] " ` shouldBe `
2015-12-22 03:44:07 +03:00
[
2015-12-22 20:47:12 +03:00
Row ( Line [ Ul ( Just " category-branch " ) [ Text " [ " , span " a " , Text " , " , Break ] ] )
( Line [ Ul ( Just " category-branch " ) [ Text " [ " , Break ] ] ) ,
2015-12-17 21:08:47 +03:00
Row EmptyLine
2015-12-22 20:47:12 +03:00
( Line [ Ul ( Just " category-branch " ) [ span " a " , Text " , " , Break ] ] ) ,
Row ( Line [ Ul ( Just " category-branch " ) [ span " b " , Text " ] " ] ] )
( Line [ Ul ( Just " category-branch " ) [ span " b " , Text " ] " ] ] )
2015-12-22 03:44:07 +03:00
]
2015-12-17 21:08:47 +03:00
it " " $
let ( sourceA , sourceB ) = ( " [ \ n a \ n , \ n b] " , " [a,b] " ) in
annotatedToRows ( formatted sourceA sourceB " branch " ( Indexed [
Free . offsetAnnotated 2 1 $ unchanged " a " " leaf " ( Leaf " " ) ,
Free . offsetAnnotated 6 3 $ unchanged " b " " leaf " ( Leaf " " )
] ) ) sourceA sourceB ` shouldBe `
2015-12-22 03:44:07 +03:00
[
2015-12-22 20:47:12 +03:00
Row ( Line [ Ul ( Just " category-branch " ) [ Text " [ " , Break ] ] )
( Line [ Ul ( Just " category-branch " ) [ Text " [ " , span " a " , Text " , " , span " b " , Text " ] " ] ] ) ,
Row ( Line [ Ul ( Just " category-branch " ) [ span " a " , Break ] ] )
2015-12-17 21:08:47 +03:00
EmptyLine ,
2015-12-22 20:47:12 +03:00
Row ( Line [ Ul ( Just " category-branch " ) [ Text " , " , Break ] ] )
2015-12-17 21:08:47 +03:00
EmptyLine ,
2015-12-22 20:47:12 +03:00
Row ( Line [ Ul ( Just " category-branch " ) [ span " b " , Text " ] " ] ] )
2015-12-17 21:08:47 +03:00
EmptyLine
2015-12-22 03:44:07 +03:00
]
2015-12-17 21:08:47 +03:00
2015-12-22 04:23:56 +03:00
it " splits multi-line deletions across multiple rows " $
2015-12-17 21:08:47 +03:00
let ( sourceA , sourceB ) = ( " /* \ n */ \ n a " , " a " ) in
annotatedToRows ( formatted sourceA sourceB " branch " ( Indexed [
2015-12-21 21:01:46 +03:00
Pure . Delete $ ( Info ( Range 0 5 ) ( Set . fromList [ " leaf " ] ) :< Leaf " " ) ,
2015-12-17 21:08:47 +03:00
Free . offsetAnnotated 6 0 $ unchanged " a " " leaf " ( Leaf " " )
] ) ) sourceA sourceB ` shouldBe `
2015-12-22 03:44:07 +03:00
[
2015-12-22 20:47:12 +03:00
Row ( Line [ Ul ( Just " category-branch " ) [ Div ( Just " delete " ) [ span " /* " , Break ] ] ] ) EmptyLine ,
Row ( Line [ Ul ( Just " category-branch " ) [ Div ( Just " delete " ) [ span " */ " ] , Break ] ] ) EmptyLine ,
Row ( Line [ Ul ( Just " category-branch " ) [ span " a " ] ] ) ( Line [ Ul ( Just " category-branch " ) [ span " a " ] ] )
2015-12-22 03:44:07 +03:00
]
2015-12-17 21:08:47 +03:00
describe " unicode " $
it " equivalent precomposed and decomposed characters are not equal " $
let ( sourceA , sourceB ) = ( " t \ 776 " , " \ 7831 " )
2015-12-21 21:01:46 +03:00
syntax = Leaf . Pure $ Replace ( info sourceA " leaf " :< Leaf " " ) ( info sourceB " leaf " :< Leaf " " )
2015-12-17 21:08:47 +03:00
in
annotatedToRows ( formatted sourceA sourceB " leaf " syntax ) sourceA sourceB ` shouldBe `
2015-12-22 20:47:12 +03:00
[ Row ( Line [ span " t \ 776 " ] ) ( Line [ span " \ 7831 " ] ) ]
2015-12-17 21:08:47 +03:00
2015-12-21 20:57:50 +03:00
describe " adjoinRowsBy " $ do
2015-12-17 21:08:47 +03:00
prop " is identity on top of no rows " $
2015-12-22 07:09:14 +03:00
\ a -> adjoinRowsBy openElement openElement [] a == [ a ]
2015-12-17 21:08:47 +03:00
2015-12-18 21:04:52 +03:00
prop " appends onto open rows " $
2015-12-21 21:01:46 +03:00
forAll ( ( arbitrary ` suchThat ` isOpen ) >>= \ a -> ( , ) a <$> ( arbitrary ` suchThat ` isOpen ) ) $
2015-12-22 20:47:12 +03:00
\ ( a @ ( Row ( Line a1 ) ( Line b1 ) ) , b @ ( Row ( Line a2 ) ( Line b2 ) ) ) ->
adjoinRowsBy openElement openElement [ a ] b ` shouldBe ` [ Row ( Line $ a1 ++ a2 ) ( Line $ b1 ++ b2 ) ]
2015-12-18 20:11:01 +03:00
2015-12-18 21:19:40 +03:00
prop " does not append onto closed rows " $
2015-12-21 21:01:46 +03:00
forAll ( ( arbitrary ` suchThat ` isClosed ) >>= \ a -> ( , ) a <$> ( arbitrary ` suchThat ` isClosed ) ) $
2015-12-22 07:09:14 +03:00
\ ( a , b ) -> adjoinRowsBy openElement openElement [ a ] b ` shouldBe ` [ b , a ]
2015-12-18 21:19:40 +03:00
2015-12-18 21:27:03 +03:00
prop " does not promote elements through empty lines onto closed lines " $
2015-12-21 21:01:46 +03:00
forAll ( ( arbitrary ` suchThat ` isClosed ) >>= \ a -> ( , ) a <$> ( arbitrary ` suchThat ` isClosed ) ) $
2015-12-22 07:09:14 +03:00
\ ( a , b ) -> adjoinRowsBy openElement openElement [ Row EmptyLine EmptyLine , a ] b ` shouldBe ` [ b , Row EmptyLine EmptyLine , a ]
2015-12-18 21:27:03 +03:00
2015-12-18 21:58:02 +03:00
prop " promotes elements through empty lines onto open lines " $
2015-12-21 21:01:46 +03:00
forAll ( ( arbitrary ` suchThat ` isOpen ) >>= \ a -> ( , ) a <$> ( arbitrary ` suchThat ` isOpen ) ) $
2015-12-22 07:09:14 +03:00
\ ( a , b ) -> adjoinRowsBy openElement openElement [ Row EmptyLine EmptyLine , a ] b ` shouldBe ` Row EmptyLine EmptyLine : adjoinRowsBy openElement openElement [ a ] b
2015-12-18 21:58:02 +03:00
2015-12-17 21:08:47 +03:00
describe " termToLines " $ do
it " splits multi-line terms into multiple lines " $
2015-12-21 21:01:46 +03:00
termToLines ( Info ( Range 0 5 ) ( Set . singleton " leaf " ) :< Leaf " " ) " /* \ n */ "
2015-12-17 21:08:47 +03:00
` shouldBe `
( [
2015-12-22 20:47:12 +03:00
Line [ span " /* " , Break ] ,
Line [ span " */ " ]
2015-12-17 21:08:47 +03:00
] , Range 0 5 )
2015-12-22 03:54:40 +03:00
describe " splitTermByLines " $ do
it " splits multi-line terms into multiple lines " $
2015-12-22 04:02:17 +03:00
splitTermByLines ( Info ( Range 0 5 ) mempty :< Leaf " " ) " /* \ n */ "
2015-12-22 03:54:40 +03:00
` shouldBe `
( [
2015-12-22 20:47:12 +03:00
Line [ Info ( Range 0 3 ) mempty :< Leaf " " ] ,
Line [ Info ( Range 3 5 ) mempty :< Leaf " " ]
2015-12-22 03:54:40 +03:00
] , Range 0 5 )
2015-12-21 18:01:00 +03:00
describe " openLineBy " $ do
2015-12-22 04:23:56 +03:00
it " produces the earliest non-empty line in a list, if open " $
2015-12-21 18:01:00 +03:00
openLineBy openElement [
2015-12-22 20:47:12 +03:00
Line [ Div ( Just " delete " ) [ span " */ " ] ] ,
Line [ Div ( Just " delete " ) [ span " * Debugging " , Break ] ] ,
Line [ Div ( Just " delete " ) [ span " /* " , Break ] ]
] ` shouldBe ` ( Just $ Line [ Div ( Just " delete " ) [ span " */ " ] ] )
2015-12-17 21:08:47 +03:00
2015-12-22 04:23:56 +03:00
it " produces the earliest non-empty line in a list, if open " $
2015-12-22 04:04:05 +03:00
openLineBy ( openTerm " \ n " ) [
2015-12-22 20:47:12 +03:00
Line [ Info ( Range 1 2 ) mempty :< Leaf " " ] ,
Line [ Info ( Range 0 1 ) mempty :< Leaf " " ]
] ` shouldBe ` ( Just $ Line [ Info ( Range 1 2 ) mempty :< Leaf " " ] )
2015-12-22 04:04:05 +03:00
2015-12-22 04:23:56 +03:00
it " returns Nothing if the earliest non-empty line is closed " $
2015-12-21 18:01:00 +03:00
openLineBy openElement [
2015-12-22 20:47:12 +03:00
Line [ Div ( Just " delete " ) [ span " * Debugging " , Break ] ]
2015-12-17 21:08:47 +03:00
] ` shouldBe ` Nothing
2015-12-22 04:23:56 +03:00
it " returns Nothing if the earliest non-empty line is closed " $
2015-12-22 04:04:05 +03:00
openLineBy ( openTerm " \ n " ) [
2015-12-22 20:47:12 +03:00
Line [ Info ( Range 0 1 ) mempty :< Leaf " " ]
2015-12-22 04:04:05 +03:00
] ` shouldBe ` Nothing
2015-12-22 04:06:19 +03:00
describe " openTerm " $ do
2015-12-22 04:08:05 +03:00
it " returns Just the term if its substring does not end with a newline " $
let term = Info ( Range 0 2 ) mempty :< Leaf " " in openTerm " " term ` shouldBe ` Just term
2015-12-22 04:07:53 +03:00
it " returns Nothing for terms whose substring ends with a newline " $
2015-12-22 04:06:19 +03:00
openTerm " \ n " ( Info ( Range 0 2 ) mempty :< Leaf " " ) ` shouldBe ` Nothing
2015-12-17 21:08:47 +03:00
where
rightRowText text = rightRow [ Text text ]
2015-12-22 20:47:12 +03:00
rightRow xs = Row EmptyLine ( Line xs )
2015-12-17 21:08:47 +03:00
leftRowText text = leftRow [ Text text ]
2015-12-22 20:47:12 +03:00
leftRow xs = Row ( Line xs ) EmptyLine
rowText a b = Row ( Line [ Text a ] ) ( Line [ Text b ] )
2015-12-17 21:08:47 +03:00
info source category = Info ( totalRange source ) ( Set . fromList [ category ] )
2015-12-21 21:01:38 +03:00
unchanged source = formatted source source
2015-12-17 21:08:47 +03:00
formatted source1 source2 category = Annotated ( info source1 category , info source2 category )
offsetInfo by ( Info ( Range start end ) categories ) = Info ( Range ( start + by ) ( end + by ) ) categories
offsetAnnotated by1 by2 ( Annotated ( left , right ) syntax ) = Annotated ( offsetInfo by1 left , offsetInfo by2 right ) syntax
span = Span ( Just " category-leaf " )
2015-12-21 21:03:08 +03:00
isOpen ( Row a b ) = Maybe . isJust ( openLineBy openElement [ a ] ) && Maybe . isJust ( openLineBy openElement [ b ] )
2015-12-22 20:47:12 +03:00
isClosed ( Row a @ ( Line _ ) b @ ( Line _ ) ) = Maybe . isNothing ( openLineBy openElement [ a ] ) && Maybe . isNothing ( openLineBy openElement [ b ] )
2015-12-18 21:19:05 +03:00
isClosed ( Row _ _ ) = False
2015-12-22 22:44:49 +03:00
isOnSingleLine ( a , _ , _ ) = filter ( /= '\ n' ) a == a