| /* |
| * Copyright (C) 2014 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 com.android.cts.tvproviderperf; |
| |
| import android.content.ComponentName; |
| import android.content.ContentProviderOperation; |
| import android.content.ContentProviderResult; |
| import android.content.ContentResolver; |
| import android.content.ContentUris; |
| import android.content.ContentValues; |
| import android.content.OperationApplicationException; |
| import android.content.pm.PackageManager; |
| import android.database.Cursor; |
| import android.cts.util.CtsAndroidTestCase; |
| import android.media.tv.TvContract; |
| import android.media.tv.TvContract.Channels; |
| import android.media.tv.TvContract.Programs; |
| import android.net.Uri; |
| import android.os.RemoteException; |
| |
| import com.android.cts.util.MeasureRun; |
| import com.android.cts.util.MeasureTime; |
| import com.android.cts.util.ResultType; |
| import com.android.cts.util.ResultUnit; |
| import com.android.cts.util.ReportLog; |
| import com.android.cts.util.TimeoutReq; |
| import com.android.cts.util.Stat; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| /** |
| * Test performance of TvProvider on a device. TvProvider typically handles hundreds of |
| * thousands of records periodically, so it is desirable to have performance under a reasonable |
| * bar. |
| */ |
| public class TvProviderPerfTest extends CtsAndroidTestCase { |
| private static final int TRANSACTION_RUNS = 100; |
| private static final int QUERY_RUNS = 10; |
| |
| private ContentResolver mContentResolver; |
| private String mInputId; |
| private boolean mHasTvInputFramework; |
| |
| @Override |
| protected void setUp() throws Exception { |
| super.setUp(); |
| mHasTvInputFramework = getContext().getPackageManager().hasSystemFeature( |
| PackageManager.FEATURE_LIVE_TV); |
| if (!mHasTvInputFramework) return; |
| mContentResolver = getContext().getContentResolver(); |
| mInputId = TvContract.buildInputId(new ComponentName(getContext(), getClass())); |
| } |
| |
| @Override |
| protected void tearDown() throws Exception { |
| try { |
| if (!mHasTvInputFramework) return; |
| mContentResolver.delete(Programs.CONTENT_URI, null, null); |
| mContentResolver.delete(Channels.CONTENT_URI, null, null); |
| } finally { |
| super.tearDown(); |
| } |
| } |
| |
| @TimeoutReq(minutes = 8) |
| public void testChannels() throws Exception { |
| if (!mHasTvInputFramework) return; |
| double[] averages = new double[5]; |
| |
| // Insert |
| final ArrayList<ContentProviderOperation> operations = new ArrayList<>(); |
| final int TRANSACTION_SIZE = 1000; |
| double[] applyBatchTimes = MeasureTime.measure(TRANSACTION_RUNS, new MeasureRun() { |
| @Override |
| public void run(int i) { |
| operations.clear(); |
| for (int j = 0; j < TRANSACTION_SIZE; ++j) { |
| ContentValues values = new ContentValues(); |
| values.put(Channels.COLUMN_INPUT_ID, mInputId); |
| values.put(Channels.COLUMN_SERVICE_TYPE, |
| Channels.SERVICE_TYPE_AUDIO_VIDEO); |
| values.put(Channels.COLUMN_TYPE, Channels.TYPE_OTHER); |
| operations.add( |
| ContentProviderOperation.newInsert(Channels.CONTENT_URI) |
| .withValues(values).build()); |
| } |
| try { |
| mContentResolver.applyBatch(TvContract.AUTHORITY, operations); |
| } catch (OperationApplicationException | RemoteException e) { |
| throw new RuntimeException(e); |
| } |
| } |
| }); |
| getReportLog().printArray("Elapsed time for insert: ", |
| applyBatchTimes, ResultType.LOWER_BETTER, ResultUnit.MS); |
| averages[0] = Stat.getAverage(applyBatchTimes); |
| |
| // Update |
| final String[] projection = { Channels._ID }; |
| try (final Cursor cursor = mContentResolver.query(Channels.CONTENT_URI, |
| projection, null, null, null)) { |
| applyBatchTimes = MeasureTime.measure(TRANSACTION_RUNS, new MeasureRun() { |
| @Override |
| public void run(int i) { |
| operations.clear(); |
| for (int j = 0; j < TRANSACTION_SIZE && cursor.moveToNext(); ++j) { |
| Uri channelUri = TvContract.buildChannelUri(cursor.getLong(0)); |
| String number = Integer.toString(i * TRANSACTION_SIZE + j); |
| operations.add( |
| ContentProviderOperation.newUpdate(channelUri) |
| .withValue(Channels.COLUMN_DISPLAY_NUMBER, number) |
| .build()); |
| } |
| try { |
| mContentResolver.applyBatch(TvContract.AUTHORITY, operations); |
| } catch (OperationApplicationException | RemoteException e) { |
| throw new RuntimeException(e); |
| } |
| } |
| }); |
| } |
| getReportLog().printArray("Elapsed time for update: ", |
| applyBatchTimes, ResultType.LOWER_BETTER, ResultUnit.MS); |
| averages[1] = Stat.getAverage(applyBatchTimes); |
| |
| // Query channels |
| applyBatchTimes = MeasureTime.measure(QUERY_RUNS, new MeasureRun() { |
| @Override |
| public void run(int i) { |
| try (Cursor cursor = mContentResolver.query(Channels.CONTENT_URI, null, null, |
| null, null)) { |
| while (cursor.moveToNext()) { |
| // Do nothing. Just iterate all the items. |
| } |
| } |
| } |
| }); |
| getReportLog().printArray("Elapsed time for query (channels): ", |
| applyBatchTimes, ResultType.LOWER_BETTER, ResultUnit.MS); |
| averages[2] = Stat.getAverage(applyBatchTimes); |
| |
| // Query a channel |
| try (final Cursor cursor = mContentResolver.query(Channels.CONTENT_URI, |
| projection, null, null, null)) { |
| applyBatchTimes = MeasureTime.measure(QUERY_RUNS, new MeasureRun() { |
| @Override |
| public void run(int i) { |
| assertTrue(cursor.moveToNext()); |
| try (Cursor c = mContentResolver.query(TvContract.buildChannelUri( |
| cursor.getLong(0)), null, null, null, null)) { |
| while (c.moveToNext()) { |
| // Do nothing. Just iterate all the items. |
| } |
| } |
| } |
| }); |
| } |
| getReportLog().printArray("Elapsed time for query (a channel): ", |
| applyBatchTimes, ResultType.LOWER_BETTER, ResultUnit.MS); |
| averages[3] = Stat.getAverage(applyBatchTimes); |
| |
| // Delete |
| applyBatchTimes = MeasureTime.measure(1, new MeasureRun() { |
| @Override |
| public void run(int i) { |
| mContentResolver.delete(TvContract.buildChannelsUriForInput(mInputId), null, null); |
| } |
| }); |
| getReportLog().printArray("Elapsed time for delete: ", |
| applyBatchTimes, ResultType.LOWER_BETTER, ResultUnit.MS); |
| averages[4] = Stat.getAverage(applyBatchTimes); |
| |
| getReportLog().printArray("Average elapsed time for insert, update, query (channels), " |
| + "query (a channel), delete: ", |
| averages, ResultType.LOWER_BETTER, ResultUnit.MS); |
| } |
| |
| @TimeoutReq(minutes = 12) |
| public void testPrograms() throws Exception { |
| if (!mHasTvInputFramework) return; |
| double[] averages = new double[7]; |
| |
| // Prepare (insert channels) |
| final ArrayList<ContentProviderOperation> operations = new ArrayList<>(); |
| final int TRANSACTION_SIZE = 1000; |
| final int NUM_CHANNELS = 100; |
| final List<Uri> channelUris = new ArrayList<>(); |
| |
| operations.clear(); |
| for (int i = 0; i < NUM_CHANNELS; ++i) { |
| ContentValues values = new ContentValues(); |
| values.put(Channels.COLUMN_INPUT_ID, mInputId); |
| values.put(Channels.COLUMN_SERVICE_TYPE, |
| Channels.SERVICE_TYPE_AUDIO_VIDEO); |
| values.put(Channels.COLUMN_TYPE, Channels.TYPE_OTHER); |
| operations.add( |
| ContentProviderOperation.newInsert(Channels.CONTENT_URI) |
| .withValues(values).build()); |
| } |
| try { |
| ContentProviderResult[] results = |
| mContentResolver.applyBatch(TvContract.AUTHORITY, operations); |
| for (ContentProviderResult result : results) { |
| channelUris.add(result.uri); |
| } |
| } catch (OperationApplicationException | RemoteException e) { |
| throw new RuntimeException(e); |
| } |
| |
| // Insert |
| double[] applyBatchTimes = MeasureTime.measure(NUM_CHANNELS, new MeasureRun() { |
| @Override |
| public void run(int i) { |
| operations.clear(); |
| Uri channelUri = channelUris.get(i); |
| long channelId = ContentUris.parseId(channelUri); |
| for (int j = 0; j < TRANSACTION_SIZE; ++j) { |
| ContentValues values = new ContentValues(); |
| values.put(Programs.COLUMN_CHANNEL_ID, channelId); |
| operations.add( |
| ContentProviderOperation.newInsert(Programs.CONTENT_URI) |
| .withValues(values).build()); |
| } |
| try { |
| mContentResolver.applyBatch(TvContract.AUTHORITY, operations); |
| } catch (OperationApplicationException | RemoteException e) { |
| throw new RuntimeException(e); |
| } |
| } |
| }); |
| getReportLog().printArray("Elapsed time for insert: ", |
| applyBatchTimes, ResultType.LOWER_BETTER, ResultUnit.MS); |
| averages[0] = Stat.getAverage(applyBatchTimes); |
| |
| // Update |
| final long PROGRAM_DURATION_MS = 60 * 1000; |
| final String[] projection = { Programs._ID }; |
| applyBatchTimes = MeasureTime.measure(NUM_CHANNELS, new MeasureRun() { |
| @Override |
| public void run(int i) { |
| Uri channelUri = channelUris.get(i); |
| operations.clear(); |
| try (Cursor cursor = mContentResolver.query( |
| TvContract.buildProgramsUriForChannel(channelUri), |
| projection, null, null, null)) { |
| long startTimeMs = 0; |
| long endTimeMs = 0; |
| while (cursor.moveToNext()) { |
| Uri programUri = TvContract.buildProgramUri(cursor.getLong(0)); |
| endTimeMs += PROGRAM_DURATION_MS; |
| operations.add( |
| ContentProviderOperation.newUpdate(programUri) |
| .withValue(Programs.COLUMN_START_TIME_UTC_MILLIS, startTimeMs) |
| .withValue(Programs.COLUMN_END_TIME_UTC_MILLIS, endTimeMs) |
| .build()); |
| startTimeMs = endTimeMs; |
| } |
| } |
| try { |
| mContentResolver.applyBatch(TvContract.AUTHORITY, operations); |
| } catch (OperationApplicationException | RemoteException e) { |
| throw new RuntimeException(e); |
| } |
| } |
| }); |
| getReportLog().printArray("Elapsed time for update: ", |
| applyBatchTimes, ResultType.LOWER_BETTER, ResultUnit.MS); |
| averages[1] = Stat.getAverage(applyBatchTimes); |
| |
| // Query programs |
| applyBatchTimes = MeasureTime.measure(QUERY_RUNS, new MeasureRun() { |
| @Override |
| public void run(int i) { |
| try (Cursor cursor = mContentResolver.query(Programs.CONTENT_URI, null, null, |
| null, null)) { |
| while (cursor.moveToNext()) { |
| // Do nothing. Just iterate all the items. |
| } |
| } |
| } |
| }); |
| getReportLog().printArray("Elapsed time for query (programs): ", |
| applyBatchTimes, ResultType.LOWER_BETTER, ResultUnit.MS); |
| averages[2] = Stat.getAverage(applyBatchTimes); |
| |
| // Query programs with selection |
| applyBatchTimes = MeasureTime.measure(QUERY_RUNS, new MeasureRun() { |
| @Override |
| public void run(int i) { |
| Uri channelUri = channelUris.get(i); |
| try (Cursor cursor = mContentResolver.query( |
| TvContract.buildProgramsUriForChannel( |
| channelUri, 0, |
| PROGRAM_DURATION_MS * TRANSACTION_SIZE / 2), |
| null, null, null, null)) { |
| while (cursor.moveToNext()) { |
| // Do nothing. Just iterate all the items. |
| } |
| } |
| } |
| }); |
| getReportLog().printArray("Elapsed time for query (programs with selection): ", |
| applyBatchTimes, ResultType.LOWER_BETTER, ResultUnit.MS); |
| averages[3] = Stat.getAverage(applyBatchTimes); |
| |
| // Query a program |
| try (final Cursor cursor = mContentResolver.query(Programs.CONTENT_URI, |
| projection, null, null, null)) { |
| applyBatchTimes = MeasureTime.measure(QUERY_RUNS, new MeasureRun() { |
| @Override |
| public void run(int i) { |
| assertTrue(cursor.moveToNext()); |
| try (Cursor c = mContentResolver.query(TvContract.buildProgramUri( |
| cursor.getLong(0)), null, null, null, null)) { |
| while (c.moveToNext()) { |
| // Do nothing. Just iterate all the items. |
| } |
| } |
| } |
| }); |
| } |
| getReportLog().printArray("Elapsed time for query (a program): ", |
| applyBatchTimes, ResultType.LOWER_BETTER, ResultUnit.MS); |
| averages[4] = Stat.getAverage(applyBatchTimes); |
| |
| // Delete programs |
| applyBatchTimes = MeasureTime.measure(NUM_CHANNELS, new MeasureRun() { |
| @Override |
| public void run(int i) { |
| Uri channelUri = channelUris.get(i); |
| mContentResolver.delete( |
| TvContract.buildProgramsUriForChannel( |
| channelUri, |
| PROGRAM_DURATION_MS * TRANSACTION_SIZE / 2, |
| PROGRAM_DURATION_MS * TRANSACTION_SIZE), |
| null, null); |
| } |
| }); |
| getReportLog().printArray("Elapsed time for delete programs: ", |
| applyBatchTimes, ResultType.LOWER_BETTER, ResultUnit.MS); |
| averages[5] = Stat.getAverage(applyBatchTimes); |
| |
| // Delete channels |
| applyBatchTimes = MeasureTime.measure(NUM_CHANNELS, new MeasureRun() { |
| @Override |
| public void run(int i) { |
| Uri channelUri = channelUris.get(i); |
| mContentResolver.delete(channelUri, null, null); |
| } |
| }); |
| getReportLog().printArray("Elapsed time for delete channels: ", |
| applyBatchTimes, ResultType.LOWER_BETTER, ResultUnit.MS); |
| averages[6] = Stat.getAverage(applyBatchTimes); |
| |
| getReportLog().printArray("Average elapsed time for insert, update, query (programs), " |
| + "query (programs with selection), query (a channel), delete (channels), " |
| + "delete (programs): ", |
| averages, ResultType.LOWER_BETTER, ResultUnit.MS); |
| } |
| } |