Merge "Implement storage files in Java" into main

This commit is contained in:
Treehugger Robot
2024-07-10 23:28:37 +00:00
committed by Gerrit Code Review
18 changed files with 1149 additions and 1 deletions

View File

@@ -98,6 +98,10 @@
{
// aconfig_storage file cpp integration tests
"name": "aconfig_storage_file.test.cpp"
},
{
// aconfig_storage file java integration tests
"name": "aconfig_storage_file.test.java"
}
],
"postsubmit": [

View File

@@ -137,3 +137,12 @@ cc_library {
min_sdk_version: "29",
double_loadable: true,
}
// storage file parse api java cc_library
java_library {
name: "aconfig_storage_file_java",
srcs: [
"srcs/**/*.java",
],
sdk_version: "core_current",
}

View File

@@ -0,0 +1,27 @@
/*
* Copyright (C) 2024 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.aconfig.storage;
public class AconfigStorageException extends RuntimeException {
public AconfigStorageException(String msg) {
super(msg);
}
public AconfigStorageException(String msg, Throwable cause) {
super(msg, cause);
}
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright (C) 2024 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.aconfig.storage;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
public class ByteBufferReader {
private ByteBuffer mByteBuffer;
public ByteBufferReader(ByteBuffer byteBuffer) {
this.mByteBuffer = byteBuffer;
this.mByteBuffer.order(ByteOrder.LITTLE_ENDIAN);
}
public int readByte() {
return Byte.toUnsignedInt(mByteBuffer.get());
}
public int readShort() {
return Short.toUnsignedInt(mByteBuffer.getShort());
}
public int readInt() {
return this.mByteBuffer.getInt();
}
public String readString() {
int length = readInt();
byte[] bytes = new byte[length];
mByteBuffer.get(bytes, 0, length);
return new String(bytes, StandardCharsets.UTF_8);
}
public void position(int newPosition) {
mByteBuffer.position(newPosition);
}
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright (C) 2024 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.aconfig.storage;
public enum FileType {
PACKAGE_MAP(0),
FLAG_MAP(1),
FLAG_VAL(2),
FLAG_INFO(3);
public final int type;
FileType(int type) {
this.type = type;
}
public static FileType fromInt(int index) {
switch (index) {
case 0:
return PACKAGE_MAP;
case 1:
return FLAG_MAP;
case 2:
return FLAG_VAL;
case 3:
return FLAG_INFO;
default:
return null;
}
}
}

View File

@@ -0,0 +1,174 @@
/*
* Copyright (C) 2024 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.aconfig.storage;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
public class FlagTable {
private Header mHeader;
private Map<String, Node> mNodeMap;
public static FlagTable fromBytes(ByteBuffer bytes) {
FlagTable flagTable = new FlagTable();
ByteBufferReader reader = new ByteBufferReader(bytes);
Header header = Header.fromBytes(reader);
flagTable.mHeader = header;
flagTable.mNodeMap = new HashMap(TableUtils.getTableSize(header.mNumFlags));
reader.position(header.mNodeOffset);
for (int i = 0; i < header.mNumFlags; i++) {
Node node = Node.fromBytes(reader);
flagTable.mNodeMap.put(makeKey(node.mPackageId, node.mFlagName), node);
}
return flagTable;
}
public Node get(int packageId, String flagName) {
return mNodeMap.get(makeKey(packageId, flagName));
}
public Header getHeader() {
return mHeader;
}
private static String makeKey(int packageId, String flagName) {
StringBuilder ret = new StringBuilder();
return ret.append(packageId).append('/').append(flagName).toString();
}
public static class Header {
private int mVersion;
private String mContainer;
private FileType mFileType;
private int mFileSize;
private int mNumFlags;
private int mBucketOffset;
private int mNodeOffset;
public static Header fromBytes(ByteBufferReader reader) {
Header header = new Header();
header.mVersion = reader.readInt();
header.mContainer = reader.readString();
header.mFileType = FileType.fromInt(reader.readByte());
header.mFileSize = reader.readInt();
header.mNumFlags = reader.readInt();
header.mBucketOffset = reader.readInt();
header.mNodeOffset = reader.readInt();
if (header.mFileType != FileType.FLAG_MAP) {
throw new AconfigStorageException("binary file is not a flag map");
}
return header;
}
public int getVersion() {
return mVersion;
}
public String getContainer() {
return mContainer;
}
public FileType getFileType() {
return mFileType;
}
public int getFileSize() {
return mFileSize;
}
public int getNumFlags() {
return mNumFlags;
}
public int getBucketOffset() {
return mBucketOffset;
}
public int getNodeOffset() {
return mNodeOffset;
}
}
public static class Node {
private String mFlagName;
private FlagType mFlagType;
private int mPackageId;
private int mFlagIndex;
private int mNextOffset;
public static Node fromBytes(ByteBufferReader reader) {
Node node = new Node();
node.mPackageId = reader.readInt();
node.mFlagName = reader.readString();
node.mFlagType = FlagType.fromInt(reader.readShort());
node.mFlagIndex = reader.readShort();
node.mNextOffset = reader.readInt();
node.mNextOffset = node.mNextOffset == 0 ? -1 : node.mNextOffset;
return node;
}
@Override
public int hashCode() {
return Objects.hash(mFlagName, mFlagType, mPackageId, mFlagIndex, mNextOffset);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || !(obj instanceof Node)) {
return false;
}
Node other = (Node) obj;
return Objects.equals(mFlagName, other.mFlagName)
&& Objects.equals(mFlagType, other.mFlagType)
&& mPackageId == other.mPackageId
&& mFlagIndex == other.mFlagIndex
&& mNextOffset == other.mNextOffset;
}
public String getFlagName() {
return mFlagName;
}
public FlagType getFlagType() {
return mFlagType;
}
public int getPackageId() {
return mPackageId;
}
public int getFlagIndex() {
return mFlagIndex;
}
public int getNextOffset() {
return mNextOffset;
}
}
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright (C) 2024 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.aconfig.storage;
public enum FlagType {
ReadWriteBoolean (0),
ReadOnlyBoolean(1),
FixedReadOnlyBoolean(2);
public final int type;
FlagType(int type) {
this.type = type;
}
public static FlagType fromInt(int index) {
switch (index) {
case 0:
return ReadWriteBoolean;
case 1:
return ReadOnlyBoolean;
case 2:
return FixedReadOnlyBoolean;
default:
return null;
}
}
}

View File

@@ -0,0 +1,106 @@
/*
* Copyright (C) 2024 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.aconfig.storage;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
public class FlagValueList {
private Header mHeader;
private List<Boolean> mList;
private int mSize;
public static FlagValueList fromBytes(ByteBuffer bytes) {
FlagValueList flagValueList = new FlagValueList();
ByteBufferReader reader = new ByteBufferReader(bytes);
Header header = Header.fromBytes(reader);
flagValueList.mHeader = header;
flagValueList.mList = new ArrayList(header.mNumFlags);
reader.position(header.mBooleanValueOffset);
for (int i = 0; i < header.mNumFlags; i++) {
boolean val = reader.readByte() == 1;
flagValueList.mList.add(val);
}
flagValueList.mSize = flagValueList.mList.size();
return flagValueList;
}
public boolean get(int index) {
return mList.get(index);
}
public Header getHeader() {
return mHeader;
}
public int size() {
return mSize;
}
public static class Header {
private int mVersion;
private String mContainer;
private FileType mFileType;
private int mFileSize;
private int mNumFlags;
private int mBooleanValueOffset;
public static Header fromBytes(ByteBufferReader reader) {
Header header = new Header();
header.mVersion = reader.readInt();
header.mContainer = reader.readString();
header.mFileType = FileType.fromInt(reader.readByte());
header.mFileSize = reader.readInt();
header.mNumFlags = reader.readInt();
header.mBooleanValueOffset = reader.readInt();
if (header.mFileType != FileType.FLAG_VAL) {
throw new AconfigStorageException("binary file is not a flag value file");
}
return header;
}
public int getVersion() {
return mVersion;
}
public String getContainer() {
return mContainer;
}
public FileType getFileType() {
return mFileType;
}
public int getFileSize() {
return mFileSize;
}
public int getNumFlags() {
return mNumFlags;
}
public int getBooleanValueOffset() {
return mBooleanValueOffset;
}
}
}

View File

@@ -0,0 +1,162 @@
/*
* Copyright (C) 2024 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.aconfig.storage;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
public class PackageTable {
private Header mHeader;
private Map<String, Node> mNodeMap;
public static PackageTable fromBytes(ByteBuffer bytes) {
PackageTable packageTable = new PackageTable();
ByteBufferReader reader = new ByteBufferReader(bytes);
Header header = Header.fromBytes(reader);
packageTable.mHeader = header;
packageTable.mNodeMap = new HashMap(TableUtils.getTableSize(header.mNumPackages));
reader.position(header.mNodeOffset);
for (int i = 0; i < header.mNumPackages; i++) {
Node node = Node.fromBytes(reader);
packageTable.mNodeMap.put(node.mPackageName, node);
}
return packageTable;
}
public Node get(String packageName) {
return mNodeMap.get(packageName);
}
public Header getHeader() {
return mHeader;
}
public static class Header {
private int mVersion;
private String mContainer;
private FileType mFileType;
private int mFileSize;
private int mNumPackages;
private int mBucketOffset;
private int mNodeOffset;
public static Header fromBytes(ByteBufferReader reader) {
Header header = new Header();
header.mVersion = reader.readInt();
header.mContainer = reader.readString();
header.mFileType = FileType.fromInt(reader.readByte());
header.mFileSize = reader.readInt();
header.mNumPackages = reader.readInt();
header.mBucketOffset = reader.readInt();
header.mNodeOffset = reader.readInt();
if (header.mFileType != FileType.PACKAGE_MAP) {
throw new AconfigStorageException("binary file is not a package map");
}
return header;
}
public int getVersion() {
return mVersion;
}
public String getContainer() {
return mContainer;
}
public FileType getFileType() {
return mFileType;
}
public int getFileSize() {
return mFileSize;
}
public int getNumPackages() {
return mNumPackages;
}
public int getBucketOffset() {
return mBucketOffset;
}
public int getNodeOffset() {
return mNodeOffset;
}
}
public static class Node {
private String mPackageName;
private int mPackageId;
private int mBooleanStartIndex;
private int mNextOffset;
public static Node fromBytes(ByteBufferReader reader) {
Node node = new Node();
node.mPackageName = reader.readString();
node.mPackageId = reader.readInt();
node.mBooleanStartIndex = reader.readInt();
node.mNextOffset = reader.readInt();
node.mNextOffset = node.mNextOffset == 0 ? -1 : node.mNextOffset;
return node;
}
@Override
public int hashCode() {
return Objects.hash(mPackageName, mPackageId, mBooleanStartIndex, mNextOffset);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || !(obj instanceof Node)) {
return false;
}
Node other = (Node) obj;
return Objects.equals(mPackageName, other.mPackageName)
&& mPackageId == other.mPackageId
&& mBooleanStartIndex == other.mBooleanStartIndex
&& mNextOffset == other.mNextOffset;
}
public String getPackageName() {
return mPackageName;
}
public int getPackageId() {
return mPackageId;
}
public int getBooleanStartIndex() {
return mBooleanStartIndex;
}
public int getNextOffset() {
return mNextOffset;
}
}
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright (C) 2024 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.aconfig.storage;
public class TableUtils {
private static final int[] HASH_PRIMES =
new int[] {
7,
17,
29,
53,
97,
193,
389,
769,
1543,
3079,
6151,
12289,
24593,
49157,
98317,
196613,
393241,
786433,
1572869,
3145739,
6291469,
12582917,
25165843,
50331653,
100663319,
201326611,
402653189,
805306457,
1610612741
};
public static int getTableSize(int numEntries) {
for (int i : HASH_PRIMES) {
if (i < 2 * numEntries) continue;
return i;
}
throw new AconfigStorageException("Number of items in a hash table exceeds limit");
}
}

View File

@@ -1,4 +1,3 @@
cc_test {
name: "aconfig_storage_file.test.cpp",
team: "trendy_team_android_core_experiments",
@@ -21,3 +20,27 @@ cc_test {
"general-tests",
],
}
android_test {
name: "aconfig_storage_file.test.java",
team: "trendy_team_android_core_experiments",
srcs: [
"srcs/**/*.java",
],
static_libs: [
"aconfig_storage_file_java",
"androidx.test.runner",
"junit",
],
sdk_version: "test_current",
test_config: "AndroidStorageJaveTest.xml",
data: [
"package.map",
"flag.map",
"flag.val",
"flag.info",
],
test_suites: [
"general-tests",
],
}

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2024 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.aconfig.storage.test">
<application>
<uses-library android:name="android.test.runner" />
</application>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="android.aconfig.storage.test" />
</manifest>

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2024 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="Test aconfig storage java tests">
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="aconfig_storage_file.test.java.apk" />
</target_preparer>
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
<option name="cleanup" value="true" />
<option name="push" value="package.map->/data/local/tmp/aconfig_storage_file_test_java/testdata/package.map" />
<option name="push" value="flag.map->/data/local/tmp/aconfig_storage_file_test_java/testdata/flag.map" />
<option name="push" value="flag.val->/data/local/tmp/aconfig_storage_file_test_java/testdata/flag.val" />
<option name="push" value="flag.info->/data/local/tmp/aconfig_storage_file_test_java/testdata/flag.info" />
</target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="android.aconfig.storage.test" />
<option name="runtime-hint" value="1m" />
</test>
</configuration>

