Loading core/java/android/util/SparseLongArray.java +17 −0 Original line number Diff line number Diff line Loading @@ -118,6 +118,23 @@ public class SparseLongArray implements Cloneable { } } /** * @hide * Remove a range of mappings as a batch. * * @param index Index to begin at * @param size Number of mappings to remove * * <p>For indices outside of the range <code>0...size()-1</code>, * the behavior is undefined.</p> */ public void removeAtRange(int index, int size) { size = Math.min(size, mSize - index); System.arraycopy(mKeys, index + size, mKeys, index, mSize - (index + size)); System.arraycopy(mValues, index + size, mValues, index, mSize - (index + size)); mSize -= size; } /** * Removes the mapping at the given index. */ Loading core/java/com/android/internal/os/BatteryStatsImpl.java +50 −10 Original line number Diff line number Diff line Loading @@ -42,7 +42,7 @@ import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; import android.os.WorkSource; import android.telephony.DataConnectionRealTimeInfo; import android.telephony.ModemActivityInfo; Loading Loading @@ -75,7 +75,7 @@ import com.android.internal.util.FastPrintWriter; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.JournaledFile; import com.android.internal.util.XmlUtils; import com.android.server.NetworkManagementSocketTagger; import libcore.util.EmptyArray; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; Loading Loading @@ -194,6 +194,17 @@ public class BatteryStatsImpl extends BatteryStats { public String getSubsystemLowPowerStats(); } public static abstract class UserInfoProvider { private int[] userIds; protected abstract @Nullable int[] getUserIds(); private final void refreshUserIds() { userIds = getUserIds(); } private final boolean exists(int userId) { return userIds != null ? ArrayUtils.contains(userIds, userId) : true; } } private final PlatformIdleStateCallback mPlatformIdleStateCallback; final class MyHandler extends Handler { Loading Loading @@ -262,6 +273,7 @@ public class BatteryStatsImpl extends BatteryStats { public final MyHandler mHandler; private ExternalStatsSync mExternalSync = null; private UserInfoProvider mUserInfoProvider = null; private BatteryCallback mCallback; Loading Loading @@ -649,6 +661,7 @@ public class BatteryStatsImpl extends BatteryStats { mDailyFile = null; mHandler = null; mPlatformIdleStateCallback = null; mUserInfoProvider = null; clearHistoryLocked(); } Loading Loading @@ -8588,16 +8601,14 @@ public class BatteryStatsImpl extends BatteryStats { return mCpuFreqs; } public BatteryStatsImpl(File systemDir, Handler handler) { this(new SystemClocks(), systemDir, handler, null); public BatteryStatsImpl(File systemDir, Handler handler, PlatformIdleStateCallback cb, UserInfoProvider userInfoProvider) { this(new SystemClocks(), systemDir, handler, cb, userInfoProvider); } public BatteryStatsImpl(File systemDir, Handler handler, PlatformIdleStateCallback cb) { this(new SystemClocks(), systemDir, handler, cb); } public BatteryStatsImpl(Clocks clocks, File systemDir, Handler handler, PlatformIdleStateCallback cb) { private BatteryStatsImpl(Clocks clocks, File systemDir, Handler handler, PlatformIdleStateCallback cb, UserInfoProvider userInfoProvider) { init(clocks); if (systemDir != null) { Loading Loading @@ -8684,6 +8695,7 @@ public class BatteryStatsImpl extends BatteryStats { clearHistoryLocked(); updateDailyDeadlineLocked(); mPlatformIdleStateCallback = cb; mUserInfoProvider = userInfoProvider; } public BatteryStatsImpl(Parcel p) { Loading Loading @@ -10172,6 +10184,7 @@ public class BatteryStatsImpl extends BatteryStats { // we read, we get a delta. If we are to distribute the cpu time, then do so. Otherwise // we just ignore the data. final long startTimeMs = mClocks.uptimeMillis(); mUserInfoProvider.refreshUserIds(); mKernelUidCpuTimeReader.readDelta(!mOnBatteryInternal ? null : new KernelUidCpuTimeReader.Callback() { @Override Loading @@ -10185,6 +10198,11 @@ public class BatteryStatsImpl extends BatteryStats { + " no mapping to owning uid: " + uid); return; } if (!mUserInfoProvider.exists(UserHandle.getUserId(uid))) { Slog.d(TAG, "Got readings for an invalid user's uid " + uid); mKernelUidCpuTimeReader.removeUid(uid); return; } final Uid u = getUidStatsLocked(uid); // Accumulate the total system and user time. Loading Loading @@ -10357,6 +10375,11 @@ public class BatteryStatsImpl extends BatteryStats { + " no mapping to owning uid: " + uid); return; } if (!mUserInfoProvider.exists(UserHandle.getUserId(uid))) { Slog.d(TAG, "Got readings for an invalid user's uid " + uid); mKernelUidCpuFreqTimeReader.removeUid(uid); return; } final Uid u = getUidStatsLocked(uid); if (u.mCpuFreqTimeMs == null) { u.mCpuFreqTimeMs = new LongSamplingCounterArray(mOnBatteryTimeBase); Loading Loading @@ -11018,6 +11041,23 @@ public class BatteryStatsImpl extends BatteryStats { return u; } public void onCleanupUserLocked(int userId) { final int firstUidForUser = UserHandle.getUid(userId, 0); final int lastUidForUser = UserHandle.getUid(userId, UserHandle.PER_USER_RANGE - 1); mKernelUidCpuFreqTimeReader.removeUidsInRange(firstUidForUser, lastUidForUser); mKernelUidCpuTimeReader.removeUidsInRange(firstUidForUser, lastUidForUser); } public void onUserRemovedLocked(int userId) { final int firstUidForUser = UserHandle.getUid(userId, 0); final int lastUidForUser = UserHandle.getUid(userId, UserHandle.PER_USER_RANGE - 1); mUidStats.put(firstUidForUser, null); mUidStats.put(lastUidForUser, null); final int firstIndex = mUidStats.indexOfKey(firstUidForUser); final int lastIndex = mUidStats.indexOfKey(lastUidForUser); mUidStats.removeAtRange(firstIndex, lastIndex - firstIndex + 1); } /** * Remove the statistics object for a particular uid. */ Loading core/java/com/android/internal/os/KernelUidCpuFreqTimeReader.java +11 −0 Original line number Diff line number Diff line Loading @@ -76,6 +76,17 @@ public class KernelUidCpuFreqTimeReader { mLastUidCpuFreqTimeMs.delete(uid); } public void removeUidsInRange(int startUid, int endUid) { if (endUid < startUid) { return; } mLastUidCpuFreqTimeMs.put(startUid, null); mLastUidCpuFreqTimeMs.put(endUid, null); final int firstIndex = mLastUidCpuFreqTimeMs.indexOfKey(startUid); final int lastIndex = mLastUidCpuFreqTimeMs.indexOfKey(endUid); mLastUidCpuFreqTimeMs.removeAtRange(firstIndex, lastIndex - firstIndex + 1); } @VisibleForTesting public void readDelta(BufferedReader reader, @Nullable Callback callback) throws IOException { String line = reader.readLine(); Loading core/java/com/android/internal/os/KernelUidCpuTimeReader.java +29 −4 Original line number Diff line number Diff line Loading @@ -130,17 +130,42 @@ public class KernelUidCpuTimeReader { * @param uid The UID to remove. */ public void removeUid(int uid) { int index = mLastUserTimeUs.indexOfKey(uid); final int index = mLastSystemTimeUs.indexOfKey(uid); if (index >= 0) { mLastUserTimeUs.removeAt(index); mLastSystemTimeUs.removeAt(index); mLastUserTimeUs.removeAt(index); } removeUidsFromKernelModule(uid, uid); } /** * Removes UIDs in a given range from the kernel module and internal accounting data. * @param startUid the first uid to remove * @param endUid the last uid to remove */ public void removeUidsInRange(int startUid, int endUid) { if (endUid < startUid) { return; } mLastSystemTimeUs.put(startUid, 0); mLastUserTimeUs.put(startUid, 0); mLastSystemTimeUs.put(endUid, 0); mLastUserTimeUs.put(endUid, 0); final int startIndex = mLastSystemTimeUs.indexOfKey(startUid); final int endIndex = mLastSystemTimeUs.indexOfKey(endUid); mLastSystemTimeUs.removeAtRange(startIndex, endIndex - startIndex + 1); mLastUserTimeUs.removeAtRange(startIndex, endIndex - startIndex + 1); removeUidsFromKernelModule(startUid, endUid); } private void removeUidsFromKernelModule(int startUid, int endUid) { Slog.d(TAG, "Removing uids " + startUid + "-" + endUid); try (FileWriter writer = new FileWriter(sRemoveUidProcFile)) { writer.write(Integer.toString(uid) + "-" + Integer.toString(uid)); writer.write(startUid + "-" + endUid); writer.flush(); } catch (IOException e) { Slog.e(TAG, "failed to remove uid from uid_cputime module", e); Slog.e(TAG, "failed to remove uids " + startUid + " - " + endUid + " from uid_cputime module", e); } } } core/tests/coretests/src/android/util/SparseLongArrayTest.java 0 → 100644 +156 −0 Original line number Diff line number Diff line /* * Copyright (C) 2007 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.util; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import android.support.annotation.NonNull; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import java.util.Random; /** * Internal tests for {@link SparseLongArray}. */ @SmallTest @RunWith(AndroidJUnit4.class) public class SparseLongArrayTest { private static final int TEST_SIZE = 1000; private SparseLongArray mSparseLongArray; private int[] mKeys; private long[] mValues; private Random mRandom; private static boolean isSame(@NonNull SparseLongArray array1, @NonNull SparseLongArray array2) { if (array1.size() != array2.size()) { return false; } for (int i = 0; i < array1.size(); i++) { if (array1.keyAt(i) != array2.keyAt(i) || array1.valueAt(i) != array2.valueAt(i)) { return false; } } return true; } private void assertRemoved(int startIndex, int endIndex) { for (int i = 0; i < TEST_SIZE; i++) { if (i >= startIndex && i <= endIndex) { assertEquals("Entry not removed", Long.MIN_VALUE, mSparseLongArray.get(mKeys[i], Long.MIN_VALUE)); } else { assertEquals("Untouched entry corrupted", mValues[i], mSparseLongArray.get(mKeys[i])); } } } /** * Generates a sorted array of distinct and random keys * * @param size the number of keys to return in the array. Should be < (2^31)/1000. * @return the array of keys */ private int[] generateRandomKeys(int size) { final int[] keys = new int[size]; keys[0] = -1 * mRandom.nextInt(size * 500); for (int i = 1; i < size; i++) { keys[i] = keys[i - 1] + 1 + mRandom.nextInt(1000); assertTrue(keys[i] > keys[i - 1]); } return keys; } @Before public void setUp() { mSparseLongArray = new SparseLongArray(); mRandom = new Random(12345); mKeys = generateRandomKeys(TEST_SIZE); mValues = new long[TEST_SIZE]; for (int i = 0; i < TEST_SIZE; i++) { mValues[i] = i + 1; mSparseLongArray.put(mKeys[i], mValues[i]); } } @Test public void testRemoveAtRange_removeHead() { mSparseLongArray.removeAtRange(0, 100); assertEquals(TEST_SIZE - 100, mSparseLongArray.size()); assertRemoved(0, 99); } @Test public void testRemoveAtRange_removeTail() { mSparseLongArray.removeAtRange(TEST_SIZE - 200, 200); assertEquals(TEST_SIZE - 200, mSparseLongArray.size()); assertRemoved(TEST_SIZE - 200, TEST_SIZE - 1); } @Test public void testRemoveAtRange_removeOverflow() { mSparseLongArray.removeAtRange(TEST_SIZE - 100, 200); assertEquals(TEST_SIZE - 100, mSparseLongArray.size()); assertRemoved(TEST_SIZE - 100, TEST_SIZE - 1); } @Test public void testRemoveAtRange_removeEverything() { mSparseLongArray.removeAtRange(0, TEST_SIZE); assertEquals(0, mSparseLongArray.size()); assertRemoved(0, TEST_SIZE - 1); } @Test public void testRemoveAtRange_removeMiddle() { mSparseLongArray.removeAtRange(200, 200); assertEquals(TEST_SIZE - 200, mSparseLongArray.size()); assertRemoved(200, 399); } @Test public void testRemoveAtRange_removeSingle() { mSparseLongArray.removeAtRange(300, 1); assertEquals(TEST_SIZE - 1, mSparseLongArray.size()); assertRemoved(300, 300); } @Test public void testRemoveAtRange_compareRemoveAt() { final SparseLongArray sparseLongArray2 = mSparseLongArray.clone(); assertTrue(isSame(mSparseLongArray, sparseLongArray2)); final int startIndex = 101; final int endIndex = 200; mSparseLongArray.removeAtRange(startIndex, endIndex - startIndex + 1); for (int i = endIndex; i >= startIndex; i--) { sparseLongArray2.removeAt(i); } assertEquals(TEST_SIZE - (endIndex - startIndex + 1), mSparseLongArray.size()); assertRemoved(startIndex, endIndex); assertTrue(isSame(sparseLongArray2, mSparseLongArray)); } } Loading
core/java/android/util/SparseLongArray.java +17 −0 Original line number Diff line number Diff line Loading @@ -118,6 +118,23 @@ public class SparseLongArray implements Cloneable { } } /** * @hide * Remove a range of mappings as a batch. * * @param index Index to begin at * @param size Number of mappings to remove * * <p>For indices outside of the range <code>0...size()-1</code>, * the behavior is undefined.</p> */ public void removeAtRange(int index, int size) { size = Math.min(size, mSize - index); System.arraycopy(mKeys, index + size, mKeys, index, mSize - (index + size)); System.arraycopy(mValues, index + size, mValues, index, mSize - (index + size)); mSize -= size; } /** * Removes the mapping at the given index. */ Loading
core/java/com/android/internal/os/BatteryStatsImpl.java +50 −10 Original line number Diff line number Diff line Loading @@ -42,7 +42,7 @@ import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; import android.os.WorkSource; import android.telephony.DataConnectionRealTimeInfo; import android.telephony.ModemActivityInfo; Loading Loading @@ -75,7 +75,7 @@ import com.android.internal.util.FastPrintWriter; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.JournaledFile; import com.android.internal.util.XmlUtils; import com.android.server.NetworkManagementSocketTagger; import libcore.util.EmptyArray; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; Loading Loading @@ -194,6 +194,17 @@ public class BatteryStatsImpl extends BatteryStats { public String getSubsystemLowPowerStats(); } public static abstract class UserInfoProvider { private int[] userIds; protected abstract @Nullable int[] getUserIds(); private final void refreshUserIds() { userIds = getUserIds(); } private final boolean exists(int userId) { return userIds != null ? ArrayUtils.contains(userIds, userId) : true; } } private final PlatformIdleStateCallback mPlatformIdleStateCallback; final class MyHandler extends Handler { Loading Loading @@ -262,6 +273,7 @@ public class BatteryStatsImpl extends BatteryStats { public final MyHandler mHandler; private ExternalStatsSync mExternalSync = null; private UserInfoProvider mUserInfoProvider = null; private BatteryCallback mCallback; Loading Loading @@ -649,6 +661,7 @@ public class BatteryStatsImpl extends BatteryStats { mDailyFile = null; mHandler = null; mPlatformIdleStateCallback = null; mUserInfoProvider = null; clearHistoryLocked(); } Loading Loading @@ -8588,16 +8601,14 @@ public class BatteryStatsImpl extends BatteryStats { return mCpuFreqs; } public BatteryStatsImpl(File systemDir, Handler handler) { this(new SystemClocks(), systemDir, handler, null); public BatteryStatsImpl(File systemDir, Handler handler, PlatformIdleStateCallback cb, UserInfoProvider userInfoProvider) { this(new SystemClocks(), systemDir, handler, cb, userInfoProvider); } public BatteryStatsImpl(File systemDir, Handler handler, PlatformIdleStateCallback cb) { this(new SystemClocks(), systemDir, handler, cb); } public BatteryStatsImpl(Clocks clocks, File systemDir, Handler handler, PlatformIdleStateCallback cb) { private BatteryStatsImpl(Clocks clocks, File systemDir, Handler handler, PlatformIdleStateCallback cb, UserInfoProvider userInfoProvider) { init(clocks); if (systemDir != null) { Loading Loading @@ -8684,6 +8695,7 @@ public class BatteryStatsImpl extends BatteryStats { clearHistoryLocked(); updateDailyDeadlineLocked(); mPlatformIdleStateCallback = cb; mUserInfoProvider = userInfoProvider; } public BatteryStatsImpl(Parcel p) { Loading Loading @@ -10172,6 +10184,7 @@ public class BatteryStatsImpl extends BatteryStats { // we read, we get a delta. If we are to distribute the cpu time, then do so. Otherwise // we just ignore the data. final long startTimeMs = mClocks.uptimeMillis(); mUserInfoProvider.refreshUserIds(); mKernelUidCpuTimeReader.readDelta(!mOnBatteryInternal ? null : new KernelUidCpuTimeReader.Callback() { @Override Loading @@ -10185,6 +10198,11 @@ public class BatteryStatsImpl extends BatteryStats { + " no mapping to owning uid: " + uid); return; } if (!mUserInfoProvider.exists(UserHandle.getUserId(uid))) { Slog.d(TAG, "Got readings for an invalid user's uid " + uid); mKernelUidCpuTimeReader.removeUid(uid); return; } final Uid u = getUidStatsLocked(uid); // Accumulate the total system and user time. Loading Loading @@ -10357,6 +10375,11 @@ public class BatteryStatsImpl extends BatteryStats { + " no mapping to owning uid: " + uid); return; } if (!mUserInfoProvider.exists(UserHandle.getUserId(uid))) { Slog.d(TAG, "Got readings for an invalid user's uid " + uid); mKernelUidCpuFreqTimeReader.removeUid(uid); return; } final Uid u = getUidStatsLocked(uid); if (u.mCpuFreqTimeMs == null) { u.mCpuFreqTimeMs = new LongSamplingCounterArray(mOnBatteryTimeBase); Loading Loading @@ -11018,6 +11041,23 @@ public class BatteryStatsImpl extends BatteryStats { return u; } public void onCleanupUserLocked(int userId) { final int firstUidForUser = UserHandle.getUid(userId, 0); final int lastUidForUser = UserHandle.getUid(userId, UserHandle.PER_USER_RANGE - 1); mKernelUidCpuFreqTimeReader.removeUidsInRange(firstUidForUser, lastUidForUser); mKernelUidCpuTimeReader.removeUidsInRange(firstUidForUser, lastUidForUser); } public void onUserRemovedLocked(int userId) { final int firstUidForUser = UserHandle.getUid(userId, 0); final int lastUidForUser = UserHandle.getUid(userId, UserHandle.PER_USER_RANGE - 1); mUidStats.put(firstUidForUser, null); mUidStats.put(lastUidForUser, null); final int firstIndex = mUidStats.indexOfKey(firstUidForUser); final int lastIndex = mUidStats.indexOfKey(lastUidForUser); mUidStats.removeAtRange(firstIndex, lastIndex - firstIndex + 1); } /** * Remove the statistics object for a particular uid. */ Loading
core/java/com/android/internal/os/KernelUidCpuFreqTimeReader.java +11 −0 Original line number Diff line number Diff line Loading @@ -76,6 +76,17 @@ public class KernelUidCpuFreqTimeReader { mLastUidCpuFreqTimeMs.delete(uid); } public void removeUidsInRange(int startUid, int endUid) { if (endUid < startUid) { return; } mLastUidCpuFreqTimeMs.put(startUid, null); mLastUidCpuFreqTimeMs.put(endUid, null); final int firstIndex = mLastUidCpuFreqTimeMs.indexOfKey(startUid); final int lastIndex = mLastUidCpuFreqTimeMs.indexOfKey(endUid); mLastUidCpuFreqTimeMs.removeAtRange(firstIndex, lastIndex - firstIndex + 1); } @VisibleForTesting public void readDelta(BufferedReader reader, @Nullable Callback callback) throws IOException { String line = reader.readLine(); Loading
core/java/com/android/internal/os/KernelUidCpuTimeReader.java +29 −4 Original line number Diff line number Diff line Loading @@ -130,17 +130,42 @@ public class KernelUidCpuTimeReader { * @param uid The UID to remove. */ public void removeUid(int uid) { int index = mLastUserTimeUs.indexOfKey(uid); final int index = mLastSystemTimeUs.indexOfKey(uid); if (index >= 0) { mLastUserTimeUs.removeAt(index); mLastSystemTimeUs.removeAt(index); mLastUserTimeUs.removeAt(index); } removeUidsFromKernelModule(uid, uid); } /** * Removes UIDs in a given range from the kernel module and internal accounting data. * @param startUid the first uid to remove * @param endUid the last uid to remove */ public void removeUidsInRange(int startUid, int endUid) { if (endUid < startUid) { return; } mLastSystemTimeUs.put(startUid, 0); mLastUserTimeUs.put(startUid, 0); mLastSystemTimeUs.put(endUid, 0); mLastUserTimeUs.put(endUid, 0); final int startIndex = mLastSystemTimeUs.indexOfKey(startUid); final int endIndex = mLastSystemTimeUs.indexOfKey(endUid); mLastSystemTimeUs.removeAtRange(startIndex, endIndex - startIndex + 1); mLastUserTimeUs.removeAtRange(startIndex, endIndex - startIndex + 1); removeUidsFromKernelModule(startUid, endUid); } private void removeUidsFromKernelModule(int startUid, int endUid) { Slog.d(TAG, "Removing uids " + startUid + "-" + endUid); try (FileWriter writer = new FileWriter(sRemoveUidProcFile)) { writer.write(Integer.toString(uid) + "-" + Integer.toString(uid)); writer.write(startUid + "-" + endUid); writer.flush(); } catch (IOException e) { Slog.e(TAG, "failed to remove uid from uid_cputime module", e); Slog.e(TAG, "failed to remove uids " + startUid + " - " + endUid + " from uid_cputime module", e); } } }
core/tests/coretests/src/android/util/SparseLongArrayTest.java 0 → 100644 +156 −0 Original line number Diff line number Diff line /* * Copyright (C) 2007 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.util; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import android.support.annotation.NonNull; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import java.util.Random; /** * Internal tests for {@link SparseLongArray}. */ @SmallTest @RunWith(AndroidJUnit4.class) public class SparseLongArrayTest { private static final int TEST_SIZE = 1000; private SparseLongArray mSparseLongArray; private int[] mKeys; private long[] mValues; private Random mRandom; private static boolean isSame(@NonNull SparseLongArray array1, @NonNull SparseLongArray array2) { if (array1.size() != array2.size()) { return false; } for (int i = 0; i < array1.size(); i++) { if (array1.keyAt(i) != array2.keyAt(i) || array1.valueAt(i) != array2.valueAt(i)) { return false; } } return true; } private void assertRemoved(int startIndex, int endIndex) { for (int i = 0; i < TEST_SIZE; i++) { if (i >= startIndex && i <= endIndex) { assertEquals("Entry not removed", Long.MIN_VALUE, mSparseLongArray.get(mKeys[i], Long.MIN_VALUE)); } else { assertEquals("Untouched entry corrupted", mValues[i], mSparseLongArray.get(mKeys[i])); } } } /** * Generates a sorted array of distinct and random keys * * @param size the number of keys to return in the array. Should be < (2^31)/1000. * @return the array of keys */ private int[] generateRandomKeys(int size) { final int[] keys = new int[size]; keys[0] = -1 * mRandom.nextInt(size * 500); for (int i = 1; i < size; i++) { keys[i] = keys[i - 1] + 1 + mRandom.nextInt(1000); assertTrue(keys[i] > keys[i - 1]); } return keys; } @Before public void setUp() { mSparseLongArray = new SparseLongArray(); mRandom = new Random(12345); mKeys = generateRandomKeys(TEST_SIZE); mValues = new long[TEST_SIZE]; for (int i = 0; i < TEST_SIZE; i++) { mValues[i] = i + 1; mSparseLongArray.put(mKeys[i], mValues[i]); } } @Test public void testRemoveAtRange_removeHead() { mSparseLongArray.removeAtRange(0, 100); assertEquals(TEST_SIZE - 100, mSparseLongArray.size()); assertRemoved(0, 99); } @Test public void testRemoveAtRange_removeTail() { mSparseLongArray.removeAtRange(TEST_SIZE - 200, 200); assertEquals(TEST_SIZE - 200, mSparseLongArray.size()); assertRemoved(TEST_SIZE - 200, TEST_SIZE - 1); } @Test public void testRemoveAtRange_removeOverflow() { mSparseLongArray.removeAtRange(TEST_SIZE - 100, 200); assertEquals(TEST_SIZE - 100, mSparseLongArray.size()); assertRemoved(TEST_SIZE - 100, TEST_SIZE - 1); } @Test public void testRemoveAtRange_removeEverything() { mSparseLongArray.removeAtRange(0, TEST_SIZE); assertEquals(0, mSparseLongArray.size()); assertRemoved(0, TEST_SIZE - 1); } @Test public void testRemoveAtRange_removeMiddle() { mSparseLongArray.removeAtRange(200, 200); assertEquals(TEST_SIZE - 200, mSparseLongArray.size()); assertRemoved(200, 399); } @Test public void testRemoveAtRange_removeSingle() { mSparseLongArray.removeAtRange(300, 1); assertEquals(TEST_SIZE - 1, mSparseLongArray.size()); assertRemoved(300, 300); } @Test public void testRemoveAtRange_compareRemoveAt() { final SparseLongArray sparseLongArray2 = mSparseLongArray.clone(); assertTrue(isSame(mSparseLongArray, sparseLongArray2)); final int startIndex = 101; final int endIndex = 200; mSparseLongArray.removeAtRange(startIndex, endIndex - startIndex + 1); for (int i = endIndex; i >= startIndex; i--) { sparseLongArray2.removeAt(i); } assertEquals(TEST_SIZE - (endIndex - startIndex + 1), mSparseLongArray.size()); assertRemoved(startIndex, endIndex); assertTrue(isSame(sparseLongArray2, mSparseLongArray)); } }