summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authortommi@chromium.org <tommi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-03-19 04:22:28 +0000
committertommi@chromium.org <tommi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-03-19 04:22:28 +0000
commite00d39192ccc3713059caef9bdb7cf74d902f7df (patch)
tree946a5d20463ca2614f4e3d778e31940f32ce609f
parent6c56c9962a3eae73f46297c7c220264d23a5004d (diff)
downloadchromium_src-e00d39192ccc3713059caef9bdb7cf74d902f7df.zip
chromium_src-e00d39192ccc3713059caef9bdb7cf74d902f7df.tar.gz
chromium_src-e00d39192ccc3713059caef9bdb7cf74d902f7df.tar.bz2
ScopedVariant implementation.
A class for automatically freeing a COM VARIANT at the end of a scope. Additionally provides a few functions to make the encapsulated VARIANT easier to use. Instead of inheriting from VARIANT, I took the containment approach in order to have more control over the usage of the variant and guard against memory leaks. Review URL: http://codereview.chromium.org/46059 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@12081 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--base/base.gyp4
-rw-r--r--base/build/base.vcproj8
-rw-r--r--base/scoped_variant_win.cc230
-rw-r--r--base/scoped_variant_win.h136
-rw-r--r--base/scoped_variant_win_unittest.cc215
5 files changed, 593 insertions, 0 deletions
diff --git a/base/base.gyp b/base/base.gyp
index e805b27..6da4c2e 100644
--- a/base/base.gyp
+++ b/base/base.gyp
@@ -203,6 +203,8 @@
'scoped_ptr.h',
'scoped_temp_dir.cc',
'scoped_temp_dir.h',
+ 'scoped_variant_win.cc',
+ 'scoped_variant_win.h',
'sha2.cc',
'sha2.h',
'shared_memory.h',
@@ -488,6 +490,7 @@
'scoped_comptr_win_unittest.cc',
'scoped_ptr_unittest.cc',
'scoped_temp_dir_unittest.cc',
+ 'scoped_variant_win_unittest.cc',
'sha2_unittest.cc',
'shared_memory_unittest.cc',
'simple_thread_unittest.cc',
@@ -562,6 +565,7 @@
'pe_image_unittest.cc',
'scoped_bstr_win_unittest.cc',
'scoped_comptr_win_unittest.cc',
+ 'scoped_variant_win_unittest.cc',
'system_monitor_unittest.cc',
'time_win_unittest.cc',
'win_util_unittest.cc',
diff --git a/base/build/base.vcproj b/base/build/base.vcproj
index cecff6e..749e3bc 100644
--- a/base/build/base.vcproj
+++ b/base/build/base.vcproj
@@ -738,6 +738,14 @@
>
</File>
<File
+ RelativePath="..\scoped_variant_win.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\scoped_variant_win.h"
+ >
+ </File>
+ <File
RelativePath="..\scoped_temp_dir.cc"
>
</File>
diff --git a/base/scoped_variant_win.cc b/base/scoped_variant_win.cc
new file mode 100644
index 0000000..cc66c12
--- /dev/null
+++ b/base/scoped_variant_win.cc
@@ -0,0 +1,230 @@
+// Copyright (c) 2009 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/scoped_variant_win.h"
+#include "base/logging.h"
+
+// Global, const instance of an empty variant.
+const VARIANT ScopedVariant::kEmptyVariant = { VT_EMPTY };
+
+ScopedVariant::~ScopedVariant() {
+ COMPILE_ASSERT(sizeof(ScopedVariant) == sizeof(VARIANT), ScopedVariantSize);
+ ::VariantClear(&var_);
+}
+
+ScopedVariant::ScopedVariant(const wchar_t* str) {
+ var_.vt = VT_EMPTY;
+ Set(str);
+}
+
+ScopedVariant::ScopedVariant(const wchar_t* str, UINT length) {
+ var_.vt = VT_BSTR;
+ var_.bstrVal = ::SysAllocStringLen(str, length);
+}
+
+ScopedVariant::ScopedVariant(int value, VARTYPE vt) {
+ var_.vt = vt;
+ var_.lVal = value;
+}
+
+void ScopedVariant::Reset(const VARIANT& var) {
+ if (&var != &var_) {
+ ::VariantClear(&var_);
+ var_ = var;
+ }
+}
+
+VARIANT ScopedVariant::Release() {
+ VARIANT var = var_;
+ var_.vt = VT_EMPTY;
+ return var;
+}
+
+void ScopedVariant::Swap(ScopedVariant& var) {
+ VARIANT tmp = var_;
+ var_ = var.var_;
+ var.var_ = tmp;
+}
+
+VARIANT* ScopedVariant::Receive() {
+ DCHECK(!IsLeakableVarType(var_.vt)) << "variant leak. type: " << var_.vt;
+ return &var_;
+}
+
+VARIANT ScopedVariant::Copy() const {
+ VARIANT ret = { VT_EMPTY };
+ ::VariantCopy(&ret, &var_);
+ return ret;
+}
+
+int ScopedVariant::Compare(const VARIANT& var, bool ignore_case) const {
+ ULONG flags = ignore_case ? NORM_IGNORECASE : 0;
+ HRESULT hr = ::VarCmp(const_cast<VARIANT*>(&var_), const_cast<VARIANT*>(&var),
+ LOCALE_USER_DEFAULT, flags);
+ int ret = 0;
+
+ switch (hr) {
+ case VARCMP_LT:
+ ret = -1;
+ break;
+
+ case VARCMP_GT:
+ case VARCMP_NULL:
+ ret = 1;
+ break;
+
+ default:
+ // Equal.
+ break;
+ }
+
+ return ret;
+}
+
+void ScopedVariant::Set(const wchar_t* str) {
+ DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
+ var_.vt = VT_BSTR;
+ var_.bstrVal = ::SysAllocString(str);
+}
+
+void ScopedVariant::Set(int8 i8) {
+ DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
+ var_.vt = VT_I1;
+ var_.cVal = i8;
+}
+
+void ScopedVariant::Set(uint8 ui8) {
+ DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
+ var_.vt = VT_UI1;
+ var_.bVal = ui8;
+}
+
+void ScopedVariant::Set(int16 i16) {
+ DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
+ var_.vt = VT_I2;
+ var_.iVal = i16;
+}
+
+void ScopedVariant::Set(uint16 ui16) {
+ DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
+ var_.vt = VT_UI2;
+ var_.uiVal = ui16;
+}
+
+void ScopedVariant::Set(int32 i32) {
+ DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
+ var_.vt = VT_I4;
+ var_.lVal = i32;
+}
+
+void ScopedVariant::Set(uint32 ui32) {
+ DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
+ var_.vt = VT_UI4;
+ var_.ulVal = ui32;
+}
+
+void ScopedVariant::Set(int64 i64) {
+ DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
+ var_.vt = VT_I8;
+ var_.llVal = i64;
+}
+
+void ScopedVariant::Set(uint64 ui64) {
+ DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
+ var_.vt = VT_UI8;
+ var_.ullVal = ui64;
+}
+
+void ScopedVariant::Set(float r32) {
+ DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
+ var_.vt = VT_R4;
+ var_.fltVal = r32;
+}
+
+void ScopedVariant::Set(double r64) {
+ DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
+ var_.vt = VT_R8;
+ var_.dblVal = r64;
+}
+
+void ScopedVariant::SetDate(DATE date) {
+ DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
+ var_.vt = VT_DATE;
+ var_.date = date;
+}
+
+void ScopedVariant::Set(IDispatch* disp) {
+ DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
+ var_.vt = VT_DISPATCH;
+ var_.pdispVal = disp;
+ if (disp)
+ disp->AddRef();
+}
+
+void ScopedVariant::Set(bool b) {
+ DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
+ var_.vt = VT_BOOL;
+ var_.boolVal = b ? VARIANT_TRUE : VARIANT_FALSE;
+}
+
+void ScopedVariant::Set(IUnknown* unk) {
+ DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
+ var_.vt = VT_UNKNOWN;
+ var_.punkVal = unk;
+ if (unk)
+ unk->AddRef();
+}
+
+void ScopedVariant::Set(SAFEARRAY* array) {
+ DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt;
+ if (SUCCEEDED(::SafeArrayGetVartype(array, &var_.vt))) {
+ var_.vt |= VT_ARRAY;
+ var_.parray = array;
+ } else {
+ DCHECK(array == NULL) << "Unable to determine safearray vartype";
+ var_.vt = VT_EMPTY;
+ }
+}
+
+#ifndef OFFICIAL_BUILD
+bool ScopedVariant::IsLeakableVarType(VARTYPE vt) {
+ bool leakable = false;
+ switch (vt & VT_TYPEMASK) {
+ case VT_BSTR:
+ case VT_DISPATCH:
+ // we treat VT_VARIANT as leakable to err on the safe side.
+ case VT_VARIANT:
+ case VT_UNKNOWN:
+ case VT_SAFEARRAY:
+
+ // very rarely used stuff (if ever):
+ case VT_VOID:
+ case VT_PTR:
+ case VT_CARRAY:
+ case VT_USERDEFINED:
+ case VT_LPSTR:
+ case VT_LPWSTR:
+ case VT_RECORD:
+ case VT_INT_PTR:
+ case VT_UINT_PTR:
+ case VT_FILETIME:
+ case VT_BLOB:
+ case VT_STREAM:
+ case VT_STORAGE:
+ case VT_STREAMED_OBJECT:
+ case VT_STORED_OBJECT:
+ case VT_BLOB_OBJECT:
+ case VT_VERSIONED_STREAM:
+ case VT_BSTR_BLOB:
+ leakable = true;
+ break;
+ }
+
+ if (!leakable && (vt & VT_ARRAY) != 0) {
+ leakable = true;
+ }
+
+ return leakable;
+}
+#endif
diff --git a/base/scoped_variant_win.h b/base/scoped_variant_win.h
new file mode 100644
index 0000000..3526f79
--- /dev/null
+++ b/base/scoped_variant_win.h
@@ -0,0 +1,136 @@
+// Copyright (c) 2009 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.
+
+#ifndef BASE_SCOPED_VARIANT_WIN_H_
+#define BASE_SCOPED_VARIANT_WIN_H_
+
+#include <windows.h>
+#include <oleauto.h>
+
+#include "base/basictypes.h" // needed to pick up OS_WIN
+#include "base/logging.h"
+
+// Scoped VARIANT class for automatically freeing a COM VARIANT at the
+// end of a scope. Additionally provides a few functions to make the
+// encapsulated VARIANT easier to use.
+// Instead of inheriting from VARIANT, we take the containment approach
+// in order to have more control over the usage of the variant and guard
+// against memory leaks.
+class ScopedVariant {
+ public:
+ // Declaration of a global variant variable that's always VT_EMPTY
+ static const VARIANT kEmptyVariant;
+
+ // Default constructor.
+ ScopedVariant() {
+ // This is equivalent to what VariantInit does, but less code.
+ var_.vt = VT_EMPTY;
+ }
+
+ // Constructor to create a new VT_BSTR VARIANT.
+ // NOTE: Do not pass a BSTR to this constructor expecting ownership to
+ // be transferred
+ explicit ScopedVariant(const wchar_t* str);
+
+ // Creates a new VT_BSTR variant of a specified length.
+ explicit ScopedVariant(const wchar_t* str, UINT length);
+
+ // Creates a new integral type variant and assigns the value to
+ // VARIANT.lVal (32 bit sized field).
+ explicit ScopedVariant(int value, VARTYPE vt = VT_I4);
+
+ ~ScopedVariant();
+
+ inline VARTYPE type() const {
+ return var_.vt;
+ }
+
+ // Give ScopedVariant ownership over an already allocated VARIANT.
+ void Reset(const VARIANT& var = kEmptyVariant);
+
+ // Releases ownership of the VARIANT to the caller.
+ VARIANT Release();
+
+ // Swap two ScopedVariant's.
+ void Swap(ScopedVariant& var);
+
+ // Returns a copy of the variant.
+ VARIANT Copy() const;
+
+ // The return value is 0 if the variants are equal, 1 if this object is
+ // greater than |var|, -1 if it is smaller.
+ int Compare(const VARIANT& var, bool ignore_case = false) const;
+
+ // Retrieves the pointer address.
+ // Used to receive a VARIANT as an out argument (and take ownership).
+ // The function DCHECKs on the current value being empty/null.
+ // Usage: GetVariant(var.receive());
+ VARIANT* Receive();
+
+ void Set(const wchar_t* str);
+
+ // Setters for simple types.
+ void Set(int8 i8);
+ void Set(uint8 ui8);
+ void Set(int16 i16);
+ void Set(uint16 ui16);
+ void Set(int32 i32);
+ void Set(uint32 ui32);
+ void Set(int64 i64);
+ void Set(uint64 ui64);
+ void Set(float r32);
+ void Set(double r64);
+ void Set(bool b);
+
+ // COM object setters
+ void Set(IDispatch* disp);
+ void Set(IUnknown* unk);
+
+ // SAFEARRAY support
+ void Set(SAFEARRAY* array);
+
+ // Special setter for DATE since DATE is a double and we already have
+ // a setter for double.
+ void SetDate(DATE date);
+
+ // Allows const access to the contained variant without DCHECKs etc.
+ // This support is necessary for the V_XYZ (e.g. V_BSTR) set of macros to
+ // work properly but still doesn't allow modifications since we want control
+ // over that.
+ const VARIANT* operator&() const {
+ return &var_;
+ }
+
+ // A hack to pass a pointer to the variant where the accepting
+ // function treats the variant as an input-only, read-only value
+ // but the function prototype requires a non const variant pointer.
+ // There's no DCHECK or anything here. Callers must know what they're doing.
+ VARIANT* AsInput() const {
+ // The nature of this function is const, so we declare
+ // it as such and cast away the constness here.
+ return const_cast<VARIANT*>(&var_);
+ }
+
+ // Allows the ScopedVariant instance to be passed to functions either by value
+ // or by const reference.
+ operator const VARIANT&() const {
+ return var_;
+ }
+
+#ifndef OFFICIAL_BUILD
+ static bool IsLeakableVarType(VARTYPE vt);
+#endif
+
+ protected:
+ VARIANT var_;
+
+ private:
+ // Comparison operators for ScopedVariant are not supported at this point.
+ // Use the Compare method instead.
+ bool operator==(const ScopedVariant& var) const;
+ bool operator!=(const ScopedVariant& var) const;
+ DISALLOW_COPY_AND_ASSIGN(ScopedVariant);
+};
+
+#endif // BASE_SCOPED_VARIANT_WIN_H_
diff --git a/base/scoped_variant_win_unittest.cc b/base/scoped_variant_win_unittest.cc
new file mode 100644
index 0000000..e826edd
--- /dev/null
+++ b/base/scoped_variant_win_unittest.cc
@@ -0,0 +1,215 @@
+// Copyright (c) 2009 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/scoped_variant_win.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+static const wchar_t kTestString1[] = L"Used to create BSTRs";
+static const wchar_t kTestString2[] = L"Also used to create BSTRs";
+
+void GiveMeAVariant(VARIANT* ret) {
+ EXPECT_TRUE(ret != NULL);
+ ret->vt = VT_BSTR;
+ V_BSTR(ret) = ::SysAllocString(kTestString1);
+}
+
+// A dummy IDispatch implementation (if you can call it that).
+// The class does nothing intelligent really. Only increments a counter
+// when AddRef is called and decrements it when Release is called.
+class FakeComObject : public IDispatch {
+ public:
+ FakeComObject() : ref_(0) {
+ }
+
+ STDMETHOD_(DWORD, AddRef)() {
+ ref_++;
+ return ref_;
+ }
+
+ STDMETHOD_(DWORD, Release)() {
+ ref_--;
+ return ref_;
+ }
+
+ STDMETHOD(QueryInterface)(REFIID, void**) {
+ return E_NOTIMPL;
+ }
+
+ STDMETHOD(GetTypeInfoCount)(UINT*) {
+ return E_NOTIMPL;
+ }
+
+ STDMETHOD(GetTypeInfo)(UINT, LCID, ITypeInfo**) {
+ return E_NOTIMPL;
+ }
+
+ STDMETHOD(GetIDsOfNames)(REFIID, LPOLESTR*, UINT, LCID, DISPID*) {
+ return E_NOTIMPL;
+ }
+
+ STDMETHOD(Invoke)(DISPID, REFIID, LCID, WORD, DISPPARAMS*, VARIANT*,
+ EXCEPINFO*, UINT*) {
+ return E_NOTIMPL;
+ }
+
+ // A way to check the internal reference count of the class.
+ int ref_count() const {
+ return ref_;
+ }
+
+ protected:
+ int ref_;
+};
+
+} // namespace
+
+TEST(ScopedVariantTest, ScopedVariant) {
+ ScopedVariant var;
+ EXPECT_TRUE(var.type() == VT_EMPTY);
+ // V_BSTR(&var) = NULL; <- NOTE: Assignment like that is not supported
+
+ ScopedVariant var_bstr(L"VT_BSTR");
+ EXPECT_EQ(VT_BSTR, V_VT(&var_bstr));
+ EXPECT_TRUE(V_BSTR(&var_bstr) != NULL); // can't use EXPECT_NE for BSTR
+ var_bstr.Reset();
+ EXPECT_NE(VT_BSTR, V_VT(&var_bstr));
+ var_bstr.Set(kTestString2);
+ EXPECT_EQ(VT_BSTR, V_VT(&var_bstr));
+
+ VARIANT tmp = var_bstr.Release();
+ EXPECT_EQ(VT_EMPTY, V_VT(&var_bstr));
+ EXPECT_EQ(VT_BSTR, V_VT(&tmp));
+ EXPECT_EQ(0, lstrcmp(V_BSTR(&tmp), kTestString2));
+
+ var.Reset(tmp);
+ EXPECT_EQ(VT_BSTR, V_VT(&var));
+ EXPECT_EQ(0, lstrcmpW(V_BSTR(&var), kTestString2));
+
+ var_bstr.Swap(var);
+ EXPECT_EQ(VT_EMPTY, V_VT(&var));
+ EXPECT_EQ(VT_BSTR, V_VT(&var_bstr));
+ EXPECT_EQ(0, lstrcmpW(V_BSTR(&var_bstr), kTestString2));
+ var_bstr.Reset();
+
+ // Test the Compare and Copy routines.
+ GiveMeAVariant(var_bstr.Receive());
+ ScopedVariant var_bstr2(V_BSTR(&var_bstr));
+ EXPECT_EQ(0, var_bstr.Compare(var_bstr2));
+ var_bstr2.Reset();
+ EXPECT_NE(0, var_bstr.Compare(var_bstr2));
+ var_bstr2.Reset(var_bstr.Copy());
+ EXPECT_EQ(0, var_bstr.Compare(var_bstr2));
+ var_bstr2.Reset();
+ var_bstr2.Set(V_BSTR(&var_bstr));
+ EXPECT_EQ(0, var_bstr.Compare(var_bstr2));
+ var_bstr2.Reset();
+ var_bstr.Reset();
+
+ // Test for the SetDate setter.
+ SYSTEMTIME sys_time;
+ ::GetSystemTime(&sys_time);
+ DATE date;
+ ::SystemTimeToVariantTime(&sys_time, &date);
+ var.Reset();
+ var.SetDate(date);
+ EXPECT_EQ(VT_DATE, var.type());
+ EXPECT_EQ(date, V_DATE(&var));
+
+ // Simple setter tests. These do not require resetting the variant
+ // after each test since the variant type is not "leakable" (i.e. doesn't
+ // need to be freed explicitly).
+
+ // We need static cast here since char defaults to int (!?).
+ var.Set(static_cast<int8>('v'));
+ EXPECT_EQ(VT_I1, var.type());
+ EXPECT_EQ('v', V_I1(&var));
+
+ var.Set(static_cast<short>(123));
+ EXPECT_EQ(VT_I2, var.type());
+ EXPECT_EQ(123, V_I2(&var));
+
+ var.Set(static_cast<int32>(123));
+ EXPECT_EQ(VT_I4, var.type());
+ EXPECT_EQ(123, V_I4(&var));
+
+ var.Set(static_cast<int64>(123));
+ EXPECT_EQ(VT_I8, var.type());
+ EXPECT_EQ(123, V_I8(&var));
+
+ var.Set(static_cast<uint8>(123));
+ EXPECT_EQ(VT_UI1, var.type());
+ EXPECT_EQ(123, V_UI1(&var));
+
+ var.Set(static_cast<unsigned short>(123));
+ EXPECT_EQ(VT_UI2, var.type());
+ EXPECT_EQ(123, V_UI2(&var));
+
+ var.Set(static_cast<uint32>(123));
+ EXPECT_EQ(VT_UI4, var.type());
+ EXPECT_EQ(123, V_UI4(&var));
+
+ var.Set(static_cast<uint64>(123));
+ EXPECT_EQ(VT_UI8, var.type());
+ EXPECT_EQ(123, V_UI8(&var));
+
+ var.Set(123.123f);
+ EXPECT_EQ(VT_R4, var.type());
+ EXPECT_EQ(123.123f, V_R4(&var));
+
+ var.Set(static_cast<double>(123.123));
+ EXPECT_EQ(VT_R8, var.type());
+ EXPECT_EQ(123.123, V_R8(&var));
+
+ var.Set(true);
+ EXPECT_EQ(VT_BOOL, var.type());
+ EXPECT_EQ(VARIANT_TRUE, V_BOOL(&var));
+ var.Set(false);
+ EXPECT_EQ(VT_BOOL, var.type());
+ EXPECT_EQ(VARIANT_FALSE, V_BOOL(&var));
+
+ // Com interface tests
+
+ var.Set(static_cast<IDispatch*>(NULL));
+ EXPECT_EQ(VT_DISPATCH, var.type());
+ EXPECT_EQ(NULL, V_DISPATCH(&var));
+ var.Reset();
+
+ var.Set(static_cast<IUnknown*>(NULL));
+ EXPECT_EQ(VT_UNKNOWN, var.type());
+ EXPECT_EQ(NULL, V_UNKNOWN(&var));
+ var.Reset();
+
+ FakeComObject faker;
+ EXPECT_EQ(0, faker.ref_count());
+ var.Set(static_cast<IDispatch*>(&faker));
+ EXPECT_EQ(VT_DISPATCH, var.type());
+ EXPECT_EQ(&faker, V_DISPATCH(&var));
+ EXPECT_EQ(1, faker.ref_count());
+ var.Reset();
+ EXPECT_EQ(0, faker.ref_count());
+
+ var.Set(static_cast<IUnknown*>(&faker));
+ EXPECT_EQ(VT_UNKNOWN, var.type());
+ EXPECT_EQ(&faker, V_UNKNOWN(&var));
+ EXPECT_EQ(1, faker.ref_count());
+ var.Reset();
+ EXPECT_EQ(0, faker.ref_count());
+
+ // SAFEARRAY tests
+ var.Set(static_cast<SAFEARRAY*>(NULL));
+ EXPECT_EQ(VT_EMPTY, var.type());
+
+ SAFEARRAY* sa = ::SafeArrayCreateVector(VT_UI1, 0, 100);
+ ASSERT_TRUE(sa != NULL);
+
+ var.Set(sa);
+#ifndef OFFICIAL_BUILD
+ EXPECT_TRUE(ScopedVariant::IsLeakableVarType(var.type()));
+#endif
+ EXPECT_EQ(VT_ARRAY | VT_UI1, var.type());
+ EXPECT_EQ(sa, V_ARRAY(&var));
+ // The array is destroyed in the destructor of var.
+}