View File

@@ -0,0 +1,79 @@
/*
* Copyright (C) 2024 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.aconfig.storage.test;
import static org.junit.Assert.assertEquals;
import android.aconfig.storage.ByteBufferReader;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
@RunWith(JUnit4.class)
public class ByteBufferReaderTest {
@Test
public void testReadByte() {
ByteBuffer buffer = ByteBuffer.allocate(1);
byte expect = 10;
buffer.put(expect).rewind();
ByteBufferReader reader = new ByteBufferReader(buffer);
assertEquals(expect, reader.readByte());
}
@Test
public void testReadShort() {
ByteBuffer buffer = ByteBuffer.allocate(4);
buffer.order(ByteOrder.LITTLE_ENDIAN);
short expect = Short.MAX_VALUE;
buffer.putShort(expect).rewind();
ByteBufferReader reader = new ByteBufferReader(buffer);
assertEquals(expect, reader.readShort());
}
@Test
public void testReadInt() {
ByteBuffer buffer = ByteBuffer.allocate(4);
buffer.order(ByteOrder.LITTLE_ENDIAN);
int expect = 10000;
buffer.putInt(expect).rewind();
ByteBufferReader reader = new ByteBufferReader(buffer);
assertEquals(expect, reader.readInt());
}
@Test
public void testReadString() {
String expect = "test read string";
byte[] bytes = expect.getBytes(StandardCharsets.UTF_8);
ByteBuffer buffer = ByteBuffer.allocate(expect.length() * 2 + 4);
buffer.order(ByteOrder.LITTLE_ENDIAN);
buffer.putInt(expect.length()).put(bytes).rewind();
ByteBufferReader reader = new ByteBufferReader(buffer);
assertEquals(expect, reader.readString());
}
}

View File

@@ -0,0 +1,103 @@
/*
* Copyright (C) 2024 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.aconfig.storage.test;
import static org.junit.Assert.assertEquals;
import android.aconfig.storage.FileType;
import android.aconfig.storage.FlagTable;
import android.aconfig.storage.FlagType;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@RunWith(JUnit4.class)
public class FlagTableTest {
@Test
public void testFlagTable_rightHeader() throws Exception {
FlagTable flagTable = FlagTable.fromBytes(TestDataUtils.getTestFlagMapByteBuffer());
FlagTable.Header header = flagTable.getHeader();
assertEquals(1, header.getVersion());
assertEquals("mockup", header.getContainer());
assertEquals(FileType.FLAG_MAP, header.getFileType());
assertEquals(321, header.getFileSize());
assertEquals(8, header.getNumFlags());
assertEquals(31, header.getBucketOffset());
assertEquals(99, header.getNodeOffset());
}
@Test
public void testFlagTable_rightNode() throws Exception {
FlagTable flagTable = FlagTable.fromBytes(TestDataUtils.getTestFlagMapByteBuffer());
FlagTable.Node node1 = flagTable.get(0, "enabled_ro");
FlagTable.Node node2 = flagTable.get(0, "enabled_rw");
FlagTable.Node node3 = flagTable.get(2, "enabled_rw");
FlagTable.Node node4 = flagTable.get(1, "disabled_rw");
FlagTable.Node node5 = flagTable.get(1, "enabled_fixed_ro");
FlagTable.Node node6 = flagTable.get(1, "enabled_ro");
FlagTable.Node node7 = flagTable.get(2, "enabled_fixed_ro");
FlagTable.Node node8 = flagTable.get(0, "disabled_rw");
assertEquals("enabled_ro", node1.getFlagName());
assertEquals("enabled_rw", node2.getFlagName());
assertEquals("enabled_rw", node3.getFlagName());
assertEquals("disabled_rw", node4.getFlagName());
assertEquals("enabled_fixed_ro", node5.getFlagName());
assertEquals("enabled_ro", node6.getFlagName());
assertEquals("enabled_fixed_ro", node7.getFlagName());
assertEquals("disabled_rw", node8.getFlagName());
assertEquals(0, node1.getPackageId());
assertEquals(0, node2.getPackageId());
assertEquals(2, node3.getPackageId());
assertEquals(1, node4.getPackageId());
assertEquals(1, node5.getPackageId());
assertEquals(1, node6.getPackageId());
assertEquals(2, node7.getPackageId());
assertEquals(0, node8.getPackageId());
assertEquals(FlagType.ReadOnlyBoolean, node1.getFlagType());
assertEquals(FlagType.ReadWriteBoolean, node2.getFlagType());
assertEquals(FlagType.ReadWriteBoolean, node3.getFlagType());
assertEquals(FlagType.ReadWriteBoolean, node4.getFlagType());
assertEquals(FlagType.FixedReadOnlyBoolean, node5.getFlagType());
assertEquals(FlagType.ReadOnlyBoolean, node6.getFlagType());
assertEquals(FlagType.FixedReadOnlyBoolean, node7.getFlagType());
assertEquals(FlagType.ReadWriteBoolean, node8.getFlagType());
assertEquals(1, node1.getFlagIndex());
assertEquals(2, node2.getFlagIndex());
assertEquals(1, node3.getFlagIndex());
assertEquals(0, node4.getFlagIndex());
assertEquals(1, node5.getFlagIndex());
assertEquals(2, node6.getFlagIndex());
assertEquals(0, node7.getFlagIndex());
assertEquals(0, node8.getFlagIndex());
assertEquals(-1, node1.getNextOffset());
assertEquals(151, node2.getNextOffset());
assertEquals(-1, node3.getNextOffset());
assertEquals(-1, node4.getNextOffset());
assertEquals(236, node5.getNextOffset());
assertEquals(-1, node6.getNextOffset());
assertEquals(-1, node7.getNextOffset());
assertEquals(-1, node8.getNextOffset());
}
}

View File

@@ -0,0 +1,77 @@
/*
* Copyright (C) 2024 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.aconfig.storage.test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import android.aconfig.storage.FileType;
import android.aconfig.storage.FlagTable;
import android.aconfig.storage.FlagValueList;
import android.aconfig.storage.PackageTable;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@RunWith(JUnit4.class)
public class FlagValueListTest {
@Test
public void testFlagValueList_rightHeader() throws Exception {
FlagValueList flagValueList =
FlagValueList.fromBytes(TestDataUtils.getTestFlagValByteBuffer());
FlagValueList.Header header = flagValueList.getHeader();
assertEquals(1, header.getVersion());
assertEquals("mockup", header.getContainer());
assertEquals(FileType.FLAG_VAL, header.getFileType());
assertEquals(35, header.getFileSize());
assertEquals(8, header.getNumFlags());
assertEquals(27, header.getBooleanValueOffset());
}
@Test
public void testFlagValueList_rightNode() throws Exception {
FlagValueList flagValueList =
FlagValueList.fromBytes(TestDataUtils.getTestFlagValByteBuffer());
boolean[] expected = new boolean[] {false, true, true, false, true, true, true, true};
assertEquals(expected.length, flagValueList.size());
for (int i = 0; i < flagValueList.size(); i++) {
assertEquals(expected[i], flagValueList.get(i));
}
}
@Test
public void testFlagValueList_getValue() throws Exception {
PackageTable packageTable =
PackageTable.fromBytes(TestDataUtils.getTestPackageMapByteBuffer());
FlagTable flagTable = FlagTable.fromBytes(TestDataUtils.getTestFlagMapByteBuffer());
FlagValueList flagValueList =
FlagValueList.fromBytes(TestDataUtils.getTestFlagValByteBuffer());
PackageTable.Node pNode = packageTable.get("com.android.aconfig.storage.test_1");
FlagTable.Node fNode = flagTable.get(pNode.getPackageId(), "enabled_rw");
assertTrue(flagValueList.get(pNode.getBooleanStartIndex() + fNode.getFlagIndex()));
pNode = packageTable.get("com.android.aconfig.storage.test_4");
fNode = flagTable.get(pNode.getPackageId(), "enabled_fixed_ro");
assertTrue(flagValueList.get(pNode.getBooleanStartIndex() + fNode.getFlagIndex()));
}
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright (C) 2024 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.aconfig.storage.test;
import static org.junit.Assert.assertEquals;
import android.aconfig.storage.FileType;
import android.aconfig.storage.PackageTable;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@RunWith(JUnit4.class)
public class PackageTableTest {
@Test
public void testPackageTable_rightHeader() throws Exception {
PackageTable packageTable =
PackageTable.fromBytes(TestDataUtils.getTestPackageMapByteBuffer());
PackageTable.Header header = packageTable.getHeader();
assertEquals(1, header.getVersion());
assertEquals("mockup", header.getContainer());
assertEquals(FileType.PACKAGE_MAP, header.getFileType());
assertEquals(209, header.getFileSize());
assertEquals(3, header.getNumPackages());
assertEquals(31, header.getBucketOffset());
assertEquals(59, header.getNodeOffset());
}
@Test
public void testPackageTable_rightNode() throws Exception {
PackageTable packageTable =
PackageTable.fromBytes(TestDataUtils.getTestPackageMapByteBuffer());
PackageTable.Node node1 = packageTable.get("com.android.aconfig.storage.test_1");
PackageTable.Node node2 = packageTable.get("com.android.aconfig.storage.test_2");
PackageTable.Node node4 = packageTable.get("com.android.aconfig.storage.test_4");
assertEquals("com.android.aconfig.storage.test_1", node1.getPackageName());
assertEquals("com.android.aconfig.storage.test_2", node2.getPackageName());
assertEquals("com.android.aconfig.storage.test_4", node4.getPackageName());
assertEquals(0, node1.getPackageId());
assertEquals(1, node2.getPackageId());
assertEquals(2, node4.getPackageId());
assertEquals(0, node1.getBooleanStartIndex());
assertEquals(3, node2.getBooleanStartIndex());
assertEquals(6, node4.getBooleanStartIndex());
assertEquals(159, node1.getNextOffset());
assertEquals(-1, node2.getNextOffset());
assertEquals(-1, node4.getNextOffset());
}
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright (C) 2024 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.aconfig.storage.test;
import java.io.FileInputStream;
import java.io.InputStream;
import java.nio.ByteBuffer;
public final class TestDataUtils {
private static final String TEST_PACKAGE_MAP_PATH = "package.map";
private static final String TEST_FLAG_MAP_PATH = "flag.map";
private static final String TEST_FLAG_VAL_PATH = "flag.val";
private static final String TEST_FLAG_INFO_PATH = "flag.info";
private static final String TESTDATA_PATH =
"/data/local/tmp/aconfig_storage_file_test_java/testdata/";
public static ByteBuffer getTestPackageMapByteBuffer() throws Exception {
return readFile(TESTDATA_PATH + TEST_PACKAGE_MAP_PATH);
}
public static ByteBuffer getTestFlagMapByteBuffer() throws Exception {
return readFile(TESTDATA_PATH + TEST_FLAG_MAP_PATH);
}
public static ByteBuffer getTestFlagValByteBuffer() throws Exception {
return readFile(TESTDATA_PATH + TEST_FLAG_VAL_PATH);
}
public static ByteBuffer getTestFlagInfoByteBuffer() throws Exception {
return readFile(TESTDATA_PATH + TEST_FLAG_INFO_PATH);
}
private static ByteBuffer readFile(String fileName) throws Exception {
InputStream input = new FileInputStream(fileName);
return ByteBuffer.wrap(input.readAllBytes());
}
}