android: send Android logs to logz (#515)
TSLog sends log messages to Android's logcat and Tailscale's logger Libtailscale wrapper is a Kotlin wrapper that allows us to get around the problems with mocking a native library Fixes tailscale/corp#23191 Signed-off-by: kari-ts <kari@tailscale.com>pull/522/head
parent
f26a828cbd
commit
08ae018468
@ -0,0 +1,44 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
package com.tailscale.ipn.util
|
||||
|
||||
import android.util.Log
|
||||
import libtailscale.Libtailscale
|
||||
|
||||
object TSLog {
|
||||
var libtailscaleWrapper = LibtailscaleWrapper()
|
||||
|
||||
fun d(tag: String?, message: String) {
|
||||
Log.d(tag, message)
|
||||
libtailscaleWrapper.sendLog(tag, message)
|
||||
}
|
||||
|
||||
fun w(tag: String, message: String) {
|
||||
Log.w(tag, message)
|
||||
libtailscaleWrapper.sendLog(tag, message)
|
||||
}
|
||||
|
||||
// Overloaded function without Throwable because Java does not support default parameters
|
||||
@JvmStatic
|
||||
fun e(tag: String?, message: String) {
|
||||
Log.e(tag, message)
|
||||
libtailscaleWrapper.sendLog(tag, message)
|
||||
}
|
||||
|
||||
fun e(tag: String?, message: String, throwable: Throwable? = null) {
|
||||
if (throwable == null) {
|
||||
Log.e(tag, message)
|
||||
libtailscaleWrapper.sendLog(tag, message)
|
||||
} else {
|
||||
Log.e(tag, message, throwable)
|
||||
libtailscaleWrapper.sendLog(tag, "$message ${throwable?.localizedMessage}")
|
||||
}
|
||||
}
|
||||
|
||||
class LibtailscaleWrapper {
|
||||
public fun sendLog(tag: String?, message: String) {
|
||||
val logTag = tag ?: ""
|
||||
Libtailscale.sendLog((logTag + ": " + message).toByteArray(Charsets.UTF_8))
|
||||
}
|
||||
}
|
||||
}
|
@ -1,60 +1,107 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
|
||||
package com.tailcale.ipn.ui.util
|
||||
|
||||
|
||||
import com.tailscale.ipn.ui.util.TimeUtil
|
||||
import com.tailscale.ipn.util.TSLog
|
||||
import com.tailscale.ipn.util.TSLog.LibtailscaleWrapper
|
||||
import org.junit.After
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNull
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mockito.ArgumentMatchers.anyString
|
||||
import org.mockito.Mockito.doNothing
|
||||
import org.mockito.Mockito.mock
|
||||
import java.time.Duration
|
||||
|
||||
|
||||
class TimeUtilTest {
|
||||
|
||||
@Test
|
||||
fun durationInvalidMsUnits() {
|
||||
val input = "5s10ms"
|
||||
val actual = TimeUtil.duration(input)
|
||||
assertNull("Should return null", actual)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun durationInvalidUsUnits() {
|
||||
val input = "5s10us"
|
||||
val actual = TimeUtil.duration(input)
|
||||
assertNull("Should return null", actual)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun durationTestHappyPath() {
|
||||
val input = arrayOf("1.0y1.0w1.0d1.0h1.0m1.0s", "1s", "1m", "1h", "1d", "1w", "1y")
|
||||
val expectedSeconds =
|
||||
arrayOf((31536000 + 604800 + 86400 + 3600 + 60 + 1), 1, 60, 3600, 86400, 604800, 31536000)
|
||||
val expected = expectedSeconds.map { Duration.ofSeconds(it.toLong()) }
|
||||
val actual = input.map { TimeUtil.duration(it) }
|
||||
assertEquals("Incorrect conversion", expected, actual)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testBadDurationString() {
|
||||
val input = "1..0y1.0w1.0d1.0h1.0m1.0s"
|
||||
val actual = TimeUtil.duration(input)
|
||||
assertNull("Should return null", actual)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testBadDInputString() {
|
||||
val input = "1.0yy1.0w1.0d1.0h1.0m1.0s"
|
||||
val actual = TimeUtil.duration(input)
|
||||
assertNull("Should return null", actual)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testIgnoreFractionalSeconds() {
|
||||
val input = "10.9s"
|
||||
val expectedSeconds = 10
|
||||
val expected = Duration.ofSeconds(expectedSeconds.toLong())
|
||||
val actual = TimeUtil.duration(input)
|
||||
assertEquals("Should return $expectedSeconds seconds", expected, actual)
|
||||
}
|
||||
|
||||
private lateinit var libtailscaleWrapperMock: LibtailscaleWrapper
|
||||
private lateinit var originalWrapper: LibtailscaleWrapper
|
||||
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
libtailscaleWrapperMock = mock(LibtailscaleWrapper::class.java)
|
||||
doNothing().`when`(libtailscaleWrapperMock).sendLog(anyString(), anyString())
|
||||
|
||||
|
||||
// Store the original wrapper so we can reset it later
|
||||
originalWrapper = TSLog.libtailscaleWrapper
|
||||
// Inject mock into TSLog
|
||||
TSLog.libtailscaleWrapper = libtailscaleWrapperMock
|
||||
}
|
||||
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
// Reset TSLog after each test to avoid side effects
|
||||
TSLog.libtailscaleWrapper = originalWrapper
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun durationInvalidMsUnits() {
|
||||
val input = "5s10ms"
|
||||
val actual = TimeUtil.duration(input)
|
||||
assertNull("Should return null", actual)
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun durationInvalidUsUnits() {
|
||||
val input = "5s10us"
|
||||
val actual = TimeUtil.duration(input)
|
||||
assertNull("Should return null", actual)
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun durationTestHappyPath() {
|
||||
val input = arrayOf("1.0y1.0w1.0d1.0h1.0m1.0s", "1s", "1m", "1h", "1d", "1w", "1y")
|
||||
val expectedSeconds =
|
||||
arrayOf((31536000 + 604800 + 86400 + 3600 + 60 + 1), 1, 60, 3600, 86400, 604800, 31536000)
|
||||
val expected = expectedSeconds.map { Duration.ofSeconds(it.toLong()) }
|
||||
val actual = input.map { TimeUtil.duration(it) }
|
||||
assertEquals("Incorrect conversion", expected, actual)
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun testBadDurationString() {
|
||||
val input = "1..0y1.0w1.0d1.0h1.0m1.0s"
|
||||
val actual = TimeUtil.duration(input)
|
||||
assertNull("Should return null", actual)
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun testBadDInputString() {
|
||||
val libtailscaleWrapperMock = mock(LibtailscaleWrapper::class.java)
|
||||
doNothing().`when`(libtailscaleWrapperMock).sendLog(anyString(), anyString())
|
||||
|
||||
|
||||
val input = "1.0yy1.0w1.0d1.0h1.0m1.0s"
|
||||
val actual = TimeUtil.duration(input)
|
||||
assertNull("Should return null", actual)
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun testIgnoreFractionalSeconds() {
|
||||
val input = "10.9s"
|
||||
val expectedSeconds = 10
|
||||
val expected = Duration.ofSeconds(expectedSeconds.toLong())
|
||||
val actual = TimeUtil.duration(input)
|
||||
assertEquals("Should return $expectedSeconds seconds", expected, actual)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue