blob: d46c5ac7df91049b97772e633d83c259ce1da55d [file] [log] [blame]
// 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"
#define EXPECT_SAME_OBJECT(a, b) \
EXPECT_TRUE(env->IsSameObject(a.obj(), b.obj()))
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:
void SetUp() override {
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;
}
void TearDown() override {
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);
// Contextual conversions to bool should be allowed.
EXPECT_TRUE(str);
EXPECT_FALSE(JavaRef<jobject>());
// All the types should convert from nullptr, even JavaRef.
{
JavaRef<jstring> null_ref(nullptr);
EXPECT_FALSE(null_ref);
ScopedJavaLocalRef<jobject> null_local(nullptr);
EXPECT_FALSE(null_local);
ScopedJavaGlobalRef<jarray> null_global(nullptr);
EXPECT_FALSE(null_global);
}
// Local and global refs should {copy,move}-{construct,assign}.
// Moves should leave the source as null.
{
ScopedJavaLocalRef<jstring> str2(str);
EXPECT_SAME_OBJECT(str2, str);
ScopedJavaLocalRef<jstring> str3(std::move(str2));
EXPECT_SAME_OBJECT(str3, str);
EXPECT_FALSE(str2);
ScopedJavaLocalRef<jstring> str4;
str4 = str;
EXPECT_SAME_OBJECT(str4, str);
ScopedJavaLocalRef<jstring> str5;
str5 = std::move(str4);
EXPECT_SAME_OBJECT(str5, str);
EXPECT_FALSE(str4);
}
{
ScopedJavaGlobalRef<jstring> str2(global);
EXPECT_SAME_OBJECT(str2, str);
ScopedJavaGlobalRef<jstring> str3(std::move(str2));
EXPECT_SAME_OBJECT(str3, str);
EXPECT_FALSE(str2);
ScopedJavaGlobalRef<jstring> str4;
str4 = global;
EXPECT_SAME_OBJECT(str4, str);
ScopedJavaGlobalRef<jstring> str5;
str5 = std::move(str4);
EXPECT_SAME_OBJECT(str5, str);
EXPECT_FALSE(str4);
}
// As above but going from jstring to jobject.
{
ScopedJavaLocalRef<jobject> obj2(str);
EXPECT_SAME_OBJECT(obj2, str);
ScopedJavaLocalRef<jobject> obj3(std::move(obj2));
EXPECT_SAME_OBJECT(obj3, str);
EXPECT_FALSE(obj2);
ScopedJavaLocalRef<jobject> obj4;
obj4 = str;
EXPECT_SAME_OBJECT(obj4, str);
ScopedJavaLocalRef<jobject> obj5;
obj5 = std::move(obj4);
EXPECT_SAME_OBJECT(obj5, str);
EXPECT_FALSE(obj4);
}
{
ScopedJavaGlobalRef<jobject> obj2(global);
EXPECT_SAME_OBJECT(obj2, str);
ScopedJavaGlobalRef<jobject> obj3(std::move(obj2));
EXPECT_SAME_OBJECT(obj3, str);
EXPECT_FALSE(obj2);
ScopedJavaGlobalRef<jobject> obj4;
obj4 = global;
EXPECT_SAME_OBJECT(obj4, str);
ScopedJavaGlobalRef<jobject> obj5;
obj5 = std::move(obj4);
EXPECT_SAME_OBJECT(obj5, str);
EXPECT_FALSE(obj4);
}
// Explicit copy construction or assignment between global<->local is allowed,
// but not implicit conversions.
{
ScopedJavaLocalRef<jstring> new_local(global);
EXPECT_SAME_OBJECT(new_local, str);
new_local = global;
EXPECT_SAME_OBJECT(new_local, str);
ScopedJavaGlobalRef<jstring> new_global(str);
EXPECT_SAME_OBJECT(new_global, str);
new_global = str;
EXPECT_SAME_OBJECT(new_local, str);
static_assert(!std::is_convertible<ScopedJavaLocalRef<jobject>,
ScopedJavaGlobalRef<jobject>>::value,
"");
static_assert(!std::is_convertible<ScopedJavaGlobalRef<jobject>,
ScopedJavaLocalRef<jobject>>::value,
"");
}
// Converting between local/global while also converting to jobject also works
// because JavaRef<jobject> is the base class.
{
ScopedJavaGlobalRef<jobject> global_obj(str);
ScopedJavaLocalRef<jobject> local_obj(global);
const JavaRef<jobject>& obj_ref1(str);
const JavaRef<jobject>& obj_ref2(global);
EXPECT_SAME_OBJECT(obj_ref1, obj_ref2);
EXPECT_SAME_OBJECT(global_obj, obj_ref2);
}
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);
auto str2 = ScopedJavaLocalRef<jstring>::Adopt(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);
{
ScopedJavaLocalRef<jstring> str4((ScopedJavaLocalRef<jstring>(str2)));
EXPECT_EQ(2, g_local_refs);
}
EXPECT_EQ(1, g_local_refs);
{
ScopedJavaLocalRef<jstring> str5;
str5 = ScopedJavaLocalRef<jstring>(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