Skip to content
Draft
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions storage/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ set(android_SRCS
src/android/controller_android.cc
src/android/metadata_android.cc
src/android/storage_android.cc
src/android/storage_reference_android.cc)
src/android/storage_reference_android.cc
src/android/list_result_android.cc)

# Source files used by the iOS implementation.
set(ios_SRCS
Expand All @@ -47,7 +48,8 @@ set(ios_SRCS
src/ios/metadata_ios.mm
src/ios/storage_ios.mm
src/ios/storage_reference_ios.mm
src/ios/util_ios.mm)
src/ios/util_ios.mm
src/ios/list_result_ios.mm)

# Source files used by the desktop implementation.
set(desktop_SRCS
Expand All @@ -58,7 +60,8 @@ set(desktop_SRCS
src/desktop/rest_operation.cc
src/desktop/storage_desktop.cc
src/desktop/storage_path.cc
src/desktop/storage_reference_desktop.cc)
src/desktop/storage_reference_desktop.cc
src/desktop/list_result_desktop.cc)

if(ANDROID)
set(storage_platform_SRCS
Expand Down
72 changes: 72 additions & 0 deletions storage/integration_test/src/integration_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "firebase/auth.h"
#include "firebase/internal/platform.h"
#include "firebase/storage.h"
#include "firebase/storage/list_result.h"
#include "firebase/util.h"
#include "firebase_test_framework.h" // NOLINT

Expand Down Expand Up @@ -1622,4 +1623,75 @@ TEST_F(FirebaseStorageTest, TestInvalidatingReferencesWhenDeletingApp) {
InitializeAppAndAuth();
}

// Test the StorageReference::ListAll() method.
// This test currently only verifies that the stubbed method returns an empty result.
TEST_F(FirebaseStorageTest, TestListAll) {
if (skip_tests_) return;

firebase::storage::Storage* storage = storage_; // Use the member variable
firebase::storage::StorageReference reference = storage->GetReference();

firebase::Future<firebase::storage::ListResult> future = reference.ListAll();
WaitForCompletion(future, "ListAll");

ASSERT_EQ(future.status(), firebase::kFutureStatusComplete);
ASSERT_EQ(future.error(), firebase::storage::kErrorNone);

const firebase::storage::ListResult* result = future.result();
ASSERT_NE(result, nullptr);
if (result != nullptr) {
EXPECT_TRUE(result->items.empty());
EXPECT_TRUE(result->prefixes.empty());
EXPECT_TRUE(result->page_token.empty());
}
}

// Test the StorageReference::List() method with no page token.
// This test currently only verifies that the stubbed method returns an empty result.
TEST_F(FirebaseStorageTest, TestListNoPageToken) {
if (skip_tests_) return;

firebase::storage::Storage* storage = storage_; // Use the member variable
firebase::storage::StorageReference reference = storage->GetReference();

firebase::Future<firebase::storage::ListResult> future = reference.List();
WaitForCompletion(future, "List (no page token)");

ASSERT_EQ(future.status(), firebase::kFutureStatusComplete);
ASSERT_EQ(future.error(), firebase::storage::kErrorNone);

const firebase::storage::ListResult* result = future.result();
ASSERT_NE(result, nullptr);
if (result != nullptr) {
EXPECT_TRUE(result->items.empty());
EXPECT_TRUE(result->prefixes.empty());
EXPECT_TRUE(result->page_token.empty());
}
}

// Test the StorageReference::List() method with a page token.
// This test currently only verifies that the stubbed method returns an empty result
// and that the page token is passed (though not used by the stub).
TEST_F(FirebaseStorageTest, TestListWithPageToken) {
if (skip_tests_) return;

firebase::storage::Storage* storage = storage_; // Use the member variable
firebase::storage::StorageReference reference = storage->GetReference();
const char* page_token = "test_page_token";

firebase::Future<firebase::storage::ListResult> future = reference.List(page_token);
WaitForCompletion(future, "List (with page token)");

ASSERT_EQ(future.status(), firebase::kFutureStatusComplete);
ASSERT_EQ(future.error(), firebase::storage::kErrorNone);

const firebase::storage::ListResult* result = future.result();
ASSERT_NE(result, nullptr);
if (result != nullptr) {
EXPECT_TRUE(result->items.empty());
EXPECT_TRUE(result->prefixes.empty());
EXPECT_TRUE(result->page_token.empty());
}
}

} // namespace firebase_testapp_automated
21 changes: 21 additions & 0 deletions storage/src/android/list_result_android.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// File: storage/src/android/list_result_android.cc
#include "storage/src/android/list_result_android.h"

