mediav2 CTS: Add unit tests for NDK AMediaFormat APIs
Bug: 193196195
Test: atest android.mediav2.cts.MediaFormatUnitTest
Change-Id: I0b7c765feaa856c88e5fd28dcb6aba263475c697
diff --git a/tests/media/Android.bp b/tests/media/Android.bp
index 81c56af..b2a2db3 100644
--- a/tests/media/Android.bp
+++ b/tests/media/Android.bp
@@ -36,6 +36,7 @@
"libctsmediav2muxer_jni",
"libctsmediav2extractor_jni",
"libctsmediav2codec_jni",
+ "libctsmediav2utils_jni",
],
srcs: ["src/**/*.java"],
// Tag this module as a cts test artifact
diff --git a/tests/media/jni/Android.bp b/tests/media/jni/Android.bp
index d4e192c..95fb613 100644
--- a/tests/media/jni/Android.bp
+++ b/tests/media/jni/Android.bp
@@ -93,3 +93,25 @@
gtest: false,
sdk_version: "29",
}
+
+cc_test_library {
+ name: "libctsmediav2utils_jni",
+ srcs: [
+ "NativeMediaFormatUnitTest.cpp",
+ ],
+ shared_libs: [
+ "libmediandk",
+ "liblog",
+ ],
+ header_libs: ["liblog_headers"],
+ include_dirs: [
+ "frameworks/av/media/ndk/include/media",
+ ],
+ stl: "libc++_static",
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+ gtest: false,
+ sdk_version: "29",
+}
diff --git a/tests/media/jni/NativeMediaFormatUnitTest.cpp b/tests/media/jni/NativeMediaFormatUnitTest.cpp
new file mode 100644
index 0000000..e3241a3
--- /dev/null
+++ b/tests/media/jni/NativeMediaFormatUnitTest.cpp
@@ -0,0 +1,654 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "NativeMediaFormatUnitTest"
+#include <log/log.h>
+#include <jni.h>
+#include <NdkMediaFormat.h>
+
+#include <cinttypes>
+#include <map>
+#include <string>
+
+static const char story[] = {"What if after you die, God asks you: 'so how was heaven'"};
+static const char dragon[] = {"e4 c5 Nf3 d6 d4 cxd4 Nxd4 Nf6 Nc3 g6"};
+
+class Rect {
+public:
+ int left;
+ int top;
+ int right;
+ int bottom;
+
+ Rect(int a, int b, int c, int d) : left{a}, top{b}, right{c}, bottom{d} {};
+};
+
+class Buffer {
+public:
+ char* buffer;
+ size_t size;
+
+ explicit Buffer(char* buffer = nullptr, size_t size = 0) : buffer{buffer}, size{size} {};
+};
+
+class NativeMediaFormatUnitTest {
+private:
+ std::map<int32_t, const char*> mInt32KeyValuePairs;
+ std::map<int64_t, const char*> mInt64KeyValuePairs;
+ std::map<float, const char*> mFloatKeyValuePairs;
+ std::map<double, const char*> mDoubleKeyValuePairs;
+ std::map<size_t, const char*> mSizeKeyValuePairs;
+ std::map<const char*, const char*> mStringKeyValuePairs;
+ std::map<Rect*, const char*> mWindowKeyValuePairs;
+ std::map<Buffer*, const char*> mBufferKeyValuePairs;
+
+public:
+ NativeMediaFormatUnitTest();
+ ~NativeMediaFormatUnitTest();
+
+ bool validateFormatInt32(AMediaFormat* fmt, int offset = 0, bool isClear = false);
+ bool validateFormatInt64(AMediaFormat* fmt, int offset = 0, bool isClear = false);
+ bool validateFormatFloat(AMediaFormat* fmt, float offset = 0.0f, bool isClear = false);
+ bool validateFormatDouble(AMediaFormat* fmt, double offset = 0.0, bool isClear = false);
+ bool validateFormatSize(AMediaFormat* fmt, size_t offset = 0, bool isClear = false);
+ bool validateFormatString(AMediaFormat* fmt, int offset = 0, bool isClear = false);
+ bool validateFormatRect(AMediaFormat* fmt, int offset = 0, bool isClear = false);
+ bool validateFormatBuffer(AMediaFormat* fmt, int offset = 0, bool isClear = false);
+ bool validateFormat(AMediaFormat* fmt, int offset = 0, bool isClear = false);
+
+ void configureFormatInt32(AMediaFormat* fmt, int offset = 0);
+ void configureFormatInt64(AMediaFormat* fmt, int offset = 0);
+ void configureFormatFloat(AMediaFormat* fmt, float offset = 0.0f);
+ void configureFormatDouble(AMediaFormat* fmt, double offset = 0.0);
+ void configureFormatSize(AMediaFormat* fmt, size_t offset = 0);
+ void configureFormatString(AMediaFormat* fmt, int offset = 0);
+ void configureFormatRect(AMediaFormat* fmt, int offset = 0);
+ void configureFormatBuffer(AMediaFormat* fmt, int offset = 0);
+ void configureFormat(AMediaFormat* fmt, int offset = 0);
+};
+
+NativeMediaFormatUnitTest::NativeMediaFormatUnitTest() {
+ mInt32KeyValuePairs.insert({118, "elements in periodic table"});
+ mInt32KeyValuePairs.insert({5778, "surface temp. of sun in kelvin"});
+ mInt32KeyValuePairs.insert({8611, "k2 peak in mts"});
+ mInt32KeyValuePairs.insert({72, "heart rate in bpm"});
+ mInt64KeyValuePairs.insert({299792458L, "vel. of em wave in free space m/s"});
+ mInt64KeyValuePairs.insert({86400L, "number of seconds in a day"});
+ mInt64KeyValuePairs.insert({1520200000L, "distance of earth from the sun in km"});
+ mInt64KeyValuePairs.insert({39000000L, "forest area of the world km^2"});
+ mFloatKeyValuePairs.insert({22.0f / 7.0f, "pi"});
+ mFloatKeyValuePairs.insert({3.6f, "not great, not terrible"});
+ mFloatKeyValuePairs.insert({15.999f, "atomic weight of oxygen 8"});
+ mFloatKeyValuePairs.insert({2.7182f, "Euler's number"});
+ mDoubleKeyValuePairs.insert({44.0 / 7, "tau"});
+ mDoubleKeyValuePairs.insert({9.80665, "g on earth m/sec^2"});
+ mSizeKeyValuePairs.insert({sizeof(int64_t), "size of int64_t"});
+ mSizeKeyValuePairs.insert({sizeof(wchar_t), "size of wide char"});
+ mSizeKeyValuePairs.insert({sizeof(intptr_t), "size of pointer variable"});
+ mSizeKeyValuePairs.insert({sizeof *this, "size of class NativeMediaFormatUnitTest"});
+ mStringKeyValuePairs.insert(
+ {"Discovered radium and polonium, and made huge contribution to finding treatments "
+ "for cancer", "Marie Curie"});
+ mStringKeyValuePairs.insert({"Sun rises in the east has zero entropy", "Shannon"});
+ mWindowKeyValuePairs.insert({new Rect{12, 15, 12, 21}, "trapezoid"});
+ mWindowKeyValuePairs.insert({new Rect{12, 12, 12, 12}, "rhombus"});
+ mWindowKeyValuePairs.insert({new Rect{12, 15, 12, 15}, "rectangle"});
+ mWindowKeyValuePairs.insert({new Rect{12, 15, 18, 21}, "quadrilateral"});
+ mBufferKeyValuePairs.insert({new Buffer(), "empty buffer"});
+ size_t sz = strlen(story) + 1;
+ auto* quote = new Buffer{new char[sz], sz};
+ memcpy(quote->buffer, story, sz);
+ mBufferKeyValuePairs.insert({quote, "one line story"});
+ sz = strlen(dragon) + 1;
+ auto* chess = new Buffer(new char[sz], sz);
+ memcpy(chess->buffer, dragon, sz);
+ mBufferKeyValuePairs.insert({chess, "sicilian dragon"});
+}
+
+NativeMediaFormatUnitTest::~NativeMediaFormatUnitTest() {
+ for (auto it : mWindowKeyValuePairs) {
+ delete it.first;
+ }
+ for (auto it : mBufferKeyValuePairs) {
+ delete[] it.first->buffer;
+ delete it.first;
+ }
+}
+
+bool NativeMediaFormatUnitTest::validateFormatInt32(AMediaFormat* fmt, int offset, bool isClear) {
+ bool status = true;
+ int32_t val;
+ const char* toString = AMediaFormat_toString(fmt);
+ for (auto it : mInt32KeyValuePairs) {
+ bool result = AMediaFormat_getInt32(fmt, it.second, &val);
+ if (isClear) {
+ if (result) {
+ ALOGE("MediaFormat is not expected to contain Key %s", it.second);
+ status &= false;
+ }
+ } else {
+ if (!result) {
+ ALOGE("MediaFormat doesn't contain key %s", it.second);
+ status &= false;
+ } else if (val != it.first + offset) {
+ ALOGE("MediaFormat Value for Key %s is not %d but %d", it.second, it.first + offset,
+ val);
+ status &= false;
+ }
+ if (strstr(toString, it.second) == nullptr) {
+ ALOGE("AMediaFormat_toString() of fmt %s doesn't contains %s", toString, it.second);
+ status &= false;
+ }
+ if (strstr(toString, std::to_string(it.first + offset).c_str()) == nullptr) {
+ ALOGE("AMediaFormat_toString() of fmt %s doesn't contains %s", toString,
+ std::to_string(it.first + offset).c_str());
+ status &= false;
+ }
+ }
+ }
+ if (AMediaFormat_getInt32(fmt, "hello world", &val)) {
+ ALOGE("MediaFormat has value for key 'hello world' ");
+ status &= false;
+ }
+ return status;
+}
+
+bool NativeMediaFormatUnitTest::validateFormatInt64(AMediaFormat* fmt, int offset, bool isClear) {
+ bool status = true;
+ int64_t val;
+ const char* toString = AMediaFormat_toString(fmt);
+ for (auto it : mInt64KeyValuePairs) {
+ bool result = AMediaFormat_getInt64(fmt, it.second, &val);
+ if (isClear) {
+ if (result) {
+ ALOGE("MediaFormat is not expected to contain Key %s", it.second);
+ status &= false;
+ }
+ } else {
+ if (!result) {
+ ALOGE("MediaFormat doesn't contain key %s", it.second);
+ status &= false;
+ } else if (val != it.first + offset) {
+ ALOGE("MediaFormat Value for Key %s is not %" PRId64 "but %" PRId64, it.second,
+ it.first + offset, val);
+ status &= false;
+ }
+ if (strstr(toString, it.second) == nullptr) {
+ ALOGE("AMediaFormat_toString() of fmt %s doesn't contains %s", toString, it.second);
+ status &= false;
+ }
+ if (strstr(toString, std::to_string(it.first + offset).c_str()) == nullptr) {
+ ALOGE("AMediaFormat_toString() of fmt %s doesn't contains %s", toString,
+ std::to_string(it.first + offset).c_str());
+ status &= false;
+ }
+ }
+ }
+ if (AMediaFormat_getInt64(fmt, "hello world", &val)) {
+ ALOGE("MediaFormat has value for key 'hello world' ");
+ status &= false;
+ }
+ return status;
+}
+
+bool NativeMediaFormatUnitTest::validateFormatFloat(AMediaFormat* fmt, float offset, bool isClear) {
+ bool status = true;
+ float val;
+ const char* toString = AMediaFormat_toString(fmt);
+ for (auto it : mFloatKeyValuePairs) {
+ bool result = AMediaFormat_getFloat(fmt, it.second, &val);
+ if (isClear) {
+ if (result) {
+ ALOGE("MediaFormat is not expected to contain Key %s", it.second);
+ status &= false;
+ }
+ } else {
+ if (!result) {
+ ALOGE("MediaFormat doesn't contain key %s", it.second);
+ status &= false;
+ } else if (val != it.first + offset) {
+ ALOGE("MediaFormat Value for Key %s is not %f but %f", it.second, it.first + offset,
+ val);
+ status &= false;
+ }
+ if (strstr(toString, it.second) == nullptr) {
+ ALOGE("AMediaFormat_toString() of fmt %s doesn't contains %s", toString, it.second);
+ status &= false;
+ }
+ if (strstr(toString, std::to_string(it.first + offset).c_str()) == nullptr) {
+ ALOGE("AMediaFormat_toString() of fmt %s doesn't contains %s", toString,
+ std::to_string(it.first + offset).c_str());
+ status &= false;
+ }
+ }
+ }
+ if (AMediaFormat_getFloat(fmt, "hello world", &val)) {
+ ALOGE("MediaFormat has value for key 'hello world' ");
+ status &= false;
+ }
+ return status;
+}
+
+bool NativeMediaFormatUnitTest::validateFormatDouble(AMediaFormat* fmt, double offset,
+ bool isClear) {
+ bool status = true;
+ double val;
+ const char* toString = AMediaFormat_toString(fmt);
+ for (auto it : mDoubleKeyValuePairs) {
+ bool result = AMediaFormat_getDouble(fmt, it.second, &val);
+ if (isClear) {
+ if (result) {
+ ALOGE("MediaFormat is not expected to contain Key %s", it.second);
+ status &= false;
+ }
+ } else {
+ if (!result) {
+ ALOGE("MediaFormat doesn't contain key %s", it.second);
+ status &= false;
+ } else if (val != it.first + offset) {
+ ALOGE("MediaFormat Value for Key %s is not %f but %f", it.second, it.first + offset,
+ val);
+ status &= false;
+ }
+ if (strstr(toString, it.second) == nullptr) {
+ ALOGE("AMediaFormat_toString() of fmt %s doesn't contains %s", toString, it.second);
+ status &= false;
+ }
+ if (strstr(toString, std::to_string(it.first + offset).c_str()) == nullptr) {
+ ALOGE("AMediaFormat_toString() of fmt %s doesn't contains %s", toString,
+ std::to_string(it.first + offset).c_str());
+ status &= false;
+ }
+ }
+ }
+ if (AMediaFormat_getDouble(fmt, "hello world", &val)) {
+ ALOGE("MediaFormat has value for key 'hello world' ");
+ status &= false;
+ }
+ return status;
+}
+
+bool NativeMediaFormatUnitTest::validateFormatSize(AMediaFormat* fmt, size_t offset, bool isClear) {
+ bool status = true;
+ size_t val;
+ const char* toString = AMediaFormat_toString(fmt);
+ for (auto it : mSizeKeyValuePairs) {
+ bool result = AMediaFormat_getSize(fmt, it.second, &val);
+ if (isClear) {
+ if (result) {
+ ALOGE("MediaFormat is not expected to contain Key %s", it.second);
+ status &= false;
+ }
+ } else {
+ if (!result) {
+ ALOGE("MediaFormat doesn't contain key %s", it.second);
+ status &= false;
+ } else if (val != it.first + offset) {
+ ALOGE("MediaFormat Value for Key %s is not %zu but %zu", it.second,
+ it.first + offset, val);
+ status &= false;
+ }
+ if (strstr(toString, it.second) == nullptr) {
+ ALOGE("AMediaFormat_toString() of fmt %s doesn't contains %s", toString, it.second);
+ status &= false;
+ }
+ if (strstr(toString, std::to_string(it.first + offset).c_str()) == nullptr) {
+ ALOGE("AMediaFormat_toString() of fmt %s doesn't contains %s", toString,
+ std::to_string(it.first + offset).c_str());
+ status &= false;
+ }
+ }
+ }
+ if (AMediaFormat_getSize(fmt, "hello world", &val)) {
+ ALOGE("MediaFormat has value for key 'hello world' ");
+ status &= false;
+ }
+ return status;
+}
+
+bool NativeMediaFormatUnitTest::validateFormatString(AMediaFormat* fmt, int offset, bool isClear) {
+ bool status = true;
+ const char* val;
+ const char* toString = AMediaFormat_toString(fmt);
+ for (auto it : mStringKeyValuePairs) {
+ bool result = AMediaFormat_getString(fmt, it.second, &val);
+ if (isClear) {
+ if (result) {
+ ALOGE("MediaFormat is not expected to contain Key %s", it.second);
+ status &= false;
+ }
+ } else {
+ std::string s = it.first + std::to_string(offset);
+ if (!result) {
+ ALOGE("MediaFormat doesn't contain key %s", it.second);
+ status &= false;
+ } else if (s != val) {
+ ALOGE("MediaFormat Value for Key %s is not %s but %s", it.second, s.c_str(), val);
+ status &= false;
+ }
+ if (strstr(toString, it.second) == nullptr) {
+ ALOGE("AMediaFormat_toString() of fmt %s doesn't contains %s", toString, it.second);
+ status &= false;
+ }
+ if (strstr(toString, s.c_str()) == nullptr) {
+ ALOGE("AMediaFormat_toString() of fmt %s doesn't contains %s", toString, s.c_str());
+ status &= false;
+ }
+ }
+ }
+ if (AMediaFormat_getString(fmt, "hello world", &val)) {
+ ALOGE("MediaFormat has value for key 'hello world' ");
+ status &= false;
+ }
+ return status;
+}
+
+bool NativeMediaFormatUnitTest::validateFormatRect(AMediaFormat* fmt, int offset, bool isClear) {
+ bool status = true;
+ int left, top, right, bottom;
+ const char* toString = AMediaFormat_toString(fmt);
+ for (auto it : mWindowKeyValuePairs) {
+ bool result = AMediaFormat_getRect(fmt, it.second, &left, &top, &right, &bottom);
+ if (isClear) {
+ if (result) {
+ ALOGE("MediaFormat is not expected to contain Key %s", it.second);
+ status &= false;
+ }
+ } else {
+ if (!result) {
+ ALOGE("MediaFormat doesn't contain key %s", it.second);
+ status &= false;
+ } else if (left != it.first->left + offset || top != it.first->top + offset ||
+ right != it.first->right + offset || bottom != it.first->bottom + offset) {
+ ALOGE("MediaFormat Value for Key %s is not (%d, %d, %d, %d)) but (%d, %d, %d, %d)",
+ it.second, it.first->left, it.first->top, it.first->right, it.first->bottom,
+ left, top, right, bottom);
+ status &= false;
+ }
+ if (strstr(toString, it.second) == nullptr) {
+ ALOGE("AMediaFormat_toString() of fmt %s doesn't contains %s", toString, it.second);
+ status &= false;
+ }
+ if (strstr(toString, std::to_string(it.first->left + offset).c_str()) == nullptr) {
+ ALOGE("AMediaFormat_toString() of fmt %s doesn't contains %s", toString,
+ std::to_string(it.first->left + offset).c_str());
+ status &= false;
+ }
+ if (strstr(toString, std::to_string(it.first->top + offset).c_str()) == nullptr) {
+ ALOGE("AMediaFormat_toString() of fmt %s doesn't contains %s", toString,
+ std::to_string(it.first->top + offset).c_str());
+ status &= false;
+ }
+ if (strstr(toString, std::to_string(it.first->right + offset).c_str()) == nullptr) {
+ ALOGE("AMediaFormat_toString() of fmt %s doesn't contains %s", toString,
+ std::to_string(it.first->right + offset).c_str());
+ status &= false;
+ }
+ if (strstr(toString, std::to_string(it.first->bottom + offset).c_str()) == nullptr) {
+ ALOGE("AMediaFormat_toString() of fmt %s doesn't contains %s", toString,
+ std::to_string(it.first->bottom + offset).c_str());
+ status &= false;
+ }
+ }
+ }
+ if (AMediaFormat_getRect(fmt, "hello world", &left, &top, &right, &bottom)) {
+ ALOGE("MediaFormat has value for key 'hello world' ");
+ status &= false;
+ }
+ return status;
+}
+
+bool NativeMediaFormatUnitTest::validateFormatBuffer(AMediaFormat* fmt, int offset, bool isClear) {
+ bool status = true;
+ void* data;
+ size_t size;
+ const char* toString = AMediaFormat_toString(fmt);
+ for (auto it : mBufferKeyValuePairs) {
+ bool result = AMediaFormat_getBuffer(fmt, it.second, &data, &size);
+ if (isClear) {
+ if (result) {
+ ALOGE("MediaFormat is not expected to contain Key %s", it.second);
+ status &= false;
+ }
+ } else {
+ if (!result) {
+ ALOGE("MediaFormat doesn't contain key %s", it.second);
+ status &= false;
+ } else if (size != (offset == 0 ? it.first->size : it.first->size / 2)) {
+ ALOGE("MediaFormat Value for Key %s is not %zu but %zu", it.second,
+ (offset == 0 ? it.first->size : it.first->size / 2), size);
+ status &= false;
+ } else {
+ if (it.first->buffer != nullptr &&
+ memcmp(data, it.first->buffer + it.first->size - size, size) != 0) {
+ ALOGE("MediaFormat Value for Key %s is not %s but %s {%zu}", it.second,
+ it.first->buffer + it.first->size - size, (char*)data, size);
+ status &= false;
+ }
+ }
+ if (strstr(toString, it.second) == nullptr) {
+ ALOGE("AMediaFormat_toString() of fmt %s doesn't contains %s", toString, it.second);
+ status &= false;
+ }
+ }
+ }
+ if (AMediaFormat_getBuffer(fmt, "hello world", &data, &size)) {
+ ALOGE("MediaFormat has value for key 'hello world' ");
+ status &= false;
+ }
+ return status;
+}
+
+bool NativeMediaFormatUnitTest::validateFormat(AMediaFormat* fmt, int offset, bool isClear) {
+ bool status = validateFormatInt32(fmt, offset, isClear);
+ status &= validateFormatInt64(fmt, offset, isClear);
+ status &= validateFormatFloat(fmt, offset, isClear);
+ status &= validateFormatDouble(fmt, offset, isClear);
+ status &= validateFormatSize(fmt, offset, isClear);
+ status &= validateFormatString(fmt, offset, isClear);
+ status &= validateFormatRect(fmt, offset, isClear);
+ status &= validateFormatBuffer(fmt, offset, isClear);
+ return status;
+}
+
+void NativeMediaFormatUnitTest::configureFormatInt32(AMediaFormat* fmt, int offset) {
+ for (auto it : mInt32KeyValuePairs) {
+ AMediaFormat_setInt32(fmt, it.second, it.first + offset);
+ }
+}
+
+void NativeMediaFormatUnitTest::configureFormatInt64(AMediaFormat* fmt, int offset) {
+ for (auto it : mInt64KeyValuePairs) {
+ AMediaFormat_setInt64(fmt, it.second, it.first + offset);
+ }
+}
+
+void NativeMediaFormatUnitTest::configureFormatFloat(AMediaFormat* fmt, float offset) {
+ for (auto it : mFloatKeyValuePairs) {
+ AMediaFormat_setFloat(fmt, it.second, it.first + offset);
+ }
+}
+
+void NativeMediaFormatUnitTest::configureFormatDouble(AMediaFormat* fmt, double offset) {
+ for (auto it : mDoubleKeyValuePairs) {
+ AMediaFormat_setDouble(fmt, it.second, it.first + offset);
+ }
+}
+
+void NativeMediaFormatUnitTest::configureFormatSize(AMediaFormat* fmt, size_t offset) {
+ for (auto it : mSizeKeyValuePairs) {
+ AMediaFormat_setSize(fmt, it.second, it.first + offset);
+ }
+}
+
+void NativeMediaFormatUnitTest::configureFormatString(AMediaFormat* fmt, int offset) {
+ for (auto it : mStringKeyValuePairs) {
+ std::string s1 = it.first + std::to_string(offset);
+ AMediaFormat_setString(fmt, it.second, s1.c_str());
+ }
+}
+
+void NativeMediaFormatUnitTest::configureFormatRect(AMediaFormat* fmt, int offset) {
+ for (auto it : mWindowKeyValuePairs) {
+ AMediaFormat_setRect(fmt, it.second, it.first->left + offset, it.first->top + offset,
+ it.first->right + offset, it.first->bottom + offset);
+ }
+}
+
+void NativeMediaFormatUnitTest::configureFormatBuffer(AMediaFormat* fmt, int offset) {
+ for (auto it : mBufferKeyValuePairs) {
+ int sz = offset == 0 ? it.first->size : it.first->size / 2;
+ AMediaFormat_setBuffer(fmt, it.second, it.first->buffer + it.first->size - sz, sz);
+ }
+}
+
+void NativeMediaFormatUnitTest::configureFormat(AMediaFormat* fmt, int offset) {
+ configureFormatInt32(fmt, offset);
+ configureFormatInt64(fmt, offset);
+ configureFormatFloat(fmt, offset);
+ configureFormatDouble(fmt, offset);
+ configureFormatSize(fmt, offset);
+ configureFormatString(fmt, offset);
+ configureFormatRect(fmt, offset);
+ configureFormatBuffer(fmt, offset);
+}
+
+// 1. configure format with default values and validate the same
+// 2. copy configured format to an empty format and validate the copied format
+// 3. overwrite copied format with default + offset values and validate the updated format
+// 4. overwrite updated format with default values using AMediaFormat_copy API and validate the same
+// 5. clear mediaformat and validate if keys are not present
+static bool testMediaFormatAllNative() {
+ auto* nmf = new NativeMediaFormatUnitTest();
+ AMediaFormat* fmtOrig = AMediaFormat_new();
+ AMediaFormat* fmtDup = AMediaFormat_new();
+ const int offset = 123;
+
+ nmf->configureFormat(fmtOrig);
+ bool status = nmf->validateFormat(fmtOrig);
+
+ AMediaFormat_copy(fmtDup, fmtOrig);
+ status &= nmf->validateFormat(fmtDup);
+
+ nmf->configureFormat(fmtDup, offset);
+ status &= nmf->validateFormat(fmtDup, offset);
+
+ AMediaFormat_copy(fmtDup, fmtOrig);
+ status &= nmf->validateFormat(fmtDup);
+
+ AMediaFormat_clear(fmtDup);
+ status &= nmf->validateFormat(fmtDup, offset, true);
+
+ AMediaFormat_delete(fmtOrig);
+ AMediaFormat_delete(fmtDup);
+ delete nmf;
+
+ return status;
+}
+
+// 1. configure format with default values and validate the same
+// 2. copy configured format to an empty format and validate the copied format
+// 3. overwrite copied format with default + offset values and validate the updated format
+// 4. overwrite updated format with default values using AMediaFormat_copy API and validate the same
+#define testMediaFormatfuncNative(func) \
+ static bool testMediaFormat##func##Native() { \
+ auto* nmf = new NativeMediaFormatUnitTest(); \
+ AMediaFormat* fmtOrig = AMediaFormat_new(); \
+ AMediaFormat* fmtDup = AMediaFormat_new(); \
+ const int offset = 12345; \
+ \
+ nmf->configureFormat##func(fmtOrig); \
+ bool status = nmf->validateFormat##func(fmtOrig); \
+ \
+ AMediaFormat_copy(fmtDup, fmtOrig); \
+ status &= nmf->validateFormat##func(fmtDup); \
+ \
+ nmf->configureFormat##func(fmtDup, offset); \
+ status &= nmf->validateFormat##func(fmtDup, offset); \
+ \
+ AMediaFormat_copy(fmtDup, fmtOrig); \
+ status &= nmf->validateFormat##func(fmtDup); \
+ \
+ AMediaFormat_clear(fmtDup); \
+ status &= nmf->validateFormat##func(fmtDup, offset, true); \
+ AMediaFormat_delete(fmtOrig); \
+ AMediaFormat_delete(fmtDup); \
+ delete nmf; \
+ return status; \
+ }
+
+testMediaFormatfuncNative(Int32)
+
+testMediaFormatfuncNative(Int64)
+
+testMediaFormatfuncNative(Float)
+
+testMediaFormatfuncNative(Double)
+
+testMediaFormatfuncNative(Size)
+
+testMediaFormatfuncNative(String)
+
+testMediaFormatfuncNative(Rect)
+
+testMediaFormatfuncNative(Buffer)
+
+#define nativeTestMediaFormatfunc(func) \
+ static jboolean nativeTestMediaFormat##func(JNIEnv*, jobject) { \
+ return static_cast<jboolean>(testMediaFormat##func##Native()); \
+ }
+
+nativeTestMediaFormatfunc(Int32)
+
+nativeTestMediaFormatfunc(Int64)
+
+nativeTestMediaFormatfunc(Float)
+
+nativeTestMediaFormatfunc(Double)
+
+nativeTestMediaFormatfunc(Size)
+
+nativeTestMediaFormatfunc(String)
+
+nativeTestMediaFormatfunc(Rect)
+
+nativeTestMediaFormatfunc(Buffer)
+
+nativeTestMediaFormatfunc(All)
+
+int registerAndroidMediaV2CtsMediaFormatUnitTest(JNIEnv* env) {
+ const JNINativeMethod methodTable[] = {
+ {"nativeTestMediaFormatInt32", "()Z", (void*)nativeTestMediaFormatInt32},
+ {"nativeTestMediaFormatInt64", "()Z", (void*)nativeTestMediaFormatInt64},
+ {"nativeTestMediaFormatFloat", "()Z", (void*)nativeTestMediaFormatFloat},
+ {"nativeTestMediaFormatDouble", "()Z", (void*)nativeTestMediaFormatDouble},
+ {"nativeTestMediaFormatSize", "()Z", (void*)nativeTestMediaFormatSize},
+ {"nativeTestMediaFormatString", "()Z", (void*)nativeTestMediaFormatString},
+ {"nativeTestMediaFormatRect", "()Z", (void*)nativeTestMediaFormatRect},
+ {"nativeTestMediaFormatBuffer", "()Z", (void*)nativeTestMediaFormatBuffer},
+ {"nativeTestMediaFormatAll", "()Z", (void*)nativeTestMediaFormatAll},
+ };
+ jclass c = env->FindClass("android/mediav2/cts/MediaFormatUnitTest");
+ return env->RegisterNatives(c, methodTable, sizeof(methodTable) / sizeof(JNINativeMethod));
+}
+
+extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void*) {
+ JNIEnv* env;
+ if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) return JNI_ERR;
+ if (registerAndroidMediaV2CtsMediaFormatUnitTest(env) != JNI_OK) return JNI_ERR;
+ return JNI_VERSION_1_6;
+}
diff --git a/tests/media/src/android/mediav2/cts/MediaFormatUnitTest.java b/tests/media/src/android/mediav2/cts/MediaFormatUnitTest.java
new file mode 100644
index 0000000..6047f30
--- /dev/null
+++ b/tests/media/src/android/mediav2/cts/MediaFormatUnitTest.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2021 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.mediav2.cts;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.Timeout;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.TimeUnit;
+
+import static org.junit.Assert.assertTrue;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class MediaFormatUnitTest {
+ static final int PER_TEST_TIMEOUT_MS = 10000;
+
+ static {
+ System.loadLibrary("ctsmediav2utils_jni");
+ }
+
+ @Rule
+ public Timeout timeout = new Timeout(PER_TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+
+ @Test
+ public void testMediaFormatNativeInt32() {
+ assertTrue(nativeTestMediaFormatInt32());
+ }
+
+ private native boolean nativeTestMediaFormatInt32();
+
+ @Test
+ public void testMediaFormatNativeInt64() {
+ assertTrue(nativeTestMediaFormatInt64());
+ }
+
+ private native boolean nativeTestMediaFormatInt64();
+
+ @Test
+ public void testMediaFormatNativeFloat() {
+ assertTrue(nativeTestMediaFormatFloat());
+ }
+
+ private native boolean nativeTestMediaFormatFloat();
+
+ @Test
+ public void testMediaFormatNativeDouble() {
+ assertTrue(nativeTestMediaFormatDouble());
+ }
+
+ private native boolean nativeTestMediaFormatDouble();
+
+ @Test
+ public void testMediaFormatNativeSize() {
+ assertTrue(nativeTestMediaFormatSize());
+ }
+
+ private native boolean nativeTestMediaFormatSize();
+
+ @Test
+ public void testMediaFormatNativeString() {
+ assertTrue(nativeTestMediaFormatString());
+ }
+
+ private native boolean nativeTestMediaFormatString();
+
+ @Test
+ public void testMediaFormatNativeRect() {
+ assertTrue(nativeTestMediaFormatRect());
+ }
+
+ private native boolean nativeTestMediaFormatRect();
+
+ @Test
+ public void testMediaFormatNativeBuffer() {
+ assertTrue(nativeTestMediaFormatBuffer());
+ }
+
+ private native boolean nativeTestMediaFormatBuffer();
+
+ @Test
+ public void testMediaFormatNativeAll() {
+ assertTrue(nativeTestMediaFormatAll());
+ }
+
+ private native boolean nativeTestMediaFormatAll();
+}