Migrate file system tests to CTSv2
bug:21762834
Change-Id: I361864f427fe37799b8e577157f50613af010c20
diff --git a/tests/filesystem/Android.mk b/tests/filesystem/Android.mk
new file mode 100644
index 0000000..00e5ee7
--- /dev/null
+++ b/tests/filesystem/Android.mk
@@ -0,0 +1,33 @@
+# Copyright (C) 2012 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.
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+# don't include this package in any target
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_STATIC_JAVA_LIBRARIES := ctsdeviceutil compatibility-device-util ctstestrunner
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := CtsFileSystemTestCases
+
+# Tag this module as a cts_v2 test artifact
+LOCAL_COMPATIBILITY_SUITE := cts_v2
+
+LOCAL_SDK_VERSION := 16
+
+include $(BUILD_CTS_PACKAGE)
+
diff --git a/tests/filesystem/AndroidManifest.xml b/tests/filesystem/AndroidManifest.xml
new file mode 100644
index 0000000..c02ac77
--- /dev/null
+++ b/tests/filesystem/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.filesystem.cts">
+
+ <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+ <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.filesystem.cts"
+ android:label="CTS tests for file system" />
+</manifest>
diff --git a/tests/filesystem/AndroidTest.xml b/tests/filesystem/AndroidTest.xml
new file mode 100644
index 0000000..65d4296
--- /dev/null
+++ b/tests/filesystem/AndroidTest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 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.
+-->
+<configuration description="Config for CTS File System Test Cases">
+ <include name="common-config" />
+ <option name="apk-installer:test-file-name" value="CtsFileSystemTestCases.apk" />
+ <test class="com.android.tradefed.testtype.InstrumentationTest" >
+ <option name="package" value="android.filesystem.cts" />
+ <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
+ </test>
+</configuration>
\ No newline at end of file
diff --git a/tests/filesystem/src/android/filesystem/cts/AlmostFullTest.java b/tests/filesystem/src/android/filesystem/cts/AlmostFullTest.java
new file mode 100644
index 0000000..991f9bf
--- /dev/null
+++ b/tests/filesystem/src/android/filesystem/cts/AlmostFullTest.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2012 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.filesystem.cts;
+
+import android.util.Log;
+
+import android.cts.util.CtsAndroidTestCase;
+import android.cts.util.SystemUtil;
+
+import com.android.compatibility.common.util.DeviceReportLog;
+import com.android.cts.util.TimeoutReq;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class AlmostFullTest extends CtsAndroidTestCase {
+
+ private static final String DIR_INITIAL_FILL = "INITIAL_FILL";
+ private static final String DIR_SEQ_UPDATE = "SEQ_UPDATE";
+ private static final String DIR_RANDOM_WR = "RANDOM_WR";
+ private static final String DIR_RANDOM_RD = "RANDOM_RD";
+ private static final String TAG = "AlmostFullTest";
+
+ private static final long FREE_SPACE_FINAL = 1000L * 1024 * 1024L;
+
+ // test runner creates multiple instances at the begging.
+ // use that to fill disk only once.
+ // set as final to initialize it only once
+ private static final AtomicInteger mRefCounter = new AtomicInteger(0);
+ private static final AtomicBoolean mDiskFilled = new AtomicBoolean(false);
+
+ public AlmostFullTest() {
+ int currentCounter = mRefCounter.incrementAndGet();
+ Log.i(TAG, "++currentCounter: " + currentCounter);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ if (mDiskFilled.compareAndSet(false, true)) {
+ Log.i(TAG, "Filling disk");
+ // initial fill done in two stage as disk can be filled by other
+ // components
+ long freeDisk = SystemUtil.getFreeDiskSize(getContext());
+ long diskToFill = freeDisk - FREE_SPACE_FINAL;
+ if (diskToFill >= 0) {
+ Log.i(TAG, "free disk " + freeDisk + ", to fill " + diskToFill);
+ } else {
+ Log.i(TAG, "free disk " + freeDisk + " too small, needs " + FREE_SPACE_FINAL);
+ return;
+ }
+ final long MAX_FILE_SIZE_TO_FILL = 1024L * 1024L * 1024L;
+ long filled = 0;
+ while (filled < diskToFill) {
+ long toFill = diskToFill - filled;
+ if (toFill > MAX_FILE_SIZE_TO_FILL) {
+ toFill = MAX_FILE_SIZE_TO_FILL;
+ }
+ Log.i(TAG, "Generating file " + toFill);
+ FileUtil.createNewFilledFile(getContext(),
+ DIR_INITIAL_FILL, toFill);
+ filled += toFill;
+ }
+ }
+ Log.i(TAG, "free disk " + SystemUtil.getFreeDiskSize(getContext()));
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ Log.i(TAG, "tearDown free disk " + SystemUtil.getFreeDiskSize(getContext()));
+ int currentCounter = mRefCounter.decrementAndGet();
+ Log.i(TAG, "--currentCounter: " + currentCounter);
+ if (currentCounter == 0) {
+ FileUtil.removeFileOrDir(getContext(), DIR_INITIAL_FILL);
+ }
+ FileUtil.removeFileOrDir(getContext(), DIR_SEQ_UPDATE);
+ FileUtil.removeFileOrDir(getContext(), DIR_RANDOM_WR);
+ FileUtil.removeFileOrDir(getContext(), DIR_RANDOM_RD);
+ Log.i(TAG, "tearDown free disk " + SystemUtil.getFreeDiskSize(getContext()));
+ super.tearDown();
+ }
+
+ @TimeoutReq(minutes = 30)
+ public void testSequentialUpdate() throws Exception {
+ // now about freeSpaceToLeave should be left
+ // and try updating exceeding the free space size
+ final long FILE_SIZE = 400L * 1024L * 1024L;
+ long freeDisk = SystemUtil.getFreeDiskSize(getContext());
+ Log.i(TAG, "Now free space is " + freeDisk);
+ if (freeDisk < FILE_SIZE) {
+ Log.w(TAG, "too little space: " + freeDisk);
+ return;
+ }
+ final int BUFFER_SIZE = 10 * 1024 * 1024;
+ final int NUMBER_REPETITION = 10;
+ DeviceReportLog report = new DeviceReportLog();
+ FileUtil.doSequentialUpdateTest(getContext(), DIR_SEQ_UPDATE, report, FILE_SIZE,
+ BUFFER_SIZE, NUMBER_REPETITION);
+ report.submit(getInstrumentation());
+ }
+
+ // TODO: file size too small and caching will give wrong better result.
+ // needs to flush cache by reading big files per each read.
+ @TimeoutReq(minutes = 60)
+ public void testRandomRead() throws Exception {
+ final int BUFFER_SIZE = 4 * 1024;
+ final long fileSize = 400L * 1024L * 1024L;
+ long freeDisk = SystemUtil.getFreeDiskSize(getContext());
+ if (freeDisk < fileSize) {
+ Log.w(TAG, "too little space: " + freeDisk);
+ return;
+ }
+ DeviceReportLog report = new DeviceReportLog();
+ FileUtil.doRandomReadTest(getContext(), DIR_RANDOM_RD, report, fileSize, BUFFER_SIZE);
+ report.submit(getInstrumentation());
+ }
+
+ @TimeoutReq(minutes = 60)
+ public void testRandomUpdate() throws Exception {
+ final int BUFFER_SIZE = 4 * 1024;
+ final long fileSize = 256L * 1024L * 1024L;
+ long freeDisk = SystemUtil.getFreeDiskSize(getContext());
+ if (freeDisk < fileSize) {
+ Log.w(TAG, "too little space: " + freeDisk);
+ return;
+ }
+ DeviceReportLog report = new DeviceReportLog();
+ FileUtil.doRandomWriteTest(getContext(), DIR_RANDOM_WR, report, fileSize, BUFFER_SIZE);
+ report.submit(getInstrumentation());
+ }
+}
diff --git a/tests/filesystem/src/android/filesystem/cts/FileUtil.java b/tests/filesystem/src/android/filesystem/cts/FileUtil.java
new file mode 100755
index 0000000..baefaa0
--- /dev/null
+++ b/tests/filesystem/src/android/filesystem/cts/FileUtil.java
@@ -0,0 +1,411 @@
+/*
+ * Copyright (C) 2012 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.filesystem.cts;
+
+import android.content.Context;
+import android.cts.util.SystemUtil;
+import android.util.Log;
+
+import com.android.compatibility.common.util.MeasureRun;
+import com.android.compatibility.common.util.MeasureTime;
+import com.android.compatibility.common.util.ReportLog;
+import com.android.compatibility.common.util.ResultType;
+import com.android.compatibility.common.util.ResultUnit;
+import com.android.compatibility.common.util.Stat;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.RandomAccessFile;
+import java.util.Random;
+
+public class FileUtil {
+ private static final String TAG = "FileUtil";
+ private static final Random mRandom = new Random(0);
+ private static long mFileId = 0;
+ /**
+ * create array with different data per each call
+ *
+ * @param length
+ * @param randomSeed
+ * @return
+ */
+ public static byte[] generateRandomData(int length) {
+ byte[] buffer = new byte[length];
+ int val = mRandom.nextInt();
+ for (int i = 0; i < length / 4; i++) {
+ // in little-endian
+ buffer[i * 4] = (byte)(val & 0x000000ff);
+ buffer[i * 4 + 1] = (byte)((val & 0x0000ff00) >> 8);
+ buffer[i * 4 + 2] = (byte)((val & 0x00ff0000) >> 16);
+ buffer[i * 4 + 3] = (byte)((val & 0xff000000) >> 24);
+ val++;
+ }
+ for (int i = (length / 4) * 4; i < length; i++) {
+ buffer[i] = 0;
+ }
+ return buffer;
+ }
+
+ /**
+ * create a new file under the given dirName.
+ * Existing files will not be affected.
+ * @param context
+ * @param dirName
+ * @return
+ */
+ public static File createNewFile(Context context, String dirName) {
+ File topDir = new File(context.getFilesDir(), dirName);
+ topDir.mkdir();
+ String[] list = topDir.list();
+
+ String newFileName;
+ while (true) {
+ newFileName = Long.toString(mFileId);
+ boolean fileExist = false;
+ for (String child : list) {
+ if (child.equals(newFileName)) {
+ fileExist = true;
+ break;
+ }
+ }
+ if (!fileExist) {
+ break;
+ }
+ mFileId++;
+ }
+ mFileId++;
+ //Log.i(TAG, "filename" + Long.toString(mFileId));
+ return new File(topDir, newFileName);
+ }
+
+ /**
+ * create multiple new files
+ * @param context
+ * @param dirName
+ * @param count number of files to create
+ * @return
+ */
+ public static File[] createNewFiles(Context context, String dirName, int count) {
+ File[] files = new File[count];
+ for (int i = 0; i < count; i++) {
+ files[i] = createNewFile(context, dirName);
+ }
+ return files;
+ }
+
+ /**
+ * write file with given byte array
+ * @param file
+ * @param data
+ * @param append will append if set true. Otherwise, write from beginning
+ * @throws IOException
+ */
+ public static void writeFile(File file, byte[] data, boolean append) throws IOException {
+ final RandomAccessFile randomFile = new RandomAccessFile(file, "rwd"); // force O_SYNC
+ if (append) {
+ randomFile.seek(randomFile.length());
+ } else {
+ randomFile.seek(0L);
+ }
+ randomFile.write(data);
+ randomFile.close();
+ }
+
+ /**
+ * create a new file with given length.
+ * @param context
+ * @param dirName
+ * @param length
+ * @return
+ * @throws IOException
+ */
+ public static File createNewFilledFile(Context context, String dirName, long length)
+ throws IOException {
+ final int BUFFER_SIZE = 10 * 1024 * 1024;
+ File file = createNewFile(context, dirName);
+ FileOutputStream out = new FileOutputStream(file);
+ byte[] data = generateRandomData(BUFFER_SIZE);
+ long written = 0;
+ while (written < length) {
+ out.write(data);
+ written += BUFFER_SIZE;
+ }
+ out.flush();
+ out.close();
+ return file;
+ }
+
+ /**
+ * remove given file or directory under the current app's files dir.
+ * @param context
+ * @param name
+ */
+ public static void removeFileOrDir(Context context, String name) {
+ File entry = new File(context.getFilesDir(), name);
+ if (entry.exists()) {
+ removeEntry(entry);
+ }
+ }
+
+ private static void removeEntry(File entry) {
+ if (entry.isDirectory()) {
+ String[] children = entry.list();
+ for (String child : children) {
+ removeEntry(new File(entry, child));
+ }
+ }
+ Log.i(TAG, "delete file " + entry.getAbsolutePath());
+ entry.delete();
+ }
+
+ /**
+ * measure time taken for each IO run with amount R/W
+ * @param count
+ * @param run
+ * @param readAmount returns amount of read in bytes for each interval.
+ * Value will not be written if /proc/self/io does not exist.
+ * @param writeAmount returns amount of write in bytes for each interval.
+ * @return time per each interval
+ * @throws IOException
+ */
+ public static double[] measureIO(int count, double[] readAmount, double[] writeAmount,
+ MeasureRun run) throws Exception {
+ double[] result = new double[count];
+ File procIo = new File("/proc/self/io");
+ boolean measureIo = procIo.exists() && procIo.canRead();
+ long prev = System.currentTimeMillis();
+ RWAmount prevAmount = new RWAmount();
+ if (measureIo) {
+ prevAmount = getRWAmount(procIo);
+ }
+ for (int i = 0; i < count; i++) {
+ run.run(i);
+ long current = System.currentTimeMillis();
+ result[i] = current - prev;
+ prev = current;
+ if (measureIo) {
+ RWAmount currentAmount = getRWAmount(procIo);
+ readAmount[i] = currentAmount.mRd - prevAmount.mRd;
+ writeAmount[i] = currentAmount.mWr - prevAmount.mWr;
+ prevAmount = currentAmount;
+ }
+ }
+ return result;
+ }
+
+ private static class RWAmount {
+ public double mRd = 0.0;
+ public double mWr = 0.0;
+ };
+
+ private static RWAmount getRWAmount(File file) throws IOException {
+ RWAmount amount = new RWAmount();
+
+ BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
+ String line;
+ while((line = br.readLine())!= null) {
+ if (line.startsWith("read_bytes")) {
+ amount.mRd = Double.parseDouble(line.split(" ")[1]);
+ } else if (line.startsWith("write_bytes")) {
+ amount.mWr = Double.parseDouble(line.split(" ")[1]);
+ }
+ }
+ br.close();
+ return amount;
+ }
+
+ /**
+ * get file size exceeding total memory size ( 2x total memory).
+ * The size is rounded in bufferSize. And the size will be bigger than 400MB.
+ * @param context
+ * @param bufferSize
+ * @return file size or 0 if there is not enough space.
+ */
+ public static long getFileSizeExceedingMemory(Context context, int bufferSize) {
+ long freeDisk = SystemUtil.getFreeDiskSize(context);
+ long memSize = SystemUtil.getTotalMemory(context);
+ long diskSizeTarget = (2 * memSize / bufferSize) * bufferSize;
+ final long minimumDiskSize = (512L * 1024L * 1024L / bufferSize) * bufferSize;
+ final long reservedDiskSize = (50L * 1024L * 1024L / bufferSize) * bufferSize;
+ if ( diskSizeTarget < minimumDiskSize ) {
+ diskSizeTarget = minimumDiskSize;
+ }
+ if (diskSizeTarget > freeDisk) {
+ Log.i(TAG, "Free disk size " + freeDisk + " too small");
+ return 0;
+ }
+ if ((freeDisk - diskSizeTarget) < reservedDiskSize) {
+ diskSizeTarget -= reservedDiskSize;
+ }
+ return diskSizeTarget;
+ }
+
+ /**
+ *
+ * @param context
+ * @param dirName
+ * @param report
+ * @param fileSize
+ * @param bufferSize should be power of two
+ * @throws IOException
+ */
+ public static void doRandomReadTest(Context context, String dirName, ReportLog report,
+ long fileSize, int bufferSize) throws Exception {
+ File file = FileUtil.createNewFilledFile(context,
+ dirName, fileSize);
+
+ final byte[] data = FileUtil.generateRandomData(bufferSize);
+ Random random = new Random(0);
+ final int totalReadCount = (int)(fileSize / bufferSize);
+ final int[] readOffsets = new int[totalReadCount];
+ for (int i = 0; i < totalReadCount; i++) {
+ // align in buffer size
+ readOffsets[i] = (int)(random.nextFloat() * (fileSize - bufferSize)) &
+ ~(bufferSize - 1);
+ }
+ final int runsInOneGo = 16;
+ final int readsInOneMeasure = totalReadCount / runsInOneGo;
+
+ final RandomAccessFile randomFile = new RandomAccessFile(file, "rw"); // do not need O_SYNC
+ double[] rdAmount = new double[runsInOneGo];
+ double[] wrAmount = new double[runsInOneGo];
+ double[] times = FileUtil.measureIO(runsInOneGo, rdAmount, wrAmount, new MeasureRun() {
+
+ @Override
+ public void run(int i) throws IOException {
+ Log.i(TAG, "starting " + i + " -th round");
+ int start = i * readsInOneMeasure;
+ int end = (i + 1) * readsInOneMeasure;
+ for (int j = start; j < end; j++) {
+ randomFile.seek(readOffsets[j]);
+ randomFile.read(data);
+ }
+ }
+ });
+ randomFile.close();
+ double[] mbps = Stat.calcRatePerSecArray((double)fileSize / runsInOneGo / 1024 / 1024,
+ times);
+ report.addValues("read throughput",
+ mbps, ResultType.HIGHER_BETTER, ResultUnit.MBPS);
+ // This is just the amount of IO returned from kernel. So this is performance neutral.
+ report.addValues("read amount", rdAmount, ResultType.NEUTRAL, ResultUnit.BYTE);
+ Stat.StatResult stat = Stat.getStat(mbps);
+
+ report.setSummary("read throughput", stat.mAverage, ResultType.HIGHER_BETTER,
+ ResultUnit.MBPS);
+ }
+
+ /**
+ *
+ * @param context
+ * @param dirName
+ * @param report
+ * @param fileSize
+ * @param bufferSize should be power of two
+ * @throws IOException
+ */
+ public static void doRandomWriteTest(Context context, String dirName, ReportLog report,
+ long fileSize, int bufferSize) throws Exception {
+ File file = FileUtil.createNewFilledFile(context,
+ dirName, fileSize);
+ final byte[] data = FileUtil.generateRandomData(bufferSize);
+ Random random = new Random(0);
+ final int totalWriteCount = (int)(fileSize / bufferSize);
+ final int[] writeOffsets = new int[totalWriteCount];
+ for (int i = 0; i < totalWriteCount; i++) {
+ writeOffsets[i] = (int)(random.nextFloat() * (fileSize - bufferSize)) &
+ ~(bufferSize - 1);
+ }
+ final int runsInOneGo = 16;
+ final int writesInOneMeasure = totalWriteCount / runsInOneGo;
+
+ final RandomAccessFile randomFile = new RandomAccessFile(file, "rwd"); // force O_SYNC
+ double[] rdAmount = new double[runsInOneGo];
+ double[] wrAmount = new double[runsInOneGo];
+ double[] times = FileUtil.measureIO(runsInOneGo, rdAmount, wrAmount, new MeasureRun() {
+
+ @Override
+ public void run(int i) throws IOException {
+ Log.i(TAG, "starting " + i + " -th round");
+ int start = i * writesInOneMeasure;
+ int end = (i + 1) * writesInOneMeasure;
+ for (int j = start; j < end; j++) {
+ randomFile.seek(writeOffsets[j]);
+ randomFile.write(data);
+ }
+ }
+ });
+ randomFile.close();
+ double[] mbps = Stat.calcRatePerSecArray((double)fileSize / runsInOneGo / 1024 / 1024,
+ times);
+ report.addValues("write throughput",
+ mbps, ResultType.HIGHER_BETTER, ResultUnit.MBPS);
+ report.addValues("write amount", wrAmount, ResultType.NEUTRAL,
+ ResultUnit.BYTE);
+ Stat.StatResult stat = Stat.getStat(mbps);
+
+ report.setSummary("write throughput", stat.mAverage, ResultType.HIGHER_BETTER,
+ ResultUnit.MBPS);
+ }
+
+ /**
+ *
+ * @param context
+ * @param dirName
+ * @param report
+ * @param fileSize fileSize should be multiple of bufferSize.
+ * @param bufferSize
+ * @param numberRepetition
+ * @throws IOException
+ */
+ public static void doSequentialUpdateTest(Context context, String dirName, ReportLog report,
+ long fileSize, int bufferSize, int numberRepetition) throws Exception {
+ File file = FileUtil.createNewFilledFile(context,
+ dirName, fileSize);
+ final byte[] data = FileUtil.generateRandomData(bufferSize);
+ int numberRepeatInOneRun = (int)(fileSize / bufferSize);
+ double[] mbpsAll = new double[numberRepetition * numberRepeatInOneRun];
+ for (int i = 0; i < numberRepetition; i++) {
+ Log.i(TAG, "starting " + i + " -th round");
+ final RandomAccessFile randomFile = new RandomAccessFile(file, "rwd"); // force O_SYNC
+ randomFile.seek(0L);
+ double[] times = MeasureTime.measure(numberRepeatInOneRun, new MeasureRun() {
+
+ @Override
+ public void run(int i) throws IOException {
+ randomFile.write(data);
+ }
+ });
+ randomFile.close();
+ double[] mbps = Stat.calcRatePerSecArray((double)bufferSize / 1024 / 1024,
+ times);
+ report.addValues(i + "-th round throughput",
+ mbps, ResultType.HIGHER_BETTER, ResultUnit.MBPS);
+ int offset = i * numberRepeatInOneRun;
+ for (int j = 0; j < mbps.length; j++) {
+ mbpsAll[offset + j] = mbps[j];
+ }
+ }
+ Stat.StatResult stat = Stat.getStat(mbpsAll);
+ report.setSummary("update throughput", stat.mAverage, ResultType.HIGHER_BETTER,
+ ResultUnit.MBPS);
+ }
+}
diff --git a/tests/filesystem/src/android/filesystem/cts/RandomRWTest.java b/tests/filesystem/src/android/filesystem/cts/RandomRWTest.java
new file mode 100644
index 0000000..dff3723
--- /dev/null
+++ b/tests/filesystem/src/android/filesystem/cts/RandomRWTest.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2012 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.filesystem.cts;
+
+import android.cts.util.CtsAndroidTestCase;
+
+import com.android.compatibility.common.util.DeviceReportLog;
+import com.android.cts.util.TimeoutReq;
+
+public class RandomRWTest extends CtsAndroidTestCase {
+ private static final String DIR_RANDOM_WR = "RANDOM_WR";
+ private static final String DIR_RANDOM_RD = "RANDOM_RD";
+
+ @Override
+ protected void tearDown() throws Exception {
+ FileUtil.removeFileOrDir(getContext(), DIR_RANDOM_WR);
+ FileUtil.removeFileOrDir(getContext(), DIR_RANDOM_RD);
+ super.tearDown();
+ }
+
+ @TimeoutReq(minutes = 60)
+ public void testRandomRead() throws Exception {
+ final int READ_BUFFER_SIZE = 4 * 1024;
+ final long fileSize = FileUtil.getFileSizeExceedingMemory(getContext(), READ_BUFFER_SIZE);
+ if (fileSize == 0) { // not enough space, give up
+ return;
+ }
+ DeviceReportLog report = new DeviceReportLog();
+ FileUtil.doRandomReadTest(getContext(), DIR_RANDOM_RD, report, fileSize,
+ READ_BUFFER_SIZE);
+ report.submit(getInstrumentation());
+ }
+
+ // It is taking too long in some device, and thus cannot run multiple times
+ @TimeoutReq(minutes = 60)
+ public void testRandomUpdate() throws Exception {
+ final int WRITE_BUFFER_SIZE = 4 * 1024;
+ final long fileSize = 256 * 1024 * 1024;
+ DeviceReportLog report = new DeviceReportLog();
+ FileUtil.doRandomWriteTest(getContext(), DIR_RANDOM_WR, report, fileSize,
+ WRITE_BUFFER_SIZE);
+ report.submit(getInstrumentation());
+ }
+}
diff --git a/tests/filesystem/src/android/filesystem/cts/SequentialRWTest.java b/tests/filesystem/src/android/filesystem/cts/SequentialRWTest.java
new file mode 100644
index 0000000..3b7a45f
--- /dev/null
+++ b/tests/filesystem/src/android/filesystem/cts/SequentialRWTest.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2012 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.filesystem.cts;
+
+import android.cts.util.CtsAndroidTestCase;
+
+import com.android.compatibility.common.util.DeviceReportLog;
+import com.android.compatibility.common.util.MeasureRun;
+import com.android.compatibility.common.util.MeasureTime;
+import com.android.compatibility.common.util.ResultType;
+import com.android.compatibility.common.util.ResultUnit;
+import com.android.compatibility.common.util.Stat;
+import com.android.cts.util.TimeoutReq;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+
+public class SequentialRWTest extends CtsAndroidTestCase {
+ private static final String DIR_SEQ_WR = "SEQ_WR";
+ private static final String DIR_SEQ_UPDATE = "SEQ_UPDATE";
+ private static final String DIR_SEQ_RD = "SEQ_RD";
+ private static final int BUFFER_SIZE = 10 * 1024 * 1024;
+
+ @Override
+ protected void tearDown() throws Exception {
+ FileUtil.removeFileOrDir(getContext(), DIR_SEQ_WR);
+ FileUtil.removeFileOrDir(getContext(), DIR_SEQ_UPDATE);
+ FileUtil.removeFileOrDir(getContext(), DIR_SEQ_RD);
+ super.tearDown();
+ }
+
+ @TimeoutReq(minutes = 30)
+ public void testSingleSequentialWrite() throws Exception {
+ final long fileSize = FileUtil.getFileSizeExceedingMemory(getContext(), BUFFER_SIZE);
+ if (fileSize == 0) { // not enough space, give up
+ return;
+ }
+ final int numberOfFiles =(int)(fileSize / BUFFER_SIZE);
+ DeviceReportLog report = new DeviceReportLog();
+ report.addValue("files", numberOfFiles, ResultType.NEUTRAL, ResultUnit.COUNT);
+ final byte[] data = FileUtil.generateRandomData(BUFFER_SIZE);
+ final File[] files = FileUtil.createNewFiles(getContext(), DIR_SEQ_WR,
+ numberOfFiles);
+ double[] rdAmount = new double[numberOfFiles];
+ double[] wrAmount = new double[numberOfFiles];
+ double[] times = FileUtil.measureIO(numberOfFiles, rdAmount, wrAmount, new MeasureRun() {
+
+ @Override
+ public void run(int i) throws IOException {
+ FileUtil.writeFile(files[i], data, false);
+ }
+ });
+ double[] mbps = Stat.calcRatePerSecArray((double)BUFFER_SIZE / 1024 / 1024, times);
+ report.addValues("write throughput",
+ mbps, ResultType.HIGHER_BETTER, ResultUnit.MBPS);
+ report.addValues("write amount", wrAmount, ResultType.NEUTRAL,
+ ResultUnit.BYTE);
+ Stat.StatResult stat = Stat.getStat(mbps);
+ report.setSummary("write throughput", stat.mAverage, ResultType.HIGHER_BETTER,
+ ResultUnit.MBPS);
+ report.submit(getInstrumentation());
+ }
+
+ @TimeoutReq(minutes = 60)
+ public void testSingleSequentialUpdate() throws Exception {
+ final long fileSize = FileUtil.getFileSizeExceedingMemory(getContext(), BUFFER_SIZE);
+ if (fileSize == 0) { // not enough space, give up
+ return;
+ }
+ final int NUMBER_REPETITION = 6;
+ DeviceReportLog report = new DeviceReportLog();
+ FileUtil.doSequentialUpdateTest(getContext(), DIR_SEQ_UPDATE, report, fileSize,
+ BUFFER_SIZE, NUMBER_REPETITION);
+ report.submit(getInstrumentation());
+ }
+
+ @TimeoutReq(minutes = 30)
+ public void testSingleSequentialRead() throws Exception {
+ final long fileSize = FileUtil.getFileSizeExceedingMemory(getContext(), BUFFER_SIZE);
+ if (fileSize == 0) { // not enough space, give up
+ return;
+ }
+ long start = System.currentTimeMillis();
+ final File file = FileUtil.createNewFilledFile(getContext(),
+ DIR_SEQ_RD, fileSize);
+ long finish = System.currentTimeMillis();
+ DeviceReportLog report = new DeviceReportLog();
+ report.addValue("write throughput for test file of length " + fileSize,
+ Stat.calcRatePerSec((double)fileSize / 1024 / 1024, finish - start),
+ ResultType.HIGHER_BETTER, ResultUnit.MBPS);
+
+ final int NUMBER_READ = 10;
+
+ final byte[] data = new byte[BUFFER_SIZE];
+ double[] times = MeasureTime.measure(NUMBER_READ, new MeasureRun() {
+
+ @Override
+ public void run(int i) throws IOException {
+ final FileInputStream in = new FileInputStream(file);
+ long read = 0;
+ while (read < fileSize) {
+ in.read(data);
+ read += BUFFER_SIZE;
+ }
+ in.close();
+ }
+ });
+ double[] mbps = Stat.calcRatePerSecArray((double)fileSize / 1024 / 1024, times);
+ report.addValues("read throughput",
+ mbps, ResultType.HIGHER_BETTER, ResultUnit.MBPS);
+ Stat.StatResult stat = Stat.getStat(mbps);
+ report.setSummary("read throughput", stat.mAverage, ResultType.HIGHER_BETTER,
+ ResultUnit.MBPS);
+ report.submit(getInstrumentation());
+ }
+}