roundTripTest
    :: forall a. (Arbitrary a, ToStg a, FromStg a, Show a, Eq a)
    => RoundtripSpec a
    -> TestTree
roundTripTest spec = testProperty (T.unpack (testName spec)) test
  where
    test :: a -> Property
    test payload = counterexample (T.unpack ((renderPlain :: Doc StgiAnn -> Text) (prettyStgi finalState)))
                                  (expected === Right payload)
      where
        source = mconcat
            [ toStg "payload" payload
            , [stg|
                main = \ => case forceUntyped payload of _ -> Done;

                forceUntyped = \value -> case value of
                    Unit -> Done;

                    Nothing -> Done;
                    Just x -> forceUntyped x;

                    True -> Done;
                    False -> Done;

                    Int# _ -> Done;

                    Left l -> forceUntyped l;
                    Right r -> forceUntyped r;

                    Pair x y -> case forceUntyped x of
                        _ -> forceUntyped y;
                    Triple x y z -> case forceUntyped x of
                        _ -> case forceUntyped y of
                            _ -> forceUntyped z;

                    Nil -> Done;
                    Cons x xs -> case forceUntyped x of
                        _ -> forceUntyped xs;

                    _ -> Error
                 |]]
        prog = initialState "main" source
        states = evalsUntil
            (RunForMaxSteps (maxSteps spec))
            (HaltIf (const False))
            (PerformGc (const Nothing))
            prog
        finalState = garbageCollect triStateTracing (NE.last states)
        expected = fromStg finalState "payload"
roundTripTest
    :: forall a. (Arbitrary a, ToStg a, FromStg a, Show a, Eq a)
    => RoundtripSpec a
    -> TestTree
roundTripTest spec = testProperty (T.unpack (testName spec)) test
  where
    test :: a -> Property
    test payload = counterexample (T.unpack ((renderPlain :: Doc StgiAnn -> Text) (prettyStgi finalState)))
                                  (expected === Right payload)
      where
        source = mconcat
            [ toStg "payload" payload
            , [stg|
                main = \ => case forceUntyped payload of _ -> Done;

                forceUntyped = \value -> case value of
                    Unit -> Done;

                    Nothing -> Done;
                    Just x -> forceUntyped x;

                    True -> Done;
                    False -> Done;

                    Int# _ -> Done;

                    Left l -> forceUntyped l;
                    Right r -> forceUntyped r;

                    Pair x y -> case forceUntyped x of
                        _ -> forceUntyped y;
                    Triple x y z -> case forceUntyped x of
                        _ -> case forceUntyped y of
                            _ -> forceUntyped z;

                    Nil -> Done;
                    Cons x xs -> case forceUntyped x of
                        _ -> forceUntyped xs;

                    _ -> Error
                 |]]
        prog = initialState "main" source
        states = evalsUntil
            (RunForMaxSteps (maxSteps spec))
            (HaltIf (const False))
            (PerformGc (const Nothing))
            prog
        finalState = garbageCollect triStateTracing (NE.last states)
        expected = fromStg finalState "payload"
roundTripTest spec = testProperty (T.unpack (testName spec)) test
  where
    test :: a -> Property
    test payload = counterexample (T.unpack ((renderPlain :: Doc StgiAnn -> Text) (prettyStgi finalState)))
                                  (expected === Right payload)
      where
        source = mconcat
            [ toStg "payload" payload
            , [stg|
                main = \ => case forceUntyped payload of _ -> Done;

                forceUntyped = \value -> case value of
                    Unit -> Done;

                    Nothing -> Done;
                    Just x -> forceUntyped x;

                    True -> Done;
                    False -> Done;

                    Int# _ -> Done;

                    Left l -> forceUntyped l;
                    Right r -> forceUntyped r;

                    Pair x y -> case forceUntyped x of
                        _ -> forceUntyped y;
                    Triple x y z -> case forceUntyped x of
                        _ -> case forceUntyped y of
                            _ -> forceUntyped z;

                    Nil -> Done;
                    Cons x xs -> case forceUntyped x of
                        _ -> forceUntyped xs;

                    _ -> Error
                 |]]
        prog = initialState "main" source
        states = evalsUntil
            (RunForMaxSteps (maxSteps spec))
            (HaltIf (const False))
            (PerformGc (const Nothing))
            prog
        finalState = garbageCollect triStateTracing (NE.last states)
        expected = fromStg finalState "payload"