1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "base/android/scoped_java_ref.h"
6
7#include "base/android/jni_android.h"
8#include "base/android/jni_string.h"
9#include "testing/gtest/include/gtest/gtest.h"
10
11namespace base {
12namespace android {
13
14namespace {
15int g_local_refs = 0;
16int g_global_refs = 0;
17
18const JNINativeInterface* g_previous_functions;
19
20jobject NewGlobalRef(JNIEnv* env, jobject obj) {
21  ++g_global_refs;
22  return g_previous_functions->NewGlobalRef(env, obj);
23}
24
25void DeleteGlobalRef(JNIEnv* env, jobject obj) {
26  --g_global_refs;
27  return g_previous_functions->DeleteGlobalRef(env, obj);
28}
29
30jobject NewLocalRef(JNIEnv* env, jobject obj) {
31  ++g_local_refs;
32  return g_previous_functions->NewLocalRef(env, obj);
33}
34
35void DeleteLocalRef(JNIEnv* env, jobject obj) {
36  --g_local_refs;
37  return g_previous_functions->DeleteLocalRef(env, obj);
38}
39}  // namespace
40
41class ScopedJavaRefTest : public testing::Test {
42 protected:
43  virtual void SetUp() {
44    g_local_refs = 0;
45    g_global_refs = 0;
46    JNIEnv* env = AttachCurrentThread();
47    g_previous_functions = env->functions;
48    hooked_functions = *g_previous_functions;
49    env->functions = &hooked_functions;
50    // We inject our own functions in JNINativeInterface so we can keep track
51    // of the reference counting ourselves.
52    hooked_functions.NewGlobalRef = &NewGlobalRef;
53    hooked_functions.DeleteGlobalRef = &DeleteGlobalRef;
54    hooked_functions.NewLocalRef = &NewLocalRef;
55    hooked_functions.DeleteLocalRef = &DeleteLocalRef;
56  }
57
58  virtual void TearDown() {
59    JNIEnv* env = AttachCurrentThread();
60    env->functions = g_previous_functions;
61  }
62  // From JellyBean release, the instance of this struct provided in JNIEnv is
63  // read-only, so we deep copy it to allow individual functions to be hooked.
64  JNINativeInterface hooked_functions;
65};
66
67// The main purpose of this is testing the various conversions compile.
68TEST_F(ScopedJavaRefTest, Conversions) {
69  JNIEnv* env = AttachCurrentThread();
70  ScopedJavaLocalRef<jstring> str = ConvertUTF8ToJavaString(env, "string");
71  ScopedJavaGlobalRef<jstring> global(str);
72  {
73    ScopedJavaGlobalRef<jobject> global_obj(str);
74    ScopedJavaLocalRef<jobject> local_obj(global);
75    const JavaRef<jobject>& obj_ref1(str);
76    const JavaRef<jobject>& obj_ref2(global);
77    EXPECT_TRUE(env->IsSameObject(obj_ref1.obj(), obj_ref2.obj()));
78    EXPECT_TRUE(env->IsSameObject(global_obj.obj(), obj_ref2.obj()));
79  }
80  global.Reset(str);
81  const JavaRef<jstring>& str_ref = str;
82  EXPECT_EQ("string", ConvertJavaStringToUTF8(str_ref));
83  str.Reset();
84}
85
86TEST_F(ScopedJavaRefTest, RefCounts) {
87  JNIEnv* env = AttachCurrentThread();
88  ScopedJavaLocalRef<jstring> str;
89  // The ConvertJavaStringToUTF8 below creates a new string that would normally
90  // return a local ref. We simulate that by starting the g_local_refs count at
91  // 1.
92  g_local_refs = 1;
93  str.Reset(ConvertUTF8ToJavaString(env, "string"));
94  EXPECT_EQ(1, g_local_refs);
95  EXPECT_EQ(0, g_global_refs);
96  {
97    ScopedJavaGlobalRef<jstring> global_str(str);
98    ScopedJavaGlobalRef<jobject> global_obj(global_str);
99    EXPECT_EQ(1, g_local_refs);
100    EXPECT_EQ(2, g_global_refs);
101
102    ScopedJavaLocalRef<jstring> str2(env, str.Release());
103    EXPECT_EQ(1, g_local_refs);
104    {
105      ScopedJavaLocalRef<jstring> str3(str2);
106      EXPECT_EQ(2, g_local_refs);
107    }
108    EXPECT_EQ(1, g_local_refs);
109    str2.Reset();
110    EXPECT_EQ(0, g_local_refs);
111    global_str.Reset();
112    EXPECT_EQ(1, g_global_refs);
113    ScopedJavaGlobalRef<jobject> global_obj2(global_obj);
114    EXPECT_EQ(2, g_global_refs);
115  }
116
117  EXPECT_EQ(0, g_local_refs);
118  EXPECT_EQ(0, g_global_refs);
119}
120
121}  // namespace android
122}  // namespace base
123