Don't depend on type set at construction time for unit equality.

Units with different types but same value when conveting to a common
type should be equal.

Example: Mass.grams(1000.0) should be equal to Mass.kilograms(1.0).
Test: Added

Change-Id: I5b21a5e145c9dcc98130050015e7325e0d60dfc8
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/units/BloodGlucose.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/units/BloodGlucose.kt
index aa41934..3b5756e 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/units/BloodGlucose.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/units/BloodGlucose.kt
@@ -18,11 +18,11 @@
 
 /**
  * Represents a unit of blood glucose level (glycaemia). Supported units:
- *
  * - mmol/L - see [BloodGlucose.millimolesPerLiter]
  * - mg/dL - see [BloodGlucose.milligramsPerDeciliter]
- **/
-class BloodGlucose private constructor(
+ */
+class BloodGlucose
+private constructor(
     private val value: Double,
     private val type: Type,
 ) : Comparable<BloodGlucose> {
@@ -43,39 +43,30 @@
     /** Returns zero [BloodGlucose] of the same [Type]. */
     internal fun zero(): BloodGlucose = ZEROS.getValue(type)
 
-    override fun compareTo(other: BloodGlucose): Int = if (type == other.type) {
-        value.compareTo(other.value)
-    } else {
-        inMillimolesPerLiter.compareTo(other.inMillimolesPerLiter)
-    }
+    override fun compareTo(other: BloodGlucose): Int =
+        if (type == other.type) {
+            value.compareTo(other.value)
+        } else {
+            inMillimolesPerLiter.compareTo(other.inMillimolesPerLiter)
+        }
 
-    /*
-     * Generated by the IDE: Code -> Generate -> "equals() and hashCode()".
-     */
     override fun equals(other: Any?): Boolean {
         if (this === other) return true
         if (other !is BloodGlucose) return false
 
-        if (value != other.value) return false
-        if (type != other.type) return false
+        if (type == other.type) {
+            return value == other.value
+        }
 
-        return true
+        return inMillimolesPerLiter == other.inMillimolesPerLiter
     }
 
-    /*
-     * Generated by the IDE: Code -> Generate -> "equals() and hashCode()".
-     */
-    override fun hashCode(): Int {
-        var result = value.hashCode()
-        result = 31 * result + type.hashCode()
-        return result
-    }
+    override fun hashCode(): Int = inMillimolesPerLiter.hashCode()
 
     override fun toString(): String = "$value ${type.title}"
 
     companion object {
-        private val ZEROS =
-            Type.values().associateWith { BloodGlucose(value = 0.0, type = it) }
+        private val ZEROS = Type.values().associateWith { BloodGlucose(value = 0.0, type = it) }
 
         /** Creates [BloodGlucose] with the specified value in mmol/L. */
         @JvmStatic
@@ -91,7 +82,8 @@
     private enum class Type {
         MILLIMOLES_PER_LITER {
             override val millimolesPerLiterPerUnit: Double = 1.0
-            override val title: String get() = "mmol/L"
+            override val title: String
+                get() = "mmol/L"
         },
         MILLIGRAMS_PER_DECILITER {
             override val millimolesPerLiterPerUnit: Double = 1 / 18.0
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/units/Energy.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/units/Energy.kt
index 942982a..606b7c2 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/units/Energy.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/units/Energy.kt
@@ -18,13 +18,13 @@
 
 /**
  * Represents a unit of energy. Supported units:
- *
  * - calories - see [Energy.calories], [Double.calories]
  * - kilocalories - see [Energy.kilocalories], [Double.kilocalories]
  * - joules - see [Energy.joules], [Double.joules]
  * - kilojoules - see [Energy.kilojoules], [Double.kilojoules]
  */
-class Energy private constructor(
+class Energy
+private constructor(
     private val value: Double,
     private val type: Type,
 ) : Comparable<Energy> {
@@ -62,27 +62,18 @@
             inCalories.compareTo(other.inCalories)
         }
 
-    /*
-     * Generated by the IDE: Code -> Generate -> "equals() and hashCode()".
-     */
     override fun equals(other: Any?): Boolean {
         if (this === other) return true
         if (other !is Energy) return false
 
-        if (value != other.value) return false
-        if (type != other.type) return false
+        if (type == other.type) {
+            return value == other.value
+        }
 
-        return true
+        return inCalories == other.inCalories
     }
 
-    /*
-     * Generated by the IDE: Code -> Generate -> "equals() and hashCode()".
-     */
-    override fun hashCode(): Int {
-        var result = value.hashCode()
-        result = 31 * result + type.hashCode()
-        return result
-    }
+    override fun hashCode(): Int = inCalories.hashCode()
 
     override fun toString(): String = "$value ${type.title}"
 
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/units/Length.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/units/Length.kt
index 30f3daf..f320e98 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/units/Length.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/units/Length.kt
@@ -61,27 +61,18 @@
             inMeters.compareTo(other.inMeters)
         }
 
-    /*
-     * Generated by the IDE: Code -> Generate -> "equals() and hashCode()".
-     */
     override fun equals(other: Any?): Boolean {
         if (this === other) return true
         if (other !is Length) return false
 
-        if (value != other.value) return false
-        if (type != other.type) return false
+        if (type == other.type) {
+            return value == other.value
+        }
 
-        return true
+        return inMeters == other.inMeters
     }
 
-    /*
-     * Generated by the IDE: Code -> Generate -> "equals() and hashCode()".
-     */
-    override fun hashCode(): Int {
-        var result = value.hashCode()
-        result = 31 * result + type.hashCode()
-        return result
-    }
+    override fun hashCode(): Int = inMeters.hashCode()
 
     override fun toString(): String = "$value ${type.name.lowercase()}"
 
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/units/Mass.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/units/Mass.kt
index babdd20..408d8f0 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/units/Mass.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/units/Mass.kt
@@ -18,7 +18,6 @@
 
 /**
  * Represents a unit of mass. Supported units:
- *
  * - grams - see [Mass.grams], [Double.grams]
  * - kilograms - see [Mass.kilograms], [Double.kilograms]
  * - milligrams - see [Mass.milligrams], [Double.milligrams]
@@ -26,7 +25,8 @@
  * - ounces - see [Mass.ounces], [Double.ounces]
  * - pounds - see [Mass.pounds], [Double.pounds]
  */
-class Mass private constructor(
+class Mass
+private constructor(
     private val value: Double,
     private val type: Type,
 ) : Comparable<Mass> {
@@ -74,27 +74,18 @@
             inGrams.compareTo(other.inGrams)
         }
 
-    /*
-     * Generated by the IDE: Code -> Generate -> "equals() and hashCode()".
-     */
     override fun equals(other: Any?): Boolean {
         if (this === other) return true
         if (other !is Mass) return false
 
-        if (value != other.value) return false
-        if (type != other.type) return false
+        if (type == other.type) {
+            return value == other.value
+        }
 
-        return true
+        return inGrams == other.inGrams
     }
 
-    /*
-     * Generated by the IDE: Code -> Generate -> "equals() and hashCode()".
-     */
-    override fun hashCode(): Int {
-        var result = value.hashCode()
-        result = 31 * result + type.hashCode()
-        return result
-    }
+    override fun hashCode(): Int = inGrams.hashCode()
 
     override fun toString(): String = "$value ${type.name.lowercase()}"
 
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/units/Percentage.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/units/Percentage.kt
index c7a13d1..34ac2c3 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/units/Percentage.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/units/Percentage.kt
@@ -21,24 +21,14 @@
 
     override fun compareTo(other: Percentage): Int = value.compareTo(other.value)
 
-    /*
-     * Generated by the IDE: Code -> Generate -> "equals() and hashCode()".
-     */
     override fun equals(other: Any?): Boolean {
         if (this === other) return true
         if (other !is Percentage) return false
 
-        if (value != other.value) return false
-
-        return true
+        return value == other.value
     }
 
-    /*
-     * Generated by the IDE: Code -> Generate -> "equals() and hashCode()".
-     */
-    override fun hashCode(): Int {
-        return value.hashCode()
-    }
+    override fun hashCode(): Int = value.hashCode()
 
     override fun toString(): String = "$value%"
 }
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/units/Power.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/units/Power.kt
index 41dfd33..6caae89 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/units/Power.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/units/Power.kt
@@ -18,11 +18,11 @@
 
 /**
  * Represents a unit of power. Supported units:
- *
  * - watts - see [Power.watts], [Double.watts]
  * - kilocalories/day - see [Power.kilocaloriesPerDay], [Double.kilocaloriesPerDay]
- **/
-class Power private constructor(
+ */
+class Power
+private constructor(
     private val value: Double,
     private val type: Type,
 ) : Comparable<Power> {
@@ -50,27 +50,18 @@
             inWatts.compareTo(other.inWatts)
         }
 
-    /*
-     * Generated by the IDE: Code -> Generate -> "equals() and hashCode()".
-     */
     override fun equals(other: Any?): Boolean {
         if (this === other) return true
         if (other !is Power) return false
 
-        if (value != other.value) return false
-        if (type != other.type) return false
+        if (type == other.type) {
+            return value == other.value
+        }
 
-        return true
+        return inWatts == other.inWatts
     }
 
-    /*
-     * Generated by the IDE: Code -> Generate -> "equals() and hashCode()".
-     */
-    override fun hashCode(): Int {
-        var result = value.hashCode()
-        result = 31 * result + type.hashCode()
-        return result
-    }
+    override fun hashCode(): Int = inWatts.hashCode()
 
     override fun toString(): String = "$value ${type.title}"
 
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/units/Pressure.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/units/Pressure.kt
index c0091f5..4ab9611 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/units/Pressure.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/units/Pressure.kt
@@ -18,11 +18,11 @@
 
 /**
  * Represents a unit of pressure. Supported units:
- *
  * - millimeters of Mercury (mmHg) - see [Pressure.millimetersOfMercury],
- * [Double.millimetersOfMercury].
+ *   [Double.millimetersOfMercury].
  */
-class Pressure private constructor(
+class Pressure
+private constructor(
     private val value: Double,
 ) : Comparable<Pressure> {
 
@@ -36,24 +36,14 @@
 
     override fun compareTo(other: Pressure): Int = value.compareTo(other.value)
 
-    /*
-     * Generated by the IDE: Code -> Generate -> "equals() and hashCode()".
-     */
     override fun equals(other: Any?): Boolean {
         if (this === other) return true
         if (other !is Pressure) return false
 
-        if (value != other.value) return false
-
-        return true
+        return value == other.value
     }
 
-    /*
-     * Generated by the IDE: Code -> Generate -> "equals() and hashCode()".
-     */
-    override fun hashCode(): Int {
-        return value.hashCode()
-    }
+    override fun hashCode(): Int = value.hashCode()
 
     override fun toString(): String = "$value mmHg"
 
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/units/Temperature.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/units/Temperature.kt
index 5a1d80e..77e1f1f 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/units/Temperature.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/units/Temperature.kt
@@ -16,12 +16,13 @@
 
 package androidx.health.connect.client.units
 
-/** Represents a unit of temperature. Supported units:
- *
+/**
+ * Represents a unit of temperature. Supported units:
  * - Celsius - see [Temperature.celsius], [Double.celsius]
  * - Fahrenheit - see [Temperature.fahrenheit], [Double.fahrenheit]
- **/
-class Temperature private constructor(
+ */
+class Temperature
+private constructor(
     private val value: Double,
     private val type: Type,
 ) : Comparable<Temperature> {
@@ -51,27 +52,18 @@
             inCelsius.compareTo(other.inCelsius)
         }
 
-    /*
-     * Generated by the IDE: Code -> Generate -> "equals() and hashCode()".
-     */
     override fun equals(other: Any?): Boolean {
         if (this === other) return true
         if (other !is Temperature) return false
 
-        if (value != other.value) return false
-        if (type != other.type) return false
+        if (type == other.type) {
+            return value == other.value
+        }
 
-        return true
+        return inCelsius == other.inCelsius
     }
 
-    /*
-     * Generated by the IDE: Code -> Generate -> "equals() and hashCode()".
-     */
-    override fun hashCode(): Int {
-        var result = value.hashCode()
-        result = 31 * result + type.hashCode()
-        return result
-    }
+    override fun hashCode(): Int = inCelsius.hashCode()
 
     override fun toString(): String = "$value ${type.title}"
 
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/units/Velocity.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/units/Velocity.kt
index 9a15e36..ae7da91 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/units/Velocity.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/units/Velocity.kt
@@ -18,12 +18,12 @@
 
 /**
  * Represents a unit of speed. Supported units:
- *
  * - metersPerSecond - see [Velocity.metersPerSecond], [Double.metersPerSecond]
  * - kilometersPerHour - see [Velocity.kilometersPerHour], [Double.kilometersPerHour]
  * - milesPerHour - see [Velocity.milesPerHour], [Double.milesPerHour]
  */
-class Velocity private constructor(
+class Velocity
+private constructor(
     private val value: Double,
     private val type: Type,
 ) : Comparable<Velocity> {
@@ -56,27 +56,18 @@
             inMetersPerSecond.compareTo(other.inMetersPerSecond)
         }
 
-    /*
-     * Generated by the IDE: Code -> Generate -> "equals() and hashCode()".
-     */
     override fun equals(other: Any?): Boolean {
         if (this === other) return true
         if (other !is Velocity) return false
 
-        if (value != other.value) return false
-        if (type != other.type) return false
+        if (type == other.type) {
+            return value == other.value
+        }
 
-        return true
+        return inMetersPerSecond == other.inMetersPerSecond
     }
 
-    /*
-     * Generated by the IDE: Code -> Generate -> "equals() and hashCode()".
-     */
-    override fun hashCode(): Int {
-        var result = value.hashCode()
-        result = 31 * result + type.hashCode()
-        return result
-    }
+    override fun hashCode(): Int = inMetersPerSecond.hashCode()
 
     override fun toString(): String = "$value ${type.title}"
 
@@ -92,8 +83,7 @@
         fun kilometersPerHour(value: Double): Velocity = Velocity(value, Type.KILOMETERS_PER_HOUR)
 
         /** Creates [Velocity] with the specified value in miles per hour. */
-        @JvmStatic
-        fun milesPerHour(value: Double): Velocity = Velocity(value, Type.MILES_PER_HOUR)
+        @JvmStatic fun milesPerHour(value: Double): Velocity = Velocity(value, Type.MILES_PER_HOUR)
     }
 
     private enum class Type {
diff --git a/health/connect/connect-client/src/main/java/androidx/health/connect/client/units/Volume.kt b/health/connect/connect-client/src/main/java/androidx/health/connect/client/units/Volume.kt
index 723be33..4ec0a30 100644
--- a/health/connect/connect-client/src/main/java/androidx/health/connect/client/units/Volume.kt
+++ b/health/connect/connect-client/src/main/java/androidx/health/connect/client/units/Volume.kt
@@ -18,12 +18,12 @@
 
 /**
  * Represents a unit of volume. Supported units:
- *
  * - liters - see [Volume.liters], [Double.liters]
  * - milliliters - see [Volume.milliliters], [Double.milliliters]
  * - US fluid ounces - see [Volume.fluidOuncesUs], [Double.fluidOuncesUs]
  */
-class Volume private constructor(
+class Volume
+private constructor(
     private val value: Double,
     private val type: Type,
 ) : Comparable<Volume> {
@@ -56,27 +56,18 @@
             inLiters.compareTo(other.inLiters)
         }
 
-    /*
-     * Generated by the IDE: Code -> Generate -> "equals() and hashCode()".
-     */
     override fun equals(other: Any?): Boolean {
         if (this === other) return true
         if (other !is Volume) return false
 
-        if (value != other.value) return false
-        if (type != other.type) return false
+        if (type == other.type) {
+            return value == other.value
+        }
 
-        return true
+        return inLiters == other.inLiters
     }
 
-    /*
-     * Generated by the IDE: Code -> Generate -> "equals() and hashCode()".
-     */
-    override fun hashCode(): Int {
-        var result = value.hashCode()
-        result = 31 * result + type.hashCode()
-        return result
-    }
+    override fun hashCode(): Int = inLiters.hashCode()
 
     override fun toString(): String = "$value ${type.title}"
 
@@ -84,16 +75,13 @@
         private val ZEROS = Type.values().associateWith { Volume(value = 0.0, type = it) }
 
         /** Creates [Volume] with the specified value in liters. */
-        @JvmStatic
-        fun liters(value: Double): Volume = Volume(value, Type.LITERS)
+        @JvmStatic fun liters(value: Double): Volume = Volume(value, Type.LITERS)
 
         /** Creates [Volume] with the specified value in milliliters. */
-        @JvmStatic
-        fun milliliters(value: Double): Volume = Volume(value, Type.MILLILITERS)
+        @JvmStatic fun milliliters(value: Double): Volume = Volume(value, Type.MILLILITERS)
 
         /** Creates [Volume] with the specified value in US fluid ounces. */
-        @JvmStatic
-        fun fluidOuncesUs(value: Double): Volume = Volume(value, Type.FLUID_OUNCES_US)
+        @JvmStatic fun fluidOuncesUs(value: Double): Volume = Volume(value, Type.FLUID_OUNCES_US)
     }
 
     private enum class Type {
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/units/BloodGlucoseTest.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/units/BloodGlucoseTest.kt
index d6d4892..8a7b4ed 100644
--- a/health/connect/connect-client/src/test/java/androidx/health/connect/client/units/BloodGlucoseTest.kt
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/units/BloodGlucoseTest.kt
@@ -25,11 +25,9 @@
 @RunWith(AndroidJUnit4::class)
 class BloodGlucoseTest {
 
-    @Rule
-    @JvmField
-    val expect = Expect.create()
+    @Rule @JvmField val expect = Expect.create()
 
-    private val tests: List<Triple<Double, Double, String>> =
+    private val conversions: List<Triple<Double, Double, String>> =
         listOf(
             Triple(
                 BloodGlucose.millimolesPerLiter(5.0).inMilligramsPerDeciliter,
@@ -54,9 +52,47 @@
         )
 
     @Test
-    fun testAll() {
-        for (test in tests) {
-            expect.withMessage(test.third).that(test.first).isWithin(0.00001).of(test.second)
+    fun conversion() {
+        for (conversion in conversions) {
+            expect
+                .withMessage(conversion.third)
+                .that(conversion.first)
+                .isWithin(0.00001)
+                .of(conversion.second)
         }
     }
-}
\ No newline at end of file
+
+    @Test
+    fun equals_sameValues_areEqual() {
+        expect
+            .that(BloodGlucose.millimolesPerLiter(5.0))
+            .isEqualTo(BloodGlucose.millimolesPerLiter(5.0))
+        expect
+            .that(BloodGlucose.millimolesPerLiter(5.0))
+            .isEqualTo(BloodGlucose.milligramsPerDeciliter(5.0 * 18.0))
+    }
+
+    @Test
+    fun equals_differentValues_areNotEqual() {
+        expect
+            .that(BloodGlucose.millimolesPerLiter(5.01))
+            .isNotEqualTo(BloodGlucose.millimolesPerLiter(5.0))
+        expect
+            .that(BloodGlucose.millimolesPerLiter(5.01))
+            .isNotEqualTo(BloodGlucose.milligramsPerDeciliter(5.0 * 18.0))
+    }
+
+    @Test
+    fun hashCode_sameValues_areEqual() {
+        expect
+            .that(BloodGlucose.millimolesPerLiter(5.0).hashCode())
+            .isEqualTo(BloodGlucose.milligramsPerDeciliter(5.0 * 18.0).hashCode())
+    }
+
+    @Test
+    fun hashCode_differentValues_areNotEqual() {
+        expect
+            .that(BloodGlucose.millimolesPerLiter(5.01).hashCode())
+            .isNotEqualTo(BloodGlucose.milligramsPerDeciliter(5.0 * 18.0).hashCode())
+    }
+}
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/units/EnergyTest.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/units/EnergyTest.kt
new file mode 100644
index 0000000..b7449012
--- /dev/null
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/units/EnergyTest.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.health.connect.client.units
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Expect
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class EnergyTest {
+
+    @Rule @JvmField val expect = Expect.create()
+
+    @Test
+    fun equals_sameValues_areEqual() {
+        expect.that(Energy.kilocalories(235.0)).isEqualTo(Energy.kilocalories(235.0))
+        expect.that(Energy.kilocalories(235.0)).isEqualTo(Energy.calories(235_000.0))
+    }
+
+    @Test
+    fun equals_differentValues_areNotEqual() {
+        expect.that(Energy.kilocalories(235.001)).isNotEqualTo(Energy.kilocalories(235.0))
+        expect.that(Energy.kilocalories(235.001)).isNotEqualTo(Energy.calories(235_000.0))
+    }
+
+    @Test
+    fun hashCode_sameValues_areEqual() {
+        expect
+            .that(Energy.kilocalories(235.0).hashCode())
+            .isEqualTo(Energy.calories(235_000.0).hashCode())
+    }
+
+    @Test
+    fun hashCode_differentValues_areNotEqual() {
+        expect
+            .that(Energy.kilocalories(235.001).hashCode())
+            .isNotEqualTo(Energy.calories(235_000.0).hashCode())
+    }
+}
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/units/LengthTest.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/units/LengthTest.kt
new file mode 100644
index 0000000..8a8977b
--- /dev/null
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/units/LengthTest.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.health.connect.client.units
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Expect
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class LengthTest {
+
+    @Rule @JvmField val expect = Expect.create()
+
+    @Test
+    fun equals_sameValues_areEqual() {
+        expect.that(Length.kilometers(0.7)).isEqualTo(Length.kilometers(0.7))
+        expect.that(Length.kilometers(0.7)).isEqualTo(Length.meters(700.0))
+    }
+
+    @Test
+    fun equals_differentValues_areNotEqual() {
+        expect.that(Length.kilometers(0.70001)).isNotEqualTo(Length.kilometers(0.7))
+        expect.that(Length.kilometers(0.70001)).isNotEqualTo(Length.meters(700.0))
+    }
+
+    @Test
+    fun hashCode_sameValues_areEqual() {
+        expect.that(Length.kilometers(0.7).hashCode()).isEqualTo(Length.meters(700.0).hashCode())
+    }
+
+    @Test
+    fun hashCode_differentValues_areNotEqual() {
+        expect
+            .that(Length.kilometers(0.70001).hashCode())
+            .isNotEqualTo(Length.meters(700.0).hashCode())
+    }
+}
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/units/MassTest.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/units/MassTest.kt
new file mode 100644
index 0000000..2e99d5b
--- /dev/null
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/units/MassTest.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.health.connect.client.units
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Expect
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class MassTest {
+
+    @Rule @JvmField val expect = Expect.create()
+
+    @Test
+    fun equals_sameValues_areEqual() {
+        expect.that(Mass.kilograms(107.0)).isEqualTo(Mass.kilograms(107.0))
+        expect.that(Mass.kilograms(107.0)).isEqualTo(Mass.grams(107_000.0))
+    }
+
+    @Test
+    fun equals_differentValues_areNotEqual() {
+        expect.that(Mass.kilograms(107.1)).isNotEqualTo(Mass.kilograms(107.0))
+        expect.that(Mass.kilograms(107.1)).isNotEqualTo(Mass.grams(107_000.0))
+    }
+
+    @Test
+    fun hashCode_sameValues_areEqual() {
+        expect.that(Mass.kilograms(107.0).hashCode()).isEqualTo(Mass.grams(107_000.0).hashCode())
+    }
+
+    @Test
+    fun hashCode_differentValues_areNotEqual() {
+        expect.that(Mass.kilograms(107.1).hashCode()).isNotEqualTo(Mass.grams(107_000.0).hashCode())
+    }
+}
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/units/PercentageTest.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/units/PercentageTest.kt
new file mode 100644
index 0000000..1977766
--- /dev/null
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/units/PercentageTest.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.health.connect.client.units
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Expect
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class PercentageTest {
+
+    @Rule @JvmField val expect = Expect.create()
+
+    @Test
+    fun equals_sameValues_areEqual() {
+        expect.that(Percentage(99.0)).isEqualTo(Percentage(99.0))
+    }
+
+    @Test
+    fun equals_differentValues_areNotEqual() {
+        expect.that(Percentage(99.9)).isNotEqualTo(Percentage(100.0))
+    }
+
+    @Test
+    fun hashCode_sameValues_areEqual() {
+        expect.that(Percentage(99.0).hashCode()).isEqualTo(Percentage(99.0).hashCode())
+    }
+
+    @Test
+    fun hashCode_differentValues_areNotEqual() {
+        expect.that(Percentage(99.9).hashCode()).isNotEqualTo(Percentage(100.0).hashCode())
+    }
+}
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/units/PowerTest.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/units/PowerTest.kt
new file mode 100644
index 0000000..cb4c7bb
--- /dev/null
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/units/PowerTest.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.health.connect.client.units
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Expect
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class PowerTest {
+
+    @Rule @JvmField val expect = Expect.create()
+
+    @Test
+    fun equals_sameValues_areEqual() {
+        expect.that(Power.kilocaloriesPerDay(500.0)).isEqualTo(Power.kilocaloriesPerDay(500.0))
+        expect.that(Power.kilocaloriesPerDay(500.0)).isEqualTo(Power.watts(24.21296295))
+    }
+
+    @Test
+    fun equals_differentValues_areNotEqual() {
+        expect.that(Power.kilocaloriesPerDay(500.1)).isNotEqualTo(Power.kilocaloriesPerDay(500.0))
+        expect.that(Power.kilocaloriesPerDay(500.1)).isNotEqualTo(Power.watts(24.21296295))
+    }
+
+    @Test
+    fun hashCode_sameValues_areEqual() {
+        expect
+            .that(Power.kilocaloriesPerDay(500.0).hashCode())
+            .isEqualTo(Power.watts(24.21296295).hashCode())
+    }
+
+    @Test
+    fun hashCode_differentValues_areNotEqual() {
+        expect
+            .that(Power.kilocaloriesPerDay(500.1).hashCode())
+            .isNotEqualTo(Power.watts(24.21296295).hashCode())
+    }
+}
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/units/PressureTest.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/units/PressureTest.kt
new file mode 100644
index 0000000..34ef710
--- /dev/null
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/units/PressureTest.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.health.connect.client.units
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Expect
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class PressureTest {
+
+    @Rule @JvmField val expect = Expect.create()
+
+    @Test
+    fun equals_sameValues_areEqual() {
+        expect
+            .that(Pressure.millimetersOfMercury(10.0))
+            .isEqualTo(Pressure.millimetersOfMercury(10.0))
+    }
+
+    @Test
+    fun equals_differentValues_areNotEqual() {
+        expect
+            .that(Pressure.millimetersOfMercury(10.1))
+            .isNotEqualTo(Pressure.millimetersOfMercury(10.0))
+    }
+
+    @Test
+    fun hashCode_sameValues_areEqual() {
+        expect
+            .that(Pressure.millimetersOfMercury(10.0).hashCode())
+            .isEqualTo(Pressure.millimetersOfMercury(10.0).hashCode())
+    }
+
+    @Test
+    fun hashCode_differentValues_areNotEqual() {
+        expect
+            .that(Pressure.millimetersOfMercury(10.1).hashCode())
+            .isNotEqualTo(Pressure.millimetersOfMercury(10.0).hashCode())
+    }
+}
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/units/TemperatureTest.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/units/TemperatureTest.kt
new file mode 100644
index 0000000..e383c3d
--- /dev/null
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/units/TemperatureTest.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.health.connect.client.units
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Expect
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class TemperatureTest {
+
+    @Rule @JvmField val expect = Expect.create()
+
+    @Test
+    fun equals_sameValues_areEqual() {
+        expect.that(Temperature.celsius(37.6)).isEqualTo(Temperature.celsius(37.6))
+        expect.that(Temperature.celsius(37.6)).isEqualTo(Temperature.fahrenheit(99.68))
+    }
+
+    @Test
+    fun equals_differentValues_areNotEqual() {
+        expect.that(Temperature.celsius(37.61)).isNotEqualTo(Temperature.celsius(37.6))
+        expect.that(Temperature.celsius(37.61)).isNotEqualTo(Temperature.fahrenheit(99.68))
+    }
+
+    @Test
+    fun hashCode_sameValues_areEqual() {
+        expect
+            .that(Temperature.celsius(37.6).hashCode())
+            .isEqualTo(Temperature.fahrenheit(99.68).hashCode())
+    }
+
+    @Test
+    fun hashCode_differentValues_areNotEqual() {
+        expect
+            .that(Temperature.celsius(37.61).hashCode())
+            .isNotEqualTo(Temperature.fahrenheit(99.68).hashCode())
+    }
+}
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/units/VelocityTest.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/units/VelocityTest.kt
new file mode 100644
index 0000000..70187d0
--- /dev/null
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/units/VelocityTest.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.health.connect.client.units
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Expect
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class VelocityTest {
+
+    @Rule @JvmField val expect = Expect.create()
+
+    @Test
+    fun equals_sameValues_areEqual() {
+        expect.that(Velocity.kilometersPerHour(12.0)).isEqualTo(Velocity.kilometersPerHour(12.0))
+        expect
+            .that(Velocity.kilometersPerHour(12.0))
+            .isEqualTo(Velocity.milesPerHour(7.456448341689335))
+    }
+
+    @Test
+    fun equals_differentValues_areNotEqual() {
+        expect
+            .that(Velocity.kilometersPerHour(12.001))
+            .isNotEqualTo(Velocity.kilometersPerHour(12.0))
+        expect
+            .that(Velocity.kilometersPerHour(12.001))
+            .isNotEqualTo(Velocity.milesPerHour(7.456448341689335))
+    }
+
+    @Test
+    fun hashCode_sameValues_areEqual() {
+        expect
+            .that(Velocity.kilometersPerHour(12.0).hashCode())
+            .isEqualTo(Velocity.milesPerHour(7.456448341689335).hashCode())
+    }
+
+    @Test
+    fun hashCode_differentValues_areNotEqual() {
+        expect
+            .that(Velocity.kilometersPerHour(12.001).hashCode())
+            .isNotEqualTo(Velocity.milesPerHour(7.456448341689335).hashCode())
+    }
+}
diff --git a/health/connect/connect-client/src/test/java/androidx/health/connect/client/units/VolumeTest.kt b/health/connect/connect-client/src/test/java/androidx/health/connect/client/units/VolumeTest.kt
new file mode 100644
index 0000000..839550b
--- /dev/null
+++ b/health/connect/connect-client/src/test/java/androidx/health/connect/client/units/VolumeTest.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.health.connect.client.units
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Expect
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class VolumeTest {
+
+    @Rule @JvmField val expect = Expect.create()
+
+    @Test
+    fun equals_sameValues_areEqual() {
+        expect.that(Volume.liters(24.0)).isEqualTo(Volume.liters(24.0))
+        expect.that(Volume.liters(24.0)).isEqualTo(Volume.milliliters(24_000.0))
+    }
+
+    @Test
+    fun equals_differentValues_areNotEqual() {
+        expect.that(Volume.liters(24.01)).isNotEqualTo(Volume.liters(24.0))
+        expect.that(Volume.liters(24.01)).isNotEqualTo(Volume.milliliters(24_000.0))
+    }
+
+    @Test
+    fun hashCode_sameValues_areEqual() {
+        expect
+            .that(Volume.liters(24.0).hashCode())
+            .isEqualTo(Volume.milliliters(24_000.0).hashCode())
+    }
+
+    @Test
+    fun hashCode_differentValues_areNotEqual() {
+        expect
+            .that(Volume.liters(24.01).hashCode())
+            .isNotEqualTo(Volume.milliliters(24_000.0).hashCode())
+    }
+}