Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/flipcash/core/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -662,6 +662,7 @@

<string name="title_settingsSectionFeatures">Features</string>
<string name="title_settingsSectionDeveloper">Developer</string>
<string name="title_settingsSectionContracts">Contracts</string>
<string name="subtitle_settingsUserFlags">User Flags</string>
<string name="title_settingsSectionAccount">Account</string>
<string name="title_userFlags">User Flags</string>
Expand Down
7 changes: 7 additions & 0 deletions apps/flipcash/features/lab/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,11 @@ dependencies {
implementation(project(":apps:flipcash:shared:featureflags"))
implementation(project(":apps:flipcash:shared:userflags"))
implementation(project(":libs:messaging"))

implementation(libs.ocp.client.protocol)
implementation(libs.flipcash2.client.protocol)

testImplementation(kotlin("test"))
testImplementation(libs.bundles.unit.testing)
testImplementation(libs.bundles.compose.ui.testing)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package com.flipcash.app.lab.internal

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import com.codeinc.flipcash.gen.Flipcash2ContractInfo
import com.codeinc.opencode.gen.OcpContractInfo
import com.getcode.theme.CodeTheme

/**
* One backend contract package, as the build carries it.
*
* [commit] is the upstream proto commit the package was generated from, already shortened by the
* package itself so both platforms truncate the same way. A package built from a local proto sync
* reports `LOCAL` instead of a SHA, which is what [isLocal] flags -- that build's contract is not
* reproducible from anything published, so it is worth seeing at a glance.
*/
internal data class ContractInfo(
val name: String,
val version: String,
val commit: String,
val isLocal: Boolean,
) {
val detail: String get() = "$version · $commit"
}

internal fun contractInfo(): List<ContractInfo> = listOf(
ContractInfo(
name = "ocp",
version = OcpContractInfo.VERSION,
commit = OcpContractInfo.shortProtoCommit,
isLocal = OcpContractInfo.isLocal,
),
ContractInfo(
name = "flipcash2",
version = Flipcash2ContractInfo.VERSION,
commit = Flipcash2ContractInfo.shortProtoCommit,
isLocal = Flipcash2ContractInfo.isLocal,
),
)

@Composable
internal fun ContractInfoRow(
info: ContractInfo,
modifier: Modifier = Modifier,
) {
Row(
modifier = modifier
.fillMaxWidth()
.padding(horizontal = CodeTheme.dimens.inset, vertical = CodeTheme.dimens.grid.x2),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
) {
Text(
text = info.name,
style = CodeTheme.typography.textMedium,
color = CodeTheme.colors.textMain,
)
Text(
text = info.detail,
style = CodeTheme.typography.textSmall,
color = if (info.isLocal) CodeTheme.colors.warning else CodeTheme.colors.textSecondary,
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,17 @@ internal fun LabsScreenContent(viewModel: LabsScreenViewModel, onboarding: Boole
navigator.navigate(AppRoute.UserFlags)
}
}

item(contentType = "section_header") {
SectionHeader(
modifier = Modifier.padding(horizontal = CodeTheme.dimens.inset),
title = stringResource(R.string.title_settingsSectionContracts)
)
}

items(contractInfo(), key = { it.name }, contentType = { "contract_info" }) { info ->
ContractInfoRow(info)
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.flipcash.app.lab.internal

import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithText
import com.getcode.theme.DesignSystem
import org.junit.Rule
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import kotlin.test.Test

@RunWith(RobolectricTestRunner::class)
class ContractInfoRowTest {

@get:Rule
val composeTestRule = createComposeRule()

@Test
fun `shows the package name and the version-commit detail`() {
composeTestRule.setContent {
DesignSystem {
ContractInfoRow(
ContractInfo(name = "ocp", version = "0.5.0", commit = "82202912", isLocal = false)
)
}
}

composeTestRule.onNodeWithText("ocp").assertIsDisplayed()
composeTestRule.onNodeWithText("0.5.0 · 82202912").assertIsDisplayed()
}

@Test
fun `a local contract shows LOCAL in place of a commit`() {
composeTestRule.setContent {
DesignSystem {
ContractInfoRow(
ContractInfo(name = "flipcash2", version = "0.9.0", commit = "LOCAL", isLocal = true)
)
}
}

composeTestRule.onNodeWithText("0.9.0 · LOCAL").assertIsDisplayed()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.flipcash.app.lab.internal

import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue

class ContractInfoTest {

@Test
fun `both contracts are listed, in a stable order`() {
val names = contractInfo().map { it.name }
assertEquals(listOf("ocp", "flipcash2"), names)
}

@Test
fun `detail joins the version and the short commit`() {
val row = ContractInfo(name = "ocp", version = "0.5.0", commit = "82202912", isLocal = false)
assertEquals("0.5.0 · 82202912", row.detail)
assertFalse(row.isLocal)
}

@Test
fun `a local contract keeps LOCAL in the detail and flags itself`() {
val row = ContractInfo(name = "ocp", version = "0.5.0", commit = "LOCAL", isLocal = true)
assertEquals("0.5.0 · LOCAL", row.detail)
assertTrue(row.isLocal)
}

@Test
fun `the real packages report a non-empty version and commit`() {
contractInfo().forEach { row ->
assertTrue(row.version.isNotEmpty(), "${row.name} has no version")
assertTrue(row.commit.isNotEmpty(), "${row.name} has no commit")
}
}
}
4 changes: 2 additions & 2 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ protovalidate-kt = "0.1.2"
# cadence and there is nothing to keep aligned.
# 0.3.0 is the first release of either package to ship R8 keep rules for its generated
# messages, which is what lets proguard-rules.pro drop its own.
ocp-client-protocol = "0.4.0"
flipcash2-client-protocol = "0.8.0"
ocp-client-protocol = "0.5.0"
flipcash2-client-protocol = "0.9.0"

# The Android port is the ONLY libphonenumber this app depends on, deliberately. Google's
# `com.googlecode` artifact used to sit alongside it; the two ship separate copies of the metadata,
Expand Down
3 changes: 3 additions & 0 deletions libs/logging/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,7 @@ dependencies {
implementation(libs.androidx.annotation)
implementation(libs.grpc.kotlin)
implementation(project(":libs:messaging"))

implementation(libs.ocp.client.protocol)
implementation(libs.flipcash2.client.protocol)
}
4 changes: 4 additions & 0 deletions libs/logging/src/main/kotlin/com/getcode/utils/FileTree.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import android.content.Context
import android.content.pm.PackageManager
import android.os.Build
import android.util.Log
import com.codeinc.flipcash.gen.Flipcash2ContractInfo
import com.codeinc.opencode.gen.OcpContractInfo
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.MutableSharedFlow
Expand Down Expand Up @@ -146,6 +148,8 @@ private fun buildDeviceHeader(context: Context): String {
appendLine("=".repeat(60))
appendLine("App Version: $versionName ($versionCode)")
appendLine("Package: ${context.packageName}")
appendLine("OCP Contract: ${OcpContractInfo.VERSION} (${OcpContractInfo.shortProtoCommit})")
appendLine("FC2 Contract: ${Flipcash2ContractInfo.VERSION} (${Flipcash2ContractInfo.shortProtoCommit})")
appendLine("User ID: ${userId ?: "not set"}")
appendLine("Device: ${Build.MANUFACTURER} ${Build.MODEL}")
appendLine("Android: ${Build.VERSION.RELEASE} (API ${Build.VERSION.SDK_INT})")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -263,4 +263,12 @@ class FileTreeTest {
assertTrue(content.contains("Android:"))
assertTrue(content.contains("Exported:"))
}

@Test
fun `exported log file names both contract packages`() {
Timber.d("contract check")
val content = fileTree.getLogFile()!!.readText()
assertTrue(content.contains("OCP Contract:"), "header has no OCP contract line")
assertTrue(content.contains("FC2 Contract:"), "header has no flipcash2 contract line")
}
}
Loading