content: {-# LANGUAGE OverloadedStrings #-}
  
  module TestEventlogStreaming where
  
  import Arbitrary ()
  import Data.Function
  import qualified Data.HashMap.Strict as HM
  import qualified Data.HashSet as HS
  import Data.Hashable
  import Data.List (foldl', sort)
  import GHC.RTS.Events
  import LogEventSerializer
  import OpenTelemetry.Common hiding (Event)
  import OpenTelemetry.Eventlog
  import OpenTelemetry.EventlogStreaming_Internal
  import OpenTelemetry.SpanContext
  import qualified System.Random.SplitMix as R
  import Test.QuickCheck
  import TextShow
  
  smgen0 :: R.SMGen
  smgen0 = R.mkSMGen 0
  
  processEvents :: [Event] -> State -> (State, [Span], [Metric])
  processEvents events st0 = foldl' go (st0, [], []) events
    where
      go (st, sps, ms) e =
        let (st', sps', ms') = processEvent e st
         in (st', sps' <> sps, ms <> ms')
  
  prop_number_of_spans_in_eventlog_is_number_of_spans_exported :: [(SpanInFlight, SpanName)] -> Property
  prop_number_of_spans_in_eventlog_is_number_of_spans_exported spans =
    let input_events = concatMap convert spans
        convert (span_serial_number, spanName) =
          [ Event 0 (logEventToUserBinaryMessage $ BeginSpanEv span_serial_number spanName) (Just 0),
            Event 42 (logEventToUserBinaryMessage $ EndSpanEv span_serial_number) (Just 0)
          ]
        (_end_state, emitted_spans, _emitted_metrics) = processEvents input_events (initialState 0 smgen0)
     in length emitted_spans === length spans
  
  prop_user_specified_span_ids_are_used :: [(SpanInFlight, SpanId, SpanName)] -> Property
  prop_user_specified_span_ids_are_used spans =
    let input_events = concatMap convert spans
        convert (span_serial_number, sid, span_name) =
          [ Event 0 (logEventToUserBinaryMessage $ BeginSpanEv span_serial_number span_name) (Just 0),
            Event 1 (logEventToUserBinaryMessage $ SetSpanEv span_serial_number sid) (Just 0),
            Event 42 (logEventToUserBinaryMessage $ EndSpanEv span_serial_number) (Just 0)
          ]
        (_end_state, emitted_spans, _emitted_metrics) = processEvents input_events (initialState 0 smgen0)
     in sort (map (\(_, x, _) -> x) spans) === sort (map spanId emitted_spans)
  
  prop_user_specified_things_are_used :: [(SpanInFlight, SpanId, SpanName)] -> Property
  prop_user_specified_things_are_used spans =
    distinct (map (\(serial, _, _) -> serial) spans)
      ==> distinct (map (\(_, span_id, _) -> span_id) spans)
      ==> classify (length spans > 1) "multiple spans"
      $ let input_events = concatMap convert spans
            convert (span_serial_number, sid@(SId sid'), span_name) =
              [ Event 0 (logEventToUserBinaryMessage $ BeginSpanEv span_serial_number span_name) (Just 0),
                Event 1 (logEventToUserBinaryMessage $ SetSpanEv span_serial_number sid) (Just 0),
                Event 2 (logEventToUserBinaryMessage $ TagEv span_serial_number (TagName "color") (TagVal $ showt sid')) (Just 0),
                Event 3 (logEventToUserBinaryMessage $ SetTraceEv span_serial_number (TId sid')) (Just 0),
                Event 4 (logEventToUserBinaryMessage $ EventEv span_serial_number (EventName "message") (EventVal $ showt sid')) (Just 0),
                Event 42 (logEventToUserBinaryMessage $ EndSpanEv span_serial_number) (Just 0)
              ]
            (_end_state, emitted_spans, _emitted_metrics) = processEvents input_events (initialState 0 smgen0)
            corresponding_span_was_emitted (_serial, SId sid, _thread_id) =
              emitted_spans
                & filter
                  ( \sp ->
                      and
                        [ spanId sp == SId sid,
                          spanTraceId sp == TId sid,
                          HM.lookup (TagName "color") (spanTags sp)
                            == Just (StringTagValue $ TagVal (showt sid)),
                          any
                            ( \SpanEvent {..} ->
                                (spanEventKey == EventName "message")
                                  && (spanEventValue == (EventVal (showt sid)))
                            )
                            (spanEvents sp)
                        ]
                  )
                & length
                & (=== (1 :: Int))
         in conjoin $ map corresponding_span_was_emitted spans
  
  prop_parenting_works_when_everything_is_on_one_thread_and_nested_properly :: [SpanInFlight] -> Property
  prop_parenting_works_when_everything_is_on_one_thread_and_nested_properly serials =
    distinct serials ==> length serials > 1
      ==> let input_events = prelude <> map convert_begin serials <> map convert_end (reverse serials)
              prelude = [Event 0 (CreateThread 1) (Just 0)]
              convert_begin serial =
                Event 1 (logEventToUserBinaryMessage $ BeginSpanEv serial $ SpanName $ showt serial) (Just 0)
              convert_end serial =
                Event 42 (logEventToUserBinaryMessage $ EndSpanEv serial) (Just 0)
              (_end_state, emitted_spans, _emitted_metrics) = processEvents input_events (initialState 0 smgen0)
              check_relationship (sp, psp) = spanParentId sp === Just (spanId psp)
           in conjoin $ map check_relationship (zip (tail emitted_spans) emitted_spans)
  
  prop_beginning_a_span_on_one_thread_and_ending_on_another_is_fine :: SpanInFlight -> ThreadId -> ThreadId -> Int -> Int -> Property
  prop_beginning_a_span_on_one_thread_and_ending_on_another_is_fine serial begin_tid end_tid begin_cap end_cap =
    let input_events =
          [ Event 0 (CreateThread begin_tid) (Just begin_cap),
            Event 1 (RunThread begin_tid) (Just begin_cap),
            Event 2 (logEventToUserBinaryMessage $ BeginSpanEv serial $ SpanName $ showt serial) (Just begin_cap),
            Event 3 (CreateThread end_tid) (Just end_cap),
            Event 4 (RunThread end_tid) (Just end_cap),
            Event 5 (logEventToUserBinaryMessage $ EndSpanEv serial) (Just end_cap)
          ]
        (end_state, emitted_spans, _emitted_metrics) = processEvents input_events (initialState 0 smgen0)
     in conjoin
          [ length emitted_spans === 1,
            spanOperation (head emitted_spans) === showt serial,
            spanStartedAt (head emitted_spans) === 2,
            spanFinishedAt (head emitted_spans) === 5,
            spanThreadId (head emitted_spans) === begin_tid,
            True === null (spans end_state),
            True === null (serial2sid end_state)
          ]
  
  distinct :: (Eq a, Hashable a) => [a] -> Bool
  distinct things = length things == HS.size (foldMap HS.singleton things)
  