Skip to content

Commit

Permalink
Add media controller with APIs to track media events (close #606)
Browse files Browse the repository at this point in the history
# Conflicts:
#	snowplow-tracker/src/main/java/com/snowplowanalytics/core/tracker/ServiceProviderInterface.kt
  • Loading branch information
matus-tomlein committed Jun 8, 2023
1 parent 5d0ffae commit 12dc78b
Show file tree
Hide file tree
Showing 52 changed files with 2,916 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import com.snowplowanalytics.core.tracker.ExceptionHandler
import com.snowplowanalytics.core.tracker.Subject
import com.snowplowanalytics.core.tracker.Tracker
import com.snowplowanalytics.core.tracker.TrackerEvent
import com.snowplowanalytics.snowplow.TestUtils
import com.snowplowanalytics.snowplow.util.TestUtils
import com.snowplowanalytics.snowplow.emitter.BufferOption
import com.snowplowanalytics.snowplow.event.ScreenView
import com.snowplowanalytics.snowplow.event.SelfDescribing
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* Copyright (c) 2015-2023 Snowplow Analytics Ltd. All rights reserved.
*
* This program is licensed to you under the Apache License Version 2.0,
* and you may not use this file except in compliance with the Apache License Version 2.0.
* You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the Apache License Version 2.0 is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
*/

package com.snowplowanalytics.snowplow.media

import androidx.test.ext.junit.runners.AndroidJUnit4
import com.snowplowanalytics.core.media.controller.MediaAdTracking
import com.snowplowanalytics.snowplow.media.entity.MediaAdBreakEntity
import com.snowplowanalytics.snowplow.media.entity.MediaAdEntity
import com.snowplowanalytics.snowplow.media.entity.MediaPlayerEntity
import com.snowplowanalytics.snowplow.media.event.MediaAdBreakStartEvent
import com.snowplowanalytics.snowplow.media.event.MediaAdStartEvent
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
class MediaAdTrackingTest {

@Test
fun updatesStartTimeOfAdBreak() {
val adTracking = MediaAdTracking()

adTracking.updateForThisEvent(
event = MediaAdBreakStartEvent(),
player = MediaPlayerEntity(currentTime = 33.0),
adBreak = MediaAdBreakEntity(breakId = "b1")
)
adTracking.updateForNextEvent(
event = MediaAdBreakStartEvent()
)

adTracking.updateForThisEvent(
event = MediaAdStartEvent(),
player = MediaPlayerEntity(currentTime = 44.0),
ad = MediaAdEntity(adId = "a1")
)
adTracking.updateForNextEvent(
event = MediaAdStartEvent()
)

assertEquals("b1", adTracking.adBreak?.breakId)
assertEquals(33.0, adTracking.adBreak?.startTime ?: 0.0, 0.0)
}

@Test
fun updatesPodPositionOfAds() {
val adTracking = MediaAdTracking()

adTracking.updateForThisEvent(
event = MediaAdBreakStartEvent(),
player = MediaPlayerEntity(),
adBreak = MediaAdBreakEntity(breakId = "b1")
)
adTracking.updateForNextEvent(
event = MediaAdBreakStartEvent()
)

adTracking.updateForThisEvent(
event = MediaAdStartEvent(),
player = MediaPlayerEntity(),
ad = MediaAdEntity(adId = "a1")
)

assertEquals(1, adTracking.ad?.podPosition)

adTracking.updateForNextEvent(
event = MediaAdStartEvent()
)

adTracking.updateForThisEvent(
event = MediaAdStartEvent(),
player = MediaPlayerEntity(),
ad = MediaAdEntity(adId = "a2")
)

assertEquals(2, adTracking.ad?.podPosition)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
/*
* Copyright (c) 2015-2023 Snowplow Analytics Ltd. All rights reserved.
*
* This program is licensed to you under the Apache License Version 2.0,
* and you may not use this file except in compliance with the Apache License Version 2.0.
* You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the Apache License Version 2.0 is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
*/

package com.snowplowanalytics.snowplow.media

import androidx.test.ext.junit.runners.AndroidJUnit4
import com.snowplowanalytics.core.media.controller.MediaSessionTrackingStats
import com.snowplowanalytics.core.media.entity.MediaSessionEntity
import com.snowplowanalytics.core.utils.Util.getDateTimeFromDate
import com.snowplowanalytics.snowplow.media.entity.*
import com.snowplowanalytics.snowplow.media.event.*
import com.snowplowanalytics.snowplow.util.TimeTraveler
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Test
import org.junit.runner.RunWith
import java.util.Date
import kotlin.time.DurationUnit
import kotlin.time.toDuration

@RunWith(AndroidJUnit4::class)
class MediaEventAndEntitySerializationTest {

@Test
fun schemaForMediaEventTypes() {
assertEquals(mediaSchema("play_event"), MediaPlayEvent().schema)
assertEquals(mediaSchema("playback_rate_change_event"), MediaPlaybackRateChangeEvent(newRate = 1.0).schema)
assertEquals(mediaSchema("ready_event"), MediaReadyEvent().schema)
assertEquals(mediaSchema("ad_resume_event"), MediaAdResumeEvent().schema)

assertEquals(mediaSchema("ad_quartile_event"), MediaAdFirstQuartileEvent().schema)
assertEquals(mediaSchema("ad_quartile_event"), MediaAdMidpointEvent().schema)
assertEquals(mediaSchema("ad_quartile_event"), MediaAdThirdQuartileEvent().schema)
assertEquals(mediaSchema("ad_complete_event"), MediaAdCompleteEvent().schema)
}

@Test
fun buildsEntityWithDefaultValuesForEmptyMediaPlayer() {
val entity = MediaPlayerEntity().entity

assertEquals(mediaSchema("player"), entity.map["schema"] as? String)
assertEquals(0.0, (entity.map["data"] as? Map<*, *>)?.get("currentTime"))
assertEquals(true, (entity.map["data"] as? Map<*, *>)?.get("paused"))
assertEquals(false, (entity.map["data"] as? Map<*, *>)?.get("ended"))
}

@Test
fun buildsEntityForMediaPlayer() {
val entity = MediaPlayerEntity(
currentTime = 33.3,
duration = 100.0,
ended = true,
fullscreen = true,
livestream = true,
label = "The Video",
loop = true,
mediaType = MediaType.Video,
muted = true,
paused = false,
pictureInPicture = false,
playerType = "AVPlayer",
playbackRate = 2.5,
quality = "1080p",
volume = 80,
)

assertEquals(mediaSchema("player"), entity.entity.map["schema"] as? String)
val data = entity.entity.map["data"] as? Map<*, *>
assertEquals(33.3, data?.get("currentTime"))
assertEquals(100.0, data?.get("duration"))
assertEquals(true, data?.get("ended"))
assertEquals(true, data?.get("fullscreen"))
assertEquals(true, data?.get("livestream"))
assertEquals("The Video", data?.get("label"))
assertEquals(true, data?.get("loop"))
assertEquals("video", data?.get("mediaType"))
assertEquals(true, data?.get("muted"))
assertEquals(false, data?.get("paused"))
assertEquals(false, data?.get("pictureInPicture"))
assertEquals("AVPlayer", data?.get("playerType"))
assertEquals(2.5, data?.get("playbackRate"))
assertEquals("1080p", data?.get("quality"))
assertEquals(80, data?.get("volume"))
}

@Test
fun buildsMediaSessionEntity() {
val date = Date()
val timeTraveler = TimeTraveler()
val session = MediaSessionEntity(id = "xxx", startedAt = date, pingInterval = 13)
val stats = MediaSessionTrackingStats(
session = session,
dateGenerator = { timeTraveler.generateDate() },
)

stats.update(
event = MediaPlayEvent(),
player = MediaPlayerEntity(currentTime = 0.0, paused = false),
)
timeTraveler.travelBy(10.toDuration(DurationUnit.SECONDS))
stats.update(
event = MediaPauseEvent(),
player = MediaPlayerEntity(currentTime = 10.0, paused = true),
)

val entity = session.entity(stats)
assertEquals(mediaSchema("session"), entity.map["schema"] as? String)
val data = entity.map["data"] as? Map<*, *>
assertEquals("xxx", data?.get("id"))
assertEquals(getDateTimeFromDate(date), data?.get("startedAt"))
assertEquals(13, data?.get("pingInterval"))
assertEquals(10.0, data?.get("timePlayed"))
assertFalse(data?.containsKey("timePaused") ?: true)
}

@Test
fun buildsAdEntity() {
val ad = MediaAdEntity(
name = "Name",
adId = "yyy",
creativeId = "zzz",
duration = 11.0,
podPosition = 2,
skippable = true
)
val entity = ad.entity

assertEquals(mediaSchema("ad"), entity.map["schema"] as? String)
val data = entity.map["data"] as? Map<*, *>
assertEquals("Name", data?.get("name"))
assertEquals("yyy", data?.get("adId"))
assertEquals("zzz", data?.get("creativeId"))
assertEquals(11.0, data?.get("duration"))
assertEquals(2, data?.get("podPosition"))
assertEquals(true, data?.get("skippable"))
}

@Test
fun buildsAdBreakEntity() {
val adBreak = MediaAdBreakEntity(
breakId = "xxx",
name = "Break 1",
breakType = MediaAdBreakType.NonLinear,
podSize = 3
)
adBreak.startTime = 100.1
val entity = adBreak.entity

assertEquals(mediaSchema("ad_break"), entity.map["schema"] as? String)
val data = entity.map["data"] as? Map<*, *>
assertEquals("xxx", data?.get("breakId"))
assertEquals("Break 1", data?.get("name"))
assertEquals("nonlinear", data?.get("breakType"))
assertEquals(3, data?.get("podSize"))
assertEquals(100.1, data?.get("startTime"))
}

private fun mediaSchema(name: String, version: String = "1-0-0"): String {
return "iglu:com.snowplowanalytics.snowplow.media/" + name + "/jsonschema/" + version
}
}

0 comments on commit 12dc78b

Please sign in to comment.