namespace firebase {
namespace storage {
namespace internal {

ListResultInternal::ListResultInternal(
StorageReferenceInternal* platform_sri,
const ListResultInternal* other_to_copy_from)
: platform_sri_(platform_sri) {
if (other_to_copy_from) {
items_ = other_to_copy_from->items_;
prefixes_ = other_to_copy_from->prefixes_;
page_token_ = other_to_copy_from->page_token_;
}
}

} // namespace internal
} // namespace storage
} // namespace firebase
46 changes: 46 additions & 0 deletions storage/src/android/list_result_android.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// File: storage/src/android/list_result_android.h
#ifndef FIREBASE_STORAGE_CLIENT_CPP_SRC_ANDROID_LIST_RESULT_ANDROID_H_
#define FIREBASE_STORAGE_CLIENT_CPP_SRC_ANDROID_LIST_RESULT_ANDROID_H_

#include <string>
#include <vector>

#include "firebase/storage/storage_reference.h"
#include "storage/src/android/storage_reference_android.h" // Defines firebase::storage::internal::StorageReferenceInternal for android
#include "storage/src/android/storage_internal_android.h" // Defines firebase::storage::internal::StorageInternal for android

namespace firebase {
namespace storage {
namespace internal {

class ListResultInternal {
public:
explicit ListResultInternal(
StorageReferenceInternal* platform_sri,
const ListResultInternal* other_to_copy_from = nullptr);

const std::vector<StorageReference>& items() const { return items_; }
const std::vector<StorageReference>& prefixes() const { return prefixes_; }
const std::string& page_token() const { return page_token_; }

StorageReferenceInternal* storage_reference_internal() const {
return platform_sri_;
}
StorageInternal* associated_storage_internal() const {
return platform_sri_ ? platform_sri_->storage_internal() : nullptr;
}

private:
ListResultInternal& operator=(const ListResultInternal&);

StorageReferenceInternal* platform_sri_;
std::vector<StorageReference> items_;
std::vector<StorageReference> prefixes_;
std::string page_token_;
};

} // namespace internal
} // namespace storage
} // namespace firebase

#endif // FIREBASE_STORAGE_CLIENT_CPP_SRC_ANDROID_LIST_RESULT_ANDROID_H_
1 change: 1 addition & 0 deletions storage/src/android/storage_reference_android.cc
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "app/src/include/firebase/app.h"
#include "app/src/util_android.h"
#include "storage/src/android/controller_android.h"
// Removed: #include "storage/src/android/list_result_android.h"
#include "storage/src/android/metadata_android.h"
#include "storage/src/android/storage_android.h"
#include "storage/src/include/firebase/storage.h"
Expand Down
1 change: 1 addition & 0 deletions storage/src/android/storage_reference_android.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "app/src/reference_counted_future_impl.h"
#include "app/src/util_android.h"
#include "storage/src/android/storage_android.h"
// Removed: #include "storage/src/common/list_result_internal.h"
#include "storage/src/include/firebase/storage/storage_reference.h"

