text: /*
   * Copyright (C) 2020 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 android.net.netstats
  
  import android.net.NetworkStats
  import android.net.NetworkStats.DEFAULT_NETWORK_NO
  import android.net.NetworkStats.DEFAULT_NETWORK_YES
  import android.net.NetworkStats.Entry
  import android.net.NetworkStats.IFACE_VT
  import android.net.NetworkStats.METERED_NO
  import android.net.NetworkStats.METERED_YES
  import android.net.NetworkStats.ROAMING_NO
  import android.net.NetworkStats.ROAMING_YES
  import android.net.NetworkStats.SET_DEFAULT
  import android.net.NetworkStats.SET_FOREGROUND
  import android.net.NetworkStats.TAG_NONE
  import android.os.Build
  import androidx.test.filters.SmallTest
  import com.android.testutils.DevSdkIgnoreRule
  import com.android.testutils.assertFieldCountEquals
  import com.android.testutils.assertNetworkStatsEquals
  import com.android.testutils.assertParcelingIsLossless
  import org.junit.Before
  import org.junit.Rule
  import org.junit.Test
  import org.junit.runner.RunWith
  import org.junit.runners.JUnit4
  import kotlin.test.assertEquals
  
  @RunWith(JUnit4::class)
  @SmallTest
  class NetworkStatsApiTest {
      @Rule
      @JvmField
      val ignoreRule = DevSdkIgnoreRule(ignoreClassUpTo = Build.VERSION_CODES.Q)
  
      private val testStatsEmpty = NetworkStats(0L, 0)
  
      // Note that these variables need to be initialized outside of constructor, initialize
      // here with methods that don't exist in Q devices will result in crash.
  
      // stats1 and stats2 will have some entries with common keys, which are expected to
      // be merged if performing add on these 2 stats.
      private lateinit var testStats1: NetworkStats
      private lateinit var testStats2: NetworkStats
  
      // This is a result of adding stats1 and stats2, while the merging of common key items is
      // subject to test later, this should not be initialized with for a loop to add stats1
      // and stats2 above.
      private lateinit var testStats3: NetworkStats
  
      companion object {
          private const val TEST_IFACE = "test0"
          private const val TEST_UID1 = 1001
          private const val TEST_UID2 = 1002
      }
  
      @Before
      fun setUp() {
          testStats1 = NetworkStats(0L, 0)
                  // Entries which only appear in set1.
                  .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE,
                          METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, 20, 3, 57, 40, 3))
                  .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE,
                          METERED_NO, ROAMING_YES, DEFAULT_NETWORK_NO, 31, 7, 24, 5, 8))
                  .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE,
                          METERED_YES, ROAMING_NO, DEFAULT_NETWORK_NO, 25, 3, 47, 8, 2))
                  .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_FOREGROUND, TAG_NONE,
                          METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 37, 52, 1, 10, 4))
                  // Entries which are common for set1 and set2.
                  .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE,
                          METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 101, 2, 103, 4, 5))
                  .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, 0x80,
                          METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 17, 2, 11, 1, 0))
                  .addEntry(Entry(TEST_IFACE, TEST_UID2, SET_DEFAULT, TAG_NONE,
                          METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 40, 1, 0, 0, 8))
                  .addEntry(Entry(IFACE_VT, TEST_UID1, SET_DEFAULT, TAG_NONE,
                          METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 3, 1, 6, 2, 0))
          assertEquals(8, testStats1.size())
  
          testStats2 = NetworkStats(0L, 0)
                  // Entries which are common for set1 and set2.
                  .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, 0x80,
                          METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 3, 15, 2, 31, 1))
                  .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_FOREGROUND, TAG_NONE,
                          METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 13, 61, 10, 1, 45))
                  .addEntry(Entry(TEST_IFACE, TEST_UID2, SET_DEFAULT, TAG_NONE,
                          METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 11, 2, 3, 4, 7))
                  .addEntry(Entry(IFACE_VT, TEST_UID1, SET_DEFAULT, TAG_NONE,
                          METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 4, 3, 2, 1, 0))
                  // Entry which only appears in set2.
                  .addEntry(Entry(IFACE_VT, TEST_UID2, SET_DEFAULT, TAG_NONE,
                          METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 2, 3, 7, 8, 0))
          assertEquals(5, testStats2.size())
  
          testStats3 = NetworkStats(0L, 9)
                  // Entries which are unique either in stats1 or stats2.
                  .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE,
                          METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 101, 2, 103, 4, 5))
                  .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE,
                          METERED_NO, ROAMING_YES, DEFAULT_NETWORK_NO, 31, 7, 24, 5, 8))
                  .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE,
                          METERED_YES, ROAMING_NO, DEFAULT_NETWORK_NO, 25, 3, 47, 8, 2))
                  .addEntry(Entry(IFACE_VT, TEST_UID2, SET_DEFAULT, TAG_NONE,
                          METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 2, 3, 7, 8, 0))
                  // Entries which are common for stats1 and stats2 are being merged.
                  .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, TAG_NONE,
                          METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, 20, 3, 57, 40, 3))
                  .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, 0x80,
                          METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 20, 17, 13, 32, 1))
                  .addEntry(Entry(TEST_IFACE, TEST_UID1, SET_FOREGROUND, TAG_NONE,
                          METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 50, 113, 11, 11, 49))
                  .addEntry(Entry(TEST_IFACE, TEST_UID2, SET_DEFAULT, TAG_NONE,
                          METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 51, 3, 3, 4, 15))
                  .addEntry(Entry(IFACE_VT, TEST_UID1, SET_DEFAULT, TAG_NONE,
                          METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 7, 4, 8, 3, 0))
          assertEquals(9, testStats3.size())
      }
  
      @Test
      fun testAddEntry() {
          val expectedEntriesInStats2 = arrayOf(
                  Entry(TEST_IFACE, TEST_UID1, SET_DEFAULT, 0x80,
                          METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 3, 15, 2, 31, 1),
                  Entry(TEST_IFACE, TEST_UID1, SET_FOREGROUND, TAG_NONE,
                          METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 13, 61, 10, 1, 45),
                  Entry(TEST_IFACE, TEST_UID2, SET_DEFAULT, TAG_NONE,
                          METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 11, 2, 3, 4, 7),
                  Entry(IFACE_VT, TEST_UID1, SET_DEFAULT, TAG_NONE,
                          METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 4, 3, 2, 1, 0),
                  Entry(IFACE_VT, TEST_UID2, SET_DEFAULT, TAG_NONE,
                          METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 2, 3, 7, 8, 0))
  
          // While testStats* are already initialized with addEntry, verify content added
          // matches expectation.
          for (i in expectedEntriesInStats2.indices) {
              val entry = testStats2.getValues(i, null)
              assertEquals(expectedEntriesInStats2[i], entry)
          }
  
          // Verify entry updated with addEntry.
          val stats = testStats2.addEntry(Entry(IFACE_VT, TEST_UID1, SET_DEFAULT, TAG_NONE,
                  METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 12, -5, 7, 0, 9))
          assertEquals(Entry(IFACE_VT, TEST_UID1, SET_DEFAULT, TAG_NONE,
                  METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 16, -2, 9, 1, 9),
                  stats.getValues(3, null))
      }
  
      @Test
      fun testAdd() {
          var stats = NetworkStats(0L, 0)
          assertNetworkStatsEquals(testStatsEmpty, stats)
          stats = stats.add(testStats2)
          assertNetworkStatsEquals(testStats2, stats)
          stats = stats.add(testStats1)
          // EMPTY + STATS2 + STATS1 = STATS3
          assertNetworkStatsEquals(testStats3, stats)
      }
  
      @Test
      fun testParcelUnparcel() {
          assertParcelingIsLossless(testStatsEmpty)
          assertParcelingIsLossless(testStats1)
          assertParcelingIsLossless(testStats2)
          assertFieldCountEquals(15, NetworkStats::class.java)
      }
  
      @Test
      fun testDescribeContents() {
          assertEquals(0, testStatsEmpty.describeContents())
          assertEquals(0, testStats1.describeContents())
          assertEquals(0, testStats2.describeContents())
          assertEquals(0, testStats3.describeContents())
      }
  
      @Test
      fun testSubtract() {
          // STATS3 - STATS2 = STATS1
          assertNetworkStatsEquals(testStats1, testStats3.subtract(testStats2))
          // STATS3 - STATS1 = STATS2
          assertNetworkStatsEquals(testStats2, testStats3.subtract(testStats1))
      }
  
      @Test
      fun testMethodsDontModifyReceiver() {
          listOf(testStatsEmpty, testStats1, testStats2, testStats3).forEach {
              val origStats = it.clone()
              it.addEntry(Entry(TEST_IFACE, TEST_UID1, SET_FOREGROUND, TAG_NONE,
                      METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 13, 61, 10, 1, 45))
              it.add(testStats3)
              it.subtract(testStats1)
              assertNetworkStatsEquals(origStats, it)
          }
      }
  }