diff --git a/.gitignore b/.gitignore index 5a979af..522f8cf 100644 --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,4 @@ out/ ### Kotlin ### .kotlin +/android-qa.log diff --git a/README.md b/README.md index 898b420..5e92e88 100644 --- a/README.md +++ b/README.md @@ -14,8 +14,13 @@ ## 실행 방법 1. STB 대여 (아래 사항들 확인 필요함) - - STB 가 접속되는 AP 이름과 비밀번호 + - STB 이 접속되는 AP 이름과 비밀번호 (QA 에 문의) - STB IP, adb 포트 번호 + - ip: + 1. 리모콘으로 번호 입력 `*7899#` + 2. 기기정보 + 3. IP 설정 + - port: 대부분 `8888` skb bko-uh600/bhx-uh600 의 경우 `5815` 1. STB 설치 1. 컴퓨터의 USB 에 캡쳐보드를 꼽고 캡쳐보드와 STB output 과 연결 1. 컴퓨터에 obs-studio 설치 및 실행 @@ -30,6 +35,11 @@ 1. application.yml 에서 anypoint.android-qa.stb 하위 정보들을 STB 의 정보로 수정 1. obs-studio 종료 후 android-qa 애플리케이션 실행 (obs-studio 종료해야 정상 작동됨) +## logcat 확인 +``` +서버를 띄우면 stb 에 알아서 붙으므로 +``` + ## TC 수행 순서 1. adb 를 사용하여 STB 이 프록시 서버를 바라보도록 변경 diff --git a/build.gradle.kts b/build.gradle.kts index 346e554..fa865b3 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -22,7 +22,6 @@ repositories { dependencies { implementation("org.springframework.boot:spring-boot-starter") implementation("org.springframework.boot:spring-boot-starter-web") - implementation("org.springframework.cloud:spring-cloud-starter-openfeign") implementation("org.jetbrains.kotlin:kotlin-reflect") testImplementation("org.springframework.boot:spring-boot-starter-test") diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 2fe63f0..fc8e70e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -9,6 +9,9 @@ kotlinx-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", kotlin-logging = { module = "io.github.microutils:kotlin-logging-jvm", version = "2.0.11" } thymeleaf = { module = "org.thymeleaf:thymeleaf", version = "3.1.2.RELEASE" } thymeleaf-spring5 = { module = "org.thymeleaf:thymeleaf-spring5", version = "3.1.2.RELEASE" } +spring-shell = { module = "org.springframework.shell:spring-shell-starter", version = "3.2.3" } +retrofit = { module = "com.squareup.retrofit2:retrofit", version = "2.11.0" } +retrofix-kotlinx-serialization = { module = "com.squareup.retrofit2:converter-kotlinx-serialization", version = "2.11.0" } kotest-runner = { module = "io.kotest:kotest-runner-junit5-jvm", version = "5.8.1" } kotest-property = { module = "io.kotest:kotest-property", version = "5.8.1" } @@ -22,7 +25,10 @@ deps = [ "kotlin-logging", "thymeleaf", "thymeleaf-spring5", - "kotlinx-coroutines" + "kotlinx-coroutines", + "spring-shell", + "retrofit", + "retrofix-kotlinx-serialization" ] test-deps = [ "kotest-runner", diff --git a/src/main/kotlin/tv/anypoint/ApplicationProperties.kt b/src/main/kotlin/tv/anypoint/ApplicationProperties.kt index a6111ff..4f55400 100644 --- a/src/main/kotlin/tv/anypoint/ApplicationProperties.kt +++ b/src/main/kotlin/tv/anypoint/ApplicationProperties.kt @@ -19,11 +19,17 @@ class ApplicationProperties { class Stb { lateinit var ip: String - var port: Int = 5555 + + /** + * 보통 8888 + * bko-uh600 / bhx-uh600 + * port : 5815 + */ + var port: Int = 8888 } class Endpoints { lateinit var auth: String lateinit var assign: String } -} \ No newline at end of file +} diff --git a/src/main/kotlin/tv/anypoint/domain/agent/ad/AssetConvertResponse.kt b/src/main/kotlin/tv/anypoint/domain/agent/ad/AssetConvertResponse.kt deleted file mode 100644 index b1829b2..0000000 --- a/src/main/kotlin/tv/anypoint/domain/agent/ad/AssetConvertResponse.kt +++ /dev/null @@ -1,3 +0,0 @@ -package tv.anypoint.domain.agent.ad - -data class AssetConvertResponse(val id: Long) \ No newline at end of file diff --git a/src/main/kotlin/tv/anypoint/domain/agent/ad/VastResponse.kt b/src/main/kotlin/tv/anypoint/domain/agent/ad/VastResponse.kt deleted file mode 100644 index 8d2d897..0000000 --- a/src/main/kotlin/tv/anypoint/domain/agent/ad/VastResponse.kt +++ /dev/null @@ -1,3 +0,0 @@ -package tv.anypoint.domain.agent.ad - -data class VastResponse(val id: Long) \ No newline at end of file diff --git a/src/main/kotlin/tv/anypoint/dsl/Dsl.kt b/src/main/kotlin/tv/anypoint/dsl/Dsl.kt index 64ccb0d..aa0cb07 100644 --- a/src/main/kotlin/tv/anypoint/dsl/Dsl.kt +++ b/src/main/kotlin/tv/anypoint/dsl/Dsl.kt @@ -1,10 +1,16 @@ package tv.anypoint.dsl -import tv.anypoint.domain.adb.ExtraKey -import tv.anypoint.domain.adb.IntentAction +import kotlinx.coroutines.delay +import kotlinx.coroutines.runBlocking +import tv.anypoint.proxy.model.adb.ExtraKey +import tv.anypoint.proxy.model.adb.IntentAction import tv.anypoint.dsl.handler.HttpHandler +import tv.anypoint.dsl.model.Log import tv.anypoint.dsl.model.Tc import tv.anypoint.dsl.service.TestCase +import java.io.BufferedReader +import java.io.FileReader +import java.io.LineNumberReader import java.time.LocalDateTime import java.time.temporal.ChronoUnit import java.util.concurrent.TimeoutException @@ -17,31 +23,37 @@ inline fun http( block: HttpHandler.() -> Unit ) = HttpHandler().also { block(it) } -data class Adb( - val a: IntentAction, - val es: Map -) - -inline fun adb( +fun adb( a: IntentAction, es: Map ) { - val adb = Adb(a, es) val aStr = "-a $a" val esStr: String = es.entries.joinToString(" ") { "--es ${it.key.string} ${it.value}" } println("[ADB] adb shell am broadcast $aStr $esStr") } -fun TestCase.expected( +fun Log.expected( expectedLog: String, timeout: Long = 10_000L ) { + val log = this val startedAt = LocalDateTime.now() + val reader = LineNumberReader(BufferedReader(FileReader(log.logcatFilePath))) while (true) { val now = LocalDateTime.now() if (ChronoUnit.MILLIS.between(startedAt, now) > timeout) { - throw TimeoutException("failed to find log. expectedLog: $expectedLog, absoluteFilePath: ${this.tc.logInfo.absoluteFilePath}") + throw TimeoutException("failed to find log. expectedLog: $expectedLog") + } + + reader.lineNumber = log.cursor + + var line: String? + while ((reader.readLine().also { line = it }) != null) { + if (line!!.contains(expectedLog)) { + return + } + log.cursor = reader.lineNumber } - // TODO: tc 로그에서 expectedLog 찾기 tc.logInfo.cursor ~ 마지막 라인까지의 로그 중에 찾으면 됨 + Thread.sleep(100) } } diff --git a/src/main/kotlin/tv/anypoint/dsl/TestCaseConfig.kt b/src/main/kotlin/tv/anypoint/dsl/TestCaseConfig.kt new file mode 100644 index 0000000..0ac2e5c --- /dev/null +++ b/src/main/kotlin/tv/anypoint/dsl/TestCaseConfig.kt @@ -0,0 +1,13 @@ +package tv.anypoint.dsl + +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.context.annotation.Scope +import tv.anypoint.dsl.model.TcContext + +@Configuration +class TestCaseConfig { + @Bean + @Scope("singleton") + fun tcContext(): TcContext = TcContext() +} diff --git a/src/main/kotlin/tv/anypoint/dsl/exception/HttpValidationException.kt b/src/main/kotlin/tv/anypoint/dsl/exception/HttpValidationException.kt index 76dadca..ac624ee 100644 --- a/src/main/kotlin/tv/anypoint/dsl/exception/HttpValidationException.kt +++ b/src/main/kotlin/tv/anypoint/dsl/exception/HttpValidationException.kt @@ -3,10 +3,9 @@ package tv.anypoint.dsl.exception import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.encodeToJsonElement -import tv.anypoint.domain.agent.AuthResponse -import tv.anypoint.domain.agent.ad.AdsResponse -import tv.anypoint.domain.agent.ad.AssetConvertResponse -import tv.anypoint.domain.agent.ad.VastResponse +import tv.anypoint.proxy.model.agent.ad.AdsResponse +import tv.anypoint.proxy.model.agent.ad.AssetConvertResponse +import tv.anypoint.proxy.model.agent.ad.VastResponse class HttpValidationException( type: HttpValidationExceptionType, @@ -27,11 +26,11 @@ inline fun httpValidationError( response: T ): HttpValidationException = HttpValidationException( type = when (T::class) { - AuthResponse::class -> HttpValidationExceptionType.AUTH + tv.anypoint.proxy.model.agent.AuthResponse::class -> HttpValidationExceptionType.AUTH AdsResponse::class -> HttpValidationExceptionType.ADS VastResponse::class -> HttpValidationExceptionType.VAST AssetConvertResponse::class -> HttpValidationExceptionType.ASSET_CONVERT else -> HttpValidationExceptionType.NONE }, response = Json.encodeToJsonElement(response) -) \ No newline at end of file +) diff --git a/src/main/kotlin/tv/anypoint/dsl/model/Log.kt b/src/main/kotlin/tv/anypoint/dsl/model/Log.kt new file mode 100644 index 0000000..6f779cc --- /dev/null +++ b/src/main/kotlin/tv/anypoint/dsl/model/Log.kt @@ -0,0 +1,12 @@ +package tv.anypoint.dsl.model + +import kotlinx.serialization.Serializable + +@Serializable +class Log( + val logcatFilePath: String +) { + var startLine: Int = 0 + var cursor: Int = 0 + var lastLine: Int = 0 +} diff --git a/src/main/kotlin/tv/anypoint/dsl/model/LogInfo.kt b/src/main/kotlin/tv/anypoint/dsl/model/LogInfo.kt deleted file mode 100644 index 4d201f0..0000000 --- a/src/main/kotlin/tv/anypoint/dsl/model/LogInfo.kt +++ /dev/null @@ -1,8 +0,0 @@ -package tv.anypoint.dsl.model - -class LogInfo( - val absoluteFilePath: String -) { - var cursor: Long = 0 - var lastLine: Long = 0 -} \ No newline at end of file diff --git a/src/main/kotlin/tv/anypoint/dsl/model/RecordingInfo.kt b/src/main/kotlin/tv/anypoint/dsl/model/Recording.kt similarity index 95% rename from src/main/kotlin/tv/anypoint/dsl/model/RecordingInfo.kt rename to src/main/kotlin/tv/anypoint/dsl/model/Recording.kt index 93280ba..8a3b825 100644 --- a/src/main/kotlin/tv/anypoint/dsl/model/RecordingInfo.kt +++ b/src/main/kotlin/tv/anypoint/dsl/model/Recording.kt @@ -5,7 +5,7 @@ import kotlinx.serialization.Serializable import java.time.LocalDateTime @Serializable -data class RecordingInfo( +data class Recording( val absoluteFilePath: String ) { @Contextual @@ -25,4 +25,4 @@ data class RecordingInfo( } -} \ No newline at end of file +} diff --git a/src/main/kotlin/tv/anypoint/dsl/model/Tc.kt b/src/main/kotlin/tv/anypoint/dsl/model/Tc.kt index b2901c3..50b054b 100644 --- a/src/main/kotlin/tv/anypoint/dsl/model/Tc.kt +++ b/src/main/kotlin/tv/anypoint/dsl/model/Tc.kt @@ -1,11 +1,11 @@ package tv.anypoint.dsl.model -import tv.anypoint.domain.agent.AuthResponse -import tv.anypoint.domain.agent.ad.AdsResponse -import tv.anypoint.domain.agent.ad.AssetConvertResponse -import tv.anypoint.domain.agent.ad.VastResponse import tv.anypoint.dsl.handler.HttpHandler import tv.anypoint.dsl.http +import tv.anypoint.proxy.model.agent.AuthResponse +import tv.anypoint.proxy.model.agent.ad.AdsResponse +import tv.anypoint.proxy.model.agent.ad.AssetConvertResponse +import tv.anypoint.proxy.model.agent.ad.VastResponse import java.time.LocalDateTime class Tc { @@ -26,6 +26,6 @@ class Tc { lateinit var finishedAt: LocalDateTime var result: Boolean? = null - lateinit var logInfo: LogInfo - var recordingInfo: RecordingInfo? = null -} \ No newline at end of file + lateinit var log: Log + var recording: Recording? = null +} diff --git a/src/main/kotlin/tv/anypoint/dsl/model/TcContext.kt b/src/main/kotlin/tv/anypoint/dsl/model/TcContext.kt new file mode 100644 index 0000000..721f15a --- /dev/null +++ b/src/main/kotlin/tv/anypoint/dsl/model/TcContext.kt @@ -0,0 +1,5 @@ +package tv.anypoint.dsl.model + +class TcContext { + var tc: Tc = Tc() +} diff --git a/src/main/kotlin/tv/anypoint/dsl/model/TcFailEvent.kt b/src/main/kotlin/tv/anypoint/dsl/model/TcFailEvent.kt new file mode 100644 index 0000000..514b630 --- /dev/null +++ b/src/main/kotlin/tv/anypoint/dsl/model/TcFailEvent.kt @@ -0,0 +1,9 @@ +package tv.anypoint.dsl.model + +import kotlinx.serialization.Serializable + +@Serializable +data class TcFailEvent( + val tcName: String, + val errorMessage: String +) diff --git a/src/main/kotlin/tv/anypoint/dsl/serialization/JsonConfiguration.kt b/src/main/kotlin/tv/anypoint/dsl/serialization/JsonConfiguration.kt index 2a6beed..262d9fe 100644 --- a/src/main/kotlin/tv/anypoint/dsl/serialization/JsonConfiguration.kt +++ b/src/main/kotlin/tv/anypoint/dsl/serialization/JsonConfiguration.kt @@ -11,9 +11,10 @@ import java.time.LocalDateTime class JsonConfiguration { @Bean fun json(): Json = Json { + ignoreUnknownKeys = true serializersModule = SerializersModule { contextual(LocalDate::class, LocalDateSerializer()) contextual(LocalDateTime::class, LocalDateTimeSerializer()) } } -} \ No newline at end of file +} diff --git a/src/main/kotlin/tv/anypoint/dsl/service/AdbTransmitter.kt b/src/main/kotlin/tv/anypoint/dsl/service/AdbTransmitter.kt deleted file mode 100644 index 1b992e2..0000000 --- a/src/main/kotlin/tv/anypoint/dsl/service/AdbTransmitter.kt +++ /dev/null @@ -1,11 +0,0 @@ -package tv.anypoint.dsl.service - -import org.springframework.stereotype.Service -import tv.anypoint.dsl.Adb - -@Service -class AdbTransmitter { - fun transmit(req: Adb) { - - } -} \ No newline at end of file diff --git a/src/main/kotlin/tv/anypoint/dsl/service/TestCase.kt b/src/main/kotlin/tv/anypoint/dsl/service/TestCase.kt index 5c989cb..cc9b499 100644 --- a/src/main/kotlin/tv/anypoint/dsl/service/TestCase.kt +++ b/src/main/kotlin/tv/anypoint/dsl/service/TestCase.kt @@ -2,15 +2,11 @@ package tv.anypoint.dsl.service import mu.KLogging import org.springframework.beans.factory.annotation.Autowired +import org.springframework.context.event.EventListener import org.springframework.stereotype.Component import tv.anypoint.ApplicationProperties -import tv.anypoint.domain.agent.AuthResponse -import tv.anypoint.domain.agent.ad.AdsResponse -import tv.anypoint.domain.agent.ad.AssetConvertResponse -import tv.anypoint.domain.agent.ad.VastResponse -import tv.anypoint.dsl.exception.httpValidationError -import tv.anypoint.dsl.model.LogInfo -import tv.anypoint.dsl.model.RecordingInfo +import tv.anypoint.dsl.model.Log +import tv.anypoint.dsl.model.Recording import tv.anypoint.dsl.model.Tc import java.time.LocalDateTime @@ -25,14 +21,13 @@ abstract class TestCase { @Autowired private lateinit var applicationProperties: ApplicationProperties - @Autowired - private lateinit var adbTransmitter: AdbTransmitter - @Autowired private lateinit var captureBoardRecorder: CaptureBoardRecorder fun executeTest() { startToDumpLog() + startRecording(tc) + tc = init() logger.info("[${tc.number}] starting...") @@ -45,12 +40,6 @@ abstract class TestCase { // TODO } - - auth() - ads() - vast() - assetConvert() - startRecording(tc) test() stopRecording(tc) @@ -67,7 +56,7 @@ abstract class TestCase { } private fun startToDumpLog() { - tc.logInfo = LogInfo("${applicationProperties.fileRoot}/${tc.number}.log") + tc.log = Log("${applicationProperties.fileRoot}/${tc.number}.log") // TODO 이 함수 호출된 후 main.log 에서 쌓이는 로그를 ${tc.number}.log 파일 새로 만들어서 적재 시작 } @@ -75,57 +64,22 @@ abstract class TestCase { // TODO main.log >> ${tc.number}.log 적재 종료 } - private fun auth() { - // TODO: auth - val actualResponse = AuthResponse(0) - val response = tc.auth.convert(actualResponse) - if (!tc.auth.validate(response)) { - throw httpValidationError(response) - } - tc.authResponse = tc.auth.convert(response) - } + @EventListener + fun failed() { - private fun ads() { - // TODO: auth - val actualResponse = AdsResponse() - val response = tc.ads.convert(actualResponse) - if (!tc.ads.validate(response)) { - throw httpValidationError(response) - } - tc.adsResponse = tc.ads.convert(response) - } - - private fun vast() { - // TODO: auth - val actualResponse = VastResponse(0) - val response = tc.vast.convert(actualResponse) - if (!tc.vast.validate(response)) { - throw httpValidationError(response) - } - tc.vastResponse = tc.vast.convert(response) - } - - private fun assetConvert() { - // TODO: auth - val actualResponse = AssetConvertResponse(0) - val response = tc.assetConvert.convert(actualResponse) - if (!tc.assetConvert.validate(response)) { - throw httpValidationError(response) - } - tc.assetConvertResponse = tc.assetConvert.convert(response) } private fun startRecording(tc: Tc) { - tc.recordingInfo = RecordingInfo("${applicationProperties.fileRoot}/${tc.number}.mp4") - logger.debug("start to record. info: {}", tc.recordingInfo) + tc.recording = Recording("${applicationProperties.fileRoot}/${tc.number}.mp4") + logger.debug("start to record. info: {}", tc.recording) // TODO } private fun stopRecording(tc: Tc) { - tc.recordingInfo!!.finish(1000L) + tc.recording!!.finish(1000L) // TODO - logger.debug("finished to record. info: {}", tc.recordingInfo) + logger.debug("finished to record. info: {}", tc.recording) } companion object : KLogging() -} \ No newline at end of file +} diff --git a/src/main/kotlin/tv/anypoint/proxy/adapter/AssignAdapter.kt b/src/main/kotlin/tv/anypoint/proxy/adapter/AssignAdapter.kt new file mode 100644 index 0000000..e2e03a9 --- /dev/null +++ b/src/main/kotlin/tv/anypoint/proxy/adapter/AssignAdapter.kt @@ -0,0 +1,41 @@ +package tv.anypoint.proxy.adapter + +import kotlinx.serialization.json.Json +import okhttp3.MediaType.Companion.toMediaType +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import retrofit2.Call +import retrofit2.Retrofit +import retrofit2.converter.kotlinx.serialization.asConverterFactory +import retrofit2.http.Body +import retrofit2.http.POST +import retrofit2.http.Query +import tv.anypoint.ApplicationProperties +import tv.anypoint.proxy.model.agent.ad.AdsResponse +import tv.anypoint.proxy.model.agent.ad.AdsSyncRequest + +interface AssignAdapter { + @POST("/v3/device/ads") + fun ads( + @Query("deviceId") deviceId: Long, + @Query("freeStorage") freeStorage: Long = 0, + @Query("usedStorage") usedStorage: Long = 0 + ): Call + + @POST("/v3/device/sync/ads") + fun syncAds( + @Body request: AdsSyncRequest + ) +} + +@Configuration +class AssignAdapterConfiguration { + @Bean + fun assignAdapter(json: Json, applicationProperties: ApplicationProperties): AssignAdapter { + val retrofit = Retrofit.Builder() + .baseUrl(applicationProperties.endpoints.assign) + .addConverterFactory(json.asConverterFactory("application/json".toMediaType())) + .build() + return retrofit.create(AssignAdapter::class.java) + } +} diff --git a/src/main/kotlin/tv/anypoint/proxy/adapter/AuthAdapter.kt b/src/main/kotlin/tv/anypoint/proxy/adapter/AuthAdapter.kt new file mode 100644 index 0000000..e9e1f41 --- /dev/null +++ b/src/main/kotlin/tv/anypoint/proxy/adapter/AuthAdapter.kt @@ -0,0 +1,34 @@ +package tv.anypoint.proxy.adapter + +import kotlinx.serialization.json.Json +import okhttp3.MediaType.Companion.toMediaType +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import retrofit2.Call +import retrofit2.Retrofit +import retrofit2.converter.kotlinx.serialization.asConverterFactory +import retrofit2.http.Body +import retrofit2.http.POST +import tv.anypoint.ApplicationProperties +import tv.anypoint.proxy.model.agent.AuthRequest +import tv.anypoint.proxy.model.agent.AuthResponse + + +interface AuthAdapter { + @POST("/v3/device/auth") + fun auth( + @Body request: AuthRequest + ): Call +} + +@Configuration +class AuthAdapterConfiguration { + @Bean + fun authAdapter(json: Json, applicationProperties: ApplicationProperties): AuthAdapter { + val retrofit = Retrofit.Builder() + .baseUrl(applicationProperties.endpoints.auth) + .addConverterFactory(json.asConverterFactory("application/json".toMediaType())) + .build() + return retrofit.create(AuthAdapter::class.java) + } +} diff --git a/src/main/kotlin/tv/anypoint/proxy/adapter/DeviceV3Adapter.kt b/src/main/kotlin/tv/anypoint/proxy/adapter/DeviceV3Adapter.kt deleted file mode 100644 index 83916ce..0000000 --- a/src/main/kotlin/tv/anypoint/proxy/adapter/DeviceV3Adapter.kt +++ /dev/null @@ -1,30 +0,0 @@ -package tv.anypoint.proxy.adapter - -import org.springframework.cloud.openfeign.FeignClient -import org.springframework.web.bind.annotation.PostMapping -import org.springframework.web.bind.annotation.RequestBody -import org.springframework.web.bind.annotation.RequestParam -import tv.anypoint.domain.agent.AuthRequest -import tv.anypoint.domain.agent.AuthResponse -import tv.anypoint.domain.agent.ad.AdsResponse -import tv.anypoint.domain.agent.ad.AdsSyncRequest - - -@FeignClient( - name = "DeviceV3Adapter", - path = "/v3/device" -) -interface DeviceV3Adapter { - @PostMapping("/auth") - fun auth(@RequestBody body: AuthRequest): AuthResponse - - @PostMapping("/ads") - fun ads( - @RequestParam(required = true) deviceId: Long, - @RequestParam("freeStorage", required = true, defaultValue = "0") freeStorage: Long = 0, - @RequestParam("usedStorage", required = true, defaultValue = "0") usedStorage: Long = 0 - ): AdsResponse - - @PostMapping("/sync/ads") - fun syncAds(@RequestBody request: AdsSyncRequest) -} diff --git a/src/main/kotlin/tv/anypoint/proxy/config/ProxyApplicationConfig.kt b/src/main/kotlin/tv/anypoint/proxy/config/ProxyApplicationConfig.kt new file mode 100644 index 0000000..4c7abe5 --- /dev/null +++ b/src/main/kotlin/tv/anypoint/proxy/config/ProxyApplicationConfig.kt @@ -0,0 +1,32 @@ +package tv.anypoint.proxy.config + +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +import mu.KLogging +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import se.vidstige.jadb.JadbDevice +import tv.anypoint.ApplicationProperties +import tv.anypoint.proxy.util.JadbDeviceSerialUtil +import tv.anypoint.proxy.util.NetworkUtil +import tv.anypoint.proxy.util.getDevice +import java.io.File + +@Configuration +class ProxyApplicationConfig( + private val applicationProperties: ApplicationProperties +) { + @Bean + fun jadbDevice(): JadbDevice = getDevice( + stbIp = applicationProperties.stb.ip, + stbPort = applicationProperties.stb.port + ) + + @Bean + fun serverIp(jadbDevice: JadbDevice): String = NetworkUtil.getPrivateIpV4( + JadbDeviceSerialUtil.serialToAddress(jadbDevice.serial) + ?: throw RuntimeException("Can't get server ip address") + ) + + companion object : KLogging() +} diff --git a/src/main/kotlin/tv/anypoint/domain/adb/ChangeCommand.kt b/src/main/kotlin/tv/anypoint/proxy/model/adb/ChangeCommand.kt similarity index 67% rename from src/main/kotlin/tv/anypoint/domain/adb/ChangeCommand.kt rename to src/main/kotlin/tv/anypoint/proxy/model/adb/ChangeCommand.kt index 4f1c583..9e6fc62 100644 --- a/src/main/kotlin/tv/anypoint/domain/adb/ChangeCommand.kt +++ b/src/main/kotlin/tv/anypoint/proxy/model/adb/ChangeCommand.kt @@ -1,4 +1,4 @@ -package tv.anypoint.domain.adb +package tv.anypoint.proxy.model.adb enum class ChangeCommand { CHANGE_API_ENDPOINT, diff --git a/src/main/kotlin/tv/anypoint/domain/adb/ExtraKey.kt b/src/main/kotlin/tv/anypoint/proxy/model/adb/ExtraKey.kt similarity index 82% rename from src/main/kotlin/tv/anypoint/domain/adb/ExtraKey.kt rename to src/main/kotlin/tv/anypoint/proxy/model/adb/ExtraKey.kt index 2df7283..c140af5 100644 --- a/src/main/kotlin/tv/anypoint/domain/adb/ExtraKey.kt +++ b/src/main/kotlin/tv/anypoint/proxy/model/adb/ExtraKey.kt @@ -1,8 +1,8 @@ -package tv.anypoint.domain.adb +package tv.anypoint.proxy.model.adb enum class ExtraKey(val string: String) { EXTRA_JSON("EXTRA_JSON"), CHANGE_COMMAND("change.command"), API_ENDPOINT("api.endpoint"), TEST_DEVICE("test.device") -} \ No newline at end of file +} diff --git a/src/main/kotlin/tv/anypoint/domain/adb/IntentAction.kt b/src/main/kotlin/tv/anypoint/proxy/model/adb/IntentAction.kt similarity index 75% rename from src/main/kotlin/tv/anypoint/domain/adb/IntentAction.kt rename to src/main/kotlin/tv/anypoint/proxy/model/adb/IntentAction.kt index 7a12bb7..7ab3170 100644 --- a/src/main/kotlin/tv/anypoint/domain/adb/IntentAction.kt +++ b/src/main/kotlin/tv/anypoint/proxy/model/adb/IntentAction.kt @@ -1,5 +1,5 @@ -package tv.anypoint.domain.adb +package tv.anypoint.proxy.model.adb enum class IntentAction(val string: String) { CHANGE_TEST_PROPERTY("tv.anypoint.agent.app.CHANGE_TEST_PROPERTY") -} \ No newline at end of file +} diff --git a/src/main/kotlin/tv/anypoint/domain/agent/AuthRequest.kt b/src/main/kotlin/tv/anypoint/proxy/model/agent/AuthRequest.kt similarity index 85% rename from src/main/kotlin/tv/anypoint/domain/agent/AuthRequest.kt rename to src/main/kotlin/tv/anypoint/proxy/model/agent/AuthRequest.kt index eb05ef1..28b4cde 100644 --- a/src/main/kotlin/tv/anypoint/domain/agent/AuthRequest.kt +++ b/src/main/kotlin/tv/anypoint/proxy/model/agent/AuthRequest.kt @@ -1,5 +1,8 @@ -package tv.anypoint.domain.agent +package tv.anypoint.proxy.model.agent +import kotlinx.serialization.Serializable + +@Serializable data class AuthRequest( var fingerPrint: String = "", val soId: Int = 0, diff --git a/src/main/kotlin/tv/anypoint/domain/agent/AuthResponse.kt b/src/main/kotlin/tv/anypoint/proxy/model/agent/AuthResponse.kt similarity index 87% rename from src/main/kotlin/tv/anypoint/domain/agent/AuthResponse.kt rename to src/main/kotlin/tv/anypoint/proxy/model/agent/AuthResponse.kt index 3b60659..1e3eb77 100644 --- a/src/main/kotlin/tv/anypoint/domain/agent/AuthResponse.kt +++ b/src/main/kotlin/tv/anypoint/proxy/model/agent/AuthResponse.kt @@ -1,7 +1,9 @@ -package tv.anypoint.domain.agent +package tv.anypoint.proxy.model.agent +import kotlinx.serialization.Serializable import java.util.concurrent.TimeUnit +@Serializable data class AuthResponse( var deviceId: Long = 0, var deviceTypeId: Int = 0, @@ -25,10 +27,11 @@ data class AuthResponse( fun authorization() = "Anypoint ${accessToken ?: ""}" fun monitoringInterval() = monitoringInterval.let { - if (it < MINIMUM_MONITOR_INTERVAL) DEFAULT_MONITOR_PERIOD else it + if (it < tv.anypoint.proxy.model.agent.AuthResponse.Companion.MINIMUM_MONITOR_INTERVAL) tv.anypoint.proxy.model.agent.AuthResponse.Companion.DEFAULT_MONITOR_PERIOD else it } } +@Serializable data class Endpoints( var auth: String = "", var requestAds: String = "", @@ -54,6 +57,7 @@ enum class VideoMediaType { } // WARNING: local db(data class Device)의 Embedded 엔티티이므로 프로퍼티 변경 시, db 버전 올려야 함 +@Serializable data class AdConfig( var maxDownloadBandwidth: Long = 150 * 1024, var maxLazyDownloadBandwidth: Long = 1024 * 1024, @@ -68,8 +72,8 @@ data class AdConfig( var remnantTimeThreshold: Int = 2_000, var maxEndAdPlaytime: Int = 15_000, var transitionDelay: Int = 0, - var videoPlayMode: VideoPlayMode = VideoPlayMode.PARALLEL, - var videoMediaType: VideoMediaType = VideoMediaType.CONCATENATING, + var videoPlayMode: tv.anypoint.proxy.model.agent.VideoPlayMode = tv.anypoint.proxy.model.agent.VideoPlayMode.PARALLEL, + var videoMediaType: tv.anypoint.proxy.model.agent.VideoMediaType = tv.anypoint.proxy.model.agent.VideoMediaType.CONCATENATING, var startDelay: Int = 0, var stopDelay: Int = 0, var startRenderDelay: Int = 0, @@ -102,6 +106,7 @@ data class AdConfig( ) // WARNING: local db(data class Device)의 Embedded 엔티티이므로 프로퍼티 변경 시, db 버전 올려야 함 +@Serializable data class KidWatermark( var imageUrl: String = "", var crc: Long = 0, @@ -109,4 +114,4 @@ data class KidWatermark( var top: Int = 0, var width: Int = 0, var height: Int = 0 -) \ No newline at end of file +) diff --git a/src/main/kotlin/tv/anypoint/domain/agent/Cue.kt b/src/main/kotlin/tv/anypoint/proxy/model/agent/Cue.kt similarity index 95% rename from src/main/kotlin/tv/anypoint/domain/agent/Cue.kt rename to src/main/kotlin/tv/anypoint/proxy/model/agent/Cue.kt index a002159..d5faa41 100644 --- a/src/main/kotlin/tv/anypoint/domain/agent/Cue.kt +++ b/src/main/kotlin/tv/anypoint/proxy/model/agent/Cue.kt @@ -1,4 +1,4 @@ -package tv.anypoint.domain.agent +package tv.anypoint.proxy.model.agent data class Cue( var id: Long = 0, diff --git a/src/main/kotlin/tv/anypoint/domain/agent/CueOwner.kt b/src/main/kotlin/tv/anypoint/proxy/model/agent/CueOwner.kt similarity index 84% rename from src/main/kotlin/tv/anypoint/domain/agent/CueOwner.kt rename to src/main/kotlin/tv/anypoint/proxy/model/agent/CueOwner.kt index 224eb57..39cc0f7 100644 --- a/src/main/kotlin/tv/anypoint/domain/agent/CueOwner.kt +++ b/src/main/kotlin/tv/anypoint/proxy/model/agent/CueOwner.kt @@ -1,4 +1,4 @@ -package tv.anypoint.domain.agent +package tv.anypoint.proxy.model.agent enum class CueOwner { PP, diff --git a/src/main/kotlin/tv/anypoint/domain/agent/PlayType.kt b/src/main/kotlin/tv/anypoint/proxy/model/agent/PlayType.kt similarity index 60% rename from src/main/kotlin/tv/anypoint/domain/agent/PlayType.kt rename to src/main/kotlin/tv/anypoint/proxy/model/agent/PlayType.kt index e923051..3e1867f 100644 --- a/src/main/kotlin/tv/anypoint/domain/agent/PlayType.kt +++ b/src/main/kotlin/tv/anypoint/proxy/model/agent/PlayType.kt @@ -1,7 +1,7 @@ -package tv.anypoint.domain.agent +package tv.anypoint.proxy.model.agent enum class PlayType { DNP, LAZY_DNP, STREAMING -} \ No newline at end of file +} diff --git a/src/main/kotlin/tv/anypoint/domain/agent/PlayerStateCheckParam.kt b/src/main/kotlin/tv/anypoint/proxy/model/agent/PlayerStateCheckParam.kt similarity index 56% rename from src/main/kotlin/tv/anypoint/domain/agent/PlayerStateCheckParam.kt rename to src/main/kotlin/tv/anypoint/proxy/model/agent/PlayerStateCheckParam.kt index f334fa4..c72493b 100644 --- a/src/main/kotlin/tv/anypoint/domain/agent/PlayerStateCheckParam.kt +++ b/src/main/kotlin/tv/anypoint/proxy/model/agent/PlayerStateCheckParam.kt @@ -1,6 +1,9 @@ -package tv.anypoint.domain.agent +package tv.anypoint.proxy.model.agent +import kotlinx.serialization.Serializable + +@Serializable data class PlayerStateCheckParam( val playerStateCheckDuration: Int = 1500, val maxPlayerInvalidateInterval: Int = 300 -) \ No newline at end of file +) diff --git a/src/main/kotlin/tv/anypoint/domain/agent/ProgramProviderChannel.kt b/src/main/kotlin/tv/anypoint/proxy/model/agent/ProgramProviderChannel.kt similarity index 93% rename from src/main/kotlin/tv/anypoint/domain/agent/ProgramProviderChannel.kt rename to src/main/kotlin/tv/anypoint/proxy/model/agent/ProgramProviderChannel.kt index c737536..7dbc1cc 100644 --- a/src/main/kotlin/tv/anypoint/domain/agent/ProgramProviderChannel.kt +++ b/src/main/kotlin/tv/anypoint/proxy/model/agent/ProgramProviderChannel.kt @@ -1,5 +1,8 @@ -package tv.anypoint.domain.agent +package tv.anypoint.proxy.model.agent +import kotlinx.serialization.Serializable + +@Serializable data class ProgramProviderChannel( val id: Int, val delay: Int = 0, diff --git a/src/main/kotlin/tv/anypoint/domain/agent/StateChangeLog.kt b/src/main/kotlin/tv/anypoint/proxy/model/agent/StateChangeLog.kt similarity index 92% rename from src/main/kotlin/tv/anypoint/domain/agent/StateChangeLog.kt rename to src/main/kotlin/tv/anypoint/proxy/model/agent/StateChangeLog.kt index f6167c8..04896f8 100644 --- a/src/main/kotlin/tv/anypoint/domain/agent/StateChangeLog.kt +++ b/src/main/kotlin/tv/anypoint/proxy/model/agent/StateChangeLog.kt @@ -1,4 +1,4 @@ -package tv.anypoint.domain.agent +package tv.anypoint.proxy.model.agent data class StateChangeLog( val soId: Int = 0, diff --git a/src/main/kotlin/tv/anypoint/domain/agent/ad/Ad.kt b/src/main/kotlin/tv/anypoint/proxy/model/agent/ad/Ad.kt similarity index 86% rename from src/main/kotlin/tv/anypoint/domain/agent/ad/Ad.kt rename to src/main/kotlin/tv/anypoint/proxy/model/agent/ad/Ad.kt index 194a00b..eb90618 100644 --- a/src/main/kotlin/tv/anypoint/domain/agent/ad/Ad.kt +++ b/src/main/kotlin/tv/anypoint/proxy/model/agent/ad/Ad.kt @@ -1,5 +1,8 @@ -package tv.anypoint.domain.agent.ad +package tv.anypoint.proxy.model.agent.ad +import kotlinx.serialization.Serializable + +@Serializable data class Ad( val id: Long, val adRequest: AdRequest? = null, @@ -25,4 +28,4 @@ enum class CampaignType { HOUSE, BUFFER, ENDING -} \ No newline at end of file +} diff --git a/src/main/kotlin/tv/anypoint/domain/agent/ad/AdRequest.kt b/src/main/kotlin/tv/anypoint/proxy/model/agent/ad/AdRequest.kt similarity index 83% rename from src/main/kotlin/tv/anypoint/domain/agent/ad/AdRequest.kt rename to src/main/kotlin/tv/anypoint/proxy/model/agent/ad/AdRequest.kt index 8eed862..e94d048 100644 --- a/src/main/kotlin/tv/anypoint/domain/agent/ad/AdRequest.kt +++ b/src/main/kotlin/tv/anypoint/proxy/model/agent/ad/AdRequest.kt @@ -1,6 +1,7 @@ -package tv.anypoint.domain.agent.ad +package tv.anypoint.proxy.model.agent.ad -import tv.anypoint.domain.agent.PlayType +import kotlinx.serialization.Serializable +import tv.anypoint.proxy.model.agent.PlayType enum class EncodingType { @@ -14,6 +15,7 @@ enum class ExtAdProtocol { GOOGLE_AD } +@Serializable data class AdRequest( var url: String, val updateInterval: Int = 0, diff --git a/src/main/kotlin/tv/anypoint/domain/agent/ad/AdsResponse.kt b/src/main/kotlin/tv/anypoint/proxy/model/agent/ad/AdsResponse.kt similarity index 74% rename from src/main/kotlin/tv/anypoint/domain/agent/ad/AdsResponse.kt rename to src/main/kotlin/tv/anypoint/proxy/model/agent/ad/AdsResponse.kt index eb05383..3636f5a 100644 --- a/src/main/kotlin/tv/anypoint/domain/agent/ad/AdsResponse.kt +++ b/src/main/kotlin/tv/anypoint/proxy/model/agent/ad/AdsResponse.kt @@ -1,5 +1,8 @@ -package tv.anypoint.domain.agent.ad +package tv.anypoint.proxy.model.agent.ad +import kotlinx.serialization.Serializable + +@Serializable data class AdsResponse( val status: AdListResponseStatus = AdListResponseStatus.OK, val ads: List = listOf(), diff --git a/src/main/kotlin/tv/anypoint/domain/agent/ad/AdsSyncRequest.kt b/src/main/kotlin/tv/anypoint/proxy/model/agent/ad/AdsSyncRequest.kt similarity index 79% rename from src/main/kotlin/tv/anypoint/domain/agent/ad/AdsSyncRequest.kt rename to src/main/kotlin/tv/anypoint/proxy/model/agent/ad/AdsSyncRequest.kt index daebc73..fa51645 100644 --- a/src/main/kotlin/tv/anypoint/domain/agent/ad/AdsSyncRequest.kt +++ b/src/main/kotlin/tv/anypoint/proxy/model/agent/ad/AdsSyncRequest.kt @@ -1,4 +1,4 @@ -package tv.anypoint.domain.agent.ad +package tv.anypoint.proxy.model.agent.ad data class AdsSyncRequest( val deviceId: Long, diff --git a/src/main/kotlin/tv/anypoint/domain/agent/ad/Asset.kt b/src/main/kotlin/tv/anypoint/proxy/model/agent/ad/Asset.kt similarity index 65% rename from src/main/kotlin/tv/anypoint/domain/agent/ad/Asset.kt rename to src/main/kotlin/tv/anypoint/proxy/model/agent/ad/Asset.kt index 2c313b3..c834800 100644 --- a/src/main/kotlin/tv/anypoint/domain/agent/ad/Asset.kt +++ b/src/main/kotlin/tv/anypoint/proxy/model/agent/ad/Asset.kt @@ -1,7 +1,9 @@ -package tv.anypoint.domain.agent.ad +package tv.anypoint.proxy.model.agent.ad -import tv.anypoint.domain.agent.PlayType +import kotlinx.serialization.Serializable +import tv.anypoint.proxy.model.agent.PlayType +@Serializable data class Asset( var assetId: Long = 0L, var crc: String = "0", diff --git a/src/main/kotlin/tv/anypoint/proxy/model/agent/ad/AssetConvertResponse.kt b/src/main/kotlin/tv/anypoint/proxy/model/agent/ad/AssetConvertResponse.kt new file mode 100644 index 0000000..986aafe --- /dev/null +++ b/src/main/kotlin/tv/anypoint/proxy/model/agent/ad/AssetConvertResponse.kt @@ -0,0 +1,3 @@ +package tv.anypoint.proxy.model.agent.ad + +data class AssetConvertResponse(val id: Long) diff --git a/src/main/kotlin/tv/anypoint/domain/agent/ad/Click.kt b/src/main/kotlin/tv/anypoint/proxy/model/agent/ad/Click.kt similarity index 57% rename from src/main/kotlin/tv/anypoint/domain/agent/ad/Click.kt rename to src/main/kotlin/tv/anypoint/proxy/model/agent/ad/Click.kt index aa55ef3..ba0dd43 100644 --- a/src/main/kotlin/tv/anypoint/domain/agent/ad/Click.kt +++ b/src/main/kotlin/tv/anypoint/proxy/model/agent/ad/Click.kt @@ -1,5 +1,8 @@ -package tv.anypoint.domain.agent.ad +package tv.anypoint.proxy.model.agent.ad +import kotlinx.serialization.Serializable + +@Serializable data class Click( val targetType: String, val targetValue: String, diff --git a/src/main/kotlin/tv/anypoint/domain/agent/ad/ProgramPlacement.kt b/src/main/kotlin/tv/anypoint/proxy/model/agent/ad/ProgramPlacement.kt similarity index 89% rename from src/main/kotlin/tv/anypoint/domain/agent/ad/ProgramPlacement.kt rename to src/main/kotlin/tv/anypoint/proxy/model/agent/ad/ProgramPlacement.kt index f2e2445..14ef3bf 100644 --- a/src/main/kotlin/tv/anypoint/domain/agent/ad/ProgramPlacement.kt +++ b/src/main/kotlin/tv/anypoint/proxy/model/agent/ad/ProgramPlacement.kt @@ -1,4 +1,4 @@ -package tv.anypoint.domain.agent.ad +package tv.anypoint.proxy.model.agent.ad data class ProgramPlacement( val id: Int, @@ -18,4 +18,4 @@ data class PlacementUniqueKey( enum class MediaType { SO, PP -} \ No newline at end of file +} diff --git a/src/main/kotlin/tv/anypoint/domain/agent/ad/TargetAd.kt b/src/main/kotlin/tv/anypoint/proxy/model/agent/ad/TargetAd.kt similarity index 89% rename from src/main/kotlin/tv/anypoint/domain/agent/ad/TargetAd.kt rename to src/main/kotlin/tv/anypoint/proxy/model/agent/ad/TargetAd.kt index 11b0555..e7e1bf6 100644 --- a/src/main/kotlin/tv/anypoint/domain/agent/ad/TargetAd.kt +++ b/src/main/kotlin/tv/anypoint/proxy/model/agent/ad/TargetAd.kt @@ -1,6 +1,9 @@ -package tv.anypoint.domain.agent.ad +package tv.anypoint.proxy.model.agent.ad +import kotlinx.serialization.Serializable + +@Serializable data class TargetAd( val hours: String? = null, val days: String? = null, diff --git a/src/main/kotlin/tv/anypoint/proxy/model/agent/ad/VastResponse.kt b/src/main/kotlin/tv/anypoint/proxy/model/agent/ad/VastResponse.kt new file mode 100644 index 0000000..ae1767a --- /dev/null +++ b/src/main/kotlin/tv/anypoint/proxy/model/agent/ad/VastResponse.kt @@ -0,0 +1,3 @@ +package tv.anypoint.proxy.model.agent.ad + +data class VastResponse(val id: Long) diff --git a/src/main/kotlin/tv/anypoint/proxy/model/push/PushCommandType.kt b/src/main/kotlin/tv/anypoint/proxy/model/push/PushCommandType.kt new file mode 100644 index 0000000..8c5df70 --- /dev/null +++ b/src/main/kotlin/tv/anypoint/proxy/model/push/PushCommandType.kt @@ -0,0 +1,6 @@ +package tv.anypoint.proxy.model.push + +enum class PushCommandType(val string: String) { + UPDATE_ASSET_COMMAND("UpdateAssetCommand"), + UPDATE_SYSTEM_INFO("UpdateSystemInfo") +} diff --git a/src/main/kotlin/tv/anypoint/proxy/service/LogcatService.kt b/src/main/kotlin/tv/anypoint/proxy/service/LogcatService.kt new file mode 100644 index 0000000..756fead --- /dev/null +++ b/src/main/kotlin/tv/anypoint/proxy/service/LogcatService.kt @@ -0,0 +1,42 @@ +package tv.anypoint.proxy.service + +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch +import mu.KLogging +import org.springframework.stereotype.Service +import tv.anypoint.ApplicationProperties +import java.io.File +import java.util.concurrent.TimeUnit + +@Service +class LogcatService( + private val applicationProperties: ApplicationProperties +) { + private var dumpProcess: Process? = null + + fun startDump() { + val filePath = "${applicationProperties.fileRoot}/all.log" + GlobalScope.launch { + logger.info("start to save logcat. path: $filePath") + launch { + File(filePath).delete() + ProcessBuilder("/bin/sh", "-c", "adb logcat -c").start() + val processBuilder = ProcessBuilder("/bin/sh", "-c", "adb logcat -s AnypointAD") + + processBuilder.redirectOutput(File(filePath)) + val process = processBuilder.start() + process.onExit().thenAcceptAsync { + logger.info("finished to save logcat. exitValue: ${it.exitValue()}, path: $filePath") + } + dumpProcess = process + Thread.sleep(TimeUnit.HOURS.toMillis(2)) + } + } + } + + fun stopDump() { + dumpProcess?.destroy() + } + + companion object : KLogging() +} diff --git a/src/main/kotlin/tv/anypoint/proxy/service/ProxyDeviceService.kt b/src/main/kotlin/tv/anypoint/proxy/service/ProxyDeviceService.kt new file mode 100644 index 0000000..fbe7bc9 --- /dev/null +++ b/src/main/kotlin/tv/anypoint/proxy/service/ProxyDeviceService.kt @@ -0,0 +1,98 @@ +package tv.anypoint.proxy.service + +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json +import mu.KLogging +import org.springframework.boot.autoconfigure.web.ServerProperties +import org.springframework.stereotype.Service +import tv.anypoint.ApplicationProperties +import tv.anypoint.dsl.exception.httpValidationError +import tv.anypoint.dsl.model.TcContext +import tv.anypoint.proxy.adapter.AssignAdapter +import tv.anypoint.proxy.adapter.AuthAdapter +import tv.anypoint.proxy.model.agent.AuthRequest +import tv.anypoint.proxy.model.agent.AuthResponse +import tv.anypoint.proxy.model.agent.Endpoints +import tv.anypoint.proxy.model.agent.ad.AdsResponse +import tv.anypoint.proxy.model.agent.ad.AdsSyncRequest + +@Service +class ProxyDeviceService( + private val json: Json, + private val serverIp: String, + private val serverProperties: ServerProperties, + private val applicationProperties: ApplicationProperties, + private val authAdapter: AuthAdapter, + private val assignAdapter: AssignAdapter, + private val tcContext: TcContext, + private val logcatService: LogcatService +) { + fun auth(request: AuthRequest): AuthResponse { + return try { + val authResponse = authAdapter.auth( + request = request + ).execute().body()!! + authResponse.endpoints.redirectToProxyServer( + serverIp = serverIp, + serverPort = serverProperties.port, + pushServerPort = applicationProperties.pushServerPort + ) + if (!tcContext.tc.auth.validate(authResponse)) { + throw httpValidationError(authResponse) + } + val response = tcContext.tc.auth.convert(authResponse) + tcContext.tc.authResponse = response + logger.info("auth response: ${json.encodeToString(response)}") + + if (tcContext.tc.reboot) { + logcatService.startDump() + // TODO: logcat 에서 `auth response body` 를 기다린 다음에 adb ADS_SYNC 호출 + } else { + // TODO: adb ADS_SYNC 호출 + } + response + } catch(e: Exception) { + logger.error("failed to auth.", e) + throw RuntimeException("failed to auth.") + } + } + + private fun Endpoints.redirectToProxyServer( + serverIp: String, + serverPort: Int, + pushServerPort: Int + ) { + val serverEndpoint = "http://$serverIp:$serverPort" + val pushServerEndpoint = "$serverIp:$pushServerPort" + this.auth = serverEndpoint + this.requestAds = serverEndpoint + this.adSyncResult = serverEndpoint + this.appLog = serverEndpoint + this.event = serverEndpoint +// this.pushServers = listOf(pushServerEndpoint) + this.ntpServers = emptyList() + this.stateLog = serverEndpoint + this.impressionLog = serverEndpoint + this.assetRequest = serverEndpoint + this.proxyAdLog = serverEndpoint + } + + fun ads(deviceId: Long, freeStorage: Long?, usedStorage: Long?): AdsResponse { + val adsResponse = assignAdapter.ads( + deviceId = deviceId, + freeStorage = freeStorage ?: 0, + usedStorage = usedStorage ?: 0 + ).execute().body()!! + val response = tcContext.tc.ads.convert(adsResponse) + tcContext.tc.adsResponse = response + logger.info("ads response: ${json.encodeToString(response)}") + + return response + } + + fun adSync(request: AdsSyncRequest) { + // nothing to do. + } + + companion object : KLogging() +} diff --git a/src/main/kotlin/tv/anypoint/proxy/service/PushServerInterface.kt b/src/main/kotlin/tv/anypoint/proxy/service/PushServerInterface.kt new file mode 100644 index 0000000..8dc4b67 --- /dev/null +++ b/src/main/kotlin/tv/anypoint/proxy/service/PushServerInterface.kt @@ -0,0 +1,10 @@ +package tv.anypoint.proxy.service + +import tv.anypoint.proxy.model.agent.Cue +import tv.anypoint.proxy.model.push.PushCommandType + +interface PushServerInterface { + fun sendCommand(commandType: PushCommandType): Boolean + + fun sendCue(cue: Cue): Boolean +} diff --git a/src/main/kotlin/tv/anypoint/proxy/service/PushServerService.kt b/src/main/kotlin/tv/anypoint/proxy/service/PushServerService.kt new file mode 100644 index 0000000..5ea89c0 --- /dev/null +++ b/src/main/kotlin/tv/anypoint/proxy/service/PushServerService.kt @@ -0,0 +1,21 @@ +package tv.anypoint.proxy.service + +import org.springframework.stereotype.Service +import tv.anypoint.ApplicationProperties +import tv.anypoint.proxy.model.agent.Cue +import tv.anypoint.proxy.model.push.PushCommandType + +@Service +class PushServerService( + private val applicationProperties: ApplicationProperties +) : PushServerInterface { + override fun sendCommand(commandType: PushCommandType): Boolean { + applicationProperties.pushServerPort + TODO("Not Implemented") + } + + override fun sendCue(cue: Cue): Boolean { + applicationProperties.pushServerPort + TODO("Not Implemented") + } +} diff --git a/src/main/kotlin/tv/anypoint/proxy/service/StbService.kt b/src/main/kotlin/tv/anypoint/proxy/service/StbService.kt new file mode 100644 index 0000000..e2cae53 --- /dev/null +++ b/src/main/kotlin/tv/anypoint/proxy/service/StbService.kt @@ -0,0 +1,54 @@ +package tv.anypoint.proxy.service + +import org.springframework.boot.autoconfigure.web.ServerProperties +import org.springframework.stereotype.Service +import se.vidstige.jadb.JadbDevice +import tv.anypoint.dsl.model.Tc +import tv.anypoint.dsl.model.TcContext +import tv.anypoint.proxy.model.push.PushCommandType +import tv.anypoint.proxy.shell.ShellController +import tv.anypoint.proxy.util.changeAuthEndpoint +import tv.anypoint.proxy.util.reboot +import tv.anypoint.proxy.util.sendNumberInput + +@Service +class StbService( + private val serverIp: String, + private val jadbDevice: JadbDevice, + private val tcContext: TcContext, + private val serverProperties: ServerProperties, + private val logcatService: LogcatService, + private val pushServerService: PushServerService +) { + fun start( + restartStb: Boolean, + serverIp: String = this.serverIp, + serverPort: Int = serverProperties.port + ) { + logcatService.startDump() + tcContext.tc = Tc().apply { + this.number = "MANUAL" + this.reboot = restartStb + } + changeAuthEndpoint(serverIp = serverIp, serverPort = serverPort) + if (restartStb) { + jadbDevice.reboot() + } else { + pushServerService.sendCommand(PushCommandType.UPDATE_SYSTEM_INFO) + } + } + + private fun changeAuthEndpoint( + serverIp: String, + serverPort: Int + ) { + jadbDevice.changeAuthEndpoint(serverIp = serverIp, serverPort = serverPort) + } + + fun changeChannelNumber(channelNumber: String) { + channelNumber.forEach { c -> + jadbDevice.sendNumberInput(c) + } + ShellController.logger.info("changed ch number: $channelNumber") + } +} diff --git a/src/main/kotlin/tv/anypoint/proxy/shell/ShellController.kt b/src/main/kotlin/tv/anypoint/proxy/shell/ShellController.kt new file mode 100644 index 0000000..59c6d9a --- /dev/null +++ b/src/main/kotlin/tv/anypoint/proxy/shell/ShellController.kt @@ -0,0 +1,40 @@ +package tv.anypoint.proxy.shell + +import mu.KLogging +import org.springframework.shell.standard.ShellComponent +import org.springframework.shell.standard.ShellMethod +import tv.anypoint.proxy.service.StbService + +@ShellComponent +class ShellController( + private val stbService: StbService +) { + + /** + * 채널 변경 + */ + @ShellMethod + fun ch(channelNumber: String) { + stbService.changeChannelNumber(channelNumber) + } + + /** + * STB 에 접속 + * ip, port 지정하지 않을 경우 설정된 STB 에 접속 + */ + @ShellMethod + fun start( + restart: Boolean = false, + ip: String? = null, + port: Int? = null + ) { + when { + ip == null && port == null -> stbService.start(restartStb = restart) + ip != null && port != null -> stbService.start(restartStb = restart, serverIp = ip, serverPort = port) + ip != null && port == null -> stbService.start(restartStb = restart, serverIp = ip) + ip == null && port != null -> stbService.start(restartStb = restart, serverPort = port) + } + } + + companion object : KLogging() +} diff --git a/src/main/kotlin/tv/anypoint/proxy/util/JadbDeviceSerialUtil.kt b/src/main/kotlin/tv/anypoint/proxy/util/JadbDeviceSerialUtil.kt new file mode 100644 index 0000000..9188255 --- /dev/null +++ b/src/main/kotlin/tv/anypoint/proxy/util/JadbDeviceSerialUtil.kt @@ -0,0 +1,22 @@ +package tv.anypoint.proxy.util + +import java.net.Inet4Address +import java.net.InetSocketAddress + +object JadbDeviceSerialUtil { + + private val serialRegex = Regex("(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}):(\\d{1,5})") + + fun addressToId(hostAddress: String, port: Int) = "$hostAddress:$port" + fun addressToId(socketAddress: InetSocketAddress) = addressToId(socketAddress.address.hostAddress, socketAddress.port) + + fun serialToSocketAddress(serial: String) : InetSocketAddress? { + val matchResult = serialRegex.matchEntire(serial) ?: return null + val address = Inet4Address.getByName(matchResult.groupValues[1]) + val port = Integer.parseInt(matchResult.groupValues[2]) + return InetSocketAddress(address, port) + } + + fun serialToAddress(serial: String) = serialToSocketAddress(serial)?.address + +} diff --git a/src/main/kotlin/tv/anypoint/proxy/service/NetworkUtil.kt b/src/main/kotlin/tv/anypoint/proxy/util/NetworkUtil.kt similarity index 97% rename from src/main/kotlin/tv/anypoint/proxy/service/NetworkUtil.kt rename to src/main/kotlin/tv/anypoint/proxy/util/NetworkUtil.kt index e0ce2f4..e287dd6 100644 --- a/src/main/kotlin/tv/anypoint/proxy/service/NetworkUtil.kt +++ b/src/main/kotlin/tv/anypoint/proxy/util/NetworkUtil.kt @@ -1,4 +1,4 @@ -package tv.anypoint.proxy.service +package tv.anypoint.proxy.util import jakarta.servlet.http.HttpServletRequest import kotlinx.coroutines.* @@ -10,7 +10,7 @@ import java.util.* object NetworkUtil { fun getPrivateIpV4(setTopBoxAddress: InetAddress): String = getPrivateIpV4List() .chooseSimilarIp(setTopBoxAddress)?.hostAddress - ?: throw RuntimeException() + ?: throw RuntimeException("셋톱과 공유하는 private IP가 없습니다") /** * Gives the private IP of this device diff --git a/src/main/kotlin/tv/anypoint/proxy/util/StbUtil.kt b/src/main/kotlin/tv/anypoint/proxy/util/StbUtil.kt new file mode 100644 index 0000000..c3af638 --- /dev/null +++ b/src/main/kotlin/tv/anypoint/proxy/util/StbUtil.kt @@ -0,0 +1,88 @@ +package tv.anypoint.proxy.util + +import org.apache.commons.io.IOUtils +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import se.vidstige.jadb.JadbConnection +import se.vidstige.jadb.JadbDevice +import tv.anypoint.proxy.model.adb.ChangeCommand +import tv.anypoint.proxy.model.adb.ExtraKey +import tv.anypoint.proxy.model.adb.IntentAction +import java.net.InetSocketAddress +import java.nio.charset.StandardCharsets + +private val logger: Logger = LoggerFactory.getLogger("tv.anypoint.proxy.util.StbUtilKt") + +fun getDevice(stbIp: String, stbPort: Int): JadbDevice { + try { + val socketAddress = InetSocketAddress(stbIp, stbPort) + val jadbc = JadbConnection() + try { + jadbc.connectToTcpDevice(socketAddress) + } catch (e: Exception) { + logger.error("Can't connect adb to $socketAddress, Reason -> ${e.message}") + } + return jadbc.devices.firstOrNull { it.serial in JadbDeviceSerialUtil.addressToId(socketAddress) } + ?: throw RuntimeException("adb server not running!") + } catch (e: Exception) { + throw RuntimeException("adb server not running!", e) + } +} + +/** + * Sends broadcast command to the device to change the auth endpoint to the server. + */ +fun JadbDevice.changeAuthEndpoint( + serverIp: String, + serverPort: Int +) { + val proxyEndpoint = "http://$serverIp:$serverPort" + executeShellDebug( + a = tv.anypoint.proxy.model.adb.IntentAction.CHANGE_TEST_PROPERTY, + es = mapOf( + tv.anypoint.proxy.model.adb.ExtraKey.CHANGE_COMMAND to tv.anypoint.proxy.model.adb.ChangeCommand.CHANGE_API_ENDPOINT.name, + tv.anypoint.proxy.model.adb.ExtraKey.API_ENDPOINT to proxyEndpoint + ) + ) + logger.info("changed auth endpoint to proxy endpoint $proxyEndpoint") +} + +fun JadbDevice.executeShellDebug(a: tv.anypoint.proxy.model.adb.IntentAction, es: Map) { + val shellContentBuilder = StringBuilder() + shellContentBuilder.append("am broadcast ") + shellContentBuilder.append("-a ${a.string} ") + es.forEach { (k, v) -> + shellContentBuilder.append("--es ${k.string} $v ") + } + this.executeShellDebug(shellContentBuilder.toString()) +} + +/** + * Sends number as an input to the device, just like + * pressing a number button in the remote controller. + */ +fun JadbDevice.sendNumberInput(numberButton: Char) { + if (numberButton < '0' || numberButton > '9') return + + val shellContent = "input keyevent KEYCODE_$numberButton" + this.executeShellDebug(shellContent) +} + +/** + * Sends shell to the device + */ +fun JadbDevice.executeShellDebug(content: String) { + logger.info("[[ cmd --> {} ]] {}", this.serial, content) + val inputStream = this.executeShell(content) + val commandResult = IOUtils.toString(inputStream, StandardCharsets.UTF_8) + for (line in commandResult.split('\n')) { + logger.info("[[ cmd <-- {} ]] {}", this.serial, line.removeNewLines()) + } +} + +/** + * Sends reboot command to the device. + */ +fun JadbDevice.reboot() { + executeShellDebug("reboot") +} diff --git a/src/main/kotlin/tv/anypoint/proxy/util/StringUtil.kt b/src/main/kotlin/tv/anypoint/proxy/util/StringUtil.kt new file mode 100644 index 0000000..3065859 --- /dev/null +++ b/src/main/kotlin/tv/anypoint/proxy/util/StringUtil.kt @@ -0,0 +1,3 @@ +package tv.anypoint.proxy.util + +fun String.removeNewLines() = this.replace("\n", "") diff --git a/src/main/kotlin/tv/anypoint/proxy/web/DeviceController.kt b/src/main/kotlin/tv/anypoint/proxy/web/DeviceController.kt index f80d399..8df604e 100644 --- a/src/main/kotlin/tv/anypoint/proxy/web/DeviceController.kt +++ b/src/main/kotlin/tv/anypoint/proxy/web/DeviceController.kt @@ -1,27 +1,27 @@ package tv.anypoint.proxy.web -import jakarta.servlet.http.HttpServletRequest +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json import mu.KLogging import org.springframework.web.bind.annotation.* -import tv.anypoint.domain.agent.AuthRequest -import tv.anypoint.domain.agent.AuthResponse -import tv.anypoint.domain.agent.StateChangeLog -import tv.anypoint.domain.agent.ad.AdsResponse -import tv.anypoint.domain.agent.ad.AdsSyncRequest +import tv.anypoint.proxy.model.agent.AuthRequest +import tv.anypoint.proxy.model.agent.AuthResponse +import tv.anypoint.proxy.model.agent.StateChangeLog +import tv.anypoint.proxy.model.agent.ad.AdsResponse +import tv.anypoint.proxy.model.agent.ad.AdsSyncRequest +import tv.anypoint.proxy.service.ProxyDeviceService @RestController @RequestMapping("/v3/device") -class DeviceController { +class DeviceController( + private val json: Json, + private val proxyDeviceService: ProxyDeviceService +) { @PostMapping("/auth") - fun authorize(request: HttpServletRequest, @RequestBody body: AuthRequest): AuthResponse { - logger.info("POST /v3/device/auth -d $body") - -// request.getIpAddress() - val authResponse: AuthResponse = TODO("authRequestHandlerService.handleRequest(HttpRequestUtil.getIpAddress(request), body)") - authResponse.adConfig.maxDownloadBandwidth = 1000 * 1024 * 1024 - authResponse.adConfig.maxLazyDownloadBandwidth = 1000 * 1024 * 1024 - return authResponse + fun authorize(@RequestBody request: AuthRequest): AuthResponse { + logger.info("POST /v3/device/auth -d ${json.encodeToString(request)}") + return proxyDeviceService.auth(request) } @GetMapping("/ads") @@ -31,31 +31,34 @@ class DeviceController { @RequestParam("usedStorage", required = false, defaultValue = "0") usedStorage: Long? ): AdsResponse { logger.info("GET /v3/device/ads?deviceId=$deviceId&freeStorage=$freeStorage&usedStorage=$usedStorage") -// return adsRequestHandlerService.handleRequest(deviceId, freeStorage, usedStorage) - return AdsResponse() + return proxyDeviceService.ads( + deviceId = deviceId, + freeStorage = freeStorage, + usedStorage = usedStorage + ) } @PostMapping("/sync/ads") - fun adSync(request: HttpServletRequest, @RequestBody body: AdsSyncRequest) { - logger.info("POST /v3/device/sync/ads -d $body") -// deviceAdSyncHandlerService.handleRequest(HttpRequestUtil.getIpAddress(request)) + fun adSync(@RequestBody request: AdsSyncRequest) { + logger.info("POST /v3/device/sync/ads -d $request") + proxyDeviceService.adSync(request) } @PostMapping("/state-logs") - fun stateLogs(@RequestBody body: List) { - logger.info("POST /v3/device/state-logs -d $body") + fun stateLogs(@RequestBody request: List) { + logger.info("POST /v3/device/state-logs -d $request") } @PostMapping("/event") @ResponseBody - fun deviceEvent(request: HttpServletRequest, @RequestBody body: String) { - logger.info("POST /v3/device/event -d $body") + fun deviceEvent(@RequestBody request: String) { + logger.info("POST /v3/device/event -d $request") } @PostMapping("/impression-logs") @ResponseBody - fun impressionLogs(request: HttpServletRequest, @RequestBody body: String) { - logger.info("POST /v3/device/ssion-logs -d $body") + fun impressionLogs(@RequestBody request: String) { + logger.info("POST /v3/device/ssion-logs -d $request") } companion object : KLogging() diff --git a/src/main/kotlin/tv/anypoint/proxy/web/TestController.kt b/src/main/kotlin/tv/anypoint/proxy/web/TestController.kt new file mode 100644 index 0000000..ba3a835 --- /dev/null +++ b/src/main/kotlin/tv/anypoint/proxy/web/TestController.kt @@ -0,0 +1,18 @@ +package tv.anypoint.proxy.web + +import mu.KLogging +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RestController + +@RequestMapping("/test") +@RestController +class TestController { + @GetMapping + fun testGet(): Boolean { + logger.info("GET /test") + return true + } + + companion object : KLogging() +} diff --git a/src/main/kotlin/tv/anypoint/tc/Base1.kt b/src/main/kotlin/tv/anypoint/tc/Base1.kt index efa9d95..f82466e 100644 --- a/src/main/kotlin/tv/anypoint/tc/Base1.kt +++ b/src/main/kotlin/tv/anypoint/tc/Base1.kt @@ -2,8 +2,6 @@ package tv.anypoint.tc import mu.KLogging import org.springframework.stereotype.Component -import tv.anypoint.domain.adb.ExtraKey -import tv.anypoint.domain.adb.IntentAction import tv.anypoint.dsl.adb import tv.anypoint.dsl.expected import tv.anypoint.dsl.http @@ -23,18 +21,18 @@ class Base1 : TestCase() { override fun test() { adb( - a = IntentAction.CHANGE_TEST_PROPERTY, - es = mapOf(ExtraKey.TEST_DEVICE to "true") + a = tv.anypoint.proxy.model.adb.IntentAction.CHANGE_TEST_PROPERTY, + es = mapOf(tv.anypoint.proxy.model.adb.ExtraKey.TEST_DEVICE to "true") ) - expected("changed to test device") + tc.log.expected("changed to test device") // AD Agent, AD SDK 통신규격상으로는 a 필수, es 는 선택사항 // https://dev-docs.anypoint.tv/books/ff-settop-ad-agent/page/ad-agent-ad-sdk // 예외 사항이 존재할 지? adb( - a = IntentAction.CHANGE_TEST_PROPERTY, - es = mapOf(ExtraKey.TEST_DEVICE to "false") + a = tv.anypoint.proxy.model.adb.IntentAction.CHANGE_TEST_PROPERTY, + es = mapOf(tv.anypoint.proxy.model.adb.ExtraKey.TEST_DEVICE to "false") ) } diff --git a/src/main/kotlin/tv/anypoint/tc/TestCaseStarter.kt b/src/main/kotlin/tv/anypoint/tc/TestCaseStarter.kt index 91a035c..20e819a 100644 --- a/src/main/kotlin/tv/anypoint/tc/TestCaseStarter.kt +++ b/src/main/kotlin/tv/anypoint/tc/TestCaseStarter.kt @@ -1,19 +1,24 @@ package tv.anypoint.tc import mu.KLogging +import org.springframework.context.event.EventListener import org.springframework.stereotype.Service +import tv.anypoint.dsl.model.TcFailEvent import tv.anypoint.dsl.service.TestCase +import tv.anypoint.proxy.service.StbService @Service class TestCaseStarter( - private val testCases: List + private val testCases: List, + private val stbService: StbService, ) { fun testAll() { - // TODO: main.log 덤프 시작 - startDumpLog() - testCases.forEach { + // [reboot] -> auth -> ads -> vast -> asset convert + stbService.start(it.tc.reboot) + + it.executeTest() } println("| tc | result | startedAt | finishedAt | log file | recording file |") @@ -27,22 +32,17 @@ class TestCaseStarter( if (it.tc.result!!) "PASS" else "FAIL", it.tc.startedAt, it.tc.finishedAt, - it.tc.logInfo.absoluteFilePath, - it.tc.recordingInfo?.absoluteFilePath ?: "-" + it.tc.log.logcatFilePath, + it.tc.recording?.absoluteFilePath ?: "-" ) ) } - - stopDumpLog() } - fun startDumpLog() { - // TODO: - } + @EventListener + fun failed(event: TcFailEvent) { - fun stopDumpLog() { - // TODO } companion object : KLogging() -} \ No newline at end of file +} diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index a1c7738..7516e5e 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -7,11 +7,13 @@ logging: level: root: INFO tv.anypoint: DEBUG +server: + port: 8080 anypoint.android-qa: stb: - ip: 192.168.0.1 + ip: 192.168.15.139 port: 5555 - file-root: /home/bean/dev/qa + file-root: /home/bean/dev/qaz --- diff --git a/src/test/kotlin/Test.kt b/src/test/kotlin/Test.kt new file mode 100644 index 0000000..c84585d --- /dev/null +++ b/src/test/kotlin/Test.kt @@ -0,0 +1,15 @@ +import java.io.BufferedReader +import java.io.FileReader +import java.io.IOException +import java.io.LineNumberReader + +class Test { + @Throws(IOException::class) + fun test() { + val lnr = LineNumberReader(BufferedReader(FileReader("all.log"))) + var line: String? + while ((lnr.readLine().also { line = it }) != null) { + println(line) + } + } +} diff --git a/src/test/kotlin/tv/anypoint/androidqa/AuthResponseTest.kt b/src/test/kotlin/tv/anypoint/androidqa/AuthResponseTest.kt index bd844be..fad9d28 100644 --- a/src/test/kotlin/tv/anypoint/androidqa/AuthResponseTest.kt +++ b/src/test/kotlin/tv/anypoint/androidqa/AuthResponseTest.kt @@ -6,7 +6,7 @@ import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.json.Json import kotlinx.serialization.json.decodeFromStream import org.springframework.core.io.ClassPathResource -import tv.anypoint.domain.agent.AuthResponse +import tv.anypoint.proxy.model.agent.AuthResponse @OptIn(ExperimentalSerializationApi::class) class AuthResponseTest : BehaviorSpec() { @@ -15,7 +15,7 @@ class AuthResponseTest : BehaviorSpec() { Given("auth-response.json file") { val file = ClassPathResource("auth-response.json").file When("deserialize") { - val actual = Json.decodeFromStream(file.inputStream()) + val actual = Json.decodeFromStream(file.inputStream()) Then("read values") { actual.deviceId shouldBe 36986237L } diff --git a/src/test/kotlin/tv/anypoint/dsl/DslKtTest.kt b/src/test/kotlin/tv/anypoint/dsl/DslKtTest.kt new file mode 100644 index 0000000..34e01e4 --- /dev/null +++ b/src/test/kotlin/tv/anypoint/dsl/DslKtTest.kt @@ -0,0 +1,51 @@ +package tv.anypoint.dsl + +import io.kotest.core.spec.style.BehaviorSpec +import io.kotest.core.spec.style.FunSpec +import io.kotest.matchers.longs.shouldBeLessThanOrEqual +import io.kotest.matchers.shouldBe +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json +import mu.KLogging +import tv.anypoint.dsl.model.Log +import java.io.File +import java.time.Duration +import java.time.LocalDateTime + +class DslKtTest : FunSpec() { + init { + test("Log.expected()") { + // given + val file = File("./test.log") + val writer = file.writer() + val startedAt = LocalDateTime.now() + val test5 = Log("./test.log") + val test7 = Log("./test.log") + + object : Thread() { + override fun run() { + (1..20).forEach { + logger.info("print test $it") + writer.write("test $it${System.lineSeparator()}") + writer.flush() + sleep(500) + } + } + } + .start() + + // when + test5.expected("test 5") + test7.expected("test 7") + + // then + test5.cursor shouldBe 4 + test7.cursor shouldBe 6 + Duration.between(startedAt, LocalDateTime.now()).seconds shouldBeLessThanOrEqual 10_000 + } + } + + companion object : KLogging() +} diff --git a/src/test/kotlin/tv/anypoint/proxy/adapter/AuthAdapterTest.kt b/src/test/kotlin/tv/anypoint/proxy/adapter/AuthAdapterTest.kt new file mode 100644 index 0000000..19579d2 --- /dev/null +++ b/src/test/kotlin/tv/anypoint/proxy/adapter/AuthAdapterTest.kt @@ -0,0 +1,44 @@ +package tv.anypoint.proxy.adapter + +import io.kotest.core.spec.style.BehaviorSpec +import io.kotest.core.spec.style.FunSpec +import io.kotest.matchers.shouldBe +import mu.KLogging +import org.springframework.boot.test.context.SpringBootTest +import tv.anypoint.proxy.model.agent.AuthRequest + +@SpringBootTest +class AuthAdapterTest( + private val authAdapter: AuthAdapter +) : BehaviorSpec() { + init { + Given("request") { + val request = AuthRequest( + fingerPrint = "", + soId = 1, + uuid = "43dd41a2-111b-48c8-b840-629978714f0b", + freeStorage = 2519707648, + usedStorage = 294611792, + cachedStorage = 203884308, + modelName = "BID-AI100", + firmwareBuildDate = "2024.04.02", + firmwareVer = "16.540.22", + fullFirmwareVer = "16.540.22-0000", + appVersion = "3.9.4-RC59_20230712", + platformAdId = "63c33b8c-f5d5-47b3-9e7d-f06342fb65fe", + sdkVersion = "2.0.1-RC12" + ) + + When("call auth API") { + val actual = authAdapter.auth(request) + .execute().body() + logger.info("actual: $actual") + Then("actual is not null") { + 1 shouldBe 1 + } + } + } + } + + companion object : KLogging() +} diff --git a/src/test/kotlin/tv/anypoint/proxy/service/LogcatServiceTest.kt b/src/test/kotlin/tv/anypoint/proxy/service/LogcatServiceTest.kt new file mode 100644 index 0000000..9002dc5 --- /dev/null +++ b/src/test/kotlin/tv/anypoint/proxy/service/LogcatServiceTest.kt @@ -0,0 +1,15 @@ +package tv.anypoint.proxy.service + +import io.kotest.core.spec.style.FunSpec +import org.springframework.boot.test.context.SpringBootTest + +@SpringBootTest +class LogcatServiceTest( + logcatService: LogcatService +) : FunSpec({ + test("start") { + logcatService.startDump() + Thread.sleep(5_000) + logcatService.stopDump() + } +})