namespace firebase {
Expand Down
195 changes: 195 additions & 0 deletions storage/src/common/list_result.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
// File: storage/src/common/list_result.cc

#include "firebase/storage/list_result.h" // For ListResult public class
#include "app/src/include/firebase/internal/platform.h" // For FIREBASE_PLATFORM defines
#include "app/src/cleanup_notifier.h" // For CleanupNotifier
#include "app/src/log.h" // For LogDebug, LogWarning
#include "firebase/storage/storage_reference.h" // For StorageReference (used by ListResult members)

// Platform-specific headers that define internal::ListResultInternal (the PIMPL class)
// and internal::StorageInternal (for CleanupNotifier context).
#if FIREBASE_PLATFORM_ANDROID
#include "storage/src/android/list_result_android.h"
#include "storage/src/android/storage_internal_android.h"
#elif FIREBASE_PLATFORM_IOS || FIREBASE_PLATFORM_TVOS
#include "storage/src/ios/list_result_ios.h"
#include "storage/src/ios/storage_internal_ios.h"
#else // Desktop
#include "storage/src/desktop/list_result_desktop.h"
#include "storage/src/desktop/storage_internal_desktop.h"
#endif


namespace firebase {
namespace storage {

// Forward declaration of the PIMPL class.
namespace internal {
class ListResultInternal;
class StorageReferenceInternal;
class StorageInternal;
} // namespace internal

namespace internal {

// ListResultInternalCommon: Provides static helper methods for managing the
// lifecycle of the ListResultInternal PIMPL object.
class ListResultInternalCommon {
public:
// Retrieves the StorageInternal context from the ListResultInternal object.
static StorageInternal* GetStorageInternalContext(
ListResultInternal* pimpl_obj) {
if (!pimpl_obj) return nullptr;
// Relies on ListResultInternal having associated_storage_internal().
StorageInternal* storage_ctx = pimpl_obj->associated_storage_internal();
if (storage_ctx == nullptr) {
LogWarning("ListResultInternal %p has no associated StorageInternal for cleanup.", pimpl_obj);
}
return storage_ctx;
}

// Callback for CleanupNotifier, invoked when the App is being destroyed.
static void CleanupPublicListResultObject(void* public_obj_void) {
ListResult* public_obj = reinterpret_cast<ListResult*>(public_obj_void);
if (public_obj) {
LogDebug("CleanupNotifier: Cleaning up ListResult %p.", public_obj);
DeleteInternalPimpl(public_obj);
} else {
LogWarning("CleanupNotifier: CleanupPublicListResultObject called with null object.");
}
}

static void RegisterForCleanup(ListResult* public_obj,
ListResultInternal* pimpl_obj) {
FIREBASE_ASSERT(public_obj != nullptr);
if (!pimpl_obj) return;
StorageInternal* storage_ctx = GetStorageInternalContext(pimpl_obj);
if (storage_ctx) {
storage_ctx->cleanup().RegisterObject(public_obj, CleanupPublicListResultObject);
LogDebug("ListResult %p (PIMPL %p) registered for cleanup.",
public_obj, pimpl_obj);
} else {
LogWarning("Could not register ListResult %p for cleanup: no StorageInternal context.",
public_obj);
}
}

static void UnregisterFromCleanup(ListResult* public_obj,
ListResultInternal* pimpl_obj) {
FIREBASE_ASSERT(public_obj != nullptr);
if (!pimpl_obj) return;
StorageInternal* storage_ctx = GetStorageInternalContext(pimpl_obj);
if (storage_ctx) {
storage_ctx->cleanup().UnregisterObject(public_obj);
LogDebug("ListResult %p (associated with PIMPL %p) unregistered from cleanup.",
public_obj, pimpl_obj);
} else {
LogWarning("Could not unregister ListResult %p: no StorageInternal context.", public_obj);
}
}

// Deletes the PIMPL object, unregisters from cleanup, and nulls the pointer in public_obj.
static void DeleteInternalPimpl(ListResult* public_obj) {
FIREBASE_ASSERT(public_obj != nullptr);
if (!public_obj->internal_) return;

ListResultInternal* pimpl_to_delete = public_obj->internal_;
UnregisterFromCleanup(public_obj, pimpl_to_delete);
public_obj->internal_ = nullptr;
delete pimpl_to_delete;
}
};

} // namespace internal

// --- Public ListResult Method Implementations ---

const std::vector<StorageReference> ListResult::s_empty_items_;
const std::vector<StorageReference> ListResult::s_empty_prefixes_;
const std::string ListResult::s_empty_page_token_;

ListResult::ListResult() : internal_(nullptr) {
}

ListResult::ListResult(internal::ListResultInternal* internal_pimpl)
: internal_(internal_pimpl) {
if (internal_) {
internal::ListResultInternalCommon::RegisterForCleanup(this, internal_);
}
}

ListResult::~ListResult() {
internal::ListResultInternalCommon::DeleteInternalPimpl(this);
}

ListResult::ListResult(const ListResult& other) : internal_(nullptr) {
if (other.internal_) {
internal::StorageReferenceInternal* sri_context =
other.internal_->storage_reference_internal();
internal_ = new internal::ListResultInternal(sri_context, other.internal_);
internal::ListResultInternalCommon::RegisterForCleanup(this, internal_);
}
}

ListResult& ListResult::operator=(const ListResult& other) {
if (this == &other) {
return *this;
}
internal::ListResultInternalCommon::DeleteInternalPimpl(this); // Clean up current

if (other.internal_) {
internal::StorageReferenceInternal* sri_context =
other.internal_->storage_reference_internal();
internal_ = new internal::ListResultInternal(sri_context, other.internal_);
internal::ListResultInternalCommon::RegisterForCleanup(this, internal_);
}
return *this;
}

#if defined(FIREBASE_USE_MOVE_OPERATORS) || defined(DOXYGEN)
ListResult::ListResult(ListResult&& other) : internal_(other.internal_) {
other.internal_ = nullptr;
if (internal_) {
// New public object 'this' takes ownership. Unregister 'other', register 'this'.
internal::ListResultInternalCommon::UnregisterFromCleanup(&other, internal_);
internal::ListResultInternalCommon::RegisterForCleanup(this, internal_);
}
}

ListResult& ListResult::operator=(ListResult&& other) {
if (this == &other) {
return *this;
}
internal::ListResultInternalCommon::DeleteInternalPimpl(this); // Clean up current

internal_ = other.internal_;
other.internal_ = nullptr;

if (internal_) {
// Similar to move constructor: unregister 'other', register 'this'.
internal::ListResultInternalCommon::UnregisterFromCleanup(&other, internal_);
internal::ListResultInternalCommon::RegisterForCleanup(this, internal_);
}
return *this;
}
#endif // defined(FIREBASE_USE_MOVE_OPERATORS) || defined(DOXYGEN)

const std::vector<StorageReference>& ListResult::items() const {
if (!is_valid()) return s_empty_items_;
return internal_->items();
}

const std::vector<StorageReference>& ListResult::prefixes() const {
if (!is_valid()) return s_empty_prefixes_;
return internal_->prefixes();
}

const std::string& ListResult::page_token() const {
if (!is_valid()) return s_empty_page_token_;
return internal_->page_token();
}

bool ListResult::is_valid() const { return internal_ != nullptr; }

} // namespace storage
} // namespace firebase
Loading
Loading