| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "base/android/scoped_java_ref.h" |
| |
| #include "base/android/jni_android.h" |
| #include "base/android/jni_string.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace base { |
| namespace android { |
| |
| namespace { |
| int g_local_refs = 0; |
| int g_global_refs = 0; |
| |
| const JNINativeInterface* g_previous_functions; |
| |
| jobject NewGlobalRef(JNIEnv* env, jobject obj) { |
| ++g_global_refs; |
| return g_previous_functions->NewGlobalRef(env, obj); |
| } |
| |
| void DeleteGlobalRef(JNIEnv* env, jobject obj) { |
| --g_global_refs; |
| return g_previous_functions->DeleteGlobalRef(env, obj); |
| } |
| |
| jobject NewLocalRef(JNIEnv* env, jobject obj) { |
| ++g_local_refs; |
| return g_previous_functions->NewLocalRef(env, obj); |
| } |
| |
| void DeleteLocalRef(JNIEnv* env, jobject obj) { |
| --g_local_refs; |
| return g_previous_functions->DeleteLocalRef(env, obj); |
| } |
| } // namespace |
| |
| class ScopedJavaRefTest : public testing::Test { |
| protected: |
| virtual void SetUp() { |
| g_local_refs = 0; |
| g_global_refs = 0; |
| JNIEnv* env = AttachCurrentThread(); |
| g_previous_functions = env->functions; |
| hooked_functions = *g_previous_functions; |
| env->functions = &hooked_functions; |
| // We inject our own functions in JNINativeInterface so we can keep track |
| // of the reference counting ourselves. |
| hooked_functions.NewGlobalRef = &NewGlobalRef; |
| hooked_functions.DeleteGlobalRef = &DeleteGlobalRef; |
| hooked_functions.NewLocalRef = &NewLocalRef; |
| hooked_functions.DeleteLocalRef = &DeleteLocalRef; |
| } |
| |
| virtual void TearDown() { |
| JNIEnv* env = AttachCurrentThread(); |
| env->functions = g_previous_functions; |
| } |
| // From JellyBean release, the instance of this struct provided in JNIEnv is |
| // read-only, so we deep copy it to allow individual functions to be hooked. |
| JNINativeInterface hooked_functions; |
| }; |
| |
| // The main purpose of this is testing the various conversions compile. |
| TEST_F(ScopedJavaRefTest, Conversions) { |
| JNIEnv* env = AttachCurrentThread(); |
| ScopedJavaLocalRef<jstring> str = ConvertUTF8ToJavaString(env, "string"); |
| ScopedJavaGlobalRef<jstring> global(str); |
| { |
| ScopedJavaGlobalRef<jobject> global_obj(str); |
| ScopedJavaLocalRef<jobject> local_obj(global); |
| const JavaRef<jobject>& obj_ref1(str); |
| const JavaRef<jobject>& obj_ref2(global); |
| EXPECT_TRUE(env->IsSameObject(obj_ref1.obj(), obj_ref2.obj())); |
| EXPECT_TRUE(env->IsSameObject(global_obj.obj(), obj_ref2.obj())); |
| } |
| global.Reset(str); |
| const JavaRef<jstring>& str_ref = str; |
| EXPECT_EQ("string", ConvertJavaStringToUTF8(str_ref)); |
| str.Reset(); |
| } |
| |
| TEST_F(ScopedJavaRefTest, RefCounts) { |
| JNIEnv* env = AttachCurrentThread(); |
| ScopedJavaLocalRef<jstring> str; |
| // The ConvertJavaStringToUTF8 below creates a new string that would normally |
| // return a local ref. We simulate that by starting the g_local_refs count at |
| // 1. |
| g_local_refs = 1; |
| str.Reset(ConvertUTF8ToJavaString(env, "string")); |
| EXPECT_EQ(1, g_local_refs); |
| EXPECT_EQ(0, g_global_refs); |
| { |
| ScopedJavaGlobalRef<jstring> global_str(str); |
| ScopedJavaGlobalRef<jobject> global_obj(global_str); |
| EXPECT_EQ(1, g_local_refs); |
| EXPECT_EQ(2, g_global_refs); |
| |
| ScopedJavaLocalRef<jstring> str2(env, str.Release()); |
| EXPECT_EQ(1, g_local_refs); |
| { |
| ScopedJavaLocalRef<jstring> str3(str2); |
| EXPECT_EQ(2, g_local_refs); |
| } |
| EXPECT_EQ(1, g_local_refs); |
| str2.Reset(); |
| EXPECT_EQ(0, g_local_refs); |
| global_str.Reset(); |
| EXPECT_EQ(1, g_global_refs); |
| ScopedJavaGlobalRef<jobject> global_obj2(global_obj); |
| EXPECT_EQ(2, g_global_refs); |
| } |
| |
| EXPECT_EQ(0, g_local_refs); |
| EXPECT_EQ(0, g_global_refs); |
| } |
| |
| } // namespace android |
| } // namespace base |