1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "base/android/scoped_java_ref.h"
10 #include "base/android/jni_android.h"
11 #include "base/android/jni_string.h"
12 #include "testing/gtest/include/gtest/gtest.h"
14 #define EXPECT_SAME_OBJECT(a, b) \
15 EXPECT_TRUE(env->IsSameObject((a).obj(), (b).obj()))
22 int g_global_refs = 0;
24 const JNINativeInterface* g_previous_functions;
26 jobject NewGlobalRef(JNIEnv* env, jobject obj) {
28 return g_previous_functions->NewGlobalRef(env, obj);
31 void DeleteGlobalRef(JNIEnv* env, jobject obj) {
33 return g_previous_functions->DeleteGlobalRef(env, obj);
36 jobject NewLocalRef(JNIEnv* env, jobject obj) {
38 return g_previous_functions->NewLocalRef(env, obj);
41 void DeleteLocalRef(JNIEnv* env, jobject obj) {
43 return g_previous_functions->DeleteLocalRef(env, obj);
47 class ScopedJavaRefTest : public testing::Test {
49 void SetUp() override {
52 JNIEnv* env = AttachCurrentThread();
53 g_previous_functions = env->functions;
54 hooked_functions = *g_previous_functions;
55 env->functions = &hooked_functions;
56 // We inject our own functions in JNINativeInterface so we can keep track
57 // of the reference counting ourselves.
58 hooked_functions.NewGlobalRef = &NewGlobalRef;
59 hooked_functions.DeleteGlobalRef = &DeleteGlobalRef;
60 hooked_functions.NewLocalRef = &NewLocalRef;
61 hooked_functions.DeleteLocalRef = &DeleteLocalRef;
64 void TearDown() override {
65 JNIEnv* env = AttachCurrentThread();
66 env->functions = g_previous_functions;
68 // From JellyBean release, the instance of this struct provided in JNIEnv is
69 // read-only, so we deep copy it to allow individual functions to be hooked.
70 JNINativeInterface hooked_functions;
73 // The main purpose of this is testing the various conversions compile.
74 TEST_F(ScopedJavaRefTest, Conversions) {
75 JNIEnv* env = AttachCurrentThread();
76 ScopedJavaLocalRef<jstring> str = ConvertUTF8ToJavaString(env, "string");
77 ScopedJavaGlobalRef<jstring> global(str);
79 // Contextual conversions to bool should be allowed.
81 EXPECT_FALSE(JavaRef<jobject>());
83 // All the types should convert from nullptr, even JavaRef.
85 JavaRef<jstring> null_ref(nullptr);
86 EXPECT_FALSE(null_ref);
87 ScopedJavaLocalRef<jobject> null_local(nullptr);
88 EXPECT_FALSE(null_local);
89 ScopedJavaGlobalRef<jarray> null_global(nullptr);
90 EXPECT_FALSE(null_global);
93 // Local and global refs should {copy,move}-{construct,assign}.
94 // Moves should leave the source as null.
96 ScopedJavaLocalRef<jstring> str2(str);
97 EXPECT_SAME_OBJECT(str2, str);
98 ScopedJavaLocalRef<jstring> str3(std::move(str2));
99 EXPECT_SAME_OBJECT(str3, str);
101 ScopedJavaLocalRef<jstring> str4;
103 EXPECT_SAME_OBJECT(str4, str);
104 ScopedJavaLocalRef<jstring> str5;
105 str5 = std::move(str4);
106 EXPECT_SAME_OBJECT(str5, str);
110 ScopedJavaGlobalRef<jstring> str2(global);
111 EXPECT_SAME_OBJECT(str2, str);
112 ScopedJavaGlobalRef<jstring> str3(std::move(str2));
113 EXPECT_SAME_OBJECT(str3, str);
115 ScopedJavaGlobalRef<jstring> str4;
117 EXPECT_SAME_OBJECT(str4, str);
118 ScopedJavaGlobalRef<jstring> str5;
119 str5 = std::move(str4);
120 EXPECT_SAME_OBJECT(str5, str);
124 // As above but going from jstring to jobject.
126 ScopedJavaLocalRef<jobject> obj2(str);
127 EXPECT_SAME_OBJECT(obj2, str);
128 ScopedJavaLocalRef<jobject> obj3(std::move(obj2));
129 EXPECT_SAME_OBJECT(obj3, str);
131 ScopedJavaLocalRef<jobject> obj4;
133 EXPECT_SAME_OBJECT(obj4, str);
134 ScopedJavaLocalRef<jobject> obj5;
135 obj5 = std::move(obj4);
136 EXPECT_SAME_OBJECT(obj5, str);
140 ScopedJavaGlobalRef<jobject> obj2(global);
141 EXPECT_SAME_OBJECT(obj2, str);
142 ScopedJavaGlobalRef<jobject> obj3(std::move(obj2));
143 EXPECT_SAME_OBJECT(obj3, str);
145 ScopedJavaGlobalRef<jobject> obj4;
147 EXPECT_SAME_OBJECT(obj4, str);
148 ScopedJavaGlobalRef<jobject> obj5;
149 obj5 = std::move(obj4);
150 EXPECT_SAME_OBJECT(obj5, str);
154 // Explicit copy construction or assignment between global<->local is allowed,
155 // but not implicit conversions.
157 ScopedJavaLocalRef<jstring> new_local(global);
158 EXPECT_SAME_OBJECT(new_local, str);
160 EXPECT_SAME_OBJECT(new_local, str);
161 ScopedJavaGlobalRef<jstring> new_global(str);
162 EXPECT_SAME_OBJECT(new_global, str);
164 EXPECT_SAME_OBJECT(new_local, str);
165 static_assert(!std::is_convertible_v<ScopedJavaLocalRef<jobject>,
166 ScopedJavaGlobalRef<jobject>>,
168 static_assert(!std::is_convertible_v<ScopedJavaGlobalRef<jobject>,
169 ScopedJavaLocalRef<jobject>>,
173 // Converting between local/global while also converting to jobject also works
174 // because JavaRef<jobject> is the base class.
176 ScopedJavaGlobalRef<jobject> global_obj(str);
177 ScopedJavaLocalRef<jobject> local_obj(global);
178 const JavaRef<jobject>& obj_ref1(str);
179 const JavaRef<jobject>& obj_ref2(global);
180 EXPECT_SAME_OBJECT(obj_ref1, obj_ref2);
181 EXPECT_SAME_OBJECT(global_obj, obj_ref2);
184 const JavaRef<jstring>& str_ref = str;
185 EXPECT_EQ("string", ConvertJavaStringToUTF8(str_ref));
189 TEST_F(ScopedJavaRefTest, RefCounts) {
190 JNIEnv* env = AttachCurrentThread();
191 ScopedJavaLocalRef<jstring> str;
192 // The ConvertJavaStringToUTF8 below creates a new string that would normally
193 // return a local ref. We simulate that by starting the g_local_refs count at
196 str.Reset(ConvertUTF8ToJavaString(env, "string"));
197 EXPECT_EQ(1, g_local_refs);
198 EXPECT_EQ(0, g_global_refs);
200 ScopedJavaGlobalRef<jstring> global_str(str);
201 ScopedJavaGlobalRef<jobject> global_obj(global_str);
202 EXPECT_EQ(1, g_local_refs);
203 EXPECT_EQ(2, g_global_refs);
205 auto str2 = ScopedJavaLocalRef<jstring>::Adopt(env, str.Release());
206 EXPECT_EQ(1, g_local_refs);
208 ScopedJavaLocalRef<jstring> str3(str2);
209 EXPECT_EQ(2, g_local_refs);
211 EXPECT_EQ(1, g_local_refs);
213 ScopedJavaLocalRef<jstring> str4((ScopedJavaLocalRef<jstring>(str2)));
214 EXPECT_EQ(2, g_local_refs);
216 EXPECT_EQ(1, g_local_refs);
218 ScopedJavaLocalRef<jstring> str5;
219 str5 = ScopedJavaLocalRef<jstring>(str2);
220 EXPECT_EQ(2, g_local_refs);
222 EXPECT_EQ(1, g_local_refs);
224 EXPECT_EQ(0, g_local_refs);
226 EXPECT_EQ(1, g_global_refs);
227 ScopedJavaGlobalRef<jobject> global_obj2(global_obj);
228 EXPECT_EQ(2, g_global_refs);
231 EXPECT_EQ(0, g_local_refs);
232 EXPECT_EQ(0, g_global_refs);
235 class JavaObjectArrayReaderTest : public testing::Test {
237 void SetUp() override {
238 JNIEnv* env = AttachCurrentThread();
239 int_class_ = GetClass(env, "java/lang/Integer");
240 int_constructor_ = MethodID::Get<MethodID::TYPE_INSTANCE>(
241 env, int_class_.obj(), "<init>", "(I)V");
242 array_ = MakeArray(array_len_);
244 // Make array_len_ different Integer objects, keep a reference to each,
245 // and add them to the array.
246 for (jint i = 0; i < array_len_; ++i) {
247 jobject member = env->NewObject(int_class_.obj(), int_constructor_, i);
248 ASSERT_NE(member, nullptr);
249 array_members_[i] = ScopedJavaLocalRef<jobject>::Adopt(env, member);
250 env->SetObjectArrayElement(array_.obj(), i, member);
254 // Make an Integer[] with len elements, all initialized to null.
255 ScopedJavaLocalRef<jobjectArray> MakeArray(jsize len) {
256 JNIEnv* env = AttachCurrentThread();
257 jobjectArray array = env->NewObjectArray(len, int_class_.obj(), nullptr);
258 EXPECT_NE(array, nullptr);
259 return ScopedJavaLocalRef<jobjectArray>::Adopt(env, array);
262 static constexpr jsize array_len_ = 10;
263 ScopedJavaLocalRef<jclass> int_class_;
264 jmethodID int_constructor_;
265 ScopedJavaLocalRef<jobject> array_members_[array_len_];
266 ScopedJavaLocalRef<jobjectArray> array_;
269 // Must actually define the variable until C++17 :(
270 constexpr jsize JavaObjectArrayReaderTest::array_len_;
272 TEST_F(JavaObjectArrayReaderTest, ZeroLengthArray) {
273 JavaObjectArrayReader<jobject> zero_length(MakeArray(0));
274 EXPECT_TRUE(zero_length.empty());
275 EXPECT_EQ(zero_length.size(), 0);
276 EXPECT_EQ(zero_length.begin(), zero_length.end());
279 // Verify that we satisfy the C++ "InputIterator" named requirements.
280 TEST_F(JavaObjectArrayReaderTest, InputIteratorRequirements) {
281 typedef JavaObjectArrayReader<jobject>::iterator It;
283 JNIEnv* env = AttachCurrentThread();
284 JavaObjectArrayReader<jobject> reader(array_);
285 It i = reader.begin();
287 EXPECT_TRUE(std::is_copy_constructible_v<It>);
292 EXPECT_TRUE(std::is_copy_assignable_v<It>);
293 It assign = reader.end();
294 It& assign2 = (assign = i);
295 EXPECT_EQ(assign, i);
296 EXPECT_EQ(assign2, assign);
298 EXPECT_TRUE(std::is_destructible_v<It>);
301 It left = reader.begin(), right = reader.end();
302 std::swap(left, right);
303 EXPECT_EQ(left, reader.end());
304 EXPECT_EQ(right, reader.begin());
306 // Basic check that iterator_traits works
307 bool same_type = std::is_same_v<std::iterator_traits<It>::iterator_category,
308 std::input_iterator_tag>;
309 EXPECT_TRUE(same_type);
312 EXPECT_EQ(reader.begin(), reader.begin());
313 EXPECT_NE(reader.begin(), reader.end());
316 ScopedJavaLocalRef<jobject> o = *(reader.begin());
317 EXPECT_SAME_OBJECT(o, array_members_[0]);
318 EXPECT_TRUE(env->IsSameObject(o.obj(), reader.begin()->obj()));
321 It preinc = ++(reader.begin());
322 EXPECT_SAME_OBJECT(*preinc, array_members_[1]);
323 It postinc = reader.begin();
324 EXPECT_SAME_OBJECT(*postinc++, array_members_[0]);
325 EXPECT_SAME_OBJECT(*postinc, array_members_[1]);
328 // Check that range-based for and the convenience function work as expected.
329 TEST_F(JavaObjectArrayReaderTest, RangeBasedFor) {
330 JNIEnv* env = AttachCurrentThread();
333 for (ScopedJavaLocalRef<jobject> element : array_.ReadElements<jobject>()) {
334 EXPECT_SAME_OBJECT(element, array_members_[i++]);
336 EXPECT_EQ(i, array_len_);
339 } // namespace android