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
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,14 @@ import com.google.firebase.dataconnect.testutil.property.arbitrary.JavaTimeEdgeC
import com.google.firebase.dataconnect.testutil.property.arbitrary.JavaTimeEdgeCases.MIN_NANO
import com.google.firebase.dataconnect.testutil.property.arbitrary.JavaTimeEdgeCases.MIN_YEAR
import com.google.firebase.dataconnect.testutil.toTimestamp
import io.kotest.common.mapError
import io.kotest.property.Arb
import io.kotest.property.arbitrary.arbitrary
import io.kotest.property.arbitrary.choice
import io.kotest.property.arbitrary.enum
import io.kotest.property.arbitrary.int
import io.kotest.property.arbitrary.of
import io.kotest.property.arbitrary.orNull
import kotlin.random.nextInt
import org.threeten.bp.Instant
import org.threeten.bp.OffsetDateTime
import org.threeten.bp.ZoneOffset
Expand Down Expand Up @@ -153,7 +153,11 @@ private fun Instant.toFdcFieldRegex(): Regex {
return Regex(pattern)
}

data class Nanoseconds(val nanoseconds: Int, val string: String)
data class Nanoseconds(
val nanoseconds: Int,
val string: String,
val digitCounts: JavaTimeArbs.NanosecondComponents
)

sealed interface TimeOffset {

Expand All @@ -177,8 +181,12 @@ sealed interface TimeOffset {

data class HhMm(val hours: Int, val minutes: Int, val sign: Sign) : TimeOffset {
init {
require(hours in 0..18) { "invalid hours: $hours (must be in the closed range 0..23)" }
require(minutes in 0..59) { "invalid minutes: $minutes (must be in the closed range 0..59)" }
require(hours in validHours) {
"invalid hours: $hours (must be in the closed range $validHours)"
}
require(minutes in validMinutes) {
"invalid minutes: $minutes (must be in the closed range $validMinutes)"
}
require(hours != 18 || minutes == 0) { "invalid minutes: $minutes (must be 0 when hours=18)" }
}

Expand All @@ -192,15 +200,44 @@ sealed interface TimeOffset {
append("$minutes".padStart(2, '0'))
}

fun toSeconds(): Int {
val absValue = (hours * SECONDS_PER_HOUR) + (minutes * SECONDS_PER_MINUTE)
return when (sign) {
Sign.Positive -> absValue
Sign.Negative -> -absValue
}
}

override fun toString() =
"HhMm(hours=$hours, minutes=$minutes, sign=$sign, " +
"zoneOffset=$zoneOffset, rfc3339String=$rfc3339String)"

operator fun compareTo(other: HhMm): Int = toSeconds() - other.toSeconds()

@Suppress("unused")
enum class Sign(val char: Char, val multiplier: Int) {
Positive('+', 1),
Negative('-', -1),
}

companion object {
private const val SECONDS_PER_MINUTE: Int = 60
private const val SECONDS_PER_HOUR: Int = 60 * SECONDS_PER_MINUTE

val validHours = 0..18
val validMinutes = 0..59

val maxSeconds: Int = 18 * SECONDS_PER_HOUR

fun forSeconds(seconds: Int, sign: Sign): HhMm {
require(seconds in 0..maxSeconds) {
"invalid seconds: $seconds (must be between 0 and $maxSeconds, inclusive)"
}
val hours = seconds / SECONDS_PER_HOUR
val minutes = (seconds - (hours * SECONDS_PER_HOUR)) / SECONDS_PER_MINUTE
return HhMm(hours = hours, minutes = minutes, sign = sign)
}
}
}
}

Expand All @@ -215,7 +252,6 @@ object JavaTimeArbs {
val minuteArb = minute()
val secondArb = second()
val nanosecondArb = nanosecond().orNull(nullProbability = 0.15)
val timeOffsetArb = timeOffset()

return arbitrary(JavaTimeInstantEdgeCases.all) {
val year = yearArb.bind()
Expand All @@ -226,7 +262,55 @@ object JavaTimeArbs {
val minute = minuteArb.bind()
val second = secondArb.bind()
val nanosecond = nanosecondArb.bind()
val timeOffset = timeOffsetArb.bind()

val instantUtc =
OffsetDateTime.of(
year,
month,
day,
hour,
minute,
second,
nanosecond?.nanoseconds ?: 0,
ZoneOffset.UTC,
)
.toInstant()

// The valid range below was copied from:
// com.google.firebase.Timestamp.Timestamp.validateRange() 253_402_300_800
val validEpochSecondRange = -62_135_596_800..253_402_300_800

val numSecondsBelowMaxEpochSecond = validEpochSecondRange.last - instantUtc.epochSecond
require(numSecondsBelowMaxEpochSecond > 0) {
"internal error gh98nqedss: " +
"invalid numSecondsBelowMaxEpochSecond: $numSecondsBelowMaxEpochSecond"
}
val minTimeZoneOffset =
if (numSecondsBelowMaxEpochSecond >= TimeOffset.HhMm.maxSeconds) {
null
} else {
TimeOffset.HhMm.forSeconds(
numSecondsBelowMaxEpochSecond.toInt(),
TimeOffset.HhMm.Sign.Negative
)
}

val numSecondsAboveMinEpochSecond = instantUtc.epochSecond - validEpochSecondRange.first
require(numSecondsAboveMinEpochSecond > 0) {
"internal error mje6a4mrbm: " +
"invalid numSecondsAboveMinEpochSecond: $numSecondsAboveMinEpochSecond"
}
val maxTimeZoneOffset =
if (numSecondsAboveMinEpochSecond >= TimeOffset.HhMm.maxSeconds) {
null
} else {
TimeOffset.HhMm.forSeconds(
numSecondsAboveMinEpochSecond.toInt(),
TimeOffset.HhMm.Sign.Positive
)
}

val timeOffset = timeOffset(min = minTimeZoneOffset, max = maxTimeZoneOffset).bind()

val instant =
OffsetDateTime.of(
Expand All @@ -241,6 +325,27 @@ object JavaTimeArbs {
)
.toInstant()

require(instant.epochSecond >= validEpochSecondRange.first) {
"internal error weppxzqj2y: " +
"instant.epochSecond out of range by " +
"${validEpochSecondRange.first - instant.epochSecond}: ${instant.epochSecond} (" +
"validEpochSecondRange.first=${validEpochSecondRange.first}, " +
"year=$year, month=$month, day=$day, " +
"hour=$hour, minute=$minute, second=$second, " +
"nanosecond=$nanosecond timeOffset=$timeOffset, " +
"minTimeZoneOffset=$minTimeZoneOffset, maxTimeZoneOffset=$maxTimeZoneOffset)"
}
require(instant.epochSecond <= validEpochSecondRange.last) {
"internal error yxga5xy9bm: " +
"instant.epochSecond out of range by " +
"${instant.epochSecond - validEpochSecondRange.last}: ${instant.epochSecond} (" +
"validEpochSecondRange.last=${validEpochSecondRange.last}, " +
"year=$year, month=$month, day=$day, " +
"hour=$hour, minute=$minute, second=$second, " +
"nanosecond=$nanosecond timeOffset=$timeOffset, " +
"minTimeZoneOffset=$minTimeZoneOffset, maxTimeZoneOffset=$maxTimeZoneOffset)"
}

val string = buildString {
append(year)
append('-')
Expand Down Expand Up @@ -268,7 +373,10 @@ object JavaTimeArbs {
}
}

fun timeOffset(): Arb<TimeOffset> = Arb.choice(timeOffsetUtc(), timeOffsetHhMm())
fun timeOffset(
min: TimeOffset.HhMm?,
max: TimeOffset.HhMm?,
): Arb<TimeOffset> = Arb.choice(timeOffsetUtc(), timeOffsetHhMm(min = min, max = max))

fun timeOffsetUtc(
case: Arb<TimeOffset.Utc.Case> = Arb.enum(),
Expand All @@ -278,20 +386,45 @@ object JavaTimeArbs {
sign: Arb<TimeOffset.HhMm.Sign> = Arb.enum(),
hour: Arb<Int> = Arb.positiveIntWithUniformNumDigitsProbability(0..18),
minute: Arb<Int> = minute(),
): Arb<TimeOffset.HhMm> =
arbitrary(
min: TimeOffset.HhMm?,
max: TimeOffset.HhMm?,
): Arb<TimeOffset.HhMm> {
require(min === null || max === null || min.toSeconds() < max.toSeconds()) {
"min must be strictly less than max, but got: " +
"min=$min (${min!!.toSeconds()} seconds), " +
"max=$max (${max!!.toSeconds()} seconds), " +
"a difference of ${min.toSeconds() - max.toSeconds()} seconds"
}

fun isBetweenMinAndMax(other: TimeOffset.HhMm): Boolean =
(min === null || other >= min) && (max === null || other <= max)

return arbitrary(
edgecases =
listOf(
TimeOffset.HhMm(hours = 0, minutes = 0, sign = TimeOffset.HhMm.Sign.Positive),
TimeOffset.HhMm(hours = 0, minutes = 0, sign = TimeOffset.HhMm.Sign.Negative),
TimeOffset.HhMm(hours = 17, minutes = 59, sign = TimeOffset.HhMm.Sign.Positive),
TimeOffset.HhMm(hours = 17, minutes = 59, sign = TimeOffset.HhMm.Sign.Negative),
TimeOffset.HhMm(hours = 18, minutes = 0, sign = TimeOffset.HhMm.Sign.Positive),
TimeOffset.HhMm(hours = 18, minutes = 0, sign = TimeOffset.HhMm.Sign.Negative),
)
TimeOffset.HhMm(hours = 0, minutes = 0, sign = TimeOffset.HhMm.Sign.Positive),
TimeOffset.HhMm(hours = 0, minutes = 0, sign = TimeOffset.HhMm.Sign.Negative),
TimeOffset.HhMm(hours = 17, minutes = 59, sign = TimeOffset.HhMm.Sign.Positive),
TimeOffset.HhMm(hours = 17, minutes = 59, sign = TimeOffset.HhMm.Sign.Negative),
TimeOffset.HhMm(hours = 18, minutes = 0, sign = TimeOffset.HhMm.Sign.Positive),
TimeOffset.HhMm(hours = 18, minutes = 0, sign = TimeOffset.HhMm.Sign.Negative),
)
.filter(::isBetweenMinAndMax)
) {
TimeOffset.HhMm(hours = hour.bind(), minutes = minute.bind(), sign = sign.bind())
var count = 0
var hhmm: TimeOffset.HhMm
while (true) {
count++
hhmm = TimeOffset.HhMm(hours = hour.bind(), minutes = minute.bind(), sign = sign.bind())
if (isBetweenMinAndMax(hhmm)) {
break
} else if (count > 1000) {
throw Exception("internal error j878fp4gmr: exhausted attempts to generate HhMm")
}
}
hhmm
}
}

fun year(): Arb<Int> = Arb.int(MIN_YEAR..MAX_YEAR)

Expand All @@ -316,8 +449,12 @@ object JavaTimeArbs {
repeat(digitCounts.leadingZeroes) { append('0') }
if (digitCounts.proper > 0) {
append(nonZeroDigits.bind())
repeat(digitCounts.proper - 2) { append(digits.bind()) }
append(nonZeroDigits.bind())
if (digitCounts.proper > 1) {
if (digitCounts.proper > 2) {
repeat(digitCounts.proper - 2) { append(digits.bind()) }
}
append(nonZeroDigits.bind())
}
}
repeat(digitCounts.trailingZeroes) { append('0') }
}
Expand All @@ -327,18 +464,29 @@ object JavaTimeArbs {
if (nanosecondsStringTrimmed.isEmpty()) {
0
} else {
nanosecondsStringTrimmed.toInt()
val toIntResult = nanosecondsStringTrimmed.runCatching { toInt() }
toIntResult.mapError { exception ->
Exception(
"internal error qbdgapmye2: " +
"failed to parse nanosecondsStringTrimmed as an int: " +
"\"$nanosecondsStringTrimmed\" (digitCounts=$digitCounts)",
exception
)
}
toIntResult.getOrThrow()
}

Nanoseconds(nanosecondsInt, nanosecondsString)
check(nanosecondsInt in 0..999_999_999) {
"internal error c7j2myw6bd: " +
"nanosecondsStringTrimmed parsed to a value outside the valid range: " +
"$nanosecondsInt (digitCounts=$digitCounts)"
}

Nanoseconds(nanosecondsInt, nanosecondsString, digitCounts)
}
}

private data class NanosecondComponents(
val leadingZeroes: Int,
val proper: Int,
val trailingZeroes: Int
)
data class NanosecondComponents(val leadingZeroes: Int, val proper: Int, val trailingZeroes: Int)

private fun nanosecondComponents(): Arb<NanosecondComponents> =
arbitrary(
Expand Down
2 changes: 2 additions & 0 deletions firebase-functions/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# Unreleased
* [fixed] Resolve Kotlin migration visibility issues
([#6522](//github.com/firebase/firebase-android-sdk/pull/6522))


# 21.1.0
Expand Down
16 changes: 8 additions & 8 deletions firebase-vertexai/api.txt
Original file line number Diff line number Diff line change
Expand Up @@ -183,14 +183,14 @@ package com.google.firebase.vertexai.type {
}

public final class CountTokensResponse {
ctor public CountTokensResponse(int totalTokens, Integer? totalBillableCharacters = null, java.util.List<com.google.firebase.vertexai.type.ModalityTokenCount>? promptTokensDetails = null);
ctor public CountTokensResponse(int totalTokens, Integer? totalBillableCharacters = null, java.util.List<com.google.firebase.vertexai.type.ModalityTokenCount> promptTokensDetails = emptyList());
method public operator int component1();
method public operator Integer? component2();
method public operator java.util.List<com.google.firebase.vertexai.type.ModalityTokenCount>? component3();
method public java.util.List<com.google.firebase.vertexai.type.ModalityTokenCount>? getPromptTokensDetails();
method public java.util.List<com.google.firebase.vertexai.type.ModalityTokenCount> getPromptTokensDetails();
method public Integer? getTotalBillableCharacters();
method public int getTotalTokens();
property public final java.util.List<com.google.firebase.vertexai.type.ModalityTokenCount>? promptTokensDetails;
property public final java.util.List<com.google.firebase.vertexai.type.ModalityTokenCount> promptTokensDetails;
property public final Integer? totalBillableCharacters;
property public final int totalTokens;
}
Expand Down Expand Up @@ -582,16 +582,16 @@ package com.google.firebase.vertexai.type {
}

public final class UsageMetadata {
ctor public UsageMetadata(int promptTokenCount, Integer? candidatesTokenCount, int totalTokenCount, java.util.List<com.google.firebase.vertexai.type.ModalityTokenCount>? promptTokensDetails, java.util.List<com.google.firebase.vertexai.type.ModalityTokenCount>? candidatesTokensDetails);
ctor public UsageMetadata(int promptTokenCount, Integer? candidatesTokenCount, int totalTokenCount, java.util.List<com.google.firebase.vertexai.type.ModalityTokenCount> promptTokensDetails, java.util.List<com.google.firebase.vertexai.type.ModalityTokenCount> candidatesTokensDetails);
method public Integer? getCandidatesTokenCount();
method public java.util.List<com.google.firebase.vertexai.type.ModalityTokenCount>? getCandidatesTokensDetails();
method public java.util.List<com.google.firebase.vertexai.type.ModalityTokenCount> getCandidatesTokensDetails();
method public int getPromptTokenCount();
method public java.util.List<com.google.firebase.vertexai.type.ModalityTokenCount>? getPromptTokensDetails();
method public java.util.List<com.google.firebase.vertexai.type.ModalityTokenCount> getPromptTokensDetails();
method public int getTotalTokenCount();
property public final Integer? candidatesTokenCount;
property public final java.util.List<com.google.firebase.vertexai.type.ModalityTokenCount>? candidatesTokensDetails;
property public final java.util.List<com.google.firebase.vertexai.type.ModalityTokenCount> candidatesTokensDetails;
property public final int promptTokenCount;
property public final java.util.List<com.google.firebase.vertexai.type.ModalityTokenCount>? promptTokensDetails;
property public final java.util.List<com.google.firebase.vertexai.type.ModalityTokenCount> promptTokensDetails;
property public final int totalTokenCount;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import kotlinx.serialization.Serializable
public class CountTokensResponse(
public val totalTokens: Int,
public val totalBillableCharacters: Int? = null,
public val promptTokensDetails: List<ModalityTokenCount>? = null,
public val promptTokensDetails: List<ModalityTokenCount> = emptyList(),
) {
public operator fun component1(): Int = totalTokens

Expand All @@ -55,7 +55,7 @@ public class CountTokensResponse(
return CountTokensResponse(
totalTokens,
totalBillableCharacters ?: 0,
promptTokensDetails?.map { it.toPublic() }
promptTokensDetails?.map { it.toPublic() } ?: emptyList()
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ public class UsageMetadata(
public val promptTokenCount: Int,
public val candidatesTokenCount: Int?,
public val totalTokenCount: Int,
public val promptTokensDetails: List<ModalityTokenCount>?,
public val candidatesTokensDetails: List<ModalityTokenCount>?,
public val promptTokensDetails: List<ModalityTokenCount>,
public val candidatesTokensDetails: List<ModalityTokenCount>,
) {

@Serializable
Expand All @@ -51,8 +51,8 @@ public class UsageMetadata(
promptTokenCount ?: 0,
candidatesTokenCount ?: 0,
totalTokenCount ?: 0,
promptTokensDetails = promptTokensDetails?.map { it.toPublic() },
candidatesTokensDetails = candidatesTokensDetails?.map { it.toPublic() }
promptTokensDetails = promptTokensDetails?.map { it.toPublic() } ?: emptyList(),
candidatesTokensDetails = candidatesTokensDetails?.map { it.toPublic() } ?: emptyList()
)
}
}
Loading
Loading