From 77ff94ec4c0a40c95eb8b2a445af9630bb607ce6 Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Wed, 24 Jun 2020 13:28:54 -0700 Subject: [PATCH 1/2] Add sample and test frameworks to integration_test directory. --- .../sample_framework/LibraryManifest.xml | 7 + .../src/android/android_app_framework.cc | 560 ++++++++++++++++++ .../google/firebase/example/LoggingUtils.java | 169 ++++++ .../firebase/example/TextEntryField.java | 106 ++++ .../sample_framework/src/app_framework.cc | 96 +++ .../sample_framework/src/app_framework.h | 131 ++++ .../src/desktop/desktop_app_framework.cc | 215 +++++++ .../src/ios/ios_app_framework.mm | 346 +++++++++++ .../test_framework/LibraryManifest.xml | 7 + .../test_framework/download_googletest.py | 79 +++ .../android_firebase_test_framework.cc | 225 +++++++ .../firebase/example/SimpleHttpRequest.java | 118 ++++ .../example/SimplePersistentStorage.java | 45 ++ .../desktop_firebase_test_framework.cc | 52 ++ .../src/firebase_test_framework.cc | 221 +++++++ .../src/firebase_test_framework.h | 180 ++++++ .../test_framework/src/gmock/gmock.h | 26 + .../test_framework/src/gtest/gtest.h | 26 + .../src/ios/ios_firebase_test_framework.mm | 190 ++++++ 19 files changed, 2799 insertions(+) create mode 100644 testing/integration_tests/sample_framework/LibraryManifest.xml create mode 100644 testing/integration_tests/sample_framework/src/android/android_app_framework.cc create mode 100644 testing/integration_tests/sample_framework/src/android/java/com/google/firebase/example/LoggingUtils.java create mode 100644 testing/integration_tests/sample_framework/src/android/java/com/google/firebase/example/TextEntryField.java create mode 100644 testing/integration_tests/sample_framework/src/app_framework.cc create mode 100644 testing/integration_tests/sample_framework/src/app_framework.h create mode 100644 testing/integration_tests/sample_framework/src/desktop/desktop_app_framework.cc create mode 100755 testing/integration_tests/sample_framework/src/ios/ios_app_framework.mm create mode 100644 testing/integration_tests/test_framework/LibraryManifest.xml create mode 100755 testing/integration_tests/test_framework/download_googletest.py create mode 100644 testing/integration_tests/test_framework/src/android/android_firebase_test_framework.cc create mode 100644 testing/integration_tests/test_framework/src/android/java/com/google/firebase/example/SimpleHttpRequest.java create mode 100644 testing/integration_tests/test_framework/src/android/java/com/google/firebase/example/SimplePersistentStorage.java create mode 100644 testing/integration_tests/test_framework/src/desktop/desktop_firebase_test_framework.cc create mode 100644 testing/integration_tests/test_framework/src/firebase_test_framework.cc create mode 100644 testing/integration_tests/test_framework/src/firebase_test_framework.h create mode 100644 testing/integration_tests/test_framework/src/gmock/gmock.h create mode 100644 testing/integration_tests/test_framework/src/gtest/gtest.h create mode 100644 testing/integration_tests/test_framework/src/ios/ios_firebase_test_framework.mm diff --git a/testing/integration_tests/sample_framework/LibraryManifest.xml b/testing/integration_tests/sample_framework/LibraryManifest.xml new file mode 100644 index 0000000000..fe187d7842 --- /dev/null +++ b/testing/integration_tests/sample_framework/LibraryManifest.xml @@ -0,0 +1,7 @@ + + + + + + diff --git a/testing/integration_tests/sample_framework/src/android/android_app_framework.cc b/testing/integration_tests/sample_framework/src/android/android_app_framework.cc new file mode 100644 index 0000000000..3971f0ce60 --- /dev/null +++ b/testing/integration_tests/sample_framework/src/android/android_app_framework.cc @@ -0,0 +1,560 @@ +// Copyright 2016 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "app_framework.h" // NOLINT + +// This implementation is derived from http://github.com/google/fplutil + +// Number of seconds to delay after the app is finished before exiting. +// (Plus a longer delay if the app returns an error code other than 0, to give +// the user more time to see the errors.) +const int kExitDelaySeconds = 10; +const int kExitDelaySecondsIfError = 60; + +static struct android_app* g_app_state = nullptr; +static bool g_destroy_requested = false; +static bool g_started = false; +static bool g_restarted = false; +static pthread_mutex_t g_started_mutex; + +// Handle state changes from via native app glue. +static void OnAppCmd(struct android_app* app, int32_t cmd) { + g_destroy_requested |= cmd == APP_CMD_DESTROY; +} + +namespace app_framework { + +// Process events pending on the main thread. +// Returns true when the app receives an event requesting exit. +bool ProcessEvents(int msec) { + int looperId; + do { + struct android_poll_source* source = nullptr; + int events; + looperId = ALooper_pollAll(msec, nullptr, &events, + reinterpret_cast(&source)); + if (looperId >= 0 && source) { + source->process(g_app_state, source); + } + } while (looperId != ALOOPER_POLL_TIMEOUT && !g_destroy_requested && + !g_restarted); + return g_destroy_requested | g_restarted; +} + +std::string PathForResource() { + ANativeActivity* nativeActivity = g_app_state->activity; + std::string result(nativeActivity->internalDataPath); + return result + "/"; +} + +// Get the activity. +jobject GetActivity() { return g_app_state->activity->clazz; } + +// Get the window context. For Android, it's a jobject pointing to the Activity. +jobject GetWindowContext() { return g_app_state->activity->clazz; } + +// Find a class, attempting to load the class if it's not found. +jclass FindClass(JNIEnv* env, jobject activity_object, const char* class_name) { + jclass class_object = env->FindClass(class_name); + if (env->ExceptionCheck()) { + env->ExceptionClear(); + // If the class isn't found it's possible NativeActivity is being used by + // the application which means the class path is set to only load system + // classes. The following falls back to loading the class using the + // Activity before retrieving a reference to it. + jclass activity_class = env->FindClass("android/app/Activity"); + jmethodID activity_get_class_loader = env->GetMethodID( + activity_class, "getClassLoader", "()Ljava/lang/ClassLoader;"); + + jobject class_loader_object = + env->CallObjectMethod(activity_object, activity_get_class_loader); + + jclass class_loader_class = env->FindClass("java/lang/ClassLoader"); + jmethodID class_loader_load_class = + env->GetMethodID(class_loader_class, "loadClass", + "(Ljava/lang/String;)Ljava/lang/Class;"); + jstring class_name_object = env->NewStringUTF(class_name); + + class_object = static_cast(env->CallObjectMethod( + class_loader_object, class_loader_load_class, class_name_object)); + + if (env->ExceptionCheck()) { + env->ExceptionClear(); + class_object = nullptr; + } + env->DeleteLocalRef(class_name_object); + env->DeleteLocalRef(class_loader_object); + } + return class_object; +} + +// Vars that we need available for appending text to the log window: +class LoggingUtilsData { + public: + LoggingUtilsData() + : logging_utils_class_(nullptr), + logging_utils_add_log_text_(0), + logging_utils_init_log_window_(0), + logging_utils_get_did_touch_(0) {} + + ~LoggingUtilsData() { + JNIEnv* env = GetJniEnv(); + assert(env); + if (logging_utils_class_) { + env->DeleteGlobalRef(logging_utils_class_); + } + } + + void Init() { + JNIEnv* env = GetJniEnv(); + assert(env); + + jclass logging_utils_class = FindClass( + env, GetActivity(), "com/google/firebase/example/LoggingUtils"); + assert(logging_utils_class != 0); + + // Need to store as global references so it don't get moved during garbage + // collection. + logging_utils_class_ = + static_cast(env->NewGlobalRef(logging_utils_class)); + env->DeleteLocalRef(logging_utils_class); + + logging_utils_init_log_window_ = env->GetStaticMethodID( + logging_utils_class_, "initLogWindow", "(Landroid/app/Activity;)V"); + logging_utils_add_log_text_ = env->GetStaticMethodID( + logging_utils_class_, "addLogText", "(Ljava/lang/String;)V"); + logging_utils_get_did_touch_ = + env->GetStaticMethodID(logging_utils_class_, "getDidTouch", "()Z"); + logging_utils_get_log_file_ = env->GetStaticMethodID( + logging_utils_class_, "getLogFile", "()Ljava/lang/String;"); + logging_utils_start_log_file_ = + env->GetStaticMethodID(logging_utils_class_, "startLogFile", + "(Landroid/app/Activity;Ljava/lang/String;)Z"); + + env->CallStaticVoidMethod(logging_utils_class_, + logging_utils_init_log_window_, GetActivity()); + } + + void AppendText(const char* text) { + if (logging_utils_class_ == 0) return; // haven't been initted yet + JNIEnv* env = GetJniEnv(); + assert(env); + jstring text_string = env->NewStringUTF(text); + env->CallStaticVoidMethod(logging_utils_class_, logging_utils_add_log_text_, + text_string); + env->DeleteLocalRef(text_string); + } + + bool DidTouch() { + if (logging_utils_class_ == 0) return false; // haven't been initted yet + JNIEnv* env = GetJniEnv(); + assert(env); + return env->CallStaticBooleanMethod(logging_utils_class_, + logging_utils_get_did_touch_); + } + + bool IsLoggingToFile() { + if (logging_utils_class_ == 0) return false; // haven't been initted yet + JNIEnv* env = GetJniEnv(); + assert(env); + jobject file_uri = env->CallStaticObjectMethod(logging_utils_class_, + logging_utils_get_log_file_); + if (file_uri == nullptr) { + return false; + } else { + env->DeleteLocalRef(file_uri); + return true; + } + } + + bool StartLoggingToFile(const char* path) { + if (logging_utils_class_ == 0) return false; // haven't been initted yet + JNIEnv* env = GetJniEnv(); + assert(env); + jstring path_string = env->NewStringUTF(path); + jboolean b = env->CallStaticBooleanMethod(logging_utils_class_, + logging_utils_start_log_file_, + GetActivity(), path_string); + env->DeleteLocalRef(path_string); + return b ? true : false; + } + + private: + jclass logging_utils_class_; + jmethodID logging_utils_add_log_text_; + jmethodID logging_utils_init_log_window_; + jmethodID logging_utils_get_did_touch_; + jmethodID logging_utils_get_log_file_; + jmethodID logging_utils_start_log_file_; +}; + +LoggingUtilsData* g_logging_utils_data; + +// Checks if a JNI exception has happened, and if so, logs it to the console. +void CheckJNIException() { + JNIEnv* env = GetJniEnv(); + if (env->ExceptionCheck()) { + // Get the exception text. + jthrowable exception = env->ExceptionOccurred(); + env->ExceptionClear(); + + // Convert the exception to a string. + jclass object_class = env->FindClass("java/lang/Object"); + jmethodID toString = + env->GetMethodID(object_class, "toString", "()Ljava/lang/String;"); + jstring s = (jstring)env->CallObjectMethod(exception, toString); + const char* exception_text = env->GetStringUTFChars(s, nullptr); + + // Log the exception text. + __android_log_print(ANDROID_LOG_INFO, TESTAPP_NAME, + "-------------------JNI exception:"); + __android_log_print(ANDROID_LOG_INFO, TESTAPP_NAME, "%s", exception_text); + __android_log_print(ANDROID_LOG_INFO, TESTAPP_NAME, "-------------------"); + + // Also, assert fail. + assert(false); + + // In the event we didn't assert fail, clean up. + env->ReleaseStringUTFChars(s, exception_text); + env->DeleteLocalRef(s); + env->DeleteLocalRef(exception); + } +} + +// Log a message that can be viewed in "adb logcat". +void LogMessageV(bool suppress, const char* format, va_list list) { + static const int kLineBufferSize = 1024; + char buffer[kLineBufferSize + 2]; + + int string_len = vsnprintf(buffer, kLineBufferSize, format, list); + string_len = string_len < kLineBufferSize ? string_len : kLineBufferSize; + // append a linebreak to the buffer: + buffer[string_len] = '\n'; + buffer[string_len + 1] = '\0'; + + if (GetPreserveFullLog()) { + AddToFullLog(buffer); + } + if (!suppress) { + fputs(buffer, stdout); + fflush(stdout); + } +} + +void LogMessage(const char* format, ...) { + va_list list; + va_start(list, format); + LogMessageV(false, format, list); + va_end(list); +} + +static bool g_save_full_log = false; +static std::vector g_full_logs; // NOLINT + +void AddToFullLog(const char* str) { g_full_logs.push_back(std::string(str)); } + +bool GetPreserveFullLog() { return g_save_full_log; } +void SetPreserveFullLog(bool b) { g_save_full_log = b; } + +void ClearFullLog() { g_full_logs.clear(); } + +void OutputFullLog() { + for (int i = 0; i < g_full_logs.size(); ++i) { + fputs(g_full_logs[i].c_str(), stdout); + } + fflush(stdout); + ClearFullLog(); +} + +// Log a message that can be viewed in the console. +void AddToTextView(const char* str) { + app_framework::g_logging_utils_data->AppendText(str); + CheckJNIException(); +} + +// Get the JNI environment. +JNIEnv* GetJniEnv() { + JavaVM* vm = g_app_state->activity->vm; + JNIEnv* env; + jint result = vm->AttachCurrentThread(&env, nullptr); + return result == JNI_OK ? env : nullptr; +} + +bool IsLoggingToFile() { + return app_framework::g_logging_utils_data->IsLoggingToFile(); +} + +bool StartLoggingToFile(const char* path) { + return app_framework::g_logging_utils_data->StartLoggingToFile(path); +} + +// Remove all lines starting with these strings. +static const char* const filter_lines[] = {"referenceTable ", nullptr}; + +bool should_filter(const char* str) { + for (int i = 0; filter_lines[i] != nullptr; ++i) { + if (strncmp(str, filter_lines[i], strlen(filter_lines[i])) == 0) + return true; + } + return false; +} + +void* stdout_logger(void* filedes_ptr) { + int fd = reinterpret_cast(filedes_ptr)[0]; + static std::string buffer; + char bufchar; + while (int n = read(fd, &bufchar, 1)) { + if (bufchar == '\0') { + break; + } else if (bufchar == '\n') { + if (!should_filter(buffer.c_str())) { + __android_log_print(ANDROID_LOG_INFO, TESTAPP_NAME, "%s", + buffer.c_str()); + buffer = buffer + bufchar; // Add the newline + app_framework::AddToTextView(buffer.c_str()); + } + buffer.clear(); + } else { + buffer = buffer + bufchar; + } + } + JavaVM* jvm; + if (app_framework::GetJniEnv()->GetJavaVM(&jvm) == 0) { + jvm->DetachCurrentThread(); + } + return nullptr; +} + +struct FunctionData { + void* (*func)(void*); + void* data; +}; + +static void* CallFunction(void* bg_void) { + FunctionData* bg = reinterpret_cast(bg_void); + void* (*func)(void*) = bg->func; + void* data = bg->data; + // Clean up the data that was passed to us. + delete bg; + // Call the background function. + void* result = func(data); + // Then clean up the Java thread. + JavaVM* jvm; + if (app_framework::GetJniEnv()->GetJavaVM(&jvm) == 0) { + jvm->DetachCurrentThread(); + } + return result; +} + +void RunOnBackgroundThread(void* (*func)(void*), void* data) { + pthread_t thread; + // Rather than running pthread_create(func, data), we must package them into a + // struct, because the c++ thread needs to clean up the JNI thread after it + // finishes. + FunctionData* bg = new FunctionData; + bg->func = func; + bg->data = data; + pthread_create(&thread, nullptr, CallFunction, bg); + pthread_detach(thread); +} + +// Vars that we need available for reading text from the user. +class TextEntryFieldData { + public: + TextEntryFieldData() + : text_entry_field_class_(nullptr), text_entry_field_read_text_(0) {} + + ~TextEntryFieldData() { + JNIEnv* env = GetJniEnv(); + assert(env); + if (text_entry_field_class_) { + env->DeleteGlobalRef(text_entry_field_class_); + } + } + + void Init() { + JNIEnv* env = GetJniEnv(); + assert(env); + + jclass text_entry_field_class = FindClass( + env, GetActivity(), "com/google/firebase/example/TextEntryField"); + if (text_entry_field_class == 0) { + // No text entry allowed in this testapp, the Java class is not loaded. + return; + } + + // Need to store as global references so it don't get moved during garbage + // collection. + text_entry_field_class_ = + static_cast(env->NewGlobalRef(text_entry_field_class)); + env->DeleteLocalRef(text_entry_field_class); + + static const JNINativeMethod kNativeMethods[] = { + {"nativeSleep", "(I)Z", reinterpret_cast(ProcessEvents)}}; + env->RegisterNatives(text_entry_field_class_, kNativeMethods, + sizeof(kNativeMethods) / sizeof(kNativeMethods[0])); + text_entry_field_read_text_ = env->GetStaticMethodID( + text_entry_field_class_, "readText", + "(Landroid/app/Activity;Ljava/lang/String;Ljava/lang/String;" + "Ljava/lang/String;)Ljava/lang/String;"); + } + + // Call TextEntryField.readText(), which shows a text entry dialog and spins + // until the user enters some text (or cancels). If the user cancels, returns + // an empty string. + std::string ReadText(const char* title, const char* message, + const char* placeholder) { + if (text_entry_field_class_ == 0) { + LogMessage( + "ERROR: ReadText() failed, no TextEntryField Java class is loaded."); + return ""; // Haven't been initialized. + } + JNIEnv* env = GetJniEnv(); + assert(env); + jstring title_string = env->NewStringUTF(title); + jstring message_string = env->NewStringUTF(message); + jstring placeholder_string = env->NewStringUTF(placeholder); + jobject result_string = env->CallStaticObjectMethod( + text_entry_field_class_, text_entry_field_read_text_, GetActivity(), + title_string, message_string, placeholder_string); + env->DeleteLocalRef(title_string); + env->DeleteLocalRef(message_string); + env->DeleteLocalRef(placeholder_string); + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + env->ExceptionClear(); + } + if (result_string == nullptr) { + // Check if readText() returned null, which will be the case if an + // exception occurred or if TextEntryField returned null for some reason. + return ""; + } + const char* result_buffer = + env->GetStringUTFChars(static_cast(result_string), 0); + std::string result(result_buffer); + env->ReleaseStringUTFChars(static_cast(result_string), + result_buffer); + return result; + } + + private: + jclass text_entry_field_class_; + jmethodID text_entry_field_read_text_; +}; + +TextEntryFieldData* g_text_entry_field_data; + +// Use a Java class, TextEntryField, to prompt the user to enter some text. +// This function blocks until text was entered or the dialog was canceled. +// If the user cancels, returns an empty string. +std::string ReadTextInput(const char* title, const char* message, + const char* placeholder) { + assert(g_text_entry_field_data); + return g_text_entry_field_data->ReadText(title, message, placeholder); +} + +} // namespace app_framework + +// Execute common_main(), flush pending events and finish the activity. +extern "C" void android_main(struct android_app* state) { + // native_app_glue spawns a new thread, calling android_main() when the + // activity onStart() or onRestart() methods are called. This code handles + // the case where we're re-entering this method on a different thread by + // signalling the existing thread to exit, waiting for it to complete before + // reinitializing the application. + if (g_started) { + g_restarted = true; + // Wait for the existing thread to exit. + pthread_mutex_lock(&g_started_mutex); + pthread_mutex_unlock(&g_started_mutex); + } else { + g_started_mutex = PTHREAD_MUTEX_INITIALIZER; + } + pthread_mutex_lock(&g_started_mutex); + g_started = true; + + // Save native app glue state and setup a callback to track the state. + g_destroy_requested = false; + g_app_state = state; + g_app_state->onAppCmd = OnAppCmd; + + // Create the logging display. + app_framework::g_logging_utils_data = new app_framework::LoggingUtilsData(); + app_framework::g_logging_utils_data->Init(); + + // Create the text entry dialog. + app_framework::g_text_entry_field_data = + new app_framework::TextEntryFieldData(); + app_framework::g_text_entry_field_data->Init(); + + // Pipe stdout to AddToTextView so we get the gtest output. + int filedes[2]; + assert(pipe(filedes) != -1); + assert(dup2(filedes[1], STDOUT_FILENO) != -1); + pthread_t thread; + pthread_create(&thread, nullptr, app_framework::stdout_logger, + reinterpret_cast(filedes)); + + // Execute cross platform entry point. + // Copy the app name into a non-const array, as googletest requires that + // main() take non-const char* argv[] so it can modify the arguments. + char* argv[1]; + argv[0] = new char[strlen(TESTAPP_NAME) + 1]; + strcpy(argv[0], TESTAPP_NAME); // NOLINT + int return_value = common_main(1, argv); + delete[] argv[0]; + argv[0] = nullptr; + + app_framework::ProcessEvents(10); + + // Signal to stdout_logger to exit. + write(filedes[1], "\0", 1); + pthread_join(thread, nullptr); + close(filedes[0]); + close(filedes[1]); + // Pause a few seconds so you can see the results. If the user touches + // the screen during that time, don't exit until they choose to. + bool should_exit = false; + int exit_delay_seconds = + return_value ? kExitDelaySecondsIfError : kExitDelaySeconds; + do { + should_exit = app_framework::ProcessEvents(exit_delay_seconds * 1000); + } while (app_framework::g_logging_utils_data->DidTouch() && !should_exit); + + // Clean up logging display. + delete app_framework::g_logging_utils_data; + app_framework::g_logging_utils_data = nullptr; + + // Finish the activity. + if (!g_restarted) ANativeActivity_finish(state->activity); + + g_app_state->activity->vm->DetachCurrentThread(); + g_started = false; + g_restarted = false; + pthread_mutex_unlock(&g_started_mutex); +} diff --git a/testing/integration_tests/sample_framework/src/android/java/com/google/firebase/example/LoggingUtils.java b/testing/integration_tests/sample_framework/src/android/java/com/google/firebase/example/LoggingUtils.java new file mode 100644 index 0000000000..4ff20e8965 --- /dev/null +++ b/testing/integration_tests/sample_framework/src/android/java/com/google/firebase/example/LoggingUtils.java @@ -0,0 +1,169 @@ +// Copyright 2016 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.firebase.example; + +import android.app.Activity; +import android.content.Intent; +import android.graphics.Typeface; +import android.net.Uri; +import android.os.Handler; +import android.os.Looper; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.MotionEvent; +import android.view.View; +import android.view.Window; +import android.widget.LinearLayout; +import android.widget.ScrollView; +import android.widget.TextView; +import java.io.DataOutputStream; +import java.io.FileNotFoundException; +import java.io.IOException; + +/** + * A utility class, encapsulating the data and methods required to log arbitrary + * text to the screen, via a non-editable TextView. + */ +public class LoggingUtils { + private static TextView textView = null; + private static ScrollView scrollView = null; + // Tracks if the log window was touched at least once since the testapp was started. + private static boolean didTouch = false; + // If a test log file is specified, this is the log file's URI... + private static Uri logFile = null; + // ...and this is the stream to write to. + private static DataOutputStream logFileStream = null; + + /** Initializes the log window with the given activity and a monospace font. */ + public static void initLogWindow(Activity activity) { + initLogWindow(activity, true); + } + + /** + * Initializes the log window with the given activity, specifying whether to use a monospaced + * font. + */ + public static void initLogWindow(Activity activity, boolean monospace) { + LinearLayout linearLayout = new LinearLayout(activity); + scrollView = new ScrollView(activity); + textView = new TextView(activity); + textView.setTag("Logger"); + if (monospace) { + textView.setTypeface(Typeface.MONOSPACE); + textView.setTextSize(10); + } + linearLayout.addView(scrollView); + scrollView.addView(textView); + Window window = activity.getWindow(); + window.takeSurface(null); + window.setContentView(linearLayout); + + // Force the TextView to stay scrolled to the bottom. + textView.addTextChangedListener( + new TextWatcher() { + @Override + public void afterTextChanged(Editable e) { + // If the user never interacted with the screen, scroll to bottom. + if (scrollView != null && !didTouch) { + new Handler(Looper.getMainLooper()) + .post( + new Runnable() { + @Override + public void run() { + scrollView.fullScroll(View.FOCUS_DOWN); + } + }); + } + } + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) {} + + @Override + public void onTextChanged(CharSequence s, int start, int count, int after) {} + }); + textView.setOnTouchListener( + new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + didTouch = true; + return false; + } + }); + + Intent launchIntent = activity.getIntent(); + // Check if we are running on Firebase Test Lab, and set up a log file if we are. + if (launchIntent.getAction().equals("com.google.intent.action.TEST_LOOP")) { + startLogFile(activity, launchIntent.getData().toString()); + } + } + + /** Adds some text to the log window. */ + public static void addLogText(final String text) { + new Handler(Looper.getMainLooper()) + .post( + new Runnable() { + @Override + public void run() { + if (textView != null) { + textView.append(text); + if (logFileStream != null) { + try { + logFileStream.writeBytes(text); + } catch (IOException e) { + // It doesn't really matter if something went wrong writing to the test log. + } + } + } + } + }); + } + + /** + * Returns true if the user ever touched the log window during this run (to scroll it or + * otherwise), false if they never have. + */ + public static boolean getDidTouch() { + return didTouch; + } + + /** Start logging to a file at the given URI string. Used in TEST_LOOP mode. */ + public static boolean startLogFile(Activity activity, String logFileUri) { + logFile = Uri.parse(logFileUri); + if (logFile != null) { + try { + logFileStream = + new DataOutputStream(activity.getContentResolver().openOutputStream(logFile)); + } catch (FileNotFoundException e) { + addLogText("Failed to open log file " + logFile.getEncodedPath() + ": " + e); + return false; + } + return true; + } + return false; + } + + /** + * If the logger is logging to a file in local storage, return the URI for that file (which you + * can later pass into startLogFile in the future), otherwise return null if not logging to file. + */ + public static String getLogFile() { + if (logFile != null && logFileStream != null) { + return logFile.toString(); + } else { + return null; + } + } +} diff --git a/testing/integration_tests/sample_framework/src/android/java/com/google/firebase/example/TextEntryField.java b/testing/integration_tests/sample_framework/src/android/java/com/google/firebase/example/TextEntryField.java new file mode 100644 index 0000000000..34c59fa55d --- /dev/null +++ b/testing/integration_tests/sample_framework/src/android/java/com/google/firebase/example/TextEntryField.java @@ -0,0 +1,106 @@ +// Copyright 2016 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.firebase.example; + +import android.app.Activity; +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.widget.EditText; + +/** + * A utility class, with a method to prompt the user to enter a line of text, and a native method to + * sleep for a given number of milliseconds. + */ +public class TextEntryField { + private static Object lock = new Object(); + private static String resultText = null; + + /** + * Prompt the user with a text field, blocking until the user fills it out, then returns the text + * they entered. If the user cancels, returns an empty string. + */ + public static String readText( + final Activity activity, final String title, final String message, final String placeholder) { + resultText = null; + // Show the alert dialog on the main thread. + activity.runOnUiThread( + new Runnable() { + @Override + public void run() { + AlertDialog.Builder alertBuilder = new AlertDialog.Builder(activity); + alertBuilder.setTitle(title); + alertBuilder.setMessage(message); + + // Set up and add the text field. + final EditText textField = new EditText(activity); + textField.setHint(placeholder); + alertBuilder.setView(textField); + + alertBuilder.setPositiveButton( + "OK", + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int whichButton) { + synchronized (lock) { + resultText = textField.getText().toString(); + } + } + }); + + alertBuilder.setNegativeButton( + "Cancel", + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int whichButton) { + synchronized (lock) { + resultText = ""; + } + } + }); + + alertBuilder.setOnCancelListener( + new DialogInterface.OnCancelListener() { + @Override + public void onCancel(DialogInterface dialog) { + synchronized (lock) { + resultText = ""; + } + } + }); + alertBuilder.show(); + } + }); + + // In our original thread, wait for the dialog to finish, then return its result. + while (true) { + // Pause a second, waiting for the user to enter text. + if (nativeSleep(1000)) { + // If this returns true, an exit was requested. + return ""; + } + synchronized (lock) { + if (resultText != null) { + // resultText will be set to non-null when a dialog button is clicked, or the dialog + // is canceled. + String result = resultText; + resultText = null; // Consume the result. + return result; + } + } + } + } + + private static native boolean nativeSleep(int milliseconds); +} diff --git a/testing/integration_tests/sample_framework/src/app_framework.cc b/testing/integration_tests/sample_framework/src/app_framework.cc new file mode 100644 index 0000000000..bed0d01526 --- /dev/null +++ b/testing/integration_tests/sample_framework/src/app_framework.cc @@ -0,0 +1,96 @@ +// Copyright 2019 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "app_framework.h" // NOLINT + +#include +#include + +#include +#include +#include +#include +#include + +namespace app_framework { + +// Base logging methods, implemented by platform-specific files. +void LogMessage(const char* format, ...); +void LogMessageV(bool filtered, const char* format, va_list list); + +enum LogLevel g_log_level = kInfo; + +void SetLogLevel(LogLevel log_level) { g_log_level = log_level; } + +LogLevel GetLogLevel() { return g_log_level; } + +void LogDebug(const char* format, ...) { + va_list list; + va_start(list, format); + std::string format_str("DEBUG: "); + format_str += format; + app_framework::LogMessageV(g_log_level > kDebug, format_str.c_str(), list); + va_end(list); +} + +void LogInfo(const char* format, ...) { + va_list list; + va_start(list, format); + std::string format_str("INFO: "); + format_str += format; + app_framework::LogMessageV(g_log_level > kInfo, format_str.c_str(), list); + va_end(list); +} + +void LogWarning(const char* format, ...) { + va_list list; + va_start(list, format); + std::string format_str("WARNING: "); + format_str += format; + app_framework::LogMessageV(g_log_level > kWarning, format_str.c_str(), list); + va_end(list); +} + +void LogError(const char* format, ...) { + va_list list; + va_start(list, format); + std::string format_str("ERROR: "); + format_str += format; + app_framework::LogMessageV(g_log_level > kError, format_str.c_str(), list); + va_end(list); +} + +#if !defined(_WIN32) // Windows requires its own version of time-handling code. +int64_t GetCurrentTimeInMicroseconds() { + struct timeval now; + gettimeofday(&now, nullptr); + return now.tv_sec * 1000000LL + now.tv_usec; +} +#endif // !defined(_WIN32) + +#if defined(__ANDROID__) || (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) +void ChangeToFileDirectory(const char*) {} +#endif // defined(__ANDROID__) || (defined(TARGET_OS_IPHONE) && + // TARGET_OS_IPHONE) + +#if defined(_WIN32) +#define stat _stat +#endif // defined(_WIN32) + +bool FileExists(const char* file_path) { + struct stat s; + return stat(file_path, &s) == 0; +} + +} // namespace app_framework diff --git a/testing/integration_tests/sample_framework/src/app_framework.h b/testing/integration_tests/sample_framework/src/app_framework.h new file mode 100644 index 0000000000..580b3bd939 --- /dev/null +++ b/testing/integration_tests/sample_framework/src/app_framework.h @@ -0,0 +1,131 @@ +// Copyright 2016 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef APP_FRAMEWORK_H_ // NOLINT +#define APP_FRAMEWORK_H_ // NOLINT + +#include +#include + +#if defined(__APPLE__) +#include +#endif // defined(__APPLE__) + +#if !defined(_WIN32) +#include +#endif +#if defined(__ANDROID__) +#include +#include +#elif defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE +extern "C" { +#include +} // extern "C" +#endif // platforms + +// Defined using -DTESTAPP_NAME=some_app_name when compiling this +// file. +#ifndef TESTAPP_NAME +#define TESTAPP_NAME "android_main" +#endif // TESTAPP_NAME + +extern "C" int common_main(int argc, char* argv[]); + +namespace app_framework { + +// Platform-independent logging methods. +enum LogLevel { kDebug = 0, kInfo, kWarning, kError }; +void SetLogLevel(LogLevel log_level); +LogLevel GetLogLevel(); +void LogError(const char* format, ...); +void LogWarning(const char* format, ...); +void LogInfo(const char* format, ...); +void LogDebug(const char* format, ...); + +// Set this to true to have all log messages saved regardless of loglevel; you +// can output them later via OutputFullLog or clear them via ClearFullLog. +void SetPreserveFullLog(bool b); +// Get the value previously set by SetPreserveFullLog. +bool GetPreserveFullLog(); + +// Add a line of text to the "full log" to be output via OutputFullLog. +void AddToFullLog(const char* str); + +// Clear the logs that were saved. +void ClearFullLog(); + +// Output the full saved logs (if you SetPreserveFullLog(true) earlier). +void OutputFullLog(); + +// Platform-independent method to flush pending events for the main thread. +// Returns true when an event requesting program-exit is received. +bool ProcessEvents(int msec); + +// Returns a path to a writable directory for the given platform. +std::string PathForResource(); + +// Returns the number of microseconds since the epoch. +int64_t GetCurrentTimeInMicroseconds(); + +// On desktop, change the current working directory to the directory +// containing the specified file. On mobile, this is a no-op. +void ChangeToFileDirectory(const char* file_path); + +// Return whether the file exists. +bool FileExists(const char* file_path); + +// WindowContext represents the handle to the parent window. Its type +// (and usage) vary based on the OS. +#if defined(__ANDROID__) +typedef jobject WindowContext; // A jobject to the Java Activity. +#elif defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE +typedef id WindowContext; // A pointer to an iOS UIView. +#else +typedef void* WindowContext; // A void* for any other environments. +#endif + +#if defined(__ANDROID__) +// Get the JNI environment. +JNIEnv* GetJniEnv(); +// Get the activity. +jobject GetActivity(); +// Find a class, attempting to load the class if it's not found. +jclass FindClass(JNIEnv* env, jobject activity_object, const char* class_name); +#endif // defined(__ANDROID__) + +// Returns true if the logger is currently logging to a file. +bool IsLoggingToFile(); + +// Start logging to the given file. You only need to do this if the app has been +// restarted since it was initially run in test loop mode. +bool StartLoggingToFile(const char* path); + +// Returns a variable that describes the window context for the app. On Android +// this will be a jobject pointing to the Activity. On iOS, it's an id pointing +// to the root view of the view controller. +WindowContext GetWindowContext(); + +// Run the given function on a detached background thread. +void RunOnBackgroundThread(void* (*func)(void* data), void* data); + +// Prompt the user with a dialog box to enter a line of text, blocking +// until the user enters the text or the dialog box is canceled. +// Returns the text that was entered, or an empty string if the user +// canceled. +std::string ReadTextInput(const char* title, const char* message, + const char* placeholder); + +} // namespace app_framework + +#endif // APP_FRAMEWORK_H_ // NOLINT diff --git a/testing/integration_tests/sample_framework/src/desktop/desktop_app_framework.cc b/testing/integration_tests/sample_framework/src/desktop/desktop_app_framework.cc new file mode 100644 index 0000000000..115ad2bce2 --- /dev/null +++ b/testing/integration_tests/sample_framework/src/desktop/desktop_app_framework.cc @@ -0,0 +1,215 @@ +// Copyright 2016 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#include +#include +#include +#include +#include // NOLINT +#include + +#ifdef _WIN32 +#include +#define chdir _chdir +#else +#include +#include +#include +#endif // _WIN32 + +#ifdef _WIN32 +#include +#endif // _WIN32 + +#include "app_framework.h" // NOLINT + +static bool quit = false; + +#ifdef _WIN32 +static BOOL WINAPI SignalHandler(DWORD event) { + if (!(event == CTRL_C_EVENT || event == CTRL_BREAK_EVENT)) { + return FALSE; + } + quit = true; + return TRUE; +} +#else +static void SignalHandler(int /* ignored */) { quit = true; } +#endif // _WIN32 + +namespace app_framework { + +bool ProcessEvents(int msec) { +#ifdef _WIN32 + Sleep(msec); +#else + usleep(msec * 1000); +#endif // _WIN32 + return quit; +} + +std::string PathForResource() { +#if defined(_WIN32) + // On Windows we should hvae TEST_TMPDIR or TEMP or TMP set. + char buf[MAX_PATH + 1]; + if (GetEnvironmentVariable("TEST_TMPDIR", buf, sizeof(buf)) || + GetEnvironmentVariable("TEMP", buf, sizeof(buf)) || + GetEnvironmentVariable("TMP", buf, sizeof(buf))) { + std::string path(buf); + // Add trailing slash. + if (path[path.size() - 1] != '\\') path += '\\'; + return path; + } +#else + // Linux and OS X should either have the TEST_TMPDIR environment variable set + // or use /tmp. + if (const char* value = getenv("TEST_TMPDIR")) { + std::string path(value); + // Add trailing slash. + if (path[path.size() - 1] != '/') path += '/'; + return path; + } + struct stat s; + if (stat("/tmp", &s) == 0) { + if (s.st_mode & S_IFDIR) { + return std::string("/tmp/"); + } + } +#endif // defined(_WIN32) + // If nothing else, use the current directory. + return std::string(); +} +void LogMessageV(bool suppress, const char* format, va_list list) { + // Save the log to the g_full_logs list regardless of whether it should be + // suppressed. + static const int kLineBufferSize = 1024; + char buffer[kLineBufferSize + 2]; + int string_len = vsnprintf(buffer, kLineBufferSize, format, list); + string_len = string_len < kLineBufferSize ? string_len : kLineBufferSize; + // Append a linebreak to the buffer. + buffer[string_len] = '\n'; + buffer[string_len + 1] = '\0'; + if (GetPreserveFullLog()) { + AddToFullLog(buffer); + } + if (!suppress) { + fputs(buffer, stdout); + fflush(stdout); + } +} + +void LogMessage(const char* format, ...) { + va_list list; + va_start(list, format); + LogMessageV(false, format, list); + va_end(list); +} + +static bool g_save_full_log = false; +static std::vector g_full_logs; // NOLINT + +void AddToFullLog(const char* str) { g_full_logs.push_back(std::string(str)); } + +bool GetPreserveFullLog() { return g_save_full_log; } +void SetPreserveFullLog(bool b) { g_save_full_log = b; } + +void ClearFullLog() { g_full_logs.clear(); } + +void OutputFullLog() { + for (int i = 0; i < g_full_logs.size(); ++i) { + fputs(g_full_logs[i].c_str(), stdout); + } + fflush(stdout); + ClearFullLog(); +} + +WindowContext GetWindowContext() { return nullptr; } + +// Change the current working directory to the directory containing the +// specified file. +void ChangeToFileDirectory(const char* file_path) { + std::string path(file_path); + std::replace(path.begin(), path.end(), '\\', '/'); + auto slash = path.rfind('/'); + if (slash != std::string::npos) { + std::string directory = path.substr(0, slash); + if (!directory.empty()) { + LogDebug("chdir %s", directory.c_str()); + chdir(directory.c_str()); + } + } +} + +#if defined(_WIN32) // The other platforms are implemented in app_framework.cc. +// Returns the number of microseconds since the epoch. +int64_t GetCurrentTimeInMicroseconds() { + FILETIME file_time; + GetSystemTimeAsFileTime(&file_time); + + ULARGE_INTEGER now; + now.LowPart = file_time.dwLowDateTime; + now.HighPart = file_time.dwHighDateTime; + + // Windows file time is expressed in 100s of nanoseconds. + // To convert to microseconds, multiply x10. + return now.QuadPart * 10LL; +} +#endif // defined(_WIN32) + +void RunOnBackgroundThread(void* (*func)(void*), void* data) { + // On desktop, use std::thread as Windows doesn't support pthreads. + std::thread thread(func, data); + thread.detach(); +} + +std::string ReadTextInput(const char* title, const char* message, + const char* placeholder) { + if (title && *title) { + int len = strlen(title); + printf("\n"); + for (int i = 0; i < len; ++i) { + printf("="); + } + printf("\n%s\n", title); + for (int i = 0; i < len; ++i) { + printf("="); + } + } + printf("\n%s", message); + if (placeholder && *placeholder) { + printf(" [%s]", placeholder); + } + printf(": "); + fflush(stdout); + std::string input_line; + std::getline(std::cin, input_line); + return input_line.empty() ? std::string(placeholder) : input_line; +} + +bool IsLoggingToFile() { return false; } + +} // namespace app_framework + +int main(int argc, char* argv[]) { +#ifdef _WIN32 + SetConsoleCtrlHandler((PHANDLER_ROUTINE)SignalHandler, TRUE); +#else + signal(SIGINT, SignalHandler); +#endif // _WIN32 + return common_main(argc, argv); +} diff --git a/testing/integration_tests/sample_framework/src/ios/ios_app_framework.mm b/testing/integration_tests/sample_framework/src/ios/ios_app_framework.mm new file mode 100755 index 0000000000..2710c1f544 --- /dev/null +++ b/testing/integration_tests/sample_framework/src/ios/ios_app_framework.mm @@ -0,0 +1,346 @@ +// Copyright 2016 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#import + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "app_framework.h" + +@interface AppDelegate : UIResponder + +@property(nonatomic, strong) UIWindow *window; + +@end + +@interface FTAViewController : UIViewController + +@property(atomic, strong) NSString *textEntryResult; + +@end + +static NSString * const kGameLoopUrlPrefix= @"firebase-game-loop"; +static NSString * const kGameLoopCompleteUrlScheme= @"firebase-game-loop-complete://"; +static const float kGameLoopSecondsToPauseBeforeQuitting = 5.0f; + +// Test Loop on iOS doesn't provide the app under test a path to save logs to, so set it here. +#define GAMELOOP_DEFAULT_LOG_FILE "Results1.json" + +static int g_exit_status = 0; +static bool g_shutdown = false; +static NSCondition *g_shutdown_complete; +static NSCondition *g_shutdown_signal; +static UITextView *g_text_view; +static UIView *g_parent_view; +static FTAViewController *g_view_controller; +static bool g_gameloop_launch = false; +static NSURL *g_results_url; +static NSString *g_file_name; +static NSString *g_file_url_path; + +@implementation FTAViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + g_parent_view = self.view; + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + // Copy the app name into a non-const array, as googletest requires that + // main() take non-const char* argv[] so it can modify the arguments. + char *argv[1]; + argv[0] = new char[strlen(TESTAPP_NAME) + 1]; + strcpy(argv[0], TESTAPP_NAME); // NOLINT + [g_shutdown_signal lock]; + g_exit_status = common_main(1, argv); + [g_shutdown_complete signal]; + delete[] argv[0]; + argv[0] = nullptr; + [NSThread sleepForTimeInterval:kGameLoopSecondsToPauseBeforeQuitting]; + [UIApplication.sharedApplication openURL:[NSURL URLWithString:kGameLoopCompleteUrlScheme] + options:[NSDictionary dictionary] + completionHandler:nil]; + + }); +} + +@end +namespace app_framework { + +bool ProcessEvents(int msec) { + [g_shutdown_signal + waitUntilDate:[NSDate dateWithTimeIntervalSinceNow:static_cast(msec) / 1000.0f]]; + return g_shutdown; +} + +std::string PathForResource() { + NSArray *paths = + NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); + NSString *documentsDirectory = paths.firstObject; + // Force a trailing slash by removing any that exists, then appending another. + return std::string( + [[documentsDirectory stringByStandardizingPath] stringByAppendingString:@"/"].UTF8String); +} + +WindowContext GetWindowContext() { + return g_parent_view; +} + +// Log a message that can be viewed in the console. +void LogMessageV(bool suppress, const char *format, va_list list) { + NSString *formatString = @(format); + + NSString *message = [[NSString alloc] initWithFormat:formatString arguments:list]; + message = [message stringByAppendingString:@"\n"]; + + if (GetPreserveFullLog()) { + AddToFullLog(message.UTF8String); + } + if (!suppress) { + fputs(message.UTF8String, stdout); + fflush(stdout); + } +} + +void LogMessage(const char *format, ...) { + va_list list; + va_start(list, format); + LogMessageV(false, format, list); + va_end(list); +} + +static bool g_save_full_log = false; +static std::vector g_full_logs; // NOLINT + +void AddToFullLog(const char* str) { g_full_logs.push_back(std::string(str)); } + +bool GetPreserveFullLog() { return g_save_full_log; } +void SetPreserveFullLog(bool b) { g_save_full_log = b; } + +void ClearFullLog() { g_full_logs.clear(); } + +void OutputFullLog() { + for (int i = 0; i < g_full_logs.size(); ++i) { + fputs(g_full_logs[i].c_str(), stdout); + } + fflush(stdout); + ClearFullLog(); +} + +// Log a message that can be viewed in the console. +void AddToTextView(const char *str) { + NSString *message = @(str); + + dispatch_async(dispatch_get_main_queue(), ^{ + g_text_view.text = [g_text_view.text stringByAppendingString:message]; + NSRange range = NSMakeRange(g_text_view.text.length, 0); + [g_text_view scrollRangeToVisible:range]; + }); + if (g_gameloop_launch) { + NSData *data = [message dataUsingEncoding:NSUTF8StringEncoding]; + if ([NSFileManager.defaultManager fileExistsAtPath:g_file_url_path]) { + NSFileHandle *fileHandler = [NSFileHandle fileHandleForUpdatingAtPath:g_file_url_path]; + [fileHandler seekToEndOfFile]; + [fileHandler writeData:data]; + [fileHandler closeFile]; + } else { + NSLog(@"Write to file %@", g_file_url_path); + [data writeToFile:g_file_url_path atomically:YES]; + } + } + +} + +// Remove all lines starting with these strings. +static const char *const filter_lines[] = {nullptr}; + +bool should_filter(const char *str) { + for (int i = 0; filter_lines[i] != nullptr; ++i) { + if (strncmp(str, filter_lines[i], strlen(filter_lines[i])) == 0) return true; + } + return false; +} + +void *stdout_logger(void *filedes_ptr) { + int fd = reinterpret_cast(filedes_ptr)[0]; + std::string buffer; + char bufchar; + while (int n = read(fd, &bufchar, 1)) { + if (bufchar == '\0') { + break; + } else if (bufchar == '\n') { + if (!should_filter(buffer.c_str())) { + NSLog(@"%s", buffer.c_str()); + buffer = buffer + bufchar; // Add the newline + app_framework::AddToTextView(buffer.c_str()); + } + buffer.clear(); + } else { + buffer = buffer + bufchar; + } + } + return nullptr; +} + +void RunOnBackgroundThread(void* (*func)(void*), void* data) { + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + func(data); + }); +} + +// Create an alert dialog via UIAlertController, and prompt the user to enter a line of text. +// This function spins until the text has been entered (or the alert dialog was canceled). +// If the user cancels, returns an empty string. +std::string ReadTextInput(const char *title, const char *message, const char *placeholder) { + assert(g_view_controller); + // This should only be called from a background thread, as it blocks, which will mess up the main + // thread. + assert(![NSThread isMainThread]); + + g_view_controller.textEntryResult = nil; + + dispatch_async(dispatch_get_main_queue(), ^{ + UIAlertController *alertController = + [UIAlertController alertControllerWithTitle:@(title) + message:@(message) + preferredStyle:UIAlertControllerStyleAlert]; + [alertController addTextFieldWithConfigurationHandler:^(UITextField *_Nonnull textField) { + textField.placeholder = @(placeholder); + }]; + UIAlertAction *confirmAction = [UIAlertAction + actionWithTitle:@"OK" + style:UIAlertActionStyleDefault + handler:^(UIAlertAction *_Nonnull action) { + g_view_controller.textEntryResult = alertController.textFields.firstObject.text; + }]; + [alertController addAction:confirmAction]; + UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"Cancel" + style:UIAlertActionStyleCancel + handler:^(UIAlertAction *_Nonnull action) { + g_view_controller.textEntryResult = @""; + }]; + [alertController addAction:cancelAction]; + [g_view_controller presentViewController:alertController animated:YES completion:nil]; + }); + + while (true) { + // Pause a second, waiting for the user to enter text. + if (ProcessEvents(1000)) { + // If this returns true, an exit was requested. + return ""; + } + if (g_view_controller.textEntryResult != nil) { + // textEntryResult will be set to non-nil when a dialog button is clicked. + std::string result = g_view_controller.textEntryResult.UTF8String; + g_view_controller.textEntryResult = nil; // Consume the result. + return result; + } + } +} + +bool IsLoggingToFile() { return g_file_url_path; } + +bool StartLoggingToFile(const char *file_path) { + NSURL *home_url = [NSURL fileURLWithPath:NSHomeDirectory()]; + g_results_url = [home_url URLByAppendingPathComponent:@"/Documents/GameLoopResults"]; + g_file_name = @(file_path); + g_file_url_path = [g_results_url URLByAppendingPathComponent:g_file_name].path; + NSError *error; + if (![NSFileManager.defaultManager fileExistsAtPath:[g_results_url path]]) { + if (![NSFileManager.defaultManager createDirectoryAtPath:g_results_url.path + withIntermediateDirectories:true + attributes:nil + error:&error]) { + app_framework::LogError("Couldn't create directory %s: %s", g_results_url.path, + error.description.UTF8String); + g_file_url_path = nil; + return false; + } + } + return true; +} + +} // namespace app_framework + +int main(int argc, char* argv[]) { + // Pipe stdout to call LogToTextView so we can see the gtest output. + int filedes[2]; + assert(pipe(filedes) != -1); + assert(dup2(filedes[1], STDOUT_FILENO) != -1); + pthread_t thread; + pthread_create(&thread, nullptr, app_framework::stdout_logger, reinterpret_cast(filedes)); + @autoreleasepool { + UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } + // Signal to stdout_logger to exit. + write(filedes[1], "\0", 1); + pthread_join(thread, nullptr); + close(filedes[0]); + close(filedes[1]); + + NSLog(@"Application Exit"); + return g_exit_status; +} + +@implementation AppDelegate +- (BOOL)application:(UIApplication *)app + openURL:(NSURL *)url + options:(NSDictionary *)options { + if ([url.scheme isEqual:kGameLoopUrlPrefix]) { + g_gameloop_launch = true; + app_framework::StartLoggingToFile(GAMELOOP_DEFAULT_LOG_FILE); + return YES; + } + NSLog(@"The testapp will not log to files since it is not launched by URL %@", + kGameLoopUrlPrefix); + return NO; +} + +- (BOOL)application:(UIApplication*)application + didFinishLaunchingWithOptions:(NSDictionary*)launchOptions { + g_shutdown_complete = [[NSCondition alloc] init]; + g_shutdown_signal = [[NSCondition alloc] init]; + [g_shutdown_complete lock]; + + self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; + g_view_controller = [[FTAViewController alloc] init]; + self.window.rootViewController = g_view_controller; + [self.window makeKeyAndVisible]; + + g_text_view = [[UITextView alloc] initWithFrame:g_view_controller.view.bounds]; + + g_text_view.accessibilityIdentifier = @"Logger"; + g_text_view.editable = NO; + g_text_view.scrollEnabled = YES; + g_text_view.userInteractionEnabled = YES; + g_text_view.font = [UIFont fontWithName:@"Courier" size:10]; + [g_view_controller.view addSubview:g_text_view]; + + return YES; +} + +- (void)applicationWillTerminate:(UIApplication *)application { + g_shutdown = true; + [g_shutdown_signal signal]; + [g_shutdown_complete wait]; +} +@end diff --git a/testing/integration_tests/test_framework/LibraryManifest.xml b/testing/integration_tests/test_framework/LibraryManifest.xml new file mode 100644 index 0000000000..f41ad72367 --- /dev/null +++ b/testing/integration_tests/test_framework/LibraryManifest.xml @@ -0,0 +1,7 @@ + + + + + + diff --git a/testing/integration_tests/test_framework/download_googletest.py b/testing/integration_tests/test_framework/download_googletest.py new file mode 100755 index 0000000000..9f3bd9883c --- /dev/null +++ b/testing/integration_tests/test_framework/download_googletest.py @@ -0,0 +1,79 @@ +#!/usr/bin/python + +# Copyright 2019 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Download GoogleTest from GitHub into a subdirectory.""" + +# pylint: disable=superfluous-parens,g-import-not-at-top + +import io +import os +import shutil +import tempfile +import zipfile + +# Import urllib in a way that is compatible with Python 2 and Python 3. +try: + import urllib.request + compatible_urlopen = urllib.request.urlopen +except ImportError: + import urllib2 + compatible_urlopen = urllib2.urlopen + +# Run from inside the script directory +SRC_DIR = os.path.relpath(os.path.dirname(__file__)) +TAG = os.path.basename(__file__) + +GOOGLETEST_ZIP = 'https://github.com/google/googletest/archive/master.zip' +# Top-level directory inside the zip file to ignore. +GOOGLETEST_DIR = os.path.join('googletest-master') + +# The GoogleTest code is copied into this subdirectory. +# This structure matches where the files are placed by CMake. +DESTINATION_DIR = os.path.join(SRC_DIR, 'external/googletest/src') + +CHECK_EXISTS = os.path.join( + SRC_DIR, 'external/googletest/src/googletest/src/gtest-all.cc') + +# Don't download it again if it already exists. +if os.path.exists(CHECK_EXISTS): + print('%s: GoogleTest already downloaded, skipping.' % (TAG)) + exit(0) + +# Download the zipfile into memory, extract into /tmp, then move into the +# current directory. + +try: + # Download to a temporary directory. + zip_extract_path = tempfile.mkdtemp(suffix='googletestdownload') + print('%s: Downloading GoogleTest from %s' % (TAG, GOOGLETEST_ZIP)) + zip_download = compatible_urlopen(GOOGLETEST_ZIP) + zip_file = io.BytesIO(zip_download.read()) + print('%s: Extracting GoogleTest...' % (TAG)) + zip_ref = zipfile.ZipFile(zip_file, mode='r') + zip_ref.extractall(zip_extract_path) + if os.path.exists(DESTINATION_DIR): + shutil.rmtree(DESTINATION_DIR) + shutil.move(os.path.join(zip_extract_path, GOOGLETEST_DIR), DESTINATION_DIR) + print('%s: Finished.' % (TAG)) +except Exception as e: + raise +finally: + # Clean up the temp directory if we created one. + if os.path.exists(zip_extract_path): + shutil.rmtree(zip_extract_path) + +if not os.path.exists(CHECK_EXISTS): + print('%s: Failed to download GoogleTest to %s' % (TAG, DESTINATION_DIR)) + exit(1) diff --git a/testing/integration_tests/test_framework/src/android/android_firebase_test_framework.cc b/testing/integration_tests/test_framework/src/android/android_firebase_test_framework.cc new file mode 100644 index 0000000000..12c092f2d9 --- /dev/null +++ b/testing/integration_tests/test_framework/src/android/android_firebase_test_framework.cc @@ -0,0 +1,225 @@ +// Copyright 2019 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "firebase_test_framework.h" // NOLINT + +namespace firebase_test_framework { + +using app_framework::LogDebug; +using app_framework::LogError; + +// Blocking HTTP request helper function. +static bool SendHttpRequest(const char* url, + const std::map& headers, + const std::string* post_body, int* response_code, + std::string* response_str) { + JNIEnv* env = app_framework::GetJniEnv(); + jobject activity = app_framework::GetActivity(); + jclass simple_http_request_class = app_framework::FindClass( + env, activity, "com/google/firebase/example/SimpleHttpRequest"); + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + env->ExceptionClear(); + return false; + } + jmethodID constructor = env->GetMethodID(simple_http_request_class, "", + "(Ljava/lang/String;)V"); + jmethodID set_post_data = + env->GetMethodID(simple_http_request_class, "setPostData", "([B)V"); + jmethodID add_header = + env->GetMethodID(simple_http_request_class, "addHeader", + "(Ljava/lang/String;Ljava/lang/String;)V"); + jmethodID perform = env->GetMethodID(simple_http_request_class, "perform", + "()Ljava/lang/String;"); + jmethodID get_response_code = + env->GetMethodID(simple_http_request_class, "getResponseCode", "()I"); + + jstring url_jstring = env->NewStringUTF(url); + // http_request = new SimpleHttpRequestClass(url); + jobject http_request = + env->NewObject(simple_http_request_class, constructor, url_jstring); + env->DeleteLocalRef(url_jstring); + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + env->ExceptionClear(); + if (http_request) env->DeleteLocalRef(http_request); + return false; + } + // for (header : headers) { + // http_request.addHeader(header.key, header.value); + // } + for (auto i = headers.begin(); i != headers.end(); ++i) { + jstring key_jstring = env->NewStringUTF(i->first.c_str()); + jstring value_jstring = env->NewStringUTF(i->second.c_str()); + env->CallVoidMethod(http_request, add_header, key_jstring, value_jstring); + env->DeleteLocalRef(key_jstring); + env->DeleteLocalRef(value_jstring); + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + env->ExceptionClear(); + if (http_request) env->DeleteLocalRef(http_request); + return false; + } + } + if (post_body != nullptr) { + // http_request.setPostBody(post_body); + jbyteArray post_body_array = env->NewByteArray(post_body->length()); + env->SetByteArrayRegion(post_body_array, 0, post_body->length(), + reinterpret_cast(post_body->c_str())); + env->CallVoidMethod(http_request, set_post_data, post_body_array); + env->DeleteLocalRef(post_body_array); + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + env->ExceptionClear(); + if (http_request) env->DeleteLocalRef(http_request); + return false; + } + } + // String response = http_request.perform(); + jobject response = env->CallObjectMethod(http_request, perform); + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + env->ExceptionClear(); + } + jstring response_jstring = static_cast(response); + // int response_code = http_request.getResponseCode(); + jint response_code_jint = env->CallIntMethod(http_request, get_response_code); + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + env->ExceptionClear(); + } + LogDebug("HTTP status code %d", response_code_jint); + if (response_code) *response_code = response_code_jint; + + env->DeleteLocalRef(http_request); + if (response_jstring == nullptr) { + return false; + } + const char* response_text = env->GetStringUTFChars(response_jstring, nullptr); + LogDebug("Got response: %s", response_text); + if (response_str) *response_str = response_text; + env->ReleaseStringUTFChars(response_jstring, response_text); + env->DeleteLocalRef(response); + return true; +} + +// Blocking HTTP request helper function, for testing only. +bool FirebaseTest::SendHttpGetRequest( + const char* url, const std::map& headers, + int* response_code, std::string* response_str) { + return SendHttpRequest(url, headers, nullptr, response_code, response_str); +} + +bool FirebaseTest::SendHttpPostRequest( + const char* url, const std::map& headers, + const std::string& post_body, int* response_code, + std::string* response_str) { + return SendHttpRequest(url, headers, &post_body, response_code, response_str); +} + +bool FirebaseTest::OpenUrlInBrowser(const char* url) { + JNIEnv* env = app_framework::GetJniEnv(); + jobject activity = app_framework::GetActivity(); + jclass simple_http_request_class = app_framework::FindClass( + env, activity, "com/google/firebase/example/SimpleHttpRequest"); + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + env->ExceptionClear(); + return false; + } + jmethodID open_url = + env->GetStaticMethodID(simple_http_request_class, "openUrlInBrowser", + "(Ljava/lang/String;Landroid/app/Activity;)V"); + jstring url_jstring = env->NewStringUTF(url); + env->CallStaticVoidMethod(simple_http_request_class, open_url, url_jstring, + activity); + env->DeleteLocalRef(url_jstring); + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + env->ExceptionClear(); + return false; + } + return true; +} + +bool FirebaseTest::SetPersistentString(const char* key, const char* value) { + if (key == nullptr) { + LogError("SetPersistentString: null key is not allowed."); + return false; + } + JNIEnv* env = app_framework::GetJniEnv(); + jobject activity = app_framework::GetActivity(); + jclass simple_persistent_storage_class = app_framework::FindClass( + env, activity, "com/google/firebase/example/SimplePersistentStorage"); + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + env->ExceptionClear(); + return false; + } + jmethodID set_string = env->GetStaticMethodID( + simple_persistent_storage_class, "setString", + "(Landroid/app/Activity;Ljava/lang/String;Ljava/lang/String;)V"); + jstring key_jstring = env->NewStringUTF(key); + jstring value_jstring = value ? env->NewStringUTF(value) : nullptr; + env->CallStaticVoidMethod(simple_persistent_storage_class, set_string, + activity, key_jstring, value_jstring); + env->DeleteLocalRef(key_jstring); + if (value_jstring) { + env->DeleteLocalRef(value_jstring); + } + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + env->ExceptionClear(); + return false; + } + return true; +} + +bool FirebaseTest::GetPersistentString(const char* key, + std::string* value_out) { + JNIEnv* env = app_framework::GetJniEnv(); + jobject activity = app_framework::GetActivity(); + jclass simple_persistent_storage_class = app_framework::FindClass( + env, activity, "com/google/firebase/example/SimplePersistentStorage"); + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + env->ExceptionClear(); + return false; + } + jmethodID get_string = env->GetStaticMethodID( + simple_persistent_storage_class, "getString", + "(Landroid/app/Activity;Ljava/lang/String;)Ljava/lang/String;"); + jstring key_jstring = env->NewStringUTF(key); + jstring value_jstring = static_cast(env->CallStaticObjectMethod( + simple_persistent_storage_class, get_string, activity, key_jstring)); + env->DeleteLocalRef(key_jstring); + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + env->ExceptionClear(); + if (value_jstring) { + env->DeleteLocalRef(value_jstring); + } + return false; + } + if (value_jstring == nullptr) { + return false; + } + const char* value_text = env->GetStringUTFChars(value_jstring, nullptr); + if (value_out) *value_out = std::string(value_text); + env->ReleaseStringUTFChars(value_jstring, value_text); + env->DeleteLocalRef(value_jstring); + return true; +} + +} // namespace firebase_test_framework diff --git a/testing/integration_tests/test_framework/src/android/java/com/google/firebase/example/SimpleHttpRequest.java b/testing/integration_tests/test_framework/src/android/java/com/google/firebase/example/SimpleHttpRequest.java new file mode 100644 index 0000000000..d56a2d6763 --- /dev/null +++ b/testing/integration_tests/test_framework/src/android/java/com/google/firebase/example/SimpleHttpRequest.java @@ -0,0 +1,118 @@ +// Copyright 2019 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.firebase.example; + +import android.app.Activity; +import android.content.Intent; +import android.net.Uri; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.HashMap; +import java.util.Map; + +/** + * A simple client for performing synchronous HTTP/HTTPS requests, used for testing purposes only. + */ +public final class SimpleHttpRequest { + private URL url; + private final HashMap headers; + private byte[] postData; + private int responseCode; + /** Create a new SimpleHttpRequest with a default URL. */ + public SimpleHttpRequest(String urlString) throws MalformedURLException { + this.headers = new HashMap<>(); + this.postData = null; + setUrl(urlString); + } + /** Set the URL to the given string, or null if it can't be parsed. */ + public void setUrl(String urlString) throws MalformedURLException { + this.url = new URL(urlString); + } + /** Get the previously-set URL. */ + public URL getUrl() { + return this.url; + } + /** Set the HTTP POST body, and set this request to a POST request. */ + public void setPostData(byte[] postData) { + this.postData = postData; + } + /** Clear out the HTTP POST body, setting this request back to a GET request. */ + public void clearPostData() { + this.postData = null; + } + /** Add a header key-value pair. */ + public void addHeader(String key, String value) { + this.headers.put(key, value); + } + /** Clear previously-set headers. */ + public void clearHeaders() { + this.headers.clear(); + } + + /** Get the response code returned by the server, after perform() is finished. */ + public int getResponseCode() { + return this.responseCode; + } + + /** + * Perform a HTTP request to the given URL, with the given headers. If postData is non-null, use a + * POST request, else use a GET request. This method blocks until getting a response. + */ + public String perform() throws IOException { + if (this.url == null) { + return null; + } + + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod(postData != null ? "POST" : "GET"); + for (Map.Entry entry : this.headers.entrySet()) { + connection.setRequestProperty(entry.getKey(), entry.getValue()); + } + if (this.postData != null) { + connection.setDoOutput(true); + connection.setFixedLengthStreamingMode(postData.length); + connection.getOutputStream().write(postData); + } + responseCode = connection.getResponseCode(); + StringBuilder result = new StringBuilder(); + BufferedReader inputStream = + new BufferedReader(new InputStreamReader(connection.getInputStream())); + String line; + while ((line = inputStream.readLine()) != null) { + result.append(line); + } + connection.disconnect(); + return result.toString(); + } + + /** A one-off helper method to simply open a URL in a browser window. */ + public static void openUrlInBrowser(String urlString, Activity activity) { + if (urlString.startsWith("data:")) { + // Use makeMainSelectorActivity to handle data: URLs. + activity.startActivity( + Intent.makeMainSelectorActivity(Intent.ACTION_MAIN, Intent.CATEGORY_APP_BROWSER) + .setData(Uri.parse(urlString))); + } else { + // Otherwise use the default intent handler for the URL. + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setData(Uri.parse(urlString)); + activity.startActivity(intent); + } + } +} diff --git a/testing/integration_tests/test_framework/src/android/java/com/google/firebase/example/SimplePersistentStorage.java b/testing/integration_tests/test_framework/src/android/java/com/google/firebase/example/SimplePersistentStorage.java new file mode 100644 index 0000000000..62d596c678 --- /dev/null +++ b/testing/integration_tests/test_framework/src/android/java/com/google/firebase/example/SimplePersistentStorage.java @@ -0,0 +1,45 @@ +// Copyright 2019 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.firebase.example; + +import android.app.Activity; +import android.content.SharedPreferences; + +/** Static utilties for saving and loading shared preference strings. */ +public final class SimplePersistentStorage { + private static final String PREF_NAME = "firebase_automated_test"; + /** + * Sets a given key's value in persistent storage to the given string. Specify null to delete the + * key. + */ + public static void setString(Activity activity, String key, String value) { + SharedPreferences pref = activity.getSharedPreferences(PREF_NAME, 0); + SharedPreferences.Editor editor = pref.edit(); + if (value != null) { + editor.putString(key, value); + } else { + editor.remove(key); + } + editor.commit(); + } + + /** Gets the value of the given key in persistent storage, or null if the key is not found. */ + public static String getString(Activity activity, String key) { + SharedPreferences pref = activity.getSharedPreferences(PREF_NAME, 0); + return pref.getString(key, null); + } + + private SimplePersistentStorage() {} +} diff --git a/testing/integration_tests/test_framework/src/desktop/desktop_firebase_test_framework.cc b/testing/integration_tests/test_framework/src/desktop/desktop_firebase_test_framework.cc new file mode 100644 index 0000000000..8ffbf9bb44 --- /dev/null +++ b/testing/integration_tests/test_framework/src/desktop/desktop_firebase_test_framework.cc @@ -0,0 +1,52 @@ +// Copyright 2019 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "firebase_test_framework.h" // NOLINT + +namespace firebase_test_framework { + +using app_framework::LogWarning; + +bool FirebaseTest::SendHttpGetRequest( + const char* url, const std::map& headers, + int* response_code, std::string* response_str) { + LogWarning("SendHttpGetRequest is not implemented on desktop."); + return false; +} + +bool FirebaseTest::SendHttpPostRequest( + const char* url, const std::map& headers, + const std::string& post_body, int* response_code, + std::string* response_str) { + LogWarning("SendHttpPostRequest is not implemented on desktop."); + return false; +} + +bool FirebaseTest::OpenUrlInBrowser(const char* url) { + LogWarning("OpenUrlInBrowser is not implemented on desktop."); + return false; +} + +bool FirebaseTest::SetPersistentString(const char* key, const char* value) { + LogWarning("SetPersistentString is not implemented on desktop."); + return false; +} + +bool FirebaseTest::GetPersistentString(const char* key, + std::string* value_out) { + LogWarning("GetPersistentString is not implemented on desktop."); + return false; +} + +} // namespace firebase_test_framework diff --git a/testing/integration_tests/test_framework/src/firebase_test_framework.cc b/testing/integration_tests/test_framework/src/firebase_test_framework.cc new file mode 100644 index 0000000000..d34f0bc2c6 --- /dev/null +++ b/testing/integration_tests/test_framework/src/firebase_test_framework.cc @@ -0,0 +1,221 @@ +// Copyright 2019 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "firebase_test_framework.h" // NOLINT + +#include + +#include "firebase/future.h" + +namespace firebase { +namespace internal { +// Borrow Firebase's internal Base64 encoder and decoder. +extern bool Base64Encode(const std::string& input, std::string* output); +extern bool Base64Decode(const std::string& input, std::string* output); +} // namespace internal +} // namespace firebase + +namespace firebase_test_framework { + +int FirebaseTest::argc_ = 0; +char** FirebaseTest::argv_ = nullptr; +bool FirebaseTest::found_config_ = false; + +FirebaseTest::FirebaseTest() : app_(nullptr) {} + +FirebaseTest::~FirebaseTest() { assert(app_ == nullptr); } + +void FirebaseTest::SetUp() {} + +void FirebaseTest::TearDown() { + if (HasFailure()) { + app_framework::SetPreserveFullLog(false); + app_framework::LogError( + "Test %s failed.\nFull test log:\n%s", + ::testing::UnitTest::GetInstance()->current_test_info()->name(), + "========================================================"); + app_framework::SetPreserveFullLog(true); + app_framework::AddToFullLog( + "========================================================\n"); + app_framework::OutputFullLog(); + } else { + app_framework::ClearFullLog(); + } +} + +void FirebaseTest::FindFirebaseConfig(const char* try_directory) { +#if !defined(__ANDROID__) && !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) + static const char kDefaultGoogleServicesPath[] = "google-services.json"; + + if (!found_config_) { + if (try_directory[0] != '\0' && app_framework::FileExists(try_directory)) { + app_framework::ChangeToFileDirectory(try_directory); + } else if (app_framework::FileExists(kDefaultGoogleServicesPath)) { + // It's in the current directory, don't do anything. + } else { + // Try the directory the binary is in. + app_framework::ChangeToFileDirectory(argv_[0]); + } + // Only do this once. + found_config_ = true; + } +#endif // !defined(__ANDROID__) && !(defined(TARGET_OS_IPHONE) && + // TARGET_OS_IPHONE) + (void)try_directory; +} + +void FirebaseTest::InitializeApp() { + if (app_) return; // Already initialized. + + app_framework::LogDebug("Initialize Firebase App."); + +#if defined(__ANDROID__) + app_ = ::firebase::App::Create(app_framework::GetJniEnv(), + app_framework::GetActivity()); +#else + app_ = ::firebase::App::Create(); +#endif // defined(__ANDROID__) +} + +void FirebaseTest::TerminateApp() { + if (!app_) return; // Already terminated. + + app_framework::LogDebug("Shutdown Firebase App."); + delete app_; + app_ = nullptr; +} + +bool FirebaseTest::WaitForCompletion(const firebase::FutureBase& future, + const char* name, int expected_error) { + app_framework::LogDebug("WaitForCompletion %s", name); + while (future.status() == firebase::kFutureStatusPending) { + app_framework::ProcessEvents(100); + } + EXPECT_EQ(future.status(), firebase::kFutureStatusComplete) + << name << " returned an invalid status."; + EXPECT_EQ(future.error(), expected_error) + << name << " returned error " << future.error() << ": " + << future.error_message(); + return (future.status() == firebase::kFutureStatusComplete && + future.error() == expected_error); +} + +static void VariantToStringInternal(const firebase::Variant& variant, + std::ostream& out, + const std::string& indent) { + if (variant.is_null()) { + out << "null"; + } else if (variant.is_int64()) { + out << variant.int64_value(); + } else if (variant.is_double()) { + out << variant.double_value(); + } else if (variant.is_bool()) { + out << (variant.bool_value() ? "true" : "false"); + } else if (variant.is_string()) { + out << variant.string_value(); + } else if (variant.is_blob()) { + out << "blob[" << variant.blob_size() << "] = <"; + char hex[3]; + for (size_t i = 0; i < variant.blob_size(); ++i) { + snprintf(hex, sizeof(hex), "%02x", variant.blob_data()[i]); + if (i != 0) out << " "; + out << hex; + } + out << ">"; + } else if (variant.is_vector()) { + out << "[" << std::endl; + const auto& v = variant.vector(); + for (auto it = v.begin(); it != v.end(); ++it) { + out << indent + " "; + VariantToStringInternal(*it, out, indent + " "); + auto next_it = it; + next_it++; + if (next_it != v.end()) out << ","; + out << std::endl; + } + out << "]"; + } else if (variant.is_map()) { + out << "[" << std::endl; + const auto& m = variant.map(); + for (auto it = m.begin(); it != m.end(); ++it) { + out << indent + " "; + VariantToStringInternal(it->first, out, indent + " "); + out << ": "; + VariantToStringInternal(it->second, out, indent + " "); + auto next_it = it; + next_it++; + if (next_it != m.end()) out << ","; + out << std::endl; + } + out << "]"; + } else { + out << ""; + } +} + +std::string FirebaseTest::VariantToString(const firebase::Variant& variant) { + std::ostringstream out; + VariantToStringInternal(variant, out, ""); + return out.str(); +} + +bool FirebaseTest::IsUserInteractionAllowed() { + // In the trivial case, just check whether we are logging to file. If not, + // assume interaction is allowed. + return !app_framework::IsLoggingToFile(); +} + +bool FirebaseTest::Base64Encode(const std::string& input, std::string* output) { + return ::firebase::internal::Base64Encode(input, output); +} + +bool FirebaseTest::Base64Decode(const std::string& input, std::string* output) { + return ::firebase::internal::Base64Decode(input, output); +} + +class LogTestEventListener : public testing::EmptyTestEventListener { + public: + void OnTestPartResult( + const testing::TestPartResult& test_part_result) override { + if (test_part_result.failed() && test_part_result.message()) { + app_framework::AddToFullLog(test_part_result.message()); + app_framework::AddToFullLog("\n"); + } + }; +}; + +} // namespace firebase_test_framework + +namespace firebase { +// gtest requires that the operator<< be in the same namespace as the item you +// are outputting. +std::ostream& operator<<(std::ostream& os, const Variant& v) { + return os << firebase_test_framework::FirebaseTest::VariantToString(v); +} +} // namespace firebase + +extern "C" int common_main(int argc, char* argv[]) { + ::testing::InitGoogleTest(&argc, argv); + firebase_test_framework::FirebaseTest::SetArgs(argc, argv); + app_framework::SetLogLevel(app_framework::kInfo); + // Anything below the given log level will be preserved, and printed out in + // the event of test failure. + app_framework::SetPreserveFullLog(true); + ::testing::TestEventListeners& listeners = + ::testing::UnitTest::GetInstance()->listeners(); + listeners.Append(new firebase_test_framework::LogTestEventListener()); + int result = RUN_ALL_TESTS(); + + return result; +} diff --git a/testing/integration_tests/test_framework/src/firebase_test_framework.h b/testing/integration_tests/test_framework/src/firebase_test_framework.h new file mode 100644 index 0000000000..4beb4fc492 --- /dev/null +++ b/testing/integration_tests/test_framework/src/firebase_test_framework.h @@ -0,0 +1,180 @@ +// Copyright 2019 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef FIREBASE_TEST_FRAMEWORK_H_ // NOLINT +#define FIREBASE_TEST_FRAMEWORK_H_ // NOLINT + +#include + +#include "app_framework.h" // NOLINT +#include "firebase/app.h" +#include "firebase/future.h" +#include "firebase/internal/platform.h" +#include "firebase/util.h" +#include "firebase/variant.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace firebase_test_framework { + +// Use this macro to skip an entire test if it requires interactivity and we are +// not running in interactive mode (for example, on FTL). +#define TEST_REQUIRES_USER_INTERACTION \ + if (!IsUserInteractionAllowed()) { \ + app_framework::LogInfo("Skipping %s, as it requires user interaction.", \ + test_info_->name()); \ + GTEST_SKIP(); \ + return; \ + } + +#if TARGET_OS_IPHONE +#define TEST_REQUIRES_USER_INTERACTION_ON_IOS TEST_REQUIRES_USER_INTERACTION +#define TEST_REQUIRES_USER_INTERACTION_ON_ANDROID ((void)0) +#elif defined(ANDROID) +#define TEST_REQUIRES_USER_INTERACTION_ON_IOS ((void)0) +#define TEST_REQUIRES_USER_INTERACTION_ON_ANDROID TEST_REQUIRES_USER_INTERACTION +#else +#define TEST_REQUIRES_USER_INTERACTION_ON_IOS ((void)0) +#define TEST_REQUIRES_USER_INTERACTION_ON_ANDROID ((void)0) +#endif // TARGET_OS_IPHONE + +#if !defined(ANDROID) && !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) +#define SKIP_TEST_ON_DESKTOP \ + { \ + app_framework::LogInfo("Skipping %s on desktop.", test_info_->name()); \ + GTEST_SKIP(); \ + return; \ + } +#else +#define SKIP_TEST_ON_DESKTOP ((void)0) +#endif // !defined(ANDROID) && !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) + +#if defined(ANDROID) || (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) +#define SKIP_TEST_ON_MOBILE \ + { \ + app_framework::LogInfo("Skipping %s on mobile.", test_info_->name()); \ + GTEST_SKIP(); \ + return; \ + } +#else +#define SKIP_TEST_ON_MOBILE ((void)0) +#endif // defined(ANDROID) || (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) + +#if defined(STLPORT) +#define SKIP_TEST_IF_USING_STLPORT \ + { \ + app_framework::LogInfo("Skipping %s due to incompatibility with STLPort.", \ + test_info_->name()); \ + GTEST_SKIP(); \ + return; \ + } +#else +#define SKIP_TEST_IF_USING_STLPORT ((void)0) +#endif // defined(STLPORT) + +#define KNOWN_FAILURE(explanation) \ + { FAIL() << test_info_->name() << " has a known failure: " << explanation; } + +#if FIREBASE_PLATFORM_LINUX || FIREBASE_PLATFORM_OSX +#define DEATHTEST_SIGABRT "SIGABRT" +#else +#define DEATHTEST_SIGABRT "" +#endif + +class FirebaseTest : public testing::Test { + public: + FirebaseTest(); + ~FirebaseTest() override; + + void SetUp() override; + void TearDown() override; + + // Check the given directory, the current directory, and the directory + // containing the binary for google-services.json, and change to whichever + // directory contains it. + static void FindFirebaseConfig(const char* try_directory); + + static void SetArgs(int argc, char* argv[]) { + argc_ = argc; + argv_ = argv; + } + + // Convert a Variant into a string (including all nested Variants) for + // debugging. + static std::string VariantToString(const firebase::Variant& variant); + + protected: + // Set up firebase::App with default settings. + void InitializeApp(); + // Shut down firebase::App. + void TerminateApp(); + + // Returns true if interactive tests are allowed, false if only + // fully-automated tests should be run. + bool AreInteractiveTestsAllowed(); + + // Get a persistent string value that was previously set via + // SetPersistentString. Returns true if the value was set, false if not or if + // something went wrong. + static bool GetPersistentString(const char* key, std::string* value_out); + // Set a persistent string value that can be accessed the next time the test + // loads. Specify nullptr for value to delete the key. Returns true if + // successful, false if something went wrong. + static bool SetPersistentString(const char* key, const char* value); + + // Returns true if the future completed as expected, fails the test and + // returns false otherwise. + static bool WaitForCompletion(const firebase::FutureBase& future, + const char* name, int expected_error = 0); + + // Blocking HTTP request helper function, for testing only. + static bool SendHttpGetRequest( + const char* url, const std::map& headers, + int* response_code, std::string* response); + + // Blocking HTTP request helper function, for testing only. + static bool SendHttpPostRequest( + const char* url, const std::map& headers, + const std::string& post_body, int* response_code, std::string* response); + + // Open a URL in a browser window, for testing only. + static bool OpenUrlInBrowser(const char* url); + + // Returns true if we can run tests that require interaction, false if not. + static bool IsUserInteractionAllowed(); + + // Encode a binary string to base64. Returns true if the encoding succeeded, + // false if it failed. + static bool Base64Encode(const std::string& input, std::string* output); + // Decode a base64 string to binary. Returns true if the decoding succeeded, + // false if it failed. + static bool Base64Decode(const std::string& input, std::string* output); + + firebase::App* app_; + static int argc_; + static char** argv_; + static bool found_config_; +}; + +} // namespace firebase_test_framework + +namespace firebase { +// Define an operator<< for Variant so that googletest can output its values +// nicely. +std::ostream& operator<<(std::ostream& os, const Variant& v); +} // namespace firebase + +extern "C" int common_main(int argc, char* argv[]); + +#endif // FIREBASE_TEST_FRAMEWORK_H_ // NOLINT diff --git a/testing/integration_tests/test_framework/src/gmock/gmock.h b/testing/integration_tests/test_framework/src/gmock/gmock.h new file mode 100644 index 0000000000..ddf2d29ab5 --- /dev/null +++ b/testing/integration_tests/test_framework/src/gmock/gmock.h @@ -0,0 +1,26 @@ +/* + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FIREBASE_APP_CLIENT_CPP_TEST_FRAMEWORK_SRC_GMOCK_GMOCK_H_ +#define FIREBASE_APP_CLIENT_CPP_TEST_FRAMEWORK_SRC_GMOCK_GMOCK_H_ + +/* This file exists so that blaze builds can include gmock/gmock.h (the public + * include path) rather than the internal include path. This file is omitted + * from the public repo. */ + +#include "testing/base/public/gmock.h" + +#endif // FIREBASE_APP_CLIENT_CPP_TEST_FRAMEWORK_SRC_GMOCK_GMOCK_H_ diff --git a/testing/integration_tests/test_framework/src/gtest/gtest.h b/testing/integration_tests/test_framework/src/gtest/gtest.h new file mode 100644 index 0000000000..0094c7f1c2 --- /dev/null +++ b/testing/integration_tests/test_framework/src/gtest/gtest.h @@ -0,0 +1,26 @@ +/* + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FIREBASE_APP_CLIENT_CPP_TEST_FRAMEWORK_SRC_GTEST_GTEST_H_ +#define FIREBASE_APP_CLIENT_CPP_TEST_FRAMEWORK_SRC_GTEST_GTEST_H_ + +/* This file exists so that blaze builds can include gtest/gtest.h (the public + * include path) rather than the internal include path. This file is omitted + * from the public repo. */ + +#include "testing/base/public/gunit.h" + +#endif // FIREBASE_APP_CLIENT_CPP_TEST_FRAMEWORK_SRC_GTEST_GTEST_H_ diff --git a/testing/integration_tests/test_framework/src/ios/ios_firebase_test_framework.mm b/testing/integration_tests/test_framework/src/ios/ios_firebase_test_framework.mm new file mode 100644 index 0000000000..d36f141d99 --- /dev/null +++ b/testing/integration_tests/test_framework/src/ios/ios_firebase_test_framework.mm @@ -0,0 +1,190 @@ +// Copyright 2019 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "firebase_test_framework.h" // NOLINT + +#import +#import + +namespace firebase_test_framework { + +using app_framework::LogDebug; +using app_framework::LogError; +using app_framework::ProcessEvents; + +// Default HTTP timeout of 1 minute. +const int kHttpTimeoutSeconds = 60; + +// A simple helper function for performing synchronous HTTP/HTTPS requests, used for testing +// purposes only. +static bool SendHttpRequest(const char* method, const char* url, + const std::map& headers, + const std::string& post_body, int* response_code, + std::string* response_str) { + NSMutableURLRequest* url_request = [[NSMutableURLRequest alloc] init]; + url_request.URL = [NSURL URLWithString:@(url)]; + url_request.HTTPMethod = @(method); + url_request.timeoutInterval = kHttpTimeoutSeconds; + if (strcmp(method, "POST") == 0) { + url_request.HTTPBody = [NSData dataWithBytes:post_body.c_str() length:post_body.length()]; + } + // Set all the headers. + for (auto i = headers.begin(); i != headers.end(); ++i) { + [url_request addValue:@(i->second.c_str()) forHTTPHeaderField:@(i->first.c_str())]; + } + __block dispatch_semaphore_t sem = dispatch_semaphore_create(0); + __block NSError* response_error; + __block NSHTTPURLResponse* http_response; + __block NSData* response_data; + LogDebug("Sending HTTP %s request to %s", method, url); + @try { + [[NSURLSession.sharedSession + dataTaskWithRequest:url_request + completionHandler:^(NSData* __nullable data, NSURLResponse* __nullable response, + NSError* __nullable error) { + response_data = data; + http_response = (NSHTTPURLResponse*)(response); + response_error = error; + dispatch_semaphore_signal(sem); + }] resume]; + } @catch (NSException* e) { + LogError("NSURLSession exception: %s", e.reason.UTF8String); + return false; + } + dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); + + LogDebug("HTTP status code %ld", http_response.statusCode); + if (http_response && response_code) { + *response_code = static_cast(http_response.statusCode); + } + std::string response_text = + response_data.bytes + ? std::string(reinterpret_cast(response_data.bytes), response_data.length) + : std::string(); + LogDebug("Got response: %s", response_text.c_str()); + if (response_str) { + *response_str = response_text; + } + if (response_error) { + LogError("HTTP error: %s", response_error.localizedDescription.UTF8String); + return false; + } + return true; +} + +// Blocking HTTP request helper function, for testing only. +bool FirebaseTest::SendHttpGetRequest(const char* url, + const std::map& headers, + int* response_code, std::string* response_str) { + return SendHttpRequest("GET", url, headers, "", response_code, response_str); +} + +bool FirebaseTest::SendHttpPostRequest(const char* url, + const std::map& headers, + const std::string& post_body, int* response_code, + std::string* response_str) { + return SendHttpRequest("POST", url, headers, post_body, response_code, response_str); +} + +bool FirebaseTest::OpenUrlInBrowser(const char* url) { + if (strncmp(url, "data:", strlen("data:")) == 0) { + // Workaround because Safari can't load data: URLs by default. + // Instead, copy the URL to the clipboard and ask the user to paste it into Safari. + + // data: URLs are in the format data:text/html, + // Preserve everything until the first comma, then URL-encode the rest. + const char* payload = strchr(url, ','); + if (payload == nullptr) { + return false; + } + payload++; // Move past the comma. + std::string scheme_and_encoding(url); + scheme_and_encoding.resize(payload - url); + UIPasteboard* pasteboard = [UIPasteboard generalPasteboard]; + pasteboard.string = [@(scheme_and_encoding.c_str()) + stringByAppendingString:[@(payload) stringByAddingPercentEncodingWithAllowedCharacters: + [NSCharacterSet URLHostAllowedCharacterSet]]]; + dispatch_semaphore_t sem = dispatch_semaphore_create(0); + UIAlertController* alert = + [UIAlertController alertControllerWithTitle:@"Paste URL" + message:@"Opening Safari. Please tap twice on the " + @"address bar and select \"Paste and Go\"." + preferredStyle:UIAlertControllerStyleAlert]; + UIAlertAction* ok = [UIAlertAction actionWithTitle:@"OK" + style:UIAlertActionStyleDefault + handler:^(UIAlertAction* action) { + dispatch_semaphore_signal(sem); + }]; + [alert addAction:ok]; + dispatch_async(dispatch_get_main_queue(), ^{ + [[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:alert + animated:YES + completion:nil]; + }); + dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); + return OpenUrlInBrowser("http://"); + } else { + // Not a data: URL, load it normally. + dispatch_semaphore_t sem = dispatch_semaphore_create(0); + __block BOOL succeeded = NO; + NSURL* nsurl = [NSURL URLWithString:@(url)]; + [UIApplication.sharedApplication openURL:nsurl + options:[NSDictionary dictionary] + completionHandler:^(BOOL success) { + succeeded = success; + dispatch_semaphore_signal(sem); + }]; + dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER); + return succeeded ? true : false; + } +} + +bool FirebaseTest::SetPersistentString(const char* key, const char* value) { + if (!key) { + LogError("SetPersistentString: null key is not allowed."); + return false; + } + NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; + if (!defaults) { + return false; + } + if (value) { + [defaults setObject:@(value) forKey:@(key)]; + } else { + // If value is null, remove this key. + [defaults removeObjectForKey:@(key)]; + } + [defaults synchronize]; + return true; +} + +bool FirebaseTest::GetPersistentString(const char* key, std::string* value_out) { + if (!key) { + return false; + } + NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; + if (!defaults) { + return false; + } + if (![defaults objectForKey:@(key)]) { + return false; // for missing key + } + NSString* str = [defaults stringForKey:@(key)]; + if (value_out) { + *value_out = std::string(str.UTF8String); + } + return true; +} + +} // namespace firebase_test_framework From d6264dfa16089cf826313ebc7b562792ad1ed774 Mon Sep 17 00:00:00 2001 From: Jon Simantov Date: Wed, 24 Jun 2020 13:31:31 -0700 Subject: [PATCH 2/2] Add integration test source files. --- .../admob/AndroidManifest.xml | 32 + .../integration_tests/admob/CMakeLists.txt | 196 +++ .../AppIcon.appiconset/Contents.json | 98 ++ .../LaunchImage.launchimage/Contents.json | 51 + testing/integration_tests/admob/Info.plist | 37 + .../admob/LaunchScreen.storyboard | 7 + .../admob/LibraryManifest.xml | 7 + testing/integration_tests/admob/Podfile | 13 + testing/integration_tests/admob/build.gradle | 64 + .../integration_tests/admob/googletest.cmake | 19 + .../admob/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 49896 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + testing/integration_tests/admob/gradlew | 164 +++ testing/integration_tests/admob/gradlew.bat | 90 ++ .../project.pbxproj | 364 +++++ testing/integration_tests/admob/proguard.pro | 2 + .../admob/res/layout/main.xml | 12 + .../admob/res/values/strings.xml | 4 + .../integration_tests/admob/settings.gradle | 36 + .../admob/src/integration_test.cc | 552 ++++++++ .../integration_tests/app/AndroidManifest.xml | 27 + testing/integration_tests/app/CMakeLists.txt | 196 +++ .../AppIcon.appiconset/Contents.json | 98 ++ .../LaunchImage.launchimage/Contents.json | 51 + testing/integration_tests/app/Info.plist | 41 + .../app/LaunchScreen.storyboard | 7 + .../integration_tests/app/LibraryManifest.xml | 7 + testing/integration_tests/app/Podfile | 13 + testing/integration_tests/app/build.gradle | 64 + .../integration_tests/app/googletest.cmake | 19 + .../app/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 49896 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + testing/integration_tests/app/gradlew | 164 +++ testing/integration_tests/app/gradlew.bat | 90 ++ .../project.pbxproj | 364 +++++ testing/integration_tests/app/proguard.pro | 2 + .../integration_tests/app/res/layout/main.xml | 12 + .../app/res/values/strings.xml | 4 + testing/integration_tests/app/settings.gradle | 36 + .../app/src/integration_test.cc | 68 + .../auth/AndroidManifest.xml | 28 + testing/integration_tests/auth/CMakeLists.txt | 196 +++ ...base_Cpp_Auth_Test_App_Dev.mobileprovision | Bin 0 -> 291491 bytes .../AppIcon.appiconset/Contents.json | 98 ++ .../LaunchImage.launchimage/Contents.json | 51 + testing/integration_tests/auth/Info.plist | 54 + .../auth/LaunchScreen.storyboard | 7 + .../auth/LibraryManifest.xml | 7 + testing/integration_tests/auth/Podfile | 13 + testing/integration_tests/auth/build.gradle | 64 + .../integration_tests/auth/googletest.cmake | 19 + .../auth/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 49896 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + testing/integration_tests/auth/gradlew | 164 +++ testing/integration_tests/auth/gradlew.bat | 90 ++ .../auth/integration_test.entitlements | 10 + .../project.pbxproj | 376 +++++ testing/integration_tests/auth/proguard.pro | 2 + .../auth/res/layout/main.xml | 12 + .../auth/res/values/strings.xml | 4 + .../integration_tests/auth/settings.gradle | 36 + .../auth/src/integration_test.cc | 1117 +++++++++++++++ .../database/AndroidManifest.xml | 28 + .../integration_tests/database/CMakeLists.txt | 196 +++ .../AppIcon.appiconset/Contents.json | 98 ++ .../LaunchImage.launchimage/Contents.json | 51 + testing/integration_tests/database/Info.plist | 40 + .../database/LaunchScreen.storyboard | 7 + .../database/LibraryManifest.xml | 7 + testing/integration_tests/database/Podfile | 14 + .../integration_tests/database/build.gradle | 65 + .../database/googletest.cmake | 19 + .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 49896 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + testing/integration_tests/database/gradlew | 164 +++ .../integration_tests/database/gradlew.bat | 90 ++ .../project.pbxproj | 364 +++++ .../integration_tests/database/proguard.pro | 2 + testing/integration_tests/database/readme.md | 215 +++ .../database/res/layout/main.xml | 12 + .../database/res/values/strings.xml | 4 + .../database/settings.gradle | 36 + .../database/src/integration_test.cc | 1210 +++++++++++++++++ .../dynamic_links/AndroidManifest.xml | 28 + .../dynamic_links/CMakeLists.txt | 197 +++ ...Dynamic_Links_Test_App_Dev.mobileprovision | Bin 0 -> 299610 bytes .../AppIcon.appiconset/Contents.json | 98 ++ .../LaunchImage.launchimage/Contents.json | 51 + .../dynamic_links/Info.plist | 56 + .../dynamic_links/LaunchScreen.storyboard | 7 + .../dynamic_links/LibraryManifest.xml | 7 + .../integration_tests/dynamic_links/Podfile | 13 + .../dynamic_links/build.gradle | 64 + .../dynamic_links/googletest.cmake | 19 + .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 49896 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + .../integration_tests/dynamic_links/gradlew | 164 +++ .../dynamic_links/gradlew.bat | 90 ++ .../integration_test.entitlements | 12 + .../project.pbxproj | 364 +++++ .../dynamic_links/proguard.pro | 2 + .../dynamic_links/res/layout/main.xml | 12 + .../dynamic_links/res/values/strings.xml | 4 + .../dynamic_links/settings.gradle | 36 + .../dynamic_links/src/integration_test.cc | 700 ++++++++++ .../firestore/AndroidManifest.xml | 27 + .../firestore/CMakeLists.txt | 197 +++ .../AppIcon.appiconset/Contents.json | 98 ++ .../LaunchImage.launchimage/Contents.json | 51 + .../integration_tests/firestore/Info.plist | 39 + .../firestore/LaunchScreen.storyboard | 7 + .../firestore/LibraryManifest.xml | 7 + testing/integration_tests/firestore/Podfile | 14 + .../integration_tests/firestore/build.gradle | 66 + .../firestore/googletest.cmake | 19 + .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 49896 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + testing/integration_tests/firestore/gradlew | 164 +++ .../integration_tests/firestore/gradlew.bat | 90 ++ .../project.pbxproj | 372 +++++ .../integration_tests/firestore/proguard.pro | 2 + .../firestore/res/layout/main.xml | 12 + .../firestore/res/values/strings.xml | 4 + .../firestore/settings.gradle | 36 + .../firestore/src/integration_test.cc | 565 ++++++++ .../functions/AndroidManifest.xml | 28 + .../functions/CMakeLists.txt | 196 +++ .../AppIcon.appiconset/Contents.json | 98 ++ .../LaunchImage.launchimage/Contents.json | 51 + .../integration_tests/functions/Info.plist | 40 + .../functions/LaunchScreen.storyboard | 7 + .../functions/LibraryManifest.xml | 7 + testing/integration_tests/functions/Podfile | 14 + .../integration_tests/functions/build.gradle | 65 + .../functions/googletest.cmake | 19 + .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 49896 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + testing/integration_tests/functions/gradlew | 164 +++ .../integration_tests/functions/gradlew.bat | 90 ++ .../project.pbxproj | 364 +++++ .../integration_tests/functions/proguard.pro | 2 + .../functions/res/layout/main.xml | 12 + .../functions/res/values/strings.xml | 4 + .../functions/settings.gradle | 36 + .../functions/src/integration_test.cc | 326 +++++ .../instance_id/AndroidManifest.xml | 28 + .../instance_id/CMakeLists.txt | 196 +++ .../AppIcon.appiconset/Contents.json | 98 ++ .../LaunchImage.launchimage/Contents.json | 51 + .../integration_tests/instance_id/Info.plist | 42 + .../instance_id/LaunchScreen.storyboard | 7 + .../instance_id/LibraryManifest.xml | 7 + testing/integration_tests/instance_id/Podfile | 14 + .../instance_id/build.gradle | 64 + .../instance_id/googletest.cmake | 19 + .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 49896 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + testing/integration_tests/instance_id/gradlew | 164 +++ .../integration_tests/instance_id/gradlew.bat | 90 ++ .../project.pbxproj | 364 +++++ .../instance_id/proguard.pro | 2 + .../instance_id/res/layout/main.xml | 12 + .../instance_id/res/values/strings.xml | 4 + .../instance_id/settings.gradle | 36 + .../instance_id/src/integration_test.cc | 224 +++ .../messaging/AndroidManifest.xml | 72 + .../messaging/CMakeLists.txt | 198 +++ ...Cpp_Messaging_Test_App_Dev.mobileprovision | Bin 0 -> 290126 bytes .../AppIcon.appiconset/Contents.json | 98 ++ .../LaunchImage.launchimage/Contents.json | 51 + .../integration_tests/messaging/Info.plist | 44 + .../messaging/LaunchScreen.storyboard | 7 + .../messaging/LibraryManifest.xml | 7 + testing/integration_tests/messaging/Podfile | 13 + .../integration_tests/messaging/build.gradle | 64 + .../messaging/googletest.cmake | 19 + .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 49896 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + testing/integration_tests/messaging/gradlew | 164 +++ .../integration_tests/messaging/gradlew.bat | 90 ++ .../messaging/integration_test.entitlements | 10 + .../project.pbxproj | 364 +++++ .../integration_tests/messaging/proguard.pro | 3 + testing/integration_tests/messaging/readme.md | 258 ++++ .../messaging/res/layout/main.xml | 12 + .../messaging/res/values/strings.xml | 4 + .../messaging/settings.gradle | 36 + .../example/SampleNativeActivity.java | 73 + .../messaging/src/integration_test.cc | 596 ++++++++ .../remote_config/AndroidManifest.xml | 28 + .../remote_config/CMakeLists.txt | 196 +++ .../AppIcon.appiconset/Contents.json | 98 ++ .../LaunchImage.launchimage/Contents.json | 51 + .../remote_config/Info.plist | 40 + .../remote_config/LaunchScreen.storyboard | 7 + .../remote_config/LibraryManifest.xml | 7 + .../integration_tests/remote_config/Podfile | 13 + .../remote_config/build.gradle | 64 + .../remote_config/googletest.cmake | 19 + .../gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 49896 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + .../integration_tests/remote_config/gradlew | 164 +++ .../remote_config/gradlew.bat | 90 ++ .../project.pbxproj | 364 +++++ .../remote_config/proguard.pro | 2 + .../remote_config/res/layout/main.xml | 12 + .../remote_config/res/values/strings.xml | 4 + .../remote_config/settings.gradle | 36 + .../remote_config/src/integration_test.cc | 430 ++++++ .../storage/AndroidManifest.xml | 28 + .../integration_tests/storage/CMakeLists.txt | 196 +++ .../AppIcon.appiconset/Contents.json | 98 ++ .../LaunchImage.launchimage/Contents.json | 51 + testing/integration_tests/storage/Info.plist | 40 + .../storage/LaunchScreen.storyboard | 7 + .../storage/LibraryManifest.xml | 7 + testing/integration_tests/storage/Podfile | 14 + .../integration_tests/storage/build.gradle | 65 + .../storage/googletest.cmake | 19 + .../storage/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 49896 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + testing/integration_tests/storage/gradlew | 164 +++ testing/integration_tests/storage/gradlew.bat | 90 ++ .../project.pbxproj | 364 +++++ .../integration_tests/storage/proguard.pro | 2 + testing/integration_tests/storage/readme.md | 215 +++ .../storage/res/layout/main.xml | 12 + .../storage/res/values/strings.xml | 4 + .../integration_tests/storage/settings.gradle | 36 + .../storage/src/integration_test.cc | 776 +++++++++++ 230 files changed, 20682 insertions(+) create mode 100644 testing/integration_tests/admob/AndroidManifest.xml create mode 100644 testing/integration_tests/admob/CMakeLists.txt create mode 100644 testing/integration_tests/admob/Images.xcassets/AppIcon.appiconset/Contents.json create mode 100644 testing/integration_tests/admob/Images.xcassets/LaunchImage.launchimage/Contents.json create mode 100644 testing/integration_tests/admob/Info.plist create mode 100644 testing/integration_tests/admob/LaunchScreen.storyboard create mode 100644 testing/integration_tests/admob/LibraryManifest.xml create mode 100644 testing/integration_tests/admob/Podfile create mode 100644 testing/integration_tests/admob/build.gradle create mode 100644 testing/integration_tests/admob/googletest.cmake create mode 100644 testing/integration_tests/admob/gradle/wrapper/gradle-wrapper.jar create mode 100644 testing/integration_tests/admob/gradle/wrapper/gradle-wrapper.properties create mode 100755 testing/integration_tests/admob/gradlew create mode 100644 testing/integration_tests/admob/gradlew.bat create mode 100644 testing/integration_tests/admob/integration_test.xcodeproj/project.pbxproj create mode 100644 testing/integration_tests/admob/proguard.pro create mode 100644 testing/integration_tests/admob/res/layout/main.xml create mode 100644 testing/integration_tests/admob/res/values/strings.xml create mode 100644 testing/integration_tests/admob/settings.gradle create mode 100644 testing/integration_tests/admob/src/integration_test.cc create mode 100644 testing/integration_tests/app/AndroidManifest.xml create mode 100644 testing/integration_tests/app/CMakeLists.txt create mode 100644 testing/integration_tests/app/Images.xcassets/AppIcon.appiconset/Contents.json create mode 100644 testing/integration_tests/app/Images.xcassets/LaunchImage.launchimage/Contents.json create mode 100644 testing/integration_tests/app/Info.plist create mode 100644 testing/integration_tests/app/LaunchScreen.storyboard create mode 100644 testing/integration_tests/app/LibraryManifest.xml create mode 100644 testing/integration_tests/app/Podfile create mode 100644 testing/integration_tests/app/build.gradle create mode 100644 testing/integration_tests/app/googletest.cmake create mode 100644 testing/integration_tests/app/gradle/wrapper/gradle-wrapper.jar create mode 100644 testing/integration_tests/app/gradle/wrapper/gradle-wrapper.properties create mode 100755 testing/integration_tests/app/gradlew create mode 100644 testing/integration_tests/app/gradlew.bat create mode 100644 testing/integration_tests/app/integration_test.xcodeproj/project.pbxproj create mode 100644 testing/integration_tests/app/proguard.pro create mode 100644 testing/integration_tests/app/res/layout/main.xml create mode 100644 testing/integration_tests/app/res/values/strings.xml create mode 100644 testing/integration_tests/app/settings.gradle create mode 100644 testing/integration_tests/app/src/integration_test.cc create mode 100644 testing/integration_tests/auth/AndroidManifest.xml create mode 100644 testing/integration_tests/auth/CMakeLists.txt create mode 100644 testing/integration_tests/auth/Firebase_Cpp_Auth_Test_App_Dev.mobileprovision create mode 100644 testing/integration_tests/auth/Images.xcassets/AppIcon.appiconset/Contents.json create mode 100644 testing/integration_tests/auth/Images.xcassets/LaunchImage.launchimage/Contents.json create mode 100644 testing/integration_tests/auth/Info.plist create mode 100644 testing/integration_tests/auth/LaunchScreen.storyboard create mode 100644 testing/integration_tests/auth/LibraryManifest.xml create mode 100644 testing/integration_tests/auth/Podfile create mode 100644 testing/integration_tests/auth/build.gradle create mode 100644 testing/integration_tests/auth/googletest.cmake create mode 100644 testing/integration_tests/auth/gradle/wrapper/gradle-wrapper.jar create mode 100644 testing/integration_tests/auth/gradle/wrapper/gradle-wrapper.properties create mode 100755 testing/integration_tests/auth/gradlew create mode 100644 testing/integration_tests/auth/gradlew.bat create mode 100644 testing/integration_tests/auth/integration_test.entitlements create mode 100644 testing/integration_tests/auth/integration_test.xcodeproj/project.pbxproj create mode 100644 testing/integration_tests/auth/proguard.pro create mode 100644 testing/integration_tests/auth/res/layout/main.xml create mode 100644 testing/integration_tests/auth/res/values/strings.xml create mode 100644 testing/integration_tests/auth/settings.gradle create mode 100644 testing/integration_tests/auth/src/integration_test.cc create mode 100644 testing/integration_tests/database/AndroidManifest.xml create mode 100644 testing/integration_tests/database/CMakeLists.txt create mode 100644 testing/integration_tests/database/Images.xcassets/AppIcon.appiconset/Contents.json create mode 100644 testing/integration_tests/database/Images.xcassets/LaunchImage.launchimage/Contents.json create mode 100644 testing/integration_tests/database/Info.plist create mode 100644 testing/integration_tests/database/LaunchScreen.storyboard create mode 100644 testing/integration_tests/database/LibraryManifest.xml create mode 100644 testing/integration_tests/database/Podfile create mode 100644 testing/integration_tests/database/build.gradle create mode 100644 testing/integration_tests/database/googletest.cmake create mode 100644 testing/integration_tests/database/gradle/wrapper/gradle-wrapper.jar create mode 100644 testing/integration_tests/database/gradle/wrapper/gradle-wrapper.properties create mode 100755 testing/integration_tests/database/gradlew create mode 100644 testing/integration_tests/database/gradlew.bat create mode 100644 testing/integration_tests/database/integration_test.xcodeproj/project.pbxproj create mode 100644 testing/integration_tests/database/proguard.pro create mode 100644 testing/integration_tests/database/readme.md create mode 100644 testing/integration_tests/database/res/layout/main.xml create mode 100644 testing/integration_tests/database/res/values/strings.xml create mode 100644 testing/integration_tests/database/settings.gradle create mode 100644 testing/integration_tests/database/src/integration_test.cc create mode 100644 testing/integration_tests/dynamic_links/AndroidManifest.xml create mode 100644 testing/integration_tests/dynamic_links/CMakeLists.txt create mode 100644 testing/integration_tests/dynamic_links/Firebase_Cpp_Dynamic_Links_Test_App_Dev.mobileprovision create mode 100644 testing/integration_tests/dynamic_links/Images.xcassets/AppIcon.appiconset/Contents.json create mode 100644 testing/integration_tests/dynamic_links/Images.xcassets/LaunchImage.launchimage/Contents.json create mode 100644 testing/integration_tests/dynamic_links/Info.plist create mode 100644 testing/integration_tests/dynamic_links/LaunchScreen.storyboard create mode 100644 testing/integration_tests/dynamic_links/LibraryManifest.xml create mode 100644 testing/integration_tests/dynamic_links/Podfile create mode 100644 testing/integration_tests/dynamic_links/build.gradle create mode 100644 testing/integration_tests/dynamic_links/googletest.cmake create mode 100644 testing/integration_tests/dynamic_links/gradle/wrapper/gradle-wrapper.jar create mode 100644 testing/integration_tests/dynamic_links/gradle/wrapper/gradle-wrapper.properties create mode 100755 testing/integration_tests/dynamic_links/gradlew create mode 100644 testing/integration_tests/dynamic_links/gradlew.bat create mode 100644 testing/integration_tests/dynamic_links/integration_test.entitlements create mode 100644 testing/integration_tests/dynamic_links/integration_test.xcodeproj/project.pbxproj create mode 100644 testing/integration_tests/dynamic_links/proguard.pro create mode 100644 testing/integration_tests/dynamic_links/res/layout/main.xml create mode 100644 testing/integration_tests/dynamic_links/res/values/strings.xml create mode 100644 testing/integration_tests/dynamic_links/settings.gradle create mode 100644 testing/integration_tests/dynamic_links/src/integration_test.cc create mode 100644 testing/integration_tests/firestore/AndroidManifest.xml create mode 100644 testing/integration_tests/firestore/CMakeLists.txt create mode 100644 testing/integration_tests/firestore/Images.xcassets/AppIcon.appiconset/Contents.json create mode 100644 testing/integration_tests/firestore/Images.xcassets/LaunchImage.launchimage/Contents.json create mode 100644 testing/integration_tests/firestore/Info.plist create mode 100644 testing/integration_tests/firestore/LaunchScreen.storyboard create mode 100644 testing/integration_tests/firestore/LibraryManifest.xml create mode 100644 testing/integration_tests/firestore/Podfile create mode 100644 testing/integration_tests/firestore/build.gradle create mode 100644 testing/integration_tests/firestore/googletest.cmake create mode 100644 testing/integration_tests/firestore/gradle/wrapper/gradle-wrapper.jar create mode 100644 testing/integration_tests/firestore/gradle/wrapper/gradle-wrapper.properties create mode 100755 testing/integration_tests/firestore/gradlew create mode 100644 testing/integration_tests/firestore/gradlew.bat create mode 100644 testing/integration_tests/firestore/integration_test.xcodeproj/project.pbxproj create mode 100644 testing/integration_tests/firestore/proguard.pro create mode 100644 testing/integration_tests/firestore/res/layout/main.xml create mode 100644 testing/integration_tests/firestore/res/values/strings.xml create mode 100644 testing/integration_tests/firestore/settings.gradle create mode 100644 testing/integration_tests/firestore/src/integration_test.cc create mode 100644 testing/integration_tests/functions/AndroidManifest.xml create mode 100644 testing/integration_tests/functions/CMakeLists.txt create mode 100644 testing/integration_tests/functions/Images.xcassets/AppIcon.appiconset/Contents.json create mode 100644 testing/integration_tests/functions/Images.xcassets/LaunchImage.launchimage/Contents.json create mode 100644 testing/integration_tests/functions/Info.plist create mode 100644 testing/integration_tests/functions/LaunchScreen.storyboard create mode 100644 testing/integration_tests/functions/LibraryManifest.xml create mode 100644 testing/integration_tests/functions/Podfile create mode 100644 testing/integration_tests/functions/build.gradle create mode 100644 testing/integration_tests/functions/googletest.cmake create mode 100644 testing/integration_tests/functions/gradle/wrapper/gradle-wrapper.jar create mode 100644 testing/integration_tests/functions/gradle/wrapper/gradle-wrapper.properties create mode 100755 testing/integration_tests/functions/gradlew create mode 100644 testing/integration_tests/functions/gradlew.bat create mode 100644 testing/integration_tests/functions/integration_test.xcodeproj/project.pbxproj create mode 100644 testing/integration_tests/functions/proguard.pro create mode 100644 testing/integration_tests/functions/res/layout/main.xml create mode 100644 testing/integration_tests/functions/res/values/strings.xml create mode 100644 testing/integration_tests/functions/settings.gradle create mode 100644 testing/integration_tests/functions/src/integration_test.cc create mode 100644 testing/integration_tests/instance_id/AndroidManifest.xml create mode 100644 testing/integration_tests/instance_id/CMakeLists.txt create mode 100644 testing/integration_tests/instance_id/Images.xcassets/AppIcon.appiconset/Contents.json create mode 100644 testing/integration_tests/instance_id/Images.xcassets/LaunchImage.launchimage/Contents.json create mode 100644 testing/integration_tests/instance_id/Info.plist create mode 100644 testing/integration_tests/instance_id/LaunchScreen.storyboard create mode 100644 testing/integration_tests/instance_id/LibraryManifest.xml create mode 100644 testing/integration_tests/instance_id/Podfile create mode 100644 testing/integration_tests/instance_id/build.gradle create mode 100644 testing/integration_tests/instance_id/googletest.cmake create mode 100644 testing/integration_tests/instance_id/gradle/wrapper/gradle-wrapper.jar create mode 100644 testing/integration_tests/instance_id/gradle/wrapper/gradle-wrapper.properties create mode 100755 testing/integration_tests/instance_id/gradlew create mode 100644 testing/integration_tests/instance_id/gradlew.bat create mode 100644 testing/integration_tests/instance_id/integration_test.xcodeproj/project.pbxproj create mode 100644 testing/integration_tests/instance_id/proguard.pro create mode 100644 testing/integration_tests/instance_id/res/layout/main.xml create mode 100644 testing/integration_tests/instance_id/res/values/strings.xml create mode 100644 testing/integration_tests/instance_id/settings.gradle create mode 100644 testing/integration_tests/instance_id/src/integration_test.cc create mode 100644 testing/integration_tests/messaging/AndroidManifest.xml create mode 100644 testing/integration_tests/messaging/CMakeLists.txt create mode 100644 testing/integration_tests/messaging/Firebase_Cpp_Messaging_Test_App_Dev.mobileprovision create mode 100644 testing/integration_tests/messaging/Images.xcassets/AppIcon.appiconset/Contents.json create mode 100644 testing/integration_tests/messaging/Images.xcassets/LaunchImage.launchimage/Contents.json create mode 100644 testing/integration_tests/messaging/Info.plist create mode 100644 testing/integration_tests/messaging/LaunchScreen.storyboard create mode 100644 testing/integration_tests/messaging/LibraryManifest.xml create mode 100644 testing/integration_tests/messaging/Podfile create mode 100644 testing/integration_tests/messaging/build.gradle create mode 100644 testing/integration_tests/messaging/googletest.cmake create mode 100644 testing/integration_tests/messaging/gradle/wrapper/gradle-wrapper.jar create mode 100644 testing/integration_tests/messaging/gradle/wrapper/gradle-wrapper.properties create mode 100755 testing/integration_tests/messaging/gradlew create mode 100644 testing/integration_tests/messaging/gradlew.bat create mode 100644 testing/integration_tests/messaging/integration_test.entitlements create mode 100644 testing/integration_tests/messaging/integration_test.xcodeproj/project.pbxproj create mode 100644 testing/integration_tests/messaging/proguard.pro create mode 100644 testing/integration_tests/messaging/readme.md create mode 100644 testing/integration_tests/messaging/res/layout/main.xml create mode 100644 testing/integration_tests/messaging/res/values/strings.xml create mode 100644 testing/integration_tests/messaging/settings.gradle create mode 100644 testing/integration_tests/messaging/src/android/java/com/google/firebase/example/SampleNativeActivity.java create mode 100644 testing/integration_tests/messaging/src/integration_test.cc create mode 100644 testing/integration_tests/remote_config/AndroidManifest.xml create mode 100644 testing/integration_tests/remote_config/CMakeLists.txt create mode 100644 testing/integration_tests/remote_config/Images.xcassets/AppIcon.appiconset/Contents.json create mode 100644 testing/integration_tests/remote_config/Images.xcassets/LaunchImage.launchimage/Contents.json create mode 100644 testing/integration_tests/remote_config/Info.plist create mode 100644 testing/integration_tests/remote_config/LaunchScreen.storyboard create mode 100644 testing/integration_tests/remote_config/LibraryManifest.xml create mode 100644 testing/integration_tests/remote_config/Podfile create mode 100644 testing/integration_tests/remote_config/build.gradle create mode 100644 testing/integration_tests/remote_config/googletest.cmake create mode 100644 testing/integration_tests/remote_config/gradle/wrapper/gradle-wrapper.jar create mode 100644 testing/integration_tests/remote_config/gradle/wrapper/gradle-wrapper.properties create mode 100755 testing/integration_tests/remote_config/gradlew create mode 100644 testing/integration_tests/remote_config/gradlew.bat create mode 100644 testing/integration_tests/remote_config/integration_test.xcodeproj/project.pbxproj create mode 100644 testing/integration_tests/remote_config/proguard.pro create mode 100644 testing/integration_tests/remote_config/res/layout/main.xml create mode 100644 testing/integration_tests/remote_config/res/values/strings.xml create mode 100644 testing/integration_tests/remote_config/settings.gradle create mode 100644 testing/integration_tests/remote_config/src/integration_test.cc create mode 100644 testing/integration_tests/storage/AndroidManifest.xml create mode 100644 testing/integration_tests/storage/CMakeLists.txt create mode 100644 testing/integration_tests/storage/Images.xcassets/AppIcon.appiconset/Contents.json create mode 100644 testing/integration_tests/storage/Images.xcassets/LaunchImage.launchimage/Contents.json create mode 100644 testing/integration_tests/storage/Info.plist create mode 100644 testing/integration_tests/storage/LaunchScreen.storyboard create mode 100644 testing/integration_tests/storage/LibraryManifest.xml create mode 100644 testing/integration_tests/storage/Podfile create mode 100644 testing/integration_tests/storage/build.gradle create mode 100644 testing/integration_tests/storage/googletest.cmake create mode 100644 testing/integration_tests/storage/gradle/wrapper/gradle-wrapper.jar create mode 100644 testing/integration_tests/storage/gradle/wrapper/gradle-wrapper.properties create mode 100755 testing/integration_tests/storage/gradlew create mode 100644 testing/integration_tests/storage/gradlew.bat create mode 100644 testing/integration_tests/storage/integration_test.xcodeproj/project.pbxproj create mode 100644 testing/integration_tests/storage/proguard.pro create mode 100644 testing/integration_tests/storage/readme.md create mode 100644 testing/integration_tests/storage/res/layout/main.xml create mode 100644 testing/integration_tests/storage/res/values/strings.xml create mode 100644 testing/integration_tests/storage/settings.gradle create mode 100644 testing/integration_tests/storage/src/integration_test.cc diff --git a/testing/integration_tests/admob/AndroidManifest.xml b/testing/integration_tests/admob/AndroidManifest.xml new file mode 100644 index 0000000000..0996b3d0df --- /dev/null +++ b/testing/integration_tests/admob/AndroidManifest.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/testing/integration_tests/admob/CMakeLists.txt b/testing/integration_tests/admob/CMakeLists.txt new file mode 100644 index 0000000000..9ae8d1b055 --- /dev/null +++ b/testing/integration_tests/admob/CMakeLists.txt @@ -0,0 +1,196 @@ +cmake_minimum_required(VERSION 2.8) + +# User settings for Firebase integration tests. +# Path to Firebase SDK. +# Try to read the path to the Firebase C++ SDK from an environment variable. +if (NOT "$ENV{FIREBASE_CPP_SDK_DIR}" STREQUAL "") + set(DEFAULT_FIREBASE_CPP_SDK_DIR "$ENV{FIREBASE_CPP_SDK_DIR}") +else() + set(DEFAULT_FIREBASE_CPP_SDK_DIR "firebase_cpp_sdk") +endif() +if ("${FIREBASE_CPP_SDK_DIR}" STREQUAL "") + set(FIREBASE_CPP_SDK_DIR ${DEFAULT_FIREBASE_CPP_SDK_DIR}) +endif() +if(NOT EXISTS ${FIREBASE_CPP_SDK_DIR}) + message(FATAL_ERROR "The Firebase C++ SDK directory does not exist: ${FIREBASE_CPP_SDK_DIR}. See the readme.md for more information") +endif() + +# Windows runtime mode, either MD or MT depending on whether you are using +# /MD or /MT. For more information see: +# https://msdn.microsoft.com/en-us/library/2kzt1wy3.aspx +set(MSVC_RUNTIME_MODE MD) + +project(firebase_testapp) + +# Integration test source files. +set(FIREBASE_APP_FRAMEWORK_SRCS + src/app_framework.cc + src/app_framework.h +) + +set(FIREBASE_TEST_FRAMEWORK_SRCS + src/firebase_test_framework.h + src/firebase_test_framework.cc +) + +set(FIREBASE_INTEGRATION_TEST_SRCS + src/integration_test.cc +) + +# The include directory for the testapp. +include_directories(src) + +# Integration test uses some features that require C++ 11, such as lambdas. +set (CMAKE_CXX_STANDARD 11) + +# Download and unpack googletest (and googlemock) at configure time +set(GOOGLETEST_ROOT ${CMAKE_CURRENT_LIST_DIR}/external/googletest) +# Note: Once googletest is downloaded once, it won't be updated or +# downloaded again unless you delete the "external/googletest" +# directory. +if (NOT EXISTS ${GOOGLETEST_ROOT}/src/googletest/src/gtest-all.cc) + configure_file(googletest.cmake + ${CMAKE_CURRENT_LIST_DIR}/external/googletest/CMakeLists.txt COPYONLY) + execute_process(COMMAND ${CMAKE_COMMAND} . + RESULT_VARIABLE result + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/external/googletest ) + if(result) + message(FATAL_ERROR "CMake step for googletest failed: ${result}") + endif() + execute_process(COMMAND ${CMAKE_COMMAND} --build . + RESULT_VARIABLE result + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/external/googletest ) + if(result) + message(FATAL_ERROR "Build step for googletest failed: ${result}") + endif() +endif() + +if(ANDROID) + # Build an Android application. + + # Source files used for the Android build. + set(FIREBASE_APP_FRAMEWORK_ANDROID_SRCS + src/android/android_app_framework.cc + ) + + # Source files used for the Android build. + set(FIREBASE_TEST_FRAMEWORK_ANDROID_SRCS + src/android/android_firebase_test_framework.cc + ) + + # Build native_app_glue as a static lib + add_library(native_app_glue STATIC + ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c) + + # Export ANativeActivity_onCreate(), + # Refer to: https://github.com/android-ndk/ndk/issues/381. + set(CMAKE_SHARED_LINKER_FLAGS + "${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate") + + add_library(gtest STATIC + ${GOOGLETEST_ROOT}/src/googletest/src/gtest-all.cc) + target_include_directories(gtest + PRIVATE ${GOOGLETEST_ROOT}/src/googletest + PUBLIC ${GOOGLETEST_ROOT}/src/googletest/include) + add_library(gmock STATIC + ${GOOGLETEST_ROOT}/src/googlemock/src/gmock-all.cc) + target_include_directories(gmock + PRIVATE ${GOOGLETEST_ROOT}/src/googletest + PRIVATE ${GOOGLETEST_ROOT}/src/googlemock + PUBLIC ${GOOGLETEST_ROOT}/src/googletest/include + PUBLIC ${GOOGLETEST_ROOT}/src/googlemock/include) + + # Define the target as a shared library, as that is what gradle expects. + set(integration_test_target_name "android_integration_test_main") + add_library(${integration_test_target_name} SHARED + ${FIREBASE_APP_FRAMEWORK_SRCS} + ${FIREBASE_APP_FRAMEWORK_ANDROID_SRCS} + ${FIREBASE_INTEGRATION_TEST_SRCS} + ${FIREBASE_TEST_FRAMEWORK_SRCS} + ${FIREBASE_TEST_FRAMEWORK_ANDROID_SRCS} + ) + + target_include_directories(${integration_test_target_name} PRIVATE + ${ANDROID_NDK}/sources/android/native_app_glue) + + set(ADDITIONAL_LIBS log android atomic native_app_glue) +else() + # Build a desktop application. + add_definitions(-D_GLIBCXX_USE_CXX11_ABI=0) + + # Prevent overriding the parent project's compiler/linker + # settings on Windows + set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) + + # Add googletest directly to our build. This defines + # the gtest and gtest_main targets. + add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/external/googletest/src + ${CMAKE_CURRENT_LIST_DIR}/external/googletest/build + EXCLUDE_FROM_ALL) + + # The gtest/gtest_main targets carry header search path + # dependencies automatically when using CMake 2.8.11 or + # later. Otherwise we have to add them here ourselves. + if (CMAKE_VERSION VERSION_LESS 2.8.11) + include_directories("${gtest_SOURCE_DIR}/include") + include_directories("${gmock_SOURCE_DIR}/include") + endif() + + # Windows runtime mode, either MD or MT depending on whether you are using + # /MD or /MT. For more information see: + # https://msdn.microsoft.com/en-us/library/2kzt1wy3.aspx + set(MSVC_RUNTIME_MODE MD) + + # Platform abstraction layer for the desktop integration test. + set(FIREBASE_APP_FRAMEWORK_DESKTOP_SRCS + src/desktop/desktop_app_framework.cc + ) + + set(integration_test_target_name "integration_test") + add_executable(${integration_test_target_name} + ${FIREBASE_APP_FRAMEWORK_SRCS} + ${FIREBASE_APP_FRAMEWORK_DESKTOP_SRCS} + ${FIREBASE_TEST_FRAMEWORK_SRCS} + ${FIREBASE_INTEGRATION_TEST_SRCS} + ) + + if(APPLE) + set(ADDITIONAL_LIBS + gssapi_krb5 + pthread + "-framework CoreFoundation" + "-framework Foundation" + "-framework GSS" + "-framework Security" + ) + elseif(MSVC) + set(ADDITIONAL_LIBS advapi32 ws2_32 crypt32) + else() + set(ADDITIONAL_LIBS pthread) + endif() + + # If a config file is present, copy it into the binary location so that it's + # possible to create the default Firebase app. + set(FOUND_JSON_FILE FALSE) + foreach(config "google-services-desktop.json" "google-services.json") + if (EXISTS ${config}) + add_custom_command( + TARGET ${integration_test_target_name} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + ${config} $) + set(FOUND_JSON_FILE TRUE) + break() + endif() + endforeach() + if(NOT FOUND_JSON_FILE) + message(WARNING "Failed to find either google-services-desktop.json or google-services.json. See the readme.md for more information.") + endif() +endif() + +# Add the Firebase libraries to the target using the function from the SDK. +add_subdirectory(${FIREBASE_CPP_SDK_DIR} bin/ EXCLUDE_FROM_ALL) +# Note that firebase_app needs to be last in the list. +set(firebase_libs firebase_admob firebase_app) +set(gtest_libs gtest gmock) +target_link_libraries(${integration_test_target_name} ${firebase_libs} + ${gtest_libs} ${ADDITIONAL_LIBS}) diff --git a/testing/integration_tests/admob/Images.xcassets/AppIcon.appiconset/Contents.json b/testing/integration_tests/admob/Images.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000000..d8db8d65fd --- /dev/null +++ b/testing/integration_tests/admob/Images.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "83.5x83.5", + "scale" : "2x" + }, + { + "idiom" : "ios-marketing", + "size" : "1024x1024", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/testing/integration_tests/admob/Images.xcassets/LaunchImage.launchimage/Contents.json b/testing/integration_tests/admob/Images.xcassets/LaunchImage.launchimage/Contents.json new file mode 100644 index 0000000000..6f870a4629 --- /dev/null +++ b/testing/integration_tests/admob/Images.xcassets/LaunchImage.launchimage/Contents.json @@ -0,0 +1,51 @@ +{ + "images" : [ + { + "orientation" : "portrait", + "idiom" : "iphone", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "orientation" : "portrait", + "idiom" : "iphone", + "subtype" : "retina4", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "orientation" : "portrait", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "1x" + }, + { + "orientation" : "landscape", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "1x" + }, + { + "orientation" : "portrait", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "orientation" : "landscape", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/testing/integration_tests/admob/Info.plist b/testing/integration_tests/admob/Info.plist new file mode 100644 index 0000000000..a3e8956703 --- /dev/null +++ b/testing/integration_tests/admob/Info.plist @@ -0,0 +1,37 @@ + + + + + GADApplicationIdentifier + ca-app-pub-3940256099942544~1458002511 + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + com.google.ios.admob.testapp + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + CFBundleURLTypes + + + CFBundleURLSchemes + + firebase-game-loop + + + + + diff --git a/testing/integration_tests/admob/LaunchScreen.storyboard b/testing/integration_tests/admob/LaunchScreen.storyboard new file mode 100644 index 0000000000..673e0f7e68 --- /dev/null +++ b/testing/integration_tests/admob/LaunchScreen.storyboard @@ -0,0 +1,7 @@ + + + + + + + diff --git a/testing/integration_tests/admob/LibraryManifest.xml b/testing/integration_tests/admob/LibraryManifest.xml new file mode 100644 index 0000000000..e1d761e160 --- /dev/null +++ b/testing/integration_tests/admob/LibraryManifest.xml @@ -0,0 +1,7 @@ + + + + + + diff --git a/testing/integration_tests/admob/Podfile b/testing/integration_tests/admob/Podfile new file mode 100644 index 0000000000..a20a2260d9 --- /dev/null +++ b/testing/integration_tests/admob/Podfile @@ -0,0 +1,13 @@ +source 'sso://cpdc-internal/firebase.git' +source 'https://github.com/CocoaPods/Specs.git' +platform :ios, '8.0' +# Firebase AdMob test application. + +target 'integration_test' do + pod 'Firebase/AdMob', '6.24.0' +end + +post_install do |installer| + system("/usr/bin/python ./download_googletest.py") +end + diff --git a/testing/integration_tests/admob/build.gradle b/testing/integration_tests/admob/build.gradle new file mode 100644 index 0000000000..4562ea5642 --- /dev/null +++ b/testing/integration_tests/admob/build.gradle @@ -0,0 +1,64 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +buildscript { + repositories { + mavenLocal() + maven { url 'https://maven.google.com' } + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:3.2.1' + classpath 'com.google.gms:google-services:4.0.1' + } +} + +allprojects { + repositories { + mavenLocal() + maven { url 'https://maven.google.com' } + jcenter() + } +} + +apply plugin: 'com.android.application' + +android { + compileSdkVersion 28 + buildToolsVersion '28.0.3' + + sourceSets { + main { + jniLibs.srcDirs = ['libs'] + manifest.srcFile 'AndroidManifest.xml' + java.srcDirs = ['src/android/java'] + res.srcDirs = ['res'] + } + } + + defaultConfig { + applicationId 'com.google.android.admob.testapp' + minSdkVersion 16 + targetSdkVersion 28 + versionCode 1 + versionName '1.0' + externalNativeBuild.cmake { + arguments "-DFIREBASE_CPP_SDK_DIR=$gradle.firebase_cpp_sdk_dir" + } + } + externalNativeBuild.cmake { + path 'CMakeLists.txt' + } + buildTypes { + release { + minifyEnabled true + proguardFile getDefaultProguardFile('proguard-android.txt') + proguardFile file('proguard.pro') + } + } +} + +apply from: "$gradle.firebase_cpp_sdk_dir/Android/firebase_dependencies.gradle" +firebaseCpp.dependencies { + admob +} + +apply plugin: 'com.google.gms.google-services' diff --git a/testing/integration_tests/admob/googletest.cmake b/testing/integration_tests/admob/googletest.cmake new file mode 100644 index 0000000000..0eeff685dd --- /dev/null +++ b/testing/integration_tests/admob/googletest.cmake @@ -0,0 +1,19 @@ +# Download GoogleTest from GitHub as an external project. +# This CMake file is taken from: +# https://github.com/google/googletest/blob/master/googletest/README.md#incorporating-into-an-existing-cmake-project + +cmake_minimum_required(VERSION 2.8.2) + +project(googletest-download NONE) + +include(ExternalProject) +ExternalProject_Add(googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG master + SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/src" + BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/build" + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + TEST_COMMAND "" +) diff --git a/testing/integration_tests/admob/gradle/wrapper/gradle-wrapper.jar b/testing/integration_tests/admob/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..8c0fb64a8698b08ecc4158d828ca593c4928e9dd GIT binary patch literal 49896 zcmagFb986H(k`5d^NVfUwr$(C?M#x1ZQHiZiEVpg+jrjgoQrerx!>1o_ul)D>ebz~ zs=Mmxr&>W81QY-S1PKWQ%N-;H^tS;2*XwVA`dej1RRn1z<;3VgfE4~kaG`A%QSPsR z#ovnZe+tS9%1MfeDyz`RirvdjPRK~p(#^q2(^5@O&NM19EHdvN-A&StN>0g6QA^VN z0Gx%Gq#PD$QMRFzmK+utjS^Y1F0e8&u&^=w5K<;4Rz|i3A=o|IKLY+g`iK6vfr9?+ z-`>gmU&i?FGSL5&F?TXFu`&Js6h;15QFkXp2M1H9|Eq~bpov-GU(uz%mH0n55wUl- zv#~ccAz`F5wlQ>e_KlJS3@{)B?^v*EQM=IxLa&76^y51a((wq|2-`qON>+4dLc{Oo z51}}o^Zen(oAjxDK7b++9_Yg`67p$bPo3~BCpGM7uAWmvIhWc5Gi+gQZ|Pwa-Gll@<1xmcPy z|NZmu6m)g5Ftu~BG&Xdxclw7Cij{xbBMBn-LMII#Slp`AElb&2^Hw+w>(3crLH!;I zN+Vk$D+wP1#^!MDCiad@vM>H#6+`Ct#~6VHL4lzmy;lSdk>`z6)=>Wh15Q2)dQtGqvn0vJU@+(B5{MUc*qs4!T+V=q=wy)<6$~ z!G>e_4dN@lGeF_$q9`Ju6Ncb*x?O7=l{anm7Eahuj_6lA{*#Gv*TaJclevPVbbVYu z(NY?5q+xxbO6%g1xF0r@Ix8fJ~u)VRUp`S%&rN$&e!Od`~s+64J z5*)*WSi*i{k%JjMSIN#X;jC{HG$-^iX+5f5BGOIHWAl*%15Z#!xntpk($-EGKCzKa zT7{siZ9;4TICsWQ$pu&wKZQTCvpI$Xvzwxoi+XkkpeE&&kFb!B?h2hi%^YlXt|-@5 zHJ~%AN!g_^tmn1?HSm^|gCE#!GRtK2(L{9pL#hp0xh zME}|DB>(5)`iE7CM)&_+S}-Bslc#@B5W4_+k4Cp$l>iVyg$KP>CN?SVGZ(&02>iZK zB<^HP$g$Lq*L$BWd?2(F?-MUbNWTJVQdW7$#8a|k_30#vHAD1Z{c#p;bETk0VnU5A zBgLe2HFJ3032$G<`m*OB!KM$*sdM20jm)It5OSru@tXpK5LT>#8)N!*skNu1$TpIw zufjjdp#lyH5bZ%|Iuo|iu9vG1HrIVWLH>278xo>aVBkPN3V$~!=KnlXQ4eDqS7%E% zQ!z^$Q$b^6Q)g#cLpwur(|<0gWHo6A6jc;n`t(V9T;LzTAU{IAu*uEQ%Ort1k+Kn+f_N`9|bxYC+~Z1 zCC1UCWv*Orx$_@ydv9mIe(liLfOr7mhbV@tKw{6)q^1DH1nmvZ0cj215R<~&I<4S| zgnr;9Cdjqpz#o8i0CQjtl`}{c*P)aSdH|abxGdrR)-3z+02-eX(k*B)Uqv6~^nh** z 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 \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/testing/integration_tests/admob/gradlew.bat b/testing/integration_tests/admob/gradlew.bat new file mode 100644 index 0000000000..8a0b282aa6 --- /dev/null +++ b/testing/integration_tests/admob/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/testing/integration_tests/admob/integration_test.xcodeproj/project.pbxproj b/testing/integration_tests/admob/integration_test.xcodeproj/project.pbxproj new file mode 100644 index 0000000000..d1d6b852d1 --- /dev/null +++ b/testing/integration_tests/admob/integration_test.xcodeproj/project.pbxproj @@ -0,0 +1,364 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 520BC0391C869159008CFBC3 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 520BC0381C869159008CFBC3 /* GoogleService-Info.plist */; }; + 529226D61C85F68000C89379 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 529226D51C85F68000C89379 /* Foundation.framework */; }; + 529226D81C85F68000C89379 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 529226D71C85F68000C89379 /* CoreGraphics.framework */; }; + 529226DA1C85F68000C89379 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 529226D91C85F68000C89379 /* UIKit.framework */; }; + D61C5F8E22BABA9C00A79141 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D61C5F8C22BABA9B00A79141 /* Images.xcassets */; }; + D61C5F9622BABAD200A79141 /* integration_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = D61C5F9222BABAD100A79141 /* integration_test.cc */; }; + D62CCBC022F367140099BE9F /* gmock-all.cc in Sources */ = {isa = PBXBuildFile; fileRef = D62CCBBF22F367140099BE9F /* gmock-all.cc */; }; + D66B16871CE46E8900E5638A /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D66B16861CE46E8900E5638A /* LaunchScreen.storyboard */; }; + D67D355822BABD2200292C1D /* gtest-all.cc in Sources */ = {isa = PBXBuildFile; fileRef = D67D355622BABD2100292C1D /* gtest-all.cc */; }; + D6C179E922CB322900C2651A /* ios_app_framework.mm in Sources */ = {isa = PBXBuildFile; fileRef = D6C179E722CB322900C2651A /* ios_app_framework.mm */; }; + D6C179EA22CB322900C2651A /* ios_firebase_test_framework.mm in Sources */ = {isa = PBXBuildFile; fileRef = D6C179E822CB322900C2651A /* ios_firebase_test_framework.mm */; }; + D6C179EE22CB323300C2651A /* firebase_test_framework.cc in Sources */ = {isa = PBXBuildFile; fileRef = D6C179EC22CB323300C2651A /* firebase_test_framework.cc */; }; + D6C179F022CB32A000C2651A /* app_framework.cc in Sources */ = {isa = PBXBuildFile; fileRef = D6C179EF22CB32A000C2651A /* app_framework.cc */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 520BC0381C869159008CFBC3 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; + 529226D21C85F68000C89379 /* integration_test.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = integration_test.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 529226D51C85F68000C89379 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + 529226D71C85F68000C89379 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; + 529226D91C85F68000C89379 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; + 529226EE1C85F68000C89379 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; + D61C5F8C22BABA9B00A79141 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; + D61C5F8D22BABA9C00A79141 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + D61C5F9222BABAD100A79141 /* integration_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = integration_test.cc; path = src/integration_test.cc; sourceTree = ""; }; + D62CCBBF22F367140099BE9F /* gmock-all.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "gmock-all.cc"; path = "external/googletest/src/googlemock/src/gmock-all.cc"; sourceTree = ""; }; + D62CCBC122F367320099BE9F /* gmock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = gmock.h; path = external/googletest/src/googlemock/include/gmock/gmock.h; sourceTree = ""; }; + D66B16861CE46E8900E5638A /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = ""; }; + D67D355622BABD2100292C1D /* gtest-all.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "gtest-all.cc"; path = "external/googletest/src/googletest/src/gtest-all.cc"; sourceTree = ""; }; + D67D355722BABD2100292C1D /* gtest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = gtest.h; path = external/googletest/src/googletest/include/gtest/gtest.h; sourceTree = ""; }; + D6C179E722CB322900C2651A /* ios_app_framework.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ios_app_framework.mm; path = src/ios/ios_app_framework.mm; sourceTree = ""; }; + D6C179E822CB322900C2651A /* ios_firebase_test_framework.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ios_firebase_test_framework.mm; path = src/ios/ios_firebase_test_framework.mm; sourceTree = ""; }; + D6C179EB22CB323300C2651A /* firebase_test_framework.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = firebase_test_framework.h; path = src/firebase_test_framework.h; sourceTree = ""; }; + D6C179EC22CB323300C2651A /* firebase_test_framework.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = firebase_test_framework.cc; path = src/firebase_test_framework.cc; sourceTree = ""; }; + D6C179ED22CB323300C2651A /* app_framework.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = app_framework.h; path = src/app_framework.h; sourceTree = ""; }; + D6C179EF22CB32A000C2651A /* app_framework.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = app_framework.cc; path = src/app_framework.cc; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 529226CF1C85F68000C89379 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 529226D81C85F68000C89379 /* CoreGraphics.framework in Frameworks */, + 529226DA1C85F68000C89379 /* UIKit.framework in Frameworks */, + 529226D61C85F68000C89379 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 529226C91C85F68000C89379 = { + isa = PBXGroup; + children = ( + D61C5F8C22BABA9B00A79141 /* Images.xcassets */, + D61C5F8D22BABA9C00A79141 /* Info.plist */, + D66B16861CE46E8900E5638A /* LaunchScreen.storyboard */, + 520BC0381C869159008CFBC3 /* GoogleService-Info.plist */, + 5292271D1C85FB5500C89379 /* src */, + 529226D41C85F68000C89379 /* Frameworks */, + 529226D31C85F68000C89379 /* Products */, + ); + sourceTree = ""; + }; + 529226D31C85F68000C89379 /* Products */ = { + isa = PBXGroup; + children = ( + 529226D21C85F68000C89379 /* integration_test.app */, + ); + name = Products; + sourceTree = ""; + }; + 529226D41C85F68000C89379 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 529226D51C85F68000C89379 /* Foundation.framework */, + 529226D71C85F68000C89379 /* CoreGraphics.framework */, + 529226D91C85F68000C89379 /* UIKit.framework */, + 529226EE1C85F68000C89379 /* XCTest.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 5292271D1C85FB5500C89379 /* src */ = { + isa = PBXGroup; + children = ( + D62CCBC122F367320099BE9F /* gmock.h */, + D62CCBBF22F367140099BE9F /* gmock-all.cc */, + D67D355622BABD2100292C1D /* gtest-all.cc */, + D67D355722BABD2100292C1D /* gtest.h */, + D6C179EF22CB32A000C2651A /* app_framework.cc */, + D6C179ED22CB323300C2651A /* app_framework.h */, + D6C179EC22CB323300C2651A /* firebase_test_framework.cc */, + D6C179EB22CB323300C2651A /* firebase_test_framework.h */, + D61C5F9222BABAD100A79141 /* integration_test.cc */, + 5292271E1C85FB5B00C89379 /* ios */, + ); + name = src; + sourceTree = ""; + }; + 5292271E1C85FB5B00C89379 /* ios */ = { + isa = PBXGroup; + children = ( + D6C179E722CB322900C2651A /* ios_app_framework.mm */, + D6C179E822CB322900C2651A /* ios_firebase_test_framework.mm */, + ); + name = ios; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 529226D11C85F68000C89379 /* integration_test */ = { + isa = PBXNativeTarget; + buildConfigurationList = 529226F91C85F68000C89379 /* Build configuration list for PBXNativeTarget "integration_test" */; + buildPhases = ( + 529226CE1C85F68000C89379 /* Sources */, + 529226CF1C85F68000C89379 /* Frameworks */, + 529226D01C85F68000C89379 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = integration_test; + productName = testapp; + productReference = 529226D21C85F68000C89379 /* integration_test.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 529226CA1C85F68000C89379 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0640; + ORGANIZATIONNAME = Google; + TargetAttributes = { + 529226D11C85F68000C89379 = { + CreatedOnToolsVersion = 6.4; + DevelopmentTeam = EQHXZ8M8AV; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = 529226CD1C85F68000C89379 /* Build configuration list for PBXProject "integration_test" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 529226C91C85F68000C89379; + productRefGroup = 529226D31C85F68000C89379 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 529226D11C85F68000C89379 /* integration_test */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 529226D01C85F68000C89379 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D61C5F8E22BABA9C00A79141 /* Images.xcassets in Resources */, + D66B16871CE46E8900E5638A /* LaunchScreen.storyboard in Resources */, + 520BC0391C869159008CFBC3 /* GoogleService-Info.plist in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 529226CE1C85F68000C89379 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D67D355822BABD2200292C1D /* gtest-all.cc in Sources */, + D62CCBC022F367140099BE9F /* gmock-all.cc in Sources */, + D6C179EA22CB322900C2651A /* ios_firebase_test_framework.mm in Sources */, + D61C5F9622BABAD200A79141 /* integration_test.cc in Sources */, + D6C179E922CB322900C2651A /* ios_app_framework.mm in Sources */, + D6C179F022CB32A000C2651A /* app_framework.cc in Sources */, + D6C179EE22CB323300C2651A /* firebase_test_framework.cc in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 529226F71C85F68000C89379 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.4; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 529226F81C85F68000C89379 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.4; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 529226FA1C85F68000C89379 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ""; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)", + ); + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + "\"$(SRCROOT)/src\"", + "\"$(SRCROOT)/external/googletest/src/googletest/include\"", + "\"$(SRCROOT)/external/googletest/src/googlemock/include\"", + "\"$(SRCROOT)/external/googletest/src/googletest\"", + "\"$(SRCROOT)/external/googletest/src/googlemock\"", + ); + INFOPLIST_FILE = "$(SRCROOT)/Info.plist"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + WRAPPER_EXTENSION = app; + }; + name = Debug; + }; + 529226FB1C85F68000C89379 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ""; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)", + ); + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + "\"$(SRCROOT)/src\"", + "\"$(SRCROOT)/external/googletest/src/googletest/include\"", + "\"$(SRCROOT)/external/googletest/src/googlemock/include\"", + "\"$(SRCROOT)/external/googletest/src/googletest\"", + "\"$(SRCROOT)/external/googletest/src/googlemock\"", + ); + INFOPLIST_FILE = "$(SRCROOT)/Info.plist"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + WRAPPER_EXTENSION = app; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 529226CD1C85F68000C89379 /* Build configuration list for PBXProject "integration_test" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 529226F71C85F68000C89379 /* Debug */, + 529226F81C85F68000C89379 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 529226F91C85F68000C89379 /* Build configuration list for PBXNativeTarget "integration_test" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 529226FA1C85F68000C89379 /* Debug */, + 529226FB1C85F68000C89379 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 529226CA1C85F68000C89379 /* Project object */; +} diff --git a/testing/integration_tests/admob/proguard.pro b/testing/integration_tests/admob/proguard.pro new file mode 100644 index 0000000000..2d04b8a9a5 --- /dev/null +++ b/testing/integration_tests/admob/proguard.pro @@ -0,0 +1,2 @@ +-ignorewarnings +-keep,includedescriptorclasses public class com.google.firebase.example.LoggingUtils { * ; } diff --git a/testing/integration_tests/admob/res/layout/main.xml b/testing/integration_tests/admob/res/layout/main.xml new file mode 100644 index 0000000000..d3ffb63082 --- /dev/null +++ b/testing/integration_tests/admob/res/layout/main.xml @@ -0,0 +1,12 @@ + + + + diff --git a/testing/integration_tests/admob/res/values/strings.xml b/testing/integration_tests/admob/res/values/strings.xml new file mode 100644 index 0000000000..f38ec9de9d --- /dev/null +++ b/testing/integration_tests/admob/res/values/strings.xml @@ -0,0 +1,4 @@ + + + Firebase AdMob Integration Test + diff --git a/testing/integration_tests/admob/settings.gradle b/testing/integration_tests/admob/settings.gradle new file mode 100644 index 0000000000..2a543b9351 --- /dev/null +++ b/testing/integration_tests/admob/settings.gradle @@ -0,0 +1,36 @@ +// Copyright 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +def firebase_cpp_sdk_dir = System.getProperty('firebase_cpp_sdk.dir') +if (firebase_cpp_sdk_dir == null || firebase_cpp_sdk_dir.isEmpty()) { + firebase_cpp_sdk_dir = System.getenv('FIREBASE_CPP_SDK_DIR') + if (firebase_cpp_sdk_dir == null || firebase_cpp_sdk_dir.isEmpty()) { + if ((new File('firebase_cpp_sdk')).exists()) { + firebase_cpp_sdk_dir = 'firebase_cpp_sdk' + } else { + throw new StopActionException( + 'firebase_cpp_sdk.dir property or the FIREBASE_CPP_SDK_DIR ' + + 'environment variable must be set to reference the Firebase C++ ' + + 'SDK install directory. This is used to configure static library ' + + 'and C/C++ include paths for the SDK.') + } + } +} +if (!(new File(firebase_cpp_sdk_dir)).exists()) { + throw new StopActionException( + sprintf('Firebase C++ SDK directory %s does not exist', + firebase_cpp_sdk_dir)) +} +gradle.ext.firebase_cpp_sdk_dir = "$firebase_cpp_sdk_dir" +includeBuild "$firebase_cpp_sdk_dir" \ No newline at end of file diff --git a/testing/integration_tests/admob/src/integration_test.cc b/testing/integration_tests/admob/src/integration_test.cc new file mode 100644 index 0000000000..b97fbba2ef --- /dev/null +++ b/testing/integration_tests/admob/src/integration_test.cc @@ -0,0 +1,552 @@ +// Copyright 2019 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include +#include +#include +#include +#include + +#include "app_framework.h" // NOLINT +#include "firebase/admob.h" +#include "firebase/app.h" +#include "firebase/util.h" +#include "firebase_test_framework.h" // NOLINT + +#if defined(ANDROID) || (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) +// includes for phone-only tests. +#include +#include +#endif // defined(ANDROID) || (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) + +// The TO_STRING macro is useful for command line defined strings as the quotes +// get stripped. +#define TO_STRING_EXPAND(X) #X +#define TO_STRING(X) TO_STRING_EXPAND(X) + +// Path to the Firebase config file to load. +#ifdef FIREBASE_CONFIG +#define FIREBASE_CONFIG_STRING TO_STRING(FIREBASE_CONFIG) +#else +#define FIREBASE_CONFIG_STRING "" +#endif // FIREBASE_CONFIG + +namespace firebase_testapp_automated { + +// The AdMob app IDs for the test app. +#if defined(__ANDROID__) +// If you change the AdMob app ID for your Android app, make sure to change it +// in AndroidManifest.xml as well. +const char* kAdMobAppID = "ca-app-pub-3940256099942544~3347511713"; +#else +// If you change the AdMob app ID for your iOS app, make sure to change the +// value for "GADApplicationIdentifier" in your Info.plist as well. +const char* kAdMobAppID = "ca-app-pub-3940256099942544~1458002511"; +#endif + +// These ad units IDs have been created specifically for testing, and will +// always return test ads. +#if defined(__ANDROID__) +const char* kBannerAdUnit = "ca-app-pub-3940256099942544/6300978111"; +const char* kInterstitialAdUnit = "ca-app-pub-3940256099942544/1033173712"; +const char* kRewardedVideoAdUnit = "ca-app-pub-3940256099942544/5224354917"; +#else +const char* kBannerAdUnit = "ca-app-pub-3940256099942544/2934735716"; +const char* kInterstitialAdUnit = "ca-app-pub-3940256099942544/4411468910"; +const char* kRewardedVideoAdUnit = "ca-app-pub-3940256099942544/1712485313"; +#endif + +using app_framework::LogDebug; +using app_framework::ProcessEvents; + +using firebase_test_framework::FirebaseTest; + +class FirebaseAdMobTest : public FirebaseTest { + public: + FirebaseAdMobTest(); + ~FirebaseAdMobTest() override; + + static void SetUpTestSuite(); + static void TearDownTestSuite(); + + void SetUp() override; + void TearDown() override; + + protected: + firebase::admob::AdRequest GetAdRequest(); + + static firebase::App* shared_app_; +}; + +firebase::App* FirebaseAdMobTest::shared_app_ = nullptr; + +void FirebaseAdMobTest::SetUpTestSuite() { + LogDebug("Initialize Firebase App."); + + FindFirebaseConfig(FIREBASE_CONFIG_STRING); + +#if defined(__ANDROID__) + shared_app_ = ::firebase::App::Create(app_framework::GetJniEnv(), + app_framework::GetActivity()); +#else + shared_app_ = ::firebase::App::Create(); +#endif // defined(__ANDROID__) + + LogDebug("Initializing AdMob."); + + ::firebase::ModuleInitializer initializer; + initializer.Initialize( + shared_app_, nullptr, [](::firebase::App* app, void* /* userdata */) { + LogDebug("Try to initialize AdMob"); + return ::firebase::admob::Initialize(*app, kAdMobAppID); + }); + + WaitForCompletion(initializer.InitializeLastResult(), "Initialize"); + + ASSERT_EQ(initializer.InitializeLastResult().error(), 0) + << initializer.InitializeLastResult().error_message(); + + LogDebug("Successfully initialized AdMob."); +} + +void FirebaseAdMobTest::TearDownTestSuite() { + LogDebug("Shutdown AdMob."); + firebase::admob::Terminate(); + LogDebug("Shutdown Firebase App."); + delete shared_app_; + shared_app_ = nullptr; +} + +FirebaseAdMobTest::FirebaseAdMobTest() {} + +FirebaseAdMobTest::~FirebaseAdMobTest() {} + +void FirebaseAdMobTest::SetUp() { FirebaseTest::SetUp(); } + +void FirebaseAdMobTest::TearDown() { FirebaseTest::TearDown(); } + +firebase::admob::AdRequest FirebaseAdMobTest::GetAdRequest() { + // Sample keywords to use in making the request. + static const char* kKeywords[] = {"AdMob", "C++", "Fun"}; + + // Sample birthday value to use in making the request. + static const int kBirthdayDay = 10; + static const int kBirthdayMonth = 11; + static const int kBirthdayYear = 1976; + + // Sample test device IDs to use in making the request. + static const char* kTestDeviceIDs[] = {"2077ef9a63d2b398840261c8221a0c9b", + "098fe087d987c9a878965454a65654d7"}; + + firebase::admob::AdRequest request; + // If the app is aware of the user's gender, it can be added to the targeting + // information. Otherwise, "unknown" should be used. + request.gender = firebase::admob::kGenderUnknown; + + // This value allows publishers to specify whether they would like the request + // to be treated as child-directed for purposes of the Children’s Online + // Privacy Protection Act (COPPA). + // See http://business.ftc.gov/privacy-and-security/childrens-privacy. + request.tagged_for_child_directed_treatment = + firebase::admob::kChildDirectedTreatmentStateTagged; + + // The user's birthday, if known. Note that months are indexed from one. + request.birthday_day = kBirthdayDay; + request.birthday_month = kBirthdayMonth; + request.birthday_year = kBirthdayYear; + + // Additional keywords to be used in targeting. + request.keyword_count = sizeof(kKeywords) / sizeof(kKeywords[0]); + request.keywords = kKeywords; + + // "Extra" key value pairs can be added to the request as well. Typically + // these are used when testing new features. + static const firebase::admob::KeyValuePair kRequestExtras[] = { + {"the_name_of_an_extra", "the_value_for_that_extra"}}; + request.extras_count = sizeof(kRequestExtras) / sizeof(kRequestExtras[0]); + request.extras = kRequestExtras; + + // This example uses ad units that are specially configured to return test ads + // for every request. When using your own ad unit IDs, however, it's important + // to register the device IDs associated with any devices that will be used to + // test the app. This ensures that regardless of the ad unit ID, those + // devices will always receive test ads in compliance with AdMob policy. + // + // Device IDs can be obtained by checking the logcat or the Xcode log while + // debugging. They appear as a long string of hex characters. + request.test_device_id_count = + sizeof(kTestDeviceIDs) / sizeof(kTestDeviceIDs[0]); + request.test_device_ids = kTestDeviceIDs; + return request; +} + +// Test cases below. + +TEST_F(FirebaseAdMobTest, TestGetAdRequest) { GetAdRequest(); } + +// A simple listener to help test changes to a BannerView. +class TestBannerViewListener : public firebase::admob::BannerView::Listener { + public: + void OnPresentationStateChanged( + firebase::admob::BannerView* banner_view, + firebase::admob::BannerView::PresentationState state) override { + presentation_states_.push_back(state); + } + void OnBoundingBoxChanged(firebase::admob::BannerView* banner_view, + firebase::admob::BoundingBox box) override { + bounding_box_changes_.push_back(box); + } + std::vector + presentation_states_; + std::vector bounding_box_changes_; +}; + +TEST_F(FirebaseAdMobTest, TestBannerView) { + // AdMob cannot be tested on Firebase Test Lab, so disable tests on FTL. + TEST_REQUIRES_USER_INTERACTION; + + static const int kBannerWidth = 320; + static const int kBannerHeight = 50; + + firebase::admob::AdSize banner_ad_size; + banner_ad_size.ad_size_type = firebase::admob::kAdSizeStandard; + banner_ad_size.width = kBannerWidth; + banner_ad_size.height = kBannerHeight; + + firebase::admob::BannerView* banner = new firebase::admob::BannerView(); + WaitForCompletion(banner->Initialize(app_framework::GetWindowContext(), + kBannerAdUnit, banner_ad_size), + "Initialize"); + + // Set the listener. + TestBannerViewListener banner_listener; + banner->SetListener(&banner_listener); + + // Load the banner ad. + firebase::admob::AdRequest request = GetAdRequest(); + WaitForCompletion(banner->LoadAd(request), "LoadAd"); + + std::vector + expected_presentation_states; + int expected_num_bounding_box_changes = 0; + + // Make the BannerView visible. + WaitForCompletion(banner->Show(), "Show 0"); + expected_presentation_states.push_back( + firebase::admob::BannerView::kPresentationStateVisibleWithAd); + expected_num_bounding_box_changes++; + + // Move to each of the six pre-defined positions. + + WaitForCompletion(banner->MoveTo(firebase::admob::BannerView::kPositionTop), + "MoveTo(Top)"); + expected_presentation_states.push_back( + firebase::admob::BannerView::kPresentationStateVisibleWithAd); + expected_num_bounding_box_changes++; + + WaitForCompletion( + banner->MoveTo(firebase::admob::BannerView::kPositionTopLeft), + "MoveTo(TopLeft)"); + expected_presentation_states.push_back( + firebase::admob::BannerView::kPresentationStateVisibleWithAd); + expected_num_bounding_box_changes++; + + WaitForCompletion( + banner->MoveTo(firebase::admob::BannerView::kPositionTopRight), + "MoveTo(TopRight)"); + expected_presentation_states.push_back( + firebase::admob::BannerView::kPresentationStateVisibleWithAd); + expected_num_bounding_box_changes++; + + WaitForCompletion( + banner->MoveTo(firebase::admob::BannerView::kPositionBottom), + "Moveto(Bottom)"); + expected_presentation_states.push_back( + firebase::admob::BannerView::kPresentationStateVisibleWithAd); + expected_num_bounding_box_changes++; + + WaitForCompletion( + banner->MoveTo(firebase::admob::BannerView::kPositionBottomLeft), + "MoveTo(BottomLeft)"); + expected_presentation_states.push_back( + firebase::admob::BannerView::kPresentationStateVisibleWithAd); + expected_num_bounding_box_changes++; + + WaitForCompletion( + banner->MoveTo(firebase::admob::BannerView::kPositionBottomRight), + "MoveTo(BottomRight)"); + expected_presentation_states.push_back( + firebase::admob::BannerView::kPresentationStateVisibleWithAd); + expected_num_bounding_box_changes++; + + // Move to some coordinates. + WaitForCompletion(banner->MoveTo(100, 300), "MoveTo(x0, y0)"); + expected_presentation_states.push_back( + firebase::admob::BannerView::kPresentationStateVisibleWithAd); + expected_num_bounding_box_changes++; + + WaitForCompletion(banner->MoveTo(100, 400), "MoveTo(x1, y1)"); + expected_presentation_states.push_back( + firebase::admob::BannerView::kPresentationStateVisibleWithAd); + expected_num_bounding_box_changes++; + + // Try hiding and showing the BannerView. + WaitForCompletion(banner->Hide(), "Hide 1"); + expected_presentation_states.push_back( + firebase::admob::BannerView::kPresentationStateHidden); + + WaitForCompletion(banner->Show(), "Show 1"); + expected_presentation_states.push_back( + firebase::admob::BannerView::kPresentationStateVisibleWithAd); + expected_num_bounding_box_changes++; + + // Move again after hiding/showing. + WaitForCompletion(banner->MoveTo(100, 300), "MoveTo(x2, y2)"); + expected_presentation_states.push_back( + firebase::admob::BannerView::kPresentationStateVisibleWithAd); + expected_num_bounding_box_changes++; + + WaitForCompletion(banner->MoveTo(100, 400), "Moveto(x3, y3)"); + expected_presentation_states.push_back( + firebase::admob::BannerView::kPresentationStateVisibleWithAd); + expected_num_bounding_box_changes++; + + WaitForCompletion(banner->Hide(), "Hide 2"); + expected_presentation_states.push_back( + firebase::admob::BannerView::kPresentationStateHidden); + + delete banner; + expected_presentation_states.push_back( + firebase::admob::BannerView::kPresentationStateHidden); + expected_num_bounding_box_changes++; + +#if defined(__ANDROID__) || TARGET_OS_IPHONE + // Ensure that we got all the presentation state changes. + EXPECT_EQ(banner_listener.presentation_states_, expected_presentation_states); + + // For the bounding box, check that we got the number of bounding box events + // we expect, since we don't know the exact bounding box coordinates to + // expect. + EXPECT_EQ(banner_listener.bounding_box_changes_.size(), + expected_num_bounding_box_changes); + + // As an extra check, all bounding boxes except the last should have the same + // size aspect ratio that we requested. For example if you requested a 320x50 + // banner, you can get one with the size 960x150. Use EXPECT_NEAR because the + // calculation can have a small bit of error. + double kAspectRatioAllowedError = 0.02; // Allow about 2% of error. + double expected_aspect_ratio = + static_cast(kBannerWidth) / static_cast(kBannerHeight); + for (int i = 0; i < banner_listener.bounding_box_changes_.size() - 1; ++i) { + double actual_aspect_ratio = + static_cast(banner_listener.bounding_box_changes_[i].width) / + static_cast(banner_listener.bounding_box_changes_[i].height); + EXPECT_NEAR(actual_aspect_ratio, expected_aspect_ratio, + kAspectRatioAllowedError) + << "Banner size " << banner_listener.bounding_box_changes_[i].width + << "x" << banner_listener.bounding_box_changes_[i].height + << " does not have the same aspect ratio as requested size " + << kBannerWidth << "x" << kBannerHeight << "."; + } + + // And finally, the last bounding box change, when the banner is deleted, + // should be (0,0,0,0). + EXPECT_TRUE(banner_listener.bounding_box_changes_.back().x == 0 && + banner_listener.bounding_box_changes_.back().y == 0 && + banner_listener.bounding_box_changes_.back().width == 0 && + banner_listener.bounding_box_changes_.back().height == 0); +#endif +} + +// A simple listener to help test changes to a InterstitialAd. +class TestInterstitialAdListener + : public firebase::admob::InterstitialAd::Listener { + public: + void OnPresentationStateChanged( + firebase::admob::InterstitialAd* interstitial_ad, + firebase::admob::InterstitialAd::PresentationState state) override { + presentation_states_.push_back(state); + } + std::vector + presentation_states_; +}; + +TEST_F(FirebaseAdMobTest, TestInterstitialAd) { + TEST_REQUIRES_USER_INTERACTION; + + firebase::admob::InterstitialAd* interstitial = + new firebase::admob::InterstitialAd(); + WaitForCompletion(interstitial->Initialize(app_framework::GetWindowContext(), + kInterstitialAdUnit), + "Initialize"); + TestInterstitialAdListener interstitial_listener; + interstitial->SetListener(&interstitial_listener); + + firebase::admob::AdRequest request = GetAdRequest(); + // When the InterstitialAd is initialized, load an ad. + WaitForCompletion(interstitial->LoadAd(request), "LoadAd"); + std::vector + expected_presentation_states; + + WaitForCompletion(interstitial->Show(), "Show"); + expected_presentation_states.push_back( + firebase::admob::InterstitialAd::PresentationState:: + kPresentationStateCoveringUI); + // Wait for the user to close the interstitial ad. + while (interstitial->presentation_state() != + firebase::admob::InterstitialAd::PresentationState:: + kPresentationStateHidden) { + app_framework::ProcessEvents(1000); + } + expected_presentation_states.push_back( + firebase::admob::InterstitialAd::PresentationState:: + kPresentationStateHidden); +#if defined(__ANDROID__) || TARGET_OS_IPHONE + EXPECT_EQ(interstitial_listener.presentation_states_, + expected_presentation_states); +#endif + delete interstitial; +} + +// A simple listener to help test changes to rewarded video state. +class TestRewardedVideoListener + : public firebase::admob::rewarded_video::Listener { + public: + TestRewardedVideoListener() { got_reward_ = false; } + void OnRewarded(firebase::admob::rewarded_video::RewardItem reward) override { + got_reward_ = true; + reward_type_ = reward.reward_type; + reward_amount_ = reward.amount; + } + void OnPresentationStateChanged( + firebase::admob::rewarded_video::PresentationState state) override { + presentation_states_.push_back(state); + } + bool got_reward_; + std::string reward_type_; + float reward_amount_; + std::vector + presentation_states_; +}; + +TEST_F(FirebaseAdMobTest, TestRewardedVideoAd) { + TEST_REQUIRES_USER_INTERACTION; + + namespace rewarded_video = firebase::admob::rewarded_video; + WaitForCompletion(rewarded_video::Initialize(), "Initialize"); + + TestRewardedVideoListener rewarded_listener; + rewarded_video::SetListener(&rewarded_listener); + + firebase::admob::AdRequest request = GetAdRequest(); + WaitForCompletion(rewarded_video::LoadAd(kRewardedVideoAdUnit, request), + "LoadAd"); + + std::vector expected_presentation_states; + + WaitForCompletion(rewarded_video::Show(app_framework::GetWindowContext()), + "Show"); + + expected_presentation_states.push_back( + rewarded_video::PresentationState::kPresentationStateCoveringUI); + expected_presentation_states.push_back( + rewarded_video::PresentationState::kPresentationStateVideoHasStarted); + + // Wait a moment, then pause, then resume. + ProcessEvents(1000); + WaitForCompletion(rewarded_video::Pause(), "Pause"); + ProcessEvents(1000); + WaitForCompletion(rewarded_video::Resume(), "Resume"); + +#if defined(__ANDROID__) || TARGET_OS_IPHONE + // Wait for video to complete. + while ( + rewarded_listener.presentation_states_.back() != + rewarded_video::PresentationState::kPresentationStateVideoHasCompleted) { + ProcessEvents(1000); + } + expected_presentation_states.push_back( + rewarded_video::PresentationState::kPresentationStateVideoHasCompleted); + + EXPECT_TRUE(rewarded_listener.got_reward_); + EXPECT_NE(rewarded_listener.reward_type_, ""); + EXPECT_NE(rewarded_listener.reward_amount_, 0); + LogDebug("Got reward: %.02f %s", rewarded_listener.reward_amount_, + rewarded_listener.reward_type_.c_str()); + + EXPECT_EQ(rewarded_listener.presentation_states_, + expected_presentation_states); +#endif + rewarded_video::Destroy(); +} + +#if defined(ANDROID) || (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) +// Test runs & compiles for phones only. + +struct ThreadArgs { + firebase::admob::BannerView* banner; + sem_t* semaphore; +}; + +static void* DeleteBannerViewOnSignal(void* args) { + ThreadArgs* thread_args = static_cast(args); + sem_wait(thread_args->semaphore); + delete thread_args->banner; + return nullptr; +} + +TEST_F(FirebaseAdMobTest, TestBannerViewMultithreadDeletion) { + SKIP_TEST_ON_DESKTOP; + + static const int kBannerWidth = 320; + static const int kBannerHeight = 50; + + firebase::admob::AdSize banner_ad_size; + banner_ad_size.ad_size_type = firebase::admob::kAdSizeStandard; + banner_ad_size.width = kBannerWidth; + banner_ad_size.height = kBannerHeight; + + for (int i = 0; i < 5; ++i) { + firebase::admob::BannerView* banner = new firebase::admob::BannerView(); + WaitForCompletion(banner->Initialize(app_framework::GetWindowContext(), + kBannerAdUnit, banner_ad_size), + "Initialize"); + sem_t semaphore; + sem_init(&semaphore, 0, 1); + + ThreadArgs args = {banner, &semaphore}; + + pthread_t t1; + int err = pthread_create(&t1, nullptr, &DeleteBannerViewOnSignal, &args); + EXPECT_EQ(err, 0); + + banner->Destroy(); + sem_post(&semaphore); + + // Blocks until DeleteBannerViewOnSignal function is done. + void* result = nullptr; + err = pthread_join(t1, &result); + + EXPECT_EQ(err, 0); + EXPECT_EQ(result, nullptr); + + sem_destroy(&semaphore); + } +} +#endif // #if defined(ANDROID) || (defined(TARGET_OS_IPHONE) && + // TARGET_OS_IPHONE) + +} // namespace firebase_testapp_automated diff --git a/testing/integration_tests/app/AndroidManifest.xml b/testing/integration_tests/app/AndroidManifest.xml new file mode 100644 index 0000000000..ffd3c1f9d0 --- /dev/null +++ b/testing/integration_tests/app/AndroidManifest.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/testing/integration_tests/app/CMakeLists.txt b/testing/integration_tests/app/CMakeLists.txt new file mode 100644 index 0000000000..8cb7755173 --- /dev/null +++ b/testing/integration_tests/app/CMakeLists.txt @@ -0,0 +1,196 @@ +cmake_minimum_required(VERSION 2.8) + +# User settings for Firebase integration tests. +# Path to Firebase SDK. +# Try to read the path to the Firebase C++ SDK from an environment variable. +if (NOT "$ENV{FIREBASE_CPP_SDK_DIR}" STREQUAL "") + set(DEFAULT_FIREBASE_CPP_SDK_DIR "$ENV{FIREBASE_CPP_SDK_DIR}") +else() + set(DEFAULT_FIREBASE_CPP_SDK_DIR "firebase_cpp_sdk") +endif() +if ("${FIREBASE_CPP_SDK_DIR}" STREQUAL "") + set(FIREBASE_CPP_SDK_DIR ${DEFAULT_FIREBASE_CPP_SDK_DIR}) +endif() +if(NOT EXISTS ${FIREBASE_CPP_SDK_DIR}) + message(FATAL_ERROR "The Firebase C++ SDK directory does not exist: ${FIREBASE_CPP_SDK_DIR}. See the readme.md for more information") +endif() + +# Windows runtime mode, either MD or MT depending on whether you are using +# /MD or /MT. For more information see: +# https://msdn.microsoft.com/en-us/library/2kzt1wy3.aspx +set(MSVC_RUNTIME_MODE MD) + +project(firebase_testapp) + +# Integration test source files. +set(FIREBASE_APP_FRAMEWORK_SRCS + src/app_framework.cc + src/app_framework.h +) + +set(FIREBASE_TEST_FRAMEWORK_SRCS + src/firebase_test_framework.h + src/firebase_test_framework.cc +) + +set(FIREBASE_INTEGRATION_TEST_SRCS + src/integration_test.cc +) + +# The include directory for the testapp. +include_directories(src) + +# Integration test uses some features that require C++ 11, such as lambdas. +set (CMAKE_CXX_STANDARD 11) + +# Download and unpack googletest (and googlemock) at configure time +set(GOOGLETEST_ROOT ${CMAKE_CURRENT_LIST_DIR}/external/googletest) +# Note: Once googletest is downloaded once, it won't be updated or +# downloaded again unless you delete the "external/googletest" +# directory. +if (NOT EXISTS ${GOOGLETEST_ROOT}/src/googletest/src/gtest-all.cc) + configure_file(googletest.cmake + ${CMAKE_CURRENT_LIST_DIR}/external/googletest/CMakeLists.txt COPYONLY) + execute_process(COMMAND ${CMAKE_COMMAND} . + RESULT_VARIABLE result + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/external/googletest ) + if(result) + message(FATAL_ERROR "CMake step for googletest failed: ${result}") + endif() + execute_process(COMMAND ${CMAKE_COMMAND} --build . + RESULT_VARIABLE result + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/external/googletest ) + if(result) + message(FATAL_ERROR "Build step for googletest failed: ${result}") + endif() +endif() + +if(ANDROID) + # Build an Android application. + + # Source files used for the Android build. + set(FIREBASE_APP_FRAMEWORK_ANDROID_SRCS + src/android/android_app_framework.cc + ) + + # Source files used for the Android build. + set(FIREBASE_TEST_FRAMEWORK_ANDROID_SRCS + src/android/android_firebase_test_framework.cc + ) + + # Build native_app_glue as a static lib + add_library(native_app_glue STATIC + ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c) + + # Export ANativeActivity_onCreate(), + # Refer to: https://github.com/android-ndk/ndk/issues/381. + set(CMAKE_SHARED_LINKER_FLAGS + "${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate") + + add_library(gtest STATIC + ${GOOGLETEST_ROOT}/src/googletest/src/gtest-all.cc) + target_include_directories(gtest + PRIVATE ${GOOGLETEST_ROOT}/src/googletest + PUBLIC ${GOOGLETEST_ROOT}/src/googletest/include) + add_library(gmock STATIC + ${GOOGLETEST_ROOT}/src/googlemock/src/gmock-all.cc) + target_include_directories(gmock + PRIVATE ${GOOGLETEST_ROOT}/src/googletest + PRIVATE ${GOOGLETEST_ROOT}/src/googlemock + PUBLIC ${GOOGLETEST_ROOT}/src/googletest/include + PUBLIC ${GOOGLETEST_ROOT}/src/googlemock/include) + + # Define the target as a shared library, as that is what gradle expects. + set(integration_test_target_name "android_integration_test_main") + add_library(${integration_test_target_name} SHARED + ${FIREBASE_APP_FRAMEWORK_SRCS} + ${FIREBASE_APP_FRAMEWORK_ANDROID_SRCS} + ${FIREBASE_INTEGRATION_TEST_SRCS} + ${FIREBASE_TEST_FRAMEWORK_SRCS} + ${FIREBASE_TEST_FRAMEWORK_ANDROID_SRCS} + ) + + target_include_directories(${integration_test_target_name} PRIVATE + ${ANDROID_NDK}/sources/android/native_app_glue) + + set(ADDITIONAL_LIBS log android atomic native_app_glue) +else() + # Build a desktop application. + add_definitions(-D_GLIBCXX_USE_CXX11_ABI=0) + + # Prevent overriding the parent project's compiler/linker + # settings on Windows + set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) + + # Add googletest directly to our build. This defines + # the gtest and gtest_main targets. + add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/external/googletest/src + ${CMAKE_CURRENT_LIST_DIR}/external/googletest/build + EXCLUDE_FROM_ALL) + + # The gtest/gtest_main targets carry header search path + # dependencies automatically when using CMake 2.8.11 or + # later. Otherwise we have to add them here ourselves. + if (CMAKE_VERSION VERSION_LESS 2.8.11) + include_directories("${gtest_SOURCE_DIR}/include") + include_directories("${gmock_SOURCE_DIR}/include") + endif() + + # Windows runtime mode, either MD or MT depending on whether you are using + # /MD or /MT. For more information see: + # https://msdn.microsoft.com/en-us/library/2kzt1wy3.aspx + set(MSVC_RUNTIME_MODE MD) + + # Platform abstraction layer for the desktop integration test. + set(FIREBASE_APP_FRAMEWORK_DESKTOP_SRCS + src/desktop/desktop_app_framework.cc + ) + + set(integration_test_target_name "integration_test") + add_executable(${integration_test_target_name} + ${FIREBASE_APP_FRAMEWORK_SRCS} + ${FIREBASE_APP_FRAMEWORK_DESKTOP_SRCS} + ${FIREBASE_TEST_FRAMEWORK_SRCS} + ${FIREBASE_INTEGRATION_TEST_SRCS} + ) + + if(APPLE) + set(ADDITIONAL_LIBS + gssapi_krb5 + pthread + "-framework CoreFoundation" + "-framework Foundation" + "-framework GSS" + "-framework Security" + ) + elseif(MSVC) + set(ADDITIONAL_LIBS advapi32 ws2_32 crypt32) + else() + set(ADDITIONAL_LIBS pthread) + endif() + + # If a config file is present, copy it into the binary location so that it's + # possible to create the default Firebase app. + set(FOUND_JSON_FILE FALSE) + foreach(config "google-services-desktop.json" "google-services.json") + if (EXISTS ${config}) + add_custom_command( + TARGET ${integration_test_target_name} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + ${config} $) + set(FOUND_JSON_FILE TRUE) + break() + endif() + endforeach() + if(NOT FOUND_JSON_FILE) + message(WARNING "Failed to find either google-services-desktop.json or google-services.json. See the readme.md for more information.") + endif() +endif() + +# Add the Firebase libraries to the target using the function from the SDK. +add_subdirectory(${FIREBASE_CPP_SDK_DIR} bin/ EXCLUDE_FROM_ALL) +# Note that firebase_app needs to be last in the list. +set(firebase_libs firebase_app) +set(gtest_libs gtest gmock) +target_link_libraries(${integration_test_target_name} ${firebase_libs} + ${gtest_libs} ${ADDITIONAL_LIBS}) diff --git a/testing/integration_tests/app/Images.xcassets/AppIcon.appiconset/Contents.json b/testing/integration_tests/app/Images.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000000..d8db8d65fd --- /dev/null +++ b/testing/integration_tests/app/Images.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "83.5x83.5", + "scale" : "2x" + }, + { + "idiom" : "ios-marketing", + "size" : "1024x1024", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/testing/integration_tests/app/Images.xcassets/LaunchImage.launchimage/Contents.json b/testing/integration_tests/app/Images.xcassets/LaunchImage.launchimage/Contents.json new file mode 100644 index 0000000000..6f870a4629 --- /dev/null +++ b/testing/integration_tests/app/Images.xcassets/LaunchImage.launchimage/Contents.json @@ -0,0 +1,51 @@ +{ + "images" : [ + { + "orientation" : "portrait", + "idiom" : "iphone", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "orientation" : "portrait", + "idiom" : "iphone", + "subtype" : "retina4", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "orientation" : "portrait", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "1x" + }, + { + "orientation" : "landscape", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "1x" + }, + { + "orientation" : "portrait", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "orientation" : "landscape", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/testing/integration_tests/app/Info.plist b/testing/integration_tests/app/Info.plist new file mode 100644 index 0000000000..d938a81f9b --- /dev/null +++ b/testing/integration_tests/app/Info.plist @@ -0,0 +1,41 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + com.google.ios.analytics.testapp + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleURLTypes + + + CFBundleTypeRole + Editor + CFBundleURLName + google + CFBundleURLSchemes + + com.googleusercontent.apps.255980362477-3a1nf8c4nl0c7hlnlnmc98hbtg2mnbue + + + + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + + diff --git a/testing/integration_tests/app/LaunchScreen.storyboard b/testing/integration_tests/app/LaunchScreen.storyboard new file mode 100644 index 0000000000..673e0f7e68 --- /dev/null +++ b/testing/integration_tests/app/LaunchScreen.storyboard @@ -0,0 +1,7 @@ + + + + + + + diff --git a/testing/integration_tests/app/LibraryManifest.xml b/testing/integration_tests/app/LibraryManifest.xml new file mode 100644 index 0000000000..855b13b5dc --- /dev/null +++ b/testing/integration_tests/app/LibraryManifest.xml @@ -0,0 +1,7 @@ + + + + + + diff --git a/testing/integration_tests/app/Podfile b/testing/integration_tests/app/Podfile new file mode 100644 index 0000000000..958aeb7390 --- /dev/null +++ b/testing/integration_tests/app/Podfile @@ -0,0 +1,13 @@ +source 'sso://cpdc-internal/firebase.git' +source 'https://github.com/CocoaPods/Specs.git' +platform :ios, '8.0' +# Firebase App test application. + +target 'integration_test' do + pod 'Firebase/Analytics', '6.24.0' +end + +post_install do |installer| + system("/usr/bin/python ./download_googletest.py") +end + diff --git a/testing/integration_tests/app/build.gradle b/testing/integration_tests/app/build.gradle new file mode 100644 index 0000000000..9d348d5e3f --- /dev/null +++ b/testing/integration_tests/app/build.gradle @@ -0,0 +1,64 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +buildscript { + repositories { + mavenLocal() + maven { url 'https://maven.google.com' } + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:3.2.1' + classpath 'com.google.gms:google-services:4.0.1' + } +} + +allprojects { + repositories { + mavenLocal() + maven { url 'https://maven.google.com' } + jcenter() + } +} + +apply plugin: 'com.android.application' + +android { + compileSdkVersion 28 + buildToolsVersion '28.0.3' + + sourceSets { + main { + jniLibs.srcDirs = ['libs'] + manifest.srcFile 'AndroidManifest.xml' + java.srcDirs = ['src/android/java'] + res.srcDirs = ['res'] + } + } + + defaultConfig { + applicationId 'com.google.android.analytics.testapp' + minSdkVersion 16 + targetSdkVersion 28 + versionCode 1 + versionName '1.0' + externalNativeBuild.cmake { + arguments "-DFIREBASE_CPP_SDK_DIR=$gradle.firebase_cpp_sdk_dir" + } + } + externalNativeBuild.cmake { + path 'CMakeLists.txt' + } + buildTypes { + release { + minifyEnabled true + proguardFile getDefaultProguardFile('proguard-android.txt') + proguardFile file('proguard.pro') + } + } +} + +apply from: "$gradle.firebase_cpp_sdk_dir/Android/firebase_dependencies.gradle" +firebaseCpp.dependencies { + app +} + +apply plugin: 'com.google.gms.google-services' diff --git a/testing/integration_tests/app/googletest.cmake b/testing/integration_tests/app/googletest.cmake new file mode 100644 index 0000000000..0eeff685dd --- /dev/null +++ b/testing/integration_tests/app/googletest.cmake @@ -0,0 +1,19 @@ +# Download GoogleTest from GitHub as an external project. +# This CMake file is taken from: +# https://github.com/google/googletest/blob/master/googletest/README.md#incorporating-into-an-existing-cmake-project + +cmake_minimum_required(VERSION 2.8.2) + +project(googletest-download NONE) + +include(ExternalProject) +ExternalProject_Add(googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG master + SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/src" + BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/build" + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + TEST_COMMAND "" +) diff --git a/testing/integration_tests/app/gradle/wrapper/gradle-wrapper.jar b/testing/integration_tests/app/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..8c0fb64a8698b08ecc4158d828ca593c4928e9dd GIT binary patch literal 49896 zcmagFb986H(k`5d^NVfUwr$(C?M#x1ZQHiZiEVpg+jrjgoQrerx!>1o_ul)D>ebz~ zs=Mmxr&>W81QY-S1PKWQ%N-;H^tS;2*XwVA`dej1RRn1z<;3VgfE4~kaG`A%QSPsR z#ovnZe+tS9%1MfeDyz`RirvdjPRK~p(#^q2(^5@O&NM19EHdvN-A&StN>0g6QA^VN z0Gx%Gq#PD$QMRFzmK+utjS^Y1F0e8&u&^=w5K<;4Rz|i3A=o|IKLY+g`iK6vfr9?+ z-`>gmU&i?FGSL5&F?TXFu`&Js6h;15QFkXp2M1H9|Eq~bpov-GU(uz%mH0n55wUl- zv#~ccAz`F5wlQ>e_KlJS3@{)B?^v*EQM=IxLa&76^y51a((wq|2-`qON>+4dLc{Oo z51}}o^Zen(oAjxDK7b++9_Yg`67p$bPo3~BCpGM7uAWmvIhWc5Gi+gQZ|Pwa-Gll@<1xmcPy z|NZmu6m)g5Ftu~BG&Xdxclw7Cij{xbBMBn-LMII#Slp`AElb&2^Hw+w>(3crLH!;I zN+Vk$D+wP1#^!MDCiad@vM>H#6+`Ct#~6VHL4lzmy;lSdk>`z6)=>Wh15Q2)dQtGqvn0vJU@+(B5{MUc*qs4!T+V=q=wy)<6$~ z!G>e_4dN@lGeF_$q9`Ju6Ncb*x?O7=l{anm7Eahuj_6lA{*#Gv*TaJclevPVbbVYu z(NY?5q+xxbO6%g1xF0r@Ix8fJ~u)VRUp`S%&rN$&e!Od`~s+64J z5*)*WSi*i{k%JjMSIN#X;jC{HG$-^iX+5f5BGOIHWAl*%15Z#!xntpk($-EGKCzKa zT7{siZ9;4TICsWQ$pu&wKZQTCvpI$Xvzwxoi+XkkpeE&&kFb!B?h2hi%^YlXt|-@5 zHJ~%AN!g_^tmn1?HSm^|gCE#!GRtK2(L{9pL#hp0xh zME}|DB>(5)`iE7CM)&_+S}-Bslc#@B5W4_+k4Cp$l>iVyg$KP>CN?SVGZ(&02>iZK zB<^HP$g$Lq*L$BWd?2(F?-MUbNWTJVQdW7$#8a|k_30#vHAD1Z{c#p;bETk0VnU5A zBgLe2HFJ3032$G<`m*OB!KM$*sdM20jm)It5OSru@tXpK5LT>#8)N!*skNu1$TpIw zufjjdp#lyH5bZ%|Iuo|iu9vG1HrIVWLH>278xo>aVBkPN3V$~!=KnlXQ4eDqS7%E% zQ!z^$Q$b^6Q)g#cLpwur(|<0gWHo6A6jc;n`t(V9T;LzTAU{IAu*uEQ%Ort1k+Kn+f_N`9|bxYC+~Z1 zCC1UCWv*Orx$_@ydv9mIe(liLfOr7mhbV@tKw{6)q^1DH1nmvZ0cj215R<~&I<4S| zgnr;9Cdjqpz#o8i0CQjtl`}{c*P)aSdH|abxGdrR)-3z+02-eX(k*B)Uqv6~^nh** z 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 \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/testing/integration_tests/app/gradlew.bat b/testing/integration_tests/app/gradlew.bat new file mode 100644 index 0000000000..8a0b282aa6 --- /dev/null +++ b/testing/integration_tests/app/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/testing/integration_tests/app/integration_test.xcodeproj/project.pbxproj b/testing/integration_tests/app/integration_test.xcodeproj/project.pbxproj new file mode 100644 index 0000000000..d1d6b852d1 --- /dev/null +++ b/testing/integration_tests/app/integration_test.xcodeproj/project.pbxproj @@ -0,0 +1,364 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 520BC0391C869159008CFBC3 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 520BC0381C869159008CFBC3 /* GoogleService-Info.plist */; }; + 529226D61C85F68000C89379 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 529226D51C85F68000C89379 /* Foundation.framework */; }; + 529226D81C85F68000C89379 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 529226D71C85F68000C89379 /* CoreGraphics.framework */; }; + 529226DA1C85F68000C89379 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 529226D91C85F68000C89379 /* UIKit.framework */; }; + D61C5F8E22BABA9C00A79141 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D61C5F8C22BABA9B00A79141 /* Images.xcassets */; }; + D61C5F9622BABAD200A79141 /* integration_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = D61C5F9222BABAD100A79141 /* integration_test.cc */; }; + D62CCBC022F367140099BE9F /* gmock-all.cc in Sources */ = {isa = PBXBuildFile; fileRef = D62CCBBF22F367140099BE9F /* gmock-all.cc */; }; + D66B16871CE46E8900E5638A /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D66B16861CE46E8900E5638A /* LaunchScreen.storyboard */; }; + D67D355822BABD2200292C1D /* gtest-all.cc in Sources */ = {isa = PBXBuildFile; fileRef = D67D355622BABD2100292C1D /* gtest-all.cc */; }; + D6C179E922CB322900C2651A /* ios_app_framework.mm in Sources */ = {isa = PBXBuildFile; fileRef = D6C179E722CB322900C2651A /* ios_app_framework.mm */; }; + D6C179EA22CB322900C2651A /* ios_firebase_test_framework.mm in Sources */ = {isa = PBXBuildFile; fileRef = D6C179E822CB322900C2651A /* ios_firebase_test_framework.mm */; }; + D6C179EE22CB323300C2651A /* firebase_test_framework.cc in Sources */ = {isa = PBXBuildFile; fileRef = D6C179EC22CB323300C2651A /* firebase_test_framework.cc */; }; + D6C179F022CB32A000C2651A /* app_framework.cc in Sources */ = {isa = PBXBuildFile; fileRef = D6C179EF22CB32A000C2651A /* app_framework.cc */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 520BC0381C869159008CFBC3 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; + 529226D21C85F68000C89379 /* integration_test.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = integration_test.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 529226D51C85F68000C89379 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + 529226D71C85F68000C89379 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; + 529226D91C85F68000C89379 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; + 529226EE1C85F68000C89379 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; + D61C5F8C22BABA9B00A79141 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; + D61C5F8D22BABA9C00A79141 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + D61C5F9222BABAD100A79141 /* integration_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = integration_test.cc; path = src/integration_test.cc; sourceTree = ""; }; + D62CCBBF22F367140099BE9F /* gmock-all.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "gmock-all.cc"; path = "external/googletest/src/googlemock/src/gmock-all.cc"; sourceTree = ""; }; + D62CCBC122F367320099BE9F /* gmock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = gmock.h; path = external/googletest/src/googlemock/include/gmock/gmock.h; sourceTree = ""; }; + D66B16861CE46E8900E5638A /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = ""; }; + D67D355622BABD2100292C1D /* gtest-all.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "gtest-all.cc"; path = "external/googletest/src/googletest/src/gtest-all.cc"; sourceTree = ""; }; + D67D355722BABD2100292C1D /* gtest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = gtest.h; path = external/googletest/src/googletest/include/gtest/gtest.h; sourceTree = ""; }; + D6C179E722CB322900C2651A /* ios_app_framework.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ios_app_framework.mm; path = src/ios/ios_app_framework.mm; sourceTree = ""; }; + D6C179E822CB322900C2651A /* ios_firebase_test_framework.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ios_firebase_test_framework.mm; path = src/ios/ios_firebase_test_framework.mm; sourceTree = ""; }; + D6C179EB22CB323300C2651A /* firebase_test_framework.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = firebase_test_framework.h; path = src/firebase_test_framework.h; sourceTree = ""; }; + D6C179EC22CB323300C2651A /* firebase_test_framework.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = firebase_test_framework.cc; path = src/firebase_test_framework.cc; sourceTree = ""; }; + D6C179ED22CB323300C2651A /* app_framework.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = app_framework.h; path = src/app_framework.h; sourceTree = ""; }; + D6C179EF22CB32A000C2651A /* app_framework.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = app_framework.cc; path = src/app_framework.cc; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 529226CF1C85F68000C89379 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 529226D81C85F68000C89379 /* CoreGraphics.framework in Frameworks */, + 529226DA1C85F68000C89379 /* UIKit.framework in Frameworks */, + 529226D61C85F68000C89379 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 529226C91C85F68000C89379 = { + isa = PBXGroup; + children = ( + D61C5F8C22BABA9B00A79141 /* Images.xcassets */, + D61C5F8D22BABA9C00A79141 /* Info.plist */, + D66B16861CE46E8900E5638A /* LaunchScreen.storyboard */, + 520BC0381C869159008CFBC3 /* GoogleService-Info.plist */, + 5292271D1C85FB5500C89379 /* src */, + 529226D41C85F68000C89379 /* Frameworks */, + 529226D31C85F68000C89379 /* Products */, + ); + sourceTree = ""; + }; + 529226D31C85F68000C89379 /* Products */ = { + isa = PBXGroup; + children = ( + 529226D21C85F68000C89379 /* integration_test.app */, + ); + name = Products; + sourceTree = ""; + }; + 529226D41C85F68000C89379 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 529226D51C85F68000C89379 /* Foundation.framework */, + 529226D71C85F68000C89379 /* CoreGraphics.framework */, + 529226D91C85F68000C89379 /* UIKit.framework */, + 529226EE1C85F68000C89379 /* XCTest.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 5292271D1C85FB5500C89379 /* src */ = { + isa = PBXGroup; + children = ( + D62CCBC122F367320099BE9F /* gmock.h */, + D62CCBBF22F367140099BE9F /* gmock-all.cc */, + D67D355622BABD2100292C1D /* gtest-all.cc */, + D67D355722BABD2100292C1D /* gtest.h */, + D6C179EF22CB32A000C2651A /* app_framework.cc */, + D6C179ED22CB323300C2651A /* app_framework.h */, + D6C179EC22CB323300C2651A /* firebase_test_framework.cc */, + D6C179EB22CB323300C2651A /* firebase_test_framework.h */, + D61C5F9222BABAD100A79141 /* integration_test.cc */, + 5292271E1C85FB5B00C89379 /* ios */, + ); + name = src; + sourceTree = ""; + }; + 5292271E1C85FB5B00C89379 /* ios */ = { + isa = PBXGroup; + children = ( + D6C179E722CB322900C2651A /* ios_app_framework.mm */, + D6C179E822CB322900C2651A /* ios_firebase_test_framework.mm */, + ); + name = ios; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 529226D11C85F68000C89379 /* integration_test */ = { + isa = PBXNativeTarget; + buildConfigurationList = 529226F91C85F68000C89379 /* Build configuration list for PBXNativeTarget "integration_test" */; + buildPhases = ( + 529226CE1C85F68000C89379 /* Sources */, + 529226CF1C85F68000C89379 /* Frameworks */, + 529226D01C85F68000C89379 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = integration_test; + productName = testapp; + productReference = 529226D21C85F68000C89379 /* integration_test.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 529226CA1C85F68000C89379 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0640; + ORGANIZATIONNAME = Google; + TargetAttributes = { + 529226D11C85F68000C89379 = { + CreatedOnToolsVersion = 6.4; + DevelopmentTeam = EQHXZ8M8AV; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = 529226CD1C85F68000C89379 /* Build configuration list for PBXProject "integration_test" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 529226C91C85F68000C89379; + productRefGroup = 529226D31C85F68000C89379 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 529226D11C85F68000C89379 /* integration_test */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 529226D01C85F68000C89379 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D61C5F8E22BABA9C00A79141 /* Images.xcassets in Resources */, + D66B16871CE46E8900E5638A /* LaunchScreen.storyboard in Resources */, + 520BC0391C869159008CFBC3 /* GoogleService-Info.plist in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 529226CE1C85F68000C89379 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D67D355822BABD2200292C1D /* gtest-all.cc in Sources */, + D62CCBC022F367140099BE9F /* gmock-all.cc in Sources */, + D6C179EA22CB322900C2651A /* ios_firebase_test_framework.mm in Sources */, + D61C5F9622BABAD200A79141 /* integration_test.cc in Sources */, + D6C179E922CB322900C2651A /* ios_app_framework.mm in Sources */, + D6C179F022CB32A000C2651A /* app_framework.cc in Sources */, + D6C179EE22CB323300C2651A /* firebase_test_framework.cc in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 529226F71C85F68000C89379 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.4; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 529226F81C85F68000C89379 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.4; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 529226FA1C85F68000C89379 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ""; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)", + ); + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + "\"$(SRCROOT)/src\"", + "\"$(SRCROOT)/external/googletest/src/googletest/include\"", + "\"$(SRCROOT)/external/googletest/src/googlemock/include\"", + "\"$(SRCROOT)/external/googletest/src/googletest\"", + "\"$(SRCROOT)/external/googletest/src/googlemock\"", + ); + INFOPLIST_FILE = "$(SRCROOT)/Info.plist"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + WRAPPER_EXTENSION = app; + }; + name = Debug; + }; + 529226FB1C85F68000C89379 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ""; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)", + ); + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + "\"$(SRCROOT)/src\"", + "\"$(SRCROOT)/external/googletest/src/googletest/include\"", + "\"$(SRCROOT)/external/googletest/src/googlemock/include\"", + "\"$(SRCROOT)/external/googletest/src/googletest\"", + "\"$(SRCROOT)/external/googletest/src/googlemock\"", + ); + INFOPLIST_FILE = "$(SRCROOT)/Info.plist"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + WRAPPER_EXTENSION = app; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 529226CD1C85F68000C89379 /* Build configuration list for PBXProject "integration_test" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 529226F71C85F68000C89379 /* Debug */, + 529226F81C85F68000C89379 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 529226F91C85F68000C89379 /* Build configuration list for PBXNativeTarget "integration_test" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 529226FA1C85F68000C89379 /* Debug */, + 529226FB1C85F68000C89379 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 529226CA1C85F68000C89379 /* Project object */; +} diff --git a/testing/integration_tests/app/proguard.pro b/testing/integration_tests/app/proguard.pro new file mode 100644 index 0000000000..2d04b8a9a5 --- /dev/null +++ b/testing/integration_tests/app/proguard.pro @@ -0,0 +1,2 @@ +-ignorewarnings +-keep,includedescriptorclasses public class com.google.firebase.example.LoggingUtils { * ; } diff --git a/testing/integration_tests/app/res/layout/main.xml b/testing/integration_tests/app/res/layout/main.xml new file mode 100644 index 0000000000..d3ffb63082 --- /dev/null +++ b/testing/integration_tests/app/res/layout/main.xml @@ -0,0 +1,12 @@ + + + + diff --git a/testing/integration_tests/app/res/values/strings.xml b/testing/integration_tests/app/res/values/strings.xml new file mode 100644 index 0000000000..15ff2a0621 --- /dev/null +++ b/testing/integration_tests/app/res/values/strings.xml @@ -0,0 +1,4 @@ + + + Firebase App Integration Test + diff --git a/testing/integration_tests/app/settings.gradle b/testing/integration_tests/app/settings.gradle new file mode 100644 index 0000000000..2a543b9351 --- /dev/null +++ b/testing/integration_tests/app/settings.gradle @@ -0,0 +1,36 @@ +// Copyright 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +def firebase_cpp_sdk_dir = System.getProperty('firebase_cpp_sdk.dir') +if (firebase_cpp_sdk_dir == null || firebase_cpp_sdk_dir.isEmpty()) { + firebase_cpp_sdk_dir = System.getenv('FIREBASE_CPP_SDK_DIR') + if (firebase_cpp_sdk_dir == null || firebase_cpp_sdk_dir.isEmpty()) { + if ((new File('firebase_cpp_sdk')).exists()) { + firebase_cpp_sdk_dir = 'firebase_cpp_sdk' + } else { + throw new StopActionException( + 'firebase_cpp_sdk.dir property or the FIREBASE_CPP_SDK_DIR ' + + 'environment variable must be set to reference the Firebase C++ ' + + 'SDK install directory. This is used to configure static library ' + + 'and C/C++ include paths for the SDK.') + } + } +} +if (!(new File(firebase_cpp_sdk_dir)).exists()) { + throw new StopActionException( + sprintf('Firebase C++ SDK directory %s does not exist', + firebase_cpp_sdk_dir)) +} +gradle.ext.firebase_cpp_sdk_dir = "$firebase_cpp_sdk_dir" +includeBuild "$firebase_cpp_sdk_dir" \ No newline at end of file diff --git a/testing/integration_tests/app/src/integration_test.cc b/testing/integration_tests/app/src/integration_test.cc new file mode 100644 index 0000000000..c22acb8bec --- /dev/null +++ b/testing/integration_tests/app/src/integration_test.cc @@ -0,0 +1,68 @@ +// Copyright 2019 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include +#include +#include +#include +#include + +#include "app_framework.h" // NOLINT +#include "firebase_test_framework.h" // NOLINT + +// The TO_STRING macro is useful for command line defined strings as the quotes +// get stripped. +#define TO_STRING_EXPAND(X) #X +#define TO_STRING(X) TO_STRING_EXPAND(X) + +// Path to the Firebase config file to load. +#ifdef FIREBASE_CONFIG +#define FIREBASE_CONFIG_STRING TO_STRING(FIREBASE_CONFIG) +#else +#define FIREBASE_CONFIG_STRING "" +#endif // FIREBASE_CONFIG + +namespace firebase_testapp_automated { + +using firebase_test_framework::FirebaseTest; + +class FirebaseAppTest : public FirebaseTest { + public: + FirebaseAppTest(); +}; + +FirebaseAppTest::FirebaseAppTest() { + FindFirebaseConfig(FIREBASE_CONFIG_STRING); +} + +// For simplicity of test code, handle the Android-specific arguments here. +#if defined(__ANDROID__) +#define APP_CREATE_PARAMS \ + app_framework::GetJniEnv(), app_framework::GetActivity() +#else +#define APP_CREATE_PARAMS +#endif // defined(__ANDROID__) + +TEST_F(FirebaseAppTest, TestDefaultAppWithDefaultOptions) { + firebase::App* default_app; + default_app = firebase::App::Create(APP_CREATE_PARAMS); + EXPECT_NE(default_app, nullptr); + + delete default_app; + default_app = nullptr; +} + +} // namespace firebase_testapp_automated diff --git a/testing/integration_tests/auth/AndroidManifest.xml b/testing/integration_tests/auth/AndroidManifest.xml new file mode 100644 index 0000000000..c1c8a0c0ce --- /dev/null +++ b/testing/integration_tests/auth/AndroidManifest.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/testing/integration_tests/auth/CMakeLists.txt b/testing/integration_tests/auth/CMakeLists.txt new file mode 100644 index 0000000000..dca5610477 --- /dev/null +++ b/testing/integration_tests/auth/CMakeLists.txt @@ -0,0 +1,196 @@ +cmake_minimum_required(VERSION 2.8) + +# User settings for Firebase integration tests. +# Path to Firebase SDK. +# Try to read the path to the Firebase C++ SDK from an environment variable. +if (NOT "$ENV{FIREBASE_CPP_SDK_DIR}" STREQUAL "") + set(DEFAULT_FIREBASE_CPP_SDK_DIR "$ENV{FIREBASE_CPP_SDK_DIR}") +else() + set(DEFAULT_FIREBASE_CPP_SDK_DIR "firebase_cpp_sdk") +endif() +if ("${FIREBASE_CPP_SDK_DIR}" STREQUAL "") + set(FIREBASE_CPP_SDK_DIR ${DEFAULT_FIREBASE_CPP_SDK_DIR}) +endif() +if(NOT EXISTS ${FIREBASE_CPP_SDK_DIR}) + message(FATAL_ERROR "The Firebase C++ SDK directory does not exist: ${FIREBASE_CPP_SDK_DIR}. See the readme.md for more information") +endif() + +# Windows runtime mode, either MD or MT depending on whether you are using +# /MD or /MT. For more information see: +# https://msdn.microsoft.com/en-us/library/2kzt1wy3.aspx +set(MSVC_RUNTIME_MODE MD) + +project(firebase_testapp) + +# Integration test source files. +set(FIREBASE_APP_FRAMEWORK_SRCS + src/app_framework.cc + src/app_framework.h +) + +set(FIREBASE_TEST_FRAMEWORK_SRCS + src/firebase_test_framework.h + src/firebase_test_framework.cc +) + +set(FIREBASE_INTEGRATION_TEST_SRCS + src/integration_test.cc +) + +# The include directory for the testapp. +include_directories(src) + +# Integration test uses some features that require C++ 11, such as lambdas. +set (CMAKE_CXX_STANDARD 11) + +# Download and unpack googletest (and googlemock) at configure time +set(GOOGLETEST_ROOT ${CMAKE_CURRENT_LIST_DIR}/external/googletest) +# Note: Once googletest is downloaded once, it won't be updated or +# downloaded again unless you delete the "external/googletest" +# directory. +if (NOT EXISTS ${GOOGLETEST_ROOT}/src/googletest/src/gtest-all.cc) + configure_file(googletest.cmake + ${CMAKE_CURRENT_LIST_DIR}/external/googletest/CMakeLists.txt COPYONLY) + execute_process(COMMAND ${CMAKE_COMMAND} . + RESULT_VARIABLE result + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/external/googletest ) + if(result) + message(FATAL_ERROR "CMake step for googletest failed: ${result}") + endif() + execute_process(COMMAND ${CMAKE_COMMAND} --build . + RESULT_VARIABLE result + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/external/googletest ) + if(result) + message(FATAL_ERROR "Build step for googletest failed: ${result}") + endif() +endif() + +if(ANDROID) + # Build an Android application. + + # Source files used for the Android build. + set(FIREBASE_APP_FRAMEWORK_ANDROID_SRCS + src/android/android_app_framework.cc + ) + + # Source files used for the Android build. + set(FIREBASE_TEST_FRAMEWORK_ANDROID_SRCS + src/android/android_firebase_test_framework.cc + ) + + # Build native_app_glue as a static lib + add_library(native_app_glue STATIC + ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c) + + # Export ANativeActivity_onCreate(), + # Refer to: https://github.com/android-ndk/ndk/issues/381. + set(CMAKE_SHARED_LINKER_FLAGS + "${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate") + + add_library(gtest STATIC + ${GOOGLETEST_ROOT}/src/googletest/src/gtest-all.cc) + target_include_directories(gtest + PRIVATE ${GOOGLETEST_ROOT}/src/googletest + PUBLIC ${GOOGLETEST_ROOT}/src/googletest/include) + add_library(gmock STATIC + ${GOOGLETEST_ROOT}/src/googlemock/src/gmock-all.cc) + target_include_directories(gmock + PRIVATE ${GOOGLETEST_ROOT}/src/googletest + PRIVATE ${GOOGLETEST_ROOT}/src/googlemock + PUBLIC ${GOOGLETEST_ROOT}/src/googletest/include + PUBLIC ${GOOGLETEST_ROOT}/src/googlemock/include) + + # Define the target as a shared library, as that is what gradle expects. + set(integration_test_target_name "android_integration_test_main") + add_library(${integration_test_target_name} SHARED + ${FIREBASE_APP_FRAMEWORK_SRCS} + ${FIREBASE_APP_FRAMEWORK_ANDROID_SRCS} + ${FIREBASE_INTEGRATION_TEST_SRCS} + ${FIREBASE_TEST_FRAMEWORK_SRCS} + ${FIREBASE_TEST_FRAMEWORK_ANDROID_SRCS} + ) + + target_include_directories(${integration_test_target_name} PRIVATE + ${ANDROID_NDK}/sources/android/native_app_glue) + + set(ADDITIONAL_LIBS log android atomic native_app_glue) +else() + # Build a desktop application. + add_definitions(-D_GLIBCXX_USE_CXX11_ABI=0) + + # Prevent overriding the parent project's compiler/linker + # settings on Windows + set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) + + # Add googletest directly to our build. This defines + # the gtest and gtest_main targets. + add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/external/googletest/src + ${CMAKE_CURRENT_LIST_DIR}/external/googletest/build + EXCLUDE_FROM_ALL) + + # The gtest/gtest_main targets carry header search path + # dependencies automatically when using CMake 2.8.11 or + # later. Otherwise we have to add them here ourselves. + if (CMAKE_VERSION VERSION_LESS 2.8.11) + include_directories("${gtest_SOURCE_DIR}/include") + include_directories("${gmock_SOURCE_DIR}/include") + endif() + + # Windows runtime mode, either MD or MT depending on whether you are using + # /MD or /MT. For more information see: + # https://msdn.microsoft.com/en-us/library/2kzt1wy3.aspx + set(MSVC_RUNTIME_MODE MD) + + # Platform abstraction layer for the desktop integration test. + set(FIREBASE_APP_FRAMEWORK_DESKTOP_SRCS + src/desktop/desktop_app_framework.cc + ) + + set(integration_test_target_name "integration_test") + add_executable(${integration_test_target_name} + ${FIREBASE_APP_FRAMEWORK_SRCS} + ${FIREBASE_APP_FRAMEWORK_DESKTOP_SRCS} + ${FIREBASE_TEST_FRAMEWORK_SRCS} + ${FIREBASE_INTEGRATION_TEST_SRCS} + ) + + if(APPLE) + set(ADDITIONAL_LIBS + gssapi_krb5 + pthread + "-framework CoreFoundation" + "-framework Foundation" + "-framework GSS" + "-framework Security" + ) + elseif(MSVC) + set(ADDITIONAL_LIBS advapi32 ws2_32 crypt32) + else() + set(ADDITIONAL_LIBS pthread) + endif() + + # If a config file is present, copy it into the binary location so that it's + # possible to create the default Firebase app. + set(FOUND_JSON_FILE FALSE) + foreach(config "google-services-desktop.json" "google-services.json") + if (EXISTS ${config}) + add_custom_command( + TARGET ${integration_test_target_name} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + ${config} $) + set(FOUND_JSON_FILE TRUE) + break() + endif() + endforeach() + if(NOT FOUND_JSON_FILE) + message(WARNING "Failed to find either google-services-desktop.json or google-services.json. See the readme.md for more information.") + endif() +endif() + +# Add the Firebase libraries to the target using the function from the SDK. +add_subdirectory(${FIREBASE_CPP_SDK_DIR} bin/ EXCLUDE_FROM_ALL) +# Note that firebase_app needs to be last in the list. +set(firebase_libs firebase_auth firebase_app) +set(gtest_libs gtest gmock) +target_link_libraries(${integration_test_target_name} ${firebase_libs} + ${gtest_libs} ${ADDITIONAL_LIBS}) diff --git a/testing/integration_tests/auth/Firebase_Cpp_Auth_Test_App_Dev.mobileprovision b/testing/integration_tests/auth/Firebase_Cpp_Auth_Test_App_Dev.mobileprovision new file mode 100644 index 0000000000000000000000000000000000000000..979b0856e655f6b5ccca362991fcf6d0aa4c1f69 GIT binary patch literal 291491 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�BBcijzY~v+}WO7JvpvR+dXWH}K8S_ETgbOuQ20AEg zTB4kt6h7*oEFZKn`@YK6?Ba~HYgz^R7&F&mDv2=(&}r9+SoXD~qSh>+Kn<+-uhKVN zX>KbP%NFvBEtq|+9{ow!y;Pl!WvuVaZO9A{NEKe`B=7G#FX+g%icV>edhBF|R{7`Y z`yt>qf66Yz$gUas9BMJexnuZ@iaJdQBSX&TmxrF|7;?MV&kVH+tOZqMc&9>ArJFql zlmqdbq@k#Hn;r~oIyM6$#-Bw<(~N~OBBqz*M?@Hxd<8#NQ}iPoh4eaE_oOL zApi+k>dkkh?$i@O5rvy17k~u=PySD&8PI^to}gp%3mykXtn*dw3IWs?_Hn>_F)y_^ zF3ZoFeYemivhvswqZyNExHA@Oi6|6W9zmYhVR}0B-Os!*;wf$s%;C#PU_65G59OoOcX1`ie;! zxp}jJ`#t};mJmo?q{_zw3y+j;*v1B)l@qy%>}$cUKCq4gj3NDZjsg4{lPQdW3$1O^ zx#K+pwie`<}V*n4~``S{2Q5&=aH!bty(5?z&PIRzTwY zu2ku?0|H$71G@smLf|4$i-449zz*Q$#QKOJ2nUJ6^c2dJymB%)K}T7nKngPq+QH2` zDm0x&W9s)}9viE`*X*5I!1jTh?oIMP2q%cs8r-wFr$P@Ep z$5>>zP-UxP11rMyb*T1t#vb&_{0Yg4C)&T)+prWAocJ=n8SpEeB*4szZvdnpP075k zbw`Rp7>BN?@>Nf00!LV;I>B;+zMz%6W{|j3T?+8#^n)=O(>Tw|hl8~?S)h1FJ1T|4D}$$L%1YkTD%AsX@~-B1@(8SK44j`klw zi8X@(1V^R-P}A(wkQhZO z=25`SHKPTkjT_}nu0#uQ5$uKOd0&fA3!5_$F<+)sB2-I@+CUGlbO1pChUT?)JNi&a z)ePd~$$IQ%zJ|~S9SqSuXSBn&R=DAnL%d)r&P8GSZszlL@T;oMgh`=j?_b3+A$x|3 zFlv;%nKmn0gGHQ#tdP~M^sPxqAjjqcy{*Yc{-%Y>Z?{H0Nk5%bJ8gQp8CTq#`TQ|HJ~xg4GJ?v70@FBN{wq?hq(u0zmB+}slk9CQ~V7u zc>{wXIG$G$Cw#Y2fEc@HH+bV!!6NBn1qhe0O92|=MF=At9Aie*5%2q5;krc{p~}Do zBn3gI9<@S!JvfrrZ*cqAgN}lO@MG4sVVY#XtVpSF0H}aHFa%x&o?+iNhFHo?CXU%tv@R`@zQ`{ZP&S`J9d0>~9J|s6?e{F0+djtN8#6k!}34YkuKAUuQ+3*oJDBR#7VNso~#VyGKMSJfUYxsMCbMrWoCnQJ5`$Z9lUFQ*!?H}a^t*rq{^`f?Cd!rE)Xn;{i`PfzW z6X8D)RxB|6q|>)p<+%NOty?u;gn}-~64S(Ga}3S3H;uExD3H4QT8N$&3iM<3^Eyc1 zCS2ITzEMu@O0GkXl*JF8O`8jPHHAp)!p~i)q6GGVFhXY7zF z;azp9f+d-6(HwX?8N2%6TPkFQikT2aOU7La%v{+c4=^3OCKTbIK*$u|Bzu!gU|2{o z5Tf#RAw7ncY3Gy>9Q~@-BJTv7%~6l=)CzbL|Q^ z$^MWkcMWVp$f_m3A{oh&4TR1g$s(5b$xKx*(Cnjb*E+=kbnJkp-#!2)>%c*&;5T%1 zLQKJK{$Bhn=)-Y`Kn0t&u%Qdh*#$xXW1<3A*zfpE+*ZmbV^BQUw#rCzsRZB+5MZ^$ zJRQf5Iq`V8Hk)ChiN3&f-7O_X%VMvS=V6vOY2HYp>nug%tBlJQpG?rVN2O&$5 z@bFWjORgve^281~!PK}(3RP`du6X=h6Mrnly`2+?+u zn5s^|+ZsqPYYoqV)H)jgeNBqF&xdsoPR13Lop^8JPwp&0xZmyytTVA;R!?r(b!_GW zFjX>QxFkZBHm0bm0GE=4gya|smgAXu6VR1fwVo6)Dt}pGgusC^q#!H=jdAX4(XaMc z#!M;lEl^|kGGnS2)wIoVHvq?~_fB|JjcQ2_v8m0(&I2qOjE@yr_QDk`*`_t_zD zeuX9hL963}kaXP0-bcVMC|N=qdJAj{xIiiHy_>d>6Y1Uag5I?#l(|^Wea&9N0Q>CL4{BRN<{|0CQ zNH0Q~(PW`bE4-gMYRwcH*W`$#4nzRa_{K^#&PWyrQ_D{~ZYUq6D`u7Ksa1iVm(-Jy zm3||yUUA4~yxy}W4Bkt}t8ndzN6ku8W>%)I5<3yqc3@xTk&Tbx$k-_x=*f&R_dHvk zuHChgcmKrxy=e5L8*wlR5p@4H=X|ffN*wZ|+wm zi!(g*vBc6HdkJaNhUNSOU8FY`q97X`jH`}dk+gHSP^Hym?t!59y!a916R@Ikv=c3X zGf?7PPl7Ns2MjBm#2n>Klc;VNd*Ba>@w(wK>G`gje(Uf6Fpd?~5P_p%4HsaBtZ0GV zij#%+waF%J!nTHo_2 zu8CJOP_2BH>En@%reWXT^b@6BeCqaah7$=GfFmu9;Y|Iiddp_CBjMiV3A2n2C)~dm z2CR*X%D3;0mYPq&+S)@lJyt@ai19prFW(EQ77d491;W-*Jk(fu5yLyM$=V*Z<-D(D zsI4GZ(aq&YreXEHjq+FB$x>=qPGq|_E75wJPVmW?Q1+xcp`LDR%3OTO%wWRyfE6;J zy*k#;Qa6B#mHcg(PMn8WBFt?Bh`^Cka>mmicr?YN-I|ga5+tx<|MIwiG#VA(zt_mc z4;wX!rUYJ#gR8P100*LgWQ@Z8cfV)vG!G;qbHD(^oDSdj>g*^8Zcq~5!eqg`Z%%nS zIi<2$C3s(RPIS=1%~Niw%1KI&em^c=-nG}L36!jIqIS@Jgnos;p>`>E_qvkU?ecu< zbeqIUHKJA->j4ieGtkFcI6HS6y>YaPj7DOD<`?yPv7j%tWedBi7!yB#FQ8qc;S=kO zG>?Ijin64hNL75aIHr}~?@DP69%L7gMITWsYF}}pYqvQ5vqZF(AerM@sTRf#azW>9inwNHX)N`QRBY)ind|(qoA$$9t9wzyaQtSDPn- zG=#-eM`C8l1yTuWj0+UsmbOi;VPwp@uf-jbXi)R;p3s^x$}%i0 z#Cr`0_h_XA-}725Rf8UX91$8__o-3~s8bN(W-lRc2%C9rz7vV3Oj6cIpO zh-2u1_UXWpyi=)5J*S$@azL7={Z@@r*XAy6!O3eo#yY#%P=@F&cOf#^HJDQ5G10KI zxeFh;n3zEoKoPQT> zBe(qVqaU;!R5JQ7{j(`VK{>sZGYmvsxQDQ1DB-|?vrku+RY>_P9j~rE@%wE(s%k3( zn@fkf-!sIOWgdbNJR%8DsUSggu9!^HyN)Lig!cEf0tq-|Z3{C6SDa!@Hy#<7ibf~x z5%~6f1^Ag7RMlTh&lKu+BxYSRF~J^ z_EQxDb3F#LD`gvo>oA3+IwoEiV<>2m8^N6 zTS}e^SW_Xi2H~=d%R9nR#PlB6Ye75=EAM>UV2(uG#)}6~trlH|^A)viDSwJ~W-+=ygX-Ewt4AS5g zH_STb*wM%Z@s#FJ{lPwxJDJwT22M;!@iNy&atm|Vd>sJ%$}6YuV*K+dx0G!!4mINZ zj#tE`378qu*tI^bi7^xXp1R_JLMoqEY~j4GS;|c68ep=CGmL2TEI7T$f^kidSK2g{{zXIdspuH#+%C5c?RKLy%?~yts&zSjFO)cW3n7Nq zT#5)mZo*tafM%=5gahm~sf%=pV2a4mkXAjQ3NeLX}53N5mk@ zFHBWoVZsFsl`>baa$F0%Fm;CkrSdQ(t)9dq`%{3ZT;8a`p?5oOFpJ^7!e0!~0VS}J z4dGHSa$WGj6o;n2WXJuUrL6{T#U}^_oWgXhe+&64UN#~mEug~Zwc@HFK>H6TYBNe- zNdw$kEum?#r(uu>UZ=EhS|Px=t^^_I`)IQv_NoC?o%+Ze!uDe{;)owGFy~pJNw{L* zplLipPos5gIWiHUd)v><%9ow6)=qR0)jEH%j!uE6rpycj)_Q0H2_VeM8m5E1I3kYT z!UJfPXh6kX9-cr%h4sE~&A9~;m`g2fRI^P-71Y}oJH*)Jg8N#t5e-)zHFL8#_?mAlT-v1bxQB5E+ju#$pK5GbEGImO=rLNwj8@5@Y)7+O*ra>&6+*HJb($576j+y}Yxn!w%qos2&9?gSdgv4wDFj$s zNU7s{6M)S%Nm7s4=cX7qsi`2;QG7_Nd9rn^J>^JeFs$F(wG<78A7TX|bAS$&d*`Bb zf+7hO#=w}|x|R~bKc@9gL`k%*@Ya4eBiD(CY&_X zI{h>`$ktCPtl;eE4*}9BoT(!@m*)Va)v0$_r4+MZ$m>0uoyC0S!_slSVyvlUXuXye ze!MbP-sRJZZ?-{hg zP?~PQWY4}9)C6v8_@y+!Od{2VLZ1K*|5T5{SZc;tBaYQ&d;taX93Buj%rCgvfhe4@ z2QDX(+H+3?pDH9^rZP!DCb3gukbM+tdkm8g82+tQ6T}TX<^A7$HCFYDvxhp9K3YhN zW7y+225i=KUV}C?#ihCkR2`5do`cW{zZIr_Dg=6K)o_Q7cWKhWibQW&h?MSWmr3j* zbhFV{rtSAk@*6HEsi=v>Tj(nwNCst)q)YU9RUF5A*1#z@fOQ!0WN{h)SxV|tUt4Gg zIhu*x_Z6ODzsd_+0pG3PhaQAV_qH8`Vj~IOzVs&s7*l7bu|yM86!=0qZJfsrtGCn?yp>Od?Pe8B%qMI|Gd zq1t`7XSUZ6v6chyjb){pKi-NInx-0=EdrC-&UXbhg~?RUKr-@j6r+q;JJM1KZ)cEU zh|+sqY0_!hoFh=bnOvQ95Usd+fnpXIY--H1-g%)w$C;sXv+)uftjK>QZHxwV6r#nZ zjM3s=SN-)=tyDk?VazxX3?>xtq(LICP+wWo`}f+j{>!VP+R$ZCMvw!fnil31E%K{9 zyyK+oM`upkMN%9V1jS+t`hVXMSc8x4P0n7!#(|wcLvy>8hEb9r5yCkYD3;C6zz-T4 zna|851Y`|Ggll5QG99U=)iI%?7+sf;ZxHEo9NgE6 z8c2G;md);DPn{djXq1p>Sy?%lKeaTZ0Byu z9cMYpJJOD9D>Ncf1%TY3ob?2rhGFmiNJDSi5*`yllWLedK-xjDNUJF3tw>}LI%UTV zfNHkudIn{bsdzfk7%T+{WsVtv8n@dOx*d%A$F*8@ESW& z?))9%sVqawGfja_ND(UNeSSBQnXm-dv|s;f4z+W9tio@d{f+P!_eq)eIdSk~VpOHQ zM5ze26a5>&nbI3B%d^A~?4I4V@xDB^oRVR-k|k?LkRvMN%W7wp9R9`pzCgkPGYxW* z$CofYf33Jgr47LOK~8tvNMa$Tsi5`AntWiNH>X;uGo?;YTx(=@UI1rMvKov_T@Bzw zEi_7Rn2vZ#{Uqt6i`vhO#Z#6�s1F%Y2P7SOZ$OS2p8sNFmBmxNEr(oKe$e1aXp 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

ZrfF+f^Kv_HzB1&M{xlE56cKc_7 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 N_&+^) + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleURLTypes + + + CFBundleTypeRole + Editor + CFBundleURLName + com.google.FirebaseCppAuthTestApp.dev + CFBundleURLSchemes + + com.google.FirebaseCppAuthTestApp.dev + + + + CFBundleTypeRole + Editor + CFBundleURLName + google + CFBundleURLSchemes + + com.googleusercontent.apps.53101460582-aosfj1hlbc89719t6qfian100u6hehh8 + firebase-game-loop + + + + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UIBackgroundModes + + fetch + + UILaunchStoryboardName + LaunchScreen + + diff --git a/testing/integration_tests/auth/LaunchScreen.storyboard b/testing/integration_tests/auth/LaunchScreen.storyboard new file mode 100644 index 0000000000..673e0f7e68 --- /dev/null +++ b/testing/integration_tests/auth/LaunchScreen.storyboard @@ -0,0 +1,7 @@ + + + + + + + diff --git a/testing/integration_tests/auth/LibraryManifest.xml b/testing/integration_tests/auth/LibraryManifest.xml new file mode 100644 index 0000000000..b6faca73f6 --- /dev/null +++ b/testing/integration_tests/auth/LibraryManifest.xml @@ -0,0 +1,7 @@ + + + + + + diff --git a/testing/integration_tests/auth/Podfile b/testing/integration_tests/auth/Podfile new file mode 100644 index 0000000000..5c9c81f935 --- /dev/null +++ b/testing/integration_tests/auth/Podfile @@ -0,0 +1,13 @@ +source 'sso://cpdc-internal/firebase.git' +source 'https://github.com/CocoaPods/Specs.git' +platform :ios, '8.0' +# Firebase Auth test application. + +target 'integration_test' do + pod 'Firebase/Auth', '6.24.0' +end + +post_install do |installer| + system("/usr/bin/python ./download_googletest.py") +end + diff --git a/testing/integration_tests/auth/build.gradle b/testing/integration_tests/auth/build.gradle new file mode 100644 index 0000000000..64cdb605c8 --- /dev/null +++ b/testing/integration_tests/auth/build.gradle @@ -0,0 +1,64 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +buildscript { + repositories { + mavenLocal() + maven { url 'https://maven.google.com' } + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:3.2.1' + classpath 'com.google.gms:google-services:4.0.1' + } +} + +allprojects { + repositories { + mavenLocal() + maven { url 'https://maven.google.com' } + jcenter() + } +} + +apply plugin: 'com.android.application' + +android { + compileSdkVersion 28 + buildToolsVersion '28.0.3' + + sourceSets { + main { + jniLibs.srcDirs = ['libs'] + manifest.srcFile 'AndroidManifest.xml' + java.srcDirs = ['src/android/java'] + res.srcDirs = ['res'] + } + } + + defaultConfig { + applicationId 'com.google.android.auth.testapp' + minSdkVersion 16 + targetSdkVersion 28 + versionCode 1 + versionName '1.0' + externalNativeBuild.cmake { + arguments "-DFIREBASE_CPP_SDK_DIR=$gradle.firebase_cpp_sdk_dir" + } + } + externalNativeBuild.cmake { + path 'CMakeLists.txt' + } + buildTypes { + release { + minifyEnabled true + proguardFile getDefaultProguardFile('proguard-android.txt') + proguardFile file('proguard.pro') + } + } +} + +apply from: "$gradle.firebase_cpp_sdk_dir/Android/firebase_dependencies.gradle" +firebaseCpp.dependencies { + auth +} + +apply plugin: 'com.google.gms.google-services' diff --git a/testing/integration_tests/auth/googletest.cmake b/testing/integration_tests/auth/googletest.cmake new file mode 100644 index 0000000000..0eeff685dd --- /dev/null +++ b/testing/integration_tests/auth/googletest.cmake @@ -0,0 +1,19 @@ +# Download GoogleTest from GitHub as an external project. +# This CMake file is taken from: +# https://github.com/google/googletest/blob/master/googletest/README.md#incorporating-into-an-existing-cmake-project + +cmake_minimum_required(VERSION 2.8.2) + +project(googletest-download NONE) + +include(ExternalProject) +ExternalProject_Add(googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG master + SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/src" + BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/build" + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + TEST_COMMAND "" +) diff --git a/testing/integration_tests/auth/gradle/wrapper/gradle-wrapper.jar b/testing/integration_tests/auth/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..8c0fb64a8698b08ecc4158d828ca593c4928e9dd GIT binary patch literal 49896 zcmagFb986H(k`5d^NVfUwr$(C?M#x1ZQHiZiEVpg+jrjgoQrerx!>1o_ul)D>ebz~ zs=Mmxr&>W81QY-S1PKWQ%N-;H^tS;2*XwVA`dej1RRn1z<;3VgfE4~kaG`A%QSPsR z#ovnZe+tS9%1MfeDyz`RirvdjPRK~p(#^q2(^5@O&NM19EHdvN-A&StN>0g6QA^VN z0Gx%Gq#PD$QMRFzmK+utjS^Y1F0e8&u&^=w5K<;4Rz|i3A=o|IKLY+g`iK6vfr9?+ z-`>gmU&i?FGSL5&F?TXFu`&Js6h;15QFkXp2M1H9|Eq~bpov-GU(uz%mH0n55wUl- zv#~ccAz`F5wlQ>e_KlJS3@{)B?^v*EQM=IxLa&76^y51a((wq|2-`qON>+4dLc{Oo z51}}o^Zen(oAjxDK7b++9_Yg`67p$bPo3~BCpGM7uAWmvIhWc5Gi+gQZ|Pwa-Gll@<1xmcPy z|NZmu6m)g5Ftu~BG&Xdxclw7Cij{xbBMBn-LMII#Slp`AElb&2^Hw+w>(3crLH!;I zN+Vk$D+wP1#^!MDCiad@vM>H#6+`Ct#~6VHL4lzmy;lSdk>`z6)=>Wh15Q2)dQtGqvn0vJU@+(B5{MUc*qs4!T+V=q=wy)<6$~ z!G>e_4dN@lGeF_$q9`Ju6Ncb*x?O7=l{anm7Eahuj_6lA{*#Gv*TaJclevPVbbVYu z(NY?5q+xxbO6%g1xF0r@Ix8fJ~u)VRUp`S%&rN$&e!Od`~s+64J z5*)*WSi*i{k%JjMSIN#X;jC{HG$-^iX+5f5BGOIHWAl*%15Z#!xntpk($-EGKCzKa zT7{siZ9;4TICsWQ$pu&wKZQTCvpI$Xvzwxoi+XkkpeE&&kFb!B?h2hi%^YlXt|-@5 zHJ~%AN!g_^tmn1?HSm^|gCE#!GRtK2(L{9pL#hp0xh zME}|DB>(5)`iE7CM)&_+S}-Bslc#@B5W4_+k4Cp$l>iVyg$KP>CN?SVGZ(&02>iZK zB<^HP$g$Lq*L$BWd?2(F?-MUbNWTJVQdW7$#8a|k_30#vHAD1Z{c#p;bETk0VnU5A zBgLe2HFJ3032$G<`m*OB!KM$*sdM20jm)It5OSru@tXpK5LT>#8)N!*skNu1$TpIw zufjjdp#lyH5bZ%|Iuo|iu9vG1HrIVWLH>278xo>aVBkPN3V$~!=KnlXQ4eDqS7%E% zQ!z^$Q$b^6Q)g#cLpwur(|<0gWHo6A6jc;n`t(V9T;LzTAU{IAu*uEQ%Ort1k+Kn+f_N`9|bxYC+~Z1 zCC1UCWv*Orx$_@ydv9mIe(liLfOr7mhbV@tKw{6)q^1DH1nmvZ0cj215R<~&I<4S| zgnr;9Cdjqpz#o8i0CQjtl`}{c*P)aSdH|abxGdrR)-3z+02-eX(k*B)Uqv6~^nh** z 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 \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/testing/integration_tests/auth/gradlew.bat b/testing/integration_tests/auth/gradlew.bat new file mode 100644 index 0000000000..8a0b282aa6 --- /dev/null +++ b/testing/integration_tests/auth/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/testing/integration_tests/auth/integration_test.entitlements b/testing/integration_tests/auth/integration_test.entitlements new file mode 100644 index 0000000000..9199daef18 --- /dev/null +++ b/testing/integration_tests/auth/integration_test.entitlements @@ -0,0 +1,10 @@ + + + + + application-identifier + $(AppIdentifierPrefix)$(CFBundleIdentifier) + aps-environment + development + + diff --git a/testing/integration_tests/auth/integration_test.xcodeproj/project.pbxproj b/testing/integration_tests/auth/integration_test.xcodeproj/project.pbxproj new file mode 100644 index 0000000000..a3901f2c6a --- /dev/null +++ b/testing/integration_tests/auth/integration_test.xcodeproj/project.pbxproj @@ -0,0 +1,376 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 520BC0391C869159008CFBC3 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 520BC0381C869159008CFBC3 /* GoogleService-Info.plist */; }; + 529226D61C85F68000C89379 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 529226D51C85F68000C89379 /* Foundation.framework */; }; + 529226D81C85F68000C89379 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 529226D71C85F68000C89379 /* CoreGraphics.framework */; }; + 529226DA1C85F68000C89379 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 529226D91C85F68000C89379 /* UIKit.framework */; }; + D61C5F8E22BABA9C00A79141 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D61C5F8C22BABA9B00A79141 /* Images.xcassets */; }; + D61C5F9622BABAD200A79141 /* integration_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = D61C5F9222BABAD100A79141 /* integration_test.cc */; }; + D62CCBC022F367140099BE9F /* gmock-all.cc in Sources */ = {isa = PBXBuildFile; fileRef = D62CCBBF22F367140099BE9F /* gmock-all.cc */; }; + D66B16871CE46E8900E5638A /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D66B16861CE46E8900E5638A /* LaunchScreen.storyboard */; }; + D67D355822BABD2200292C1D /* gtest-all.cc in Sources */ = {isa = PBXBuildFile; fileRef = D67D355622BABD2100292C1D /* gtest-all.cc */; }; + D6C179E922CB322900C2651A /* ios_app_framework.mm in Sources */ = {isa = PBXBuildFile; fileRef = D6C179E722CB322900C2651A /* ios_app_framework.mm */; }; + D6C179EA22CB322900C2651A /* ios_firebase_test_framework.mm in Sources */ = {isa = PBXBuildFile; fileRef = D6C179E822CB322900C2651A /* ios_firebase_test_framework.mm */; }; + D6C179EE22CB323300C2651A /* firebase_test_framework.cc in Sources */ = {isa = PBXBuildFile; fileRef = D6C179EC22CB323300C2651A /* firebase_test_framework.cc */; }; + D6C179F022CB32A000C2651A /* app_framework.cc in Sources */ = {isa = PBXBuildFile; fileRef = D6C179EF22CB32A000C2651A /* app_framework.cc */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 520BC0381C869159008CFBC3 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; + 529226D21C85F68000C89379 /* integration_test.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = integration_test.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 529226D51C85F68000C89379 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + 529226D71C85F68000C89379 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; + 529226D91C85F68000C89379 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; + 529226EE1C85F68000C89379 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; + D61C5F8C22BABA9B00A79141 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; + D61C5F8D22BABA9C00A79141 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + D61C5F9222BABAD100A79141 /* integration_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = integration_test.cc; path = src/integration_test.cc; sourceTree = ""; }; + D66B16861CE46E8900E5638A /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = ""; }; + D67D355622BABD2100292C1D /* gtest-all.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "gtest-all.cc"; path = "external/googletest/src/googletest/src/gtest-all.cc"; sourceTree = ""; }; + D67D355722BABD2100292C1D /* gtest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = gtest.h; path = external/googletest/src/googletest/include/gtest/gtest.h; sourceTree = ""; }; + D62CCBBF22F367140099BE9F /* gmock-all.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "gmock-all.cc"; path = "external/googletest/src/googlemock/src/gmock-all.cc"; sourceTree = ""; }; + D62CCBC122F367320099BE9F /* gmock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = gmock.h; path = external/googletest/src/googlemock/include/gmock/gmock.h; sourceTree = ""; }; + D6C179E722CB322900C2651A /* ios_app_framework.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ios_app_framework.mm; path = src/ios/ios_app_framework.mm; sourceTree = ""; }; + D6C179E822CB322900C2651A /* ios_firebase_test_framework.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ios_firebase_test_framework.mm; path = src/ios/ios_firebase_test_framework.mm; sourceTree = ""; }; + D6C179EB22CB323300C2651A /* firebase_test_framework.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = firebase_test_framework.h; path = src/firebase_test_framework.h; sourceTree = ""; }; + D6C179EC22CB323300C2651A /* firebase_test_framework.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = firebase_test_framework.cc; path = src/firebase_test_framework.cc; sourceTree = ""; }; + D6C179ED22CB323300C2651A /* app_framework.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = app_framework.h; path = src/app_framework.h; sourceTree = ""; }; + D6C179EF22CB32A000C2651A /* app_framework.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = app_framework.cc; path = src/app_framework.cc; sourceTree = ""; }; + D6E7D43C22D51D9900EDBD35 /* UserNotifications.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UserNotifications.framework; path = System/Library/Frameworks/UserNotifications.framework; sourceTree = SDKROOT; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 529226CF1C85F68000C89379 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 529226D81C85F68000C89379 /* CoreGraphics.framework in Frameworks */, + 529226DA1C85F68000C89379 /* UIKit.framework in Frameworks */, + 529226D61C85F68000C89379 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 529226C91C85F68000C89379 = { + isa = PBXGroup; + children = ( + D61C5F8C22BABA9B00A79141 /* Images.xcassets */, + D61C5F8D22BABA9C00A79141 /* Info.plist */, + D66B16861CE46E8900E5638A /* LaunchScreen.storyboard */, + 520BC0381C869159008CFBC3 /* GoogleService-Info.plist */, + 5292271D1C85FB5500C89379 /* src */, + 529226D41C85F68000C89379 /* Frameworks */, + 529226D31C85F68000C89379 /* Products */, + ); + sourceTree = ""; + }; + 529226D31C85F68000C89379 /* Products */ = { + isa = PBXGroup; + children = ( + 529226D21C85F68000C89379 /* integration_test.app */, + ); + name = Products; + sourceTree = ""; + }; + 529226D41C85F68000C89379 /* Frameworks */ = { + isa = PBXGroup; + children = ( + D6E7D43C22D51D9900EDBD35 /* UserNotifications.framework */, + 529226D51C85F68000C89379 /* Foundation.framework */, + 529226D71C85F68000C89379 /* CoreGraphics.framework */, + 529226D91C85F68000C89379 /* UIKit.framework */, + 529226EE1C85F68000C89379 /* XCTest.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 5292271D1C85FB5500C89379 /* src */ = { + isa = PBXGroup; + children = ( + D62CCBC122F367320099BE9F /* gmock.h */, + D62CCBBF22F367140099BE9F /* gmock-all.cc */, + D67D355622BABD2100292C1D /* gtest-all.cc */, + D67D355722BABD2100292C1D /* gtest.h */, + D6C179EF22CB32A000C2651A /* app_framework.cc */, + D6C179ED22CB323300C2651A /* app_framework.h */, + D6C179EC22CB323300C2651A /* firebase_test_framework.cc */, + D6C179EB22CB323300C2651A /* firebase_test_framework.h */, + D61C5F9222BABAD100A79141 /* integration_test.cc */, + 5292271E1C85FB5B00C89379 /* ios */, + ); + name = src; + sourceTree = ""; + }; + 5292271E1C85FB5B00C89379 /* ios */ = { + isa = PBXGroup; + children = ( + D6C179E722CB322900C2651A /* ios_app_framework.mm */, + D6C179E822CB322900C2651A /* ios_firebase_test_framework.mm */, + ); + name = ios; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 529226D11C85F68000C89379 /* integration_test */ = { + isa = PBXNativeTarget; + buildConfigurationList = 529226F91C85F68000C89379 /* Build configuration list for PBXNativeTarget "integration_test" */; + buildPhases = ( + 529226CE1C85F68000C89379 /* Sources */, + 529226CF1C85F68000C89379 /* Frameworks */, + 529226D01C85F68000C89379 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = integration_test; + productName = testapp; + productReference = 529226D21C85F68000C89379 /* integration_test.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 529226CA1C85F68000C89379 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0640; + ORGANIZATIONNAME = Google; + TargetAttributes = { + 529226D11C85F68000C89379 = { + CreatedOnToolsVersion = 6.4; + DevelopmentTeam = EQHXZ8M8AV; + ProvisioningStyle = Automatic; + SystemCapabilities = { + com.apple.BackgroundModes = { + enabled = 1; + }; + com.apple.Push = { + enabled = 1; + }; + }; + }; + }; + }; + buildConfigurationList = 529226CD1C85F68000C89379 /* Build configuration list for PBXProject "integration_test" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 529226C91C85F68000C89379; + productRefGroup = 529226D31C85F68000C89379 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 529226D11C85F68000C89379 /* integration_test */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 529226D01C85F68000C89379 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D61C5F8E22BABA9C00A79141 /* Images.xcassets in Resources */, + D66B16871CE46E8900E5638A /* LaunchScreen.storyboard in Resources */, + 520BC0391C869159008CFBC3 /* GoogleService-Info.plist in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 529226CE1C85F68000C89379 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D67D355822BABD2200292C1D /* gtest-all.cc in Sources */, + D62CCBC022F367140099BE9F /* gmock-all.cc in Sources */, + D6C179EA22CB322900C2651A /* ios_firebase_test_framework.mm in Sources */, + D61C5F9622BABAD200A79141 /* integration_test.cc in Sources */, + D6C179E922CB322900C2651A /* ios_app_framework.mm in Sources */, + D6C179F022CB32A000C2651A /* app_framework.cc in Sources */, + D6C179EE22CB323300C2651A /* firebase_test_framework.cc in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 529226F71C85F68000C89379 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.4; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 529226F81C85F68000C89379 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.4; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 529226FA1C85F68000C89379 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ""; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)", + ); + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + "\"$(SRCROOT)/src\"", + "\"$(SRCROOT)/external/googletest/src/googletest/include\"", + "\"$(SRCROOT)/external/googletest/src/googlemock/include\"", + "\"$(SRCROOT)/external/googletest/src/googletest\"", + "\"$(SRCROOT)/external/googletest/src/googlemock\"", + ); + INFOPLIST_FILE = "$(SRCROOT)/Info.plist"; + PRODUCT_BUNDLE_IDENTIFIER = com.google.FirebaseCppAuthTestApp.dev; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + WRAPPER_EXTENSION = app; + }; + name = Debug; + }; + 529226FB1C85F68000C89379 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ""; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)", + ); + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + "\"$(SRCROOT)/src\"", + "\"$(SRCROOT)/external/googletest/src/googletest/include\"", + "\"$(SRCROOT)/external/googletest/src/googlemock/include\"", + "\"$(SRCROOT)/external/googletest/src/googletest\"", + "\"$(SRCROOT)/external/googletest/src/googlemock\"", + ); + INFOPLIST_FILE = "$(SRCROOT)/Info.plist"; + PRODUCT_BUNDLE_IDENTIFIER = com.google.FirebaseCppAuthTestApp.dev; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + WRAPPER_EXTENSION = app; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 529226CD1C85F68000C89379 /* Build configuration list for PBXProject "integration_test" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 529226F71C85F68000C89379 /* Debug */, + 529226F81C85F68000C89379 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 529226F91C85F68000C89379 /* Build configuration list for PBXNativeTarget "integration_test" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 529226FA1C85F68000C89379 /* Debug */, + 529226FB1C85F68000C89379 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 529226CA1C85F68000C89379 /* Project object */; +} diff --git a/testing/integration_tests/auth/proguard.pro b/testing/integration_tests/auth/proguard.pro new file mode 100644 index 0000000000..2d04b8a9a5 --- /dev/null +++ b/testing/integration_tests/auth/proguard.pro @@ -0,0 +1,2 @@ +-ignorewarnings +-keep,includedescriptorclasses public class com.google.firebase.example.LoggingUtils { * ; } diff --git a/testing/integration_tests/auth/res/layout/main.xml b/testing/integration_tests/auth/res/layout/main.xml new file mode 100644 index 0000000000..d3ffb63082 --- /dev/null +++ b/testing/integration_tests/auth/res/layout/main.xml @@ -0,0 +1,12 @@ + + + + diff --git a/testing/integration_tests/auth/res/values/strings.xml b/testing/integration_tests/auth/res/values/strings.xml new file mode 100644 index 0000000000..cfd0d03516 --- /dev/null +++ b/testing/integration_tests/auth/res/values/strings.xml @@ -0,0 +1,4 @@ + + + Firebase Auth Integration Test + diff --git a/testing/integration_tests/auth/settings.gradle b/testing/integration_tests/auth/settings.gradle new file mode 100644 index 0000000000..2a543b9351 --- /dev/null +++ b/testing/integration_tests/auth/settings.gradle @@ -0,0 +1,36 @@ +// Copyright 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +def firebase_cpp_sdk_dir = System.getProperty('firebase_cpp_sdk.dir') +if (firebase_cpp_sdk_dir == null || firebase_cpp_sdk_dir.isEmpty()) { + firebase_cpp_sdk_dir = System.getenv('FIREBASE_CPP_SDK_DIR') + if (firebase_cpp_sdk_dir == null || firebase_cpp_sdk_dir.isEmpty()) { + if ((new File('firebase_cpp_sdk')).exists()) { + firebase_cpp_sdk_dir = 'firebase_cpp_sdk' + } else { + throw new StopActionException( + 'firebase_cpp_sdk.dir property or the FIREBASE_CPP_SDK_DIR ' + + 'environment variable must be set to reference the Firebase C++ ' + + 'SDK install directory. This is used to configure static library ' + + 'and C/C++ include paths for the SDK.') + } + } +} +if (!(new File(firebase_cpp_sdk_dir)).exists()) { + throw new StopActionException( + sprintf('Firebase C++ SDK directory %s does not exist', + firebase_cpp_sdk_dir)) +} +gradle.ext.firebase_cpp_sdk_dir = "$firebase_cpp_sdk_dir" +includeBuild "$firebase_cpp_sdk_dir" \ No newline at end of file diff --git a/testing/integration_tests/auth/src/integration_test.cc b/testing/integration_tests/auth/src/integration_test.cc new file mode 100644 index 0000000000..2eb35a5bfc --- /dev/null +++ b/testing/integration_tests/auth/src/integration_test.cc @@ -0,0 +1,1117 @@ +// Copyright 2019 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include +#include +#include +#include +#include + +#include "app_framework.h" // NOLINT +#include "firebase/app.h" +#include "firebase/auth.h" +#include "firebase/auth/credential.h" +#include "firebase/auth/user.h" +#include "firebase/util.h" +#include "firebase/variant.h" +#include "firebase_test_framework.h" // NOLINT + +// The TO_STRING macro is useful for command line defined strings as the quotes +// get stripped. +#define TO_STRING_EXPAND(X) #X +#define TO_STRING(X) TO_STRING_EXPAND(X) + +// Path to the Firebase config file to load. +#ifdef FIREBASE_CONFIG +#define FIREBASE_CONFIG_STRING TO_STRING(FIREBASE_CONFIG) +#else +#define FIREBASE_CONFIG_STRING "" +#endif // FIREBASE_CONFIG + +namespace firebase_testapp_automated { + +// Set kCustomTestEmail and kCustomTestPassword if you want to test email and +// password login using a custom account you've already set up on your Firebase +// project. +static const char kCustomTestEmail[] = "put_custom_test_account_here@gmail.com"; +static const char kCustomTestPassword[] = ""; + +static const int kWaitIntervalMs = 300; // NOLINT +static const int kPhoneAuthCodeSendWaitMs = 600000; // NOLINT +static const int kPhoneAuthCompletionWaitMs = 8000; // NOLINT +static const int kPhoneAuthTimeoutMs = 0; // NOLINT + +// Set these in Firebase Console for your app. +static const char kPhoneAuthTestPhoneNumber[] = "+12345556789"; // NOLINT +static const char kPhoneAuthTestVerificationCode[] = "123456"; // NOLINT + +static const char kTestPassword[] = "testEmailPassword123"; +static const char kTestEmailBad[] = "bad.test.email@example.com"; +static const char kTestPasswordBad[] = "badTestPassword"; +static const char kTestIdTokenBad[] = "bad id token for testing"; +static const char kTestAccessTokenBad[] = "bad access token for testing"; +static const char kTestPasswordUpdated[] = "testpasswordupdated"; +static const char kTestIdProviderIdBad[] = "bad provider id for testing"; +static const char kTestServerAuthCodeBad[] = "bad server auth code"; // NOLINT + +using app_framework::LogDebug; +using app_framework::LogError; // NOLINT +using app_framework::LogInfo; +using app_framework::ProcessEvents; + +using firebase_test_framework::FirebaseTest; + +class FirebaseAuthTest : public FirebaseTest { + public: + FirebaseAuthTest(); + ~FirebaseAuthTest() override; + + void SetUp() override; + void TearDown() override; + + protected: + // Initialize Firebase App and Firebase Auth. + void Initialize(); + // Shut down Firebase App and Firebase Auth. + void Terminate(); + + // Sign out of any user we were signed into. This is automatically called + // before and after every test. + void SignOut(); + + // Delete the current user if it's currently signed in. + void DeleteUser(); + + // Passthrough method to the base class's WaitForCompletion. + bool WaitForCompletion(firebase::Future future, const char* fn, + int expected_error = firebase::auth::kAuthErrorNone) { + return FirebaseTest::WaitForCompletion(future, fn, expected_error); + } + + // Passthrough method to the base class's WaitForCompletion. + bool WaitForCompletion(firebase::Future future, const char* fn, + int expected_error = firebase::auth::kAuthErrorNone) { + return FirebaseTest::WaitForCompletion(future, fn, expected_error); + } + + // Custom WaitForCompletion that checks if User matches afterwards. + bool WaitForCompletion(firebase::Future future, + const char* fn, + int expected_error = firebase::auth::kAuthErrorNone); + // Custom WaitForCompletion that checks if User matches afterwards. + bool WaitForCompletion(firebase::Future future, + const char* fn, + int expected_error = firebase::auth::kAuthErrorNone); + + // Custom WaitForCompletion that checks if User and Provider ID matches + // afterwards. + bool WaitForCompletion(firebase::Future future, + const char* fn, const std::string& provider_id); + + bool initialized_; + firebase::auth::Auth* auth_; +}; + +FirebaseAuthTest::FirebaseAuthTest() : initialized_(false), auth_(nullptr) { + FindFirebaseConfig(FIREBASE_CONFIG_STRING); +} + +FirebaseAuthTest::~FirebaseAuthTest() { + // Must be cleaned up on exit. + assert(app_ == nullptr); + assert(auth_ == nullptr); +} + +void FirebaseAuthTest::SetUp() { + FirebaseTest::SetUp(); + Initialize(); + SignOut(); +} + +void FirebaseAuthTest::TearDown() { + SignOut(); + Terminate(); + FirebaseTest::TearDown(); +} + +void FirebaseAuthTest::Initialize() { + if (initialized_) return; + + InitializeApp(); + + LogDebug("Initializing Firebase Auth."); + + ::firebase::ModuleInitializer initializer; + initializer.Initialize(app_, &auth_, [](::firebase::App* app, void* target) { + LogDebug("Try to initialize Firebase Auth"); + firebase::InitResult result; + firebase::auth::Auth** auth_ptr = + reinterpret_cast(target); + *auth_ptr = firebase::auth::Auth::GetAuth(app, &result); + return result; + }); + + FirebaseTest::WaitForCompletion(initializer.InitializeLastResult(), + "Initialize"); + + ASSERT_EQ(initializer.InitializeLastResult().error(), 0) + << initializer.InitializeLastResult().error_message(); + + LogDebug("Successfully initialized Firebase Auth."); + + initialized_ = true; +} + +void FirebaseAuthTest::Terminate() { + if (!initialized_) return; + + if (auth_) { + LogDebug("Shutdown the Auth library."); + delete auth_; + auth_ = nullptr; + } + + TerminateApp(); + + initialized_ = false; + + ProcessEvents(100); +} + +bool FirebaseAuthTest::WaitForCompletion( + firebase::Future future, const char* fn, + int expected_error) { + bool succeeded = FirebaseTest::WaitForCompletion(future, fn, expected_error); + + if (succeeded) { + if (expected_error == ::firebase::auth::kAuthErrorNone) { + const firebase::auth::User* future_result_user = + future.result() ? *future.result() : nullptr; + const firebase::auth::User* auth_user = auth_->current_user(); + EXPECT_EQ(future_result_user, auth_user) + << "User returned by Future doesn't match User in Auth"; + return (future_result_user == auth_user); + } + } + return succeeded; +} + +bool FirebaseAuthTest::WaitForCompletion( + firebase::Future future, const char* fn, + int expected_error) { + bool succeeded = FirebaseTest::WaitForCompletion(future, fn, expected_error); + + if (succeeded) { + if (expected_error == ::firebase::auth::kAuthErrorNone) { + const firebase::auth::User* future_result_user = + (future.result() && future.result()->user) ? future.result()->user + : nullptr; + const firebase::auth::User* auth_user = auth_->current_user(); + EXPECT_EQ(future_result_user, auth_user) + << "User returned by Future doesn't match User in Auth"; + return (future_result_user == auth_user); + } + } + return succeeded; +} + +bool FirebaseAuthTest::WaitForCompletion( + firebase::Future future, const char* fn, + const std::string& provider_id) { + bool succeeded = FirebaseTest::WaitForCompletion(future, fn); + if (succeeded) { + const firebase::auth::SignInResult* result_ptr = future.result(); + EXPECT_NE(result_ptr->user, nullptr); + EXPECT_EQ(result_ptr->info.provider_id, provider_id); + } + return succeeded; +} + +void FirebaseAuthTest::SignOut() { + if (auth_ == nullptr) { + // Auth is not set up. + return; + } + if (auth_->current_user() == nullptr) { + // Already signed out. + return; + } + auth_->SignOut(); + // Wait for the sign-out to finish. + while (auth_->current_user() != nullptr) { + if (ProcessEvents(100)) break; + } + ProcessEvents(100); + EXPECT_EQ(auth_->current_user(), nullptr); +} + +void FirebaseAuthTest::DeleteUser() { + if (auth_ != nullptr && auth_->current_user() != nullptr) { + FirebaseTest::WaitForCompletion(auth_->current_user()->Delete(), + "Delete User"); + ProcessEvents(100); + } +} + +TEST_F(FirebaseAuthTest, TestInitialization) { + // Initialized in SetUp and terminated in TearDown. + EXPECT_NE(app_, nullptr); + EXPECT_NE(auth_, nullptr); +} + +TEST_F(FirebaseAuthTest, TestAnonymousSignin) { + // Test notification on SignIn(). + WaitForCompletion(auth_->SignInAnonymously(), "SignInAnonymously"); + EXPECT_NE(auth_->current_user(), nullptr); + EXPECT_TRUE(auth_->current_user()->is_anonymous()); + DeleteUser(); +} + +TEST_F(FirebaseAuthTest, TestCredentialCopy) { + // --- Credential copy tests ------------------------------------------------- + { + firebase::auth::Credential email_cred = + firebase::auth::EmailAuthProvider::GetCredential(kCustomTestEmail, + kTestPassword); + firebase::auth::Credential facebook_cred = + firebase::auth::FacebookAuthProvider::GetCredential( + kTestAccessTokenBad); + + std::string email_provider = email_cred.provider(); + std::string facebook_provider = facebook_cred.provider(); + + // Test copy constructor. + firebase::auth::Credential cred_copy(email_cred); + EXPECT_EQ(cred_copy.provider(), email_provider); + // Test assignment operator. + cred_copy = facebook_cred; + EXPECT_EQ(cred_copy.provider(), facebook_provider); + } +} + +class TestAuthStateListener : public firebase::auth::AuthStateListener { + public: + virtual void OnAuthStateChanged(firebase::auth::Auth* auth) { // NOLINT + // Log the provider ID. + std::string provider = + auth->current_user() ? auth->current_user()->provider_id() : ""; + LogDebug("OnAuthStateChanged called, provider=%s", provider.c_str()); + if (auth_states_.empty() || auth_states_.back() != provider) { + // Only log unique events. + auth_states_.push_back(provider); + } + } + const std::vector& auth_states() { return auth_states_; } + + private: + std::vector auth_states_; +}; + +class TestIdTokenListener : public firebase::auth::IdTokenListener { + public: + virtual void OnIdTokenChanged(firebase::auth::Auth* auth) { // NOLINT + // Log the auth token (if available). + std::string token = ""; + if (auth->current_user()) { + firebase::Future token_future = + auth->current_user()->GetToken(false); + if (token_future.status() == firebase::kFutureStatusComplete) { + if (token_future.error() == 0) { + token = *token_future.result(); + } + } else { + token = "[in progress]"; + } + } + LogDebug("OnIdTokenChanged called, token=%s", token.c_str()); + if (token_states_.empty() || !token.empty() || + token_states_.back() != token) { + // Only log unique empty events. + token_states_.push_back(token); + } + } + + const std::vector& token_states() { return token_states_; } + + private: + std::vector token_states_; +}; + +using testing::AnyOf; +using testing::ElementsAre; +using testing::Not; +using testing::StrCaseEq; + +TEST_F(FirebaseAuthTest, TestTokensAndAuthStateListeners) { + TestAuthStateListener listener; + TestIdTokenListener token_listener; + auth_->AddAuthStateListener(&listener); + auth_->AddIdTokenListener(&token_listener); + WaitForCompletion(auth_->SignInAnonymously(), "SignInAnonymously"); + // Get an initial token. + firebase::Future token_future = + auth_->current_user()->GetToken(false); + WaitForCompletion(token_future, "GetToken(false)"); + std::string first_token = *token_future.result(); + // Force a token refresh. + ProcessEvents(1000); + token_future = auth_->current_user()->GetToken(true); + WaitForCompletion(token_future, "GetToken(true)"); + EXPECT_NE(*token_future.result(), ""); + std::string second_token = *token_future.result(); + EXPECT_NE(first_token, second_token); + + DeleteUser(); + SignOut(); + auth_->RemoveAuthStateListener(&listener); + auth_->RemoveIdTokenListener(&token_listener); + // Providers should be blank, then Firebase, then blank. + EXPECT_THAT(listener.auth_states(), + ElementsAre("", StrCaseEq("Firebase"), "")); + // We should have blank, then two (or sometimes three) tokens, then blank. + EXPECT_THAT(token_listener.token_states(), + AnyOf(ElementsAre("", Not(""), Not(""), ""), + ElementsAre("", Not(""), Not(""), Not(""), ""))); +} + +static std::string GenerateEmailAddress() { + std::string email = + "random_user_" + + std::to_string(app_framework::GetCurrentTimeInMicroseconds()) + + "@gmail.com"; + LogDebug("Generated email address: %s", email.c_str()); + return email; +} + +TEST_F(FirebaseAuthTest, TestEmailAndPasswordSignin) { + std::string email = GenerateEmailAddress(); + // Register a random email and password. This signs us in as that user. + std::string password = kTestPassword; + firebase::Future create_user = + auth_->CreateUserWithEmailAndPassword(email.c_str(), password.c_str()); + WaitForCompletion(create_user, "CreateUserWithEmailAndPassword"); + EXPECT_NE(auth_->current_user(), nullptr); + // Sign out and log in using SignInWithCredential(EmailCredential). + SignOut(); + { + firebase::auth::Credential email_credential = + firebase::auth::EmailAuthProvider::GetCredential(email.c_str(), + password.c_str()); + WaitForCompletion(auth_->SignInWithCredential(email_credential), + "SignInWithCredential"); + EXPECT_NE(auth_->current_user(), nullptr); + } + // Sign out and log in using + // SignInAndRetrieveDataWithCredential(EmailCredential). + SignOut(); + { + firebase::auth::Credential email_credential = + firebase::auth::EmailAuthProvider::GetCredential(email.c_str(), + password.c_str()); + WaitForCompletion( + auth_->SignInAndRetrieveDataWithCredential(email_credential), + "SignAndRetrieveDataInWithCredential"); + EXPECT_NE(auth_->current_user(), nullptr); + } + SignOut(); + // Sign in with SignInWithEmailAndPassword values. + firebase::Future sign_in_user = + auth_->SignInWithEmailAndPassword(email.c_str(), password.c_str()); + WaitForCompletion(sign_in_user, "SignInWithEmailAndPassword"); + ASSERT_NE(auth_->current_user(), nullptr); + + // Then delete the account. + firebase::Future delete_user = auth_->current_user()->Delete(); + WaitForCompletion(delete_user, "Delete"); + firebase::Future invalid_sign_in_user = + auth_->SignInWithEmailAndPassword(email.c_str(), password.c_str()); + WaitForCompletion(invalid_sign_in_user, + "SignInWithEmailAndPassword (invalid user)", + firebase::auth::kAuthErrorUserNotFound); + EXPECT_EQ(auth_->current_user(), nullptr); +} + +TEST_F(FirebaseAuthTest, TestUpdateUserProfile) { + std::string email = GenerateEmailAddress(); + firebase::Future create_user = + auth_->CreateUserWithEmailAndPassword(email.c_str(), kTestPassword); + WaitForCompletion(create_user, "CreateUserWithEmailAndPassword"); + EXPECT_NE(auth_->current_user(), nullptr); + // Set some user profile properties. + firebase::auth::User* user = *create_user.result(); + const char kDisplayName[] = "Hello World"; + const char kPhotoUrl[] = "http://example.com/image.jpg"; + firebase::auth::User::UserProfile user_profile; + user_profile.display_name = kDisplayName; + user_profile.photo_url = kPhotoUrl; + firebase::Future update_profile = user->UpdateUserProfile(user_profile); + WaitForCompletion(update_profile, "UpdateUserProfile"); + EXPECT_EQ(user->display_name(), kDisplayName); + EXPECT_EQ(user->photo_url(), kPhotoUrl); + SignOut(); + WaitForCompletion( + auth_->SignInWithEmailAndPassword(email.c_str(), kTestPassword), + "SignInWithEmailAndPassword"); + EXPECT_EQ(user->display_name(), kDisplayName); + EXPECT_EQ(user->photo_url(), kPhotoUrl); + DeleteUser(); +} + +TEST_F(FirebaseAuthTest, TestUpdateEmailAndPassword) { + std::string email = GenerateEmailAddress(); + WaitForCompletion( + auth_->CreateUserWithEmailAndPassword(email.c_str(), kTestPassword), + "CreateUserWithEmailAndPassword"); + ASSERT_NE(auth_->current_user(), nullptr); + firebase::auth::User* user = auth_->current_user(); + + // Update the user's email and password. + const std::string new_email = "new_" + email; + WaitForCompletion(user->UpdateEmail(new_email.c_str()), "UpdateEmail"); + WaitForCompletion(user->UpdatePassword(kTestPasswordUpdated), + "UpdatePassword"); + + firebase::auth::Credential new_email_cred = + firebase::auth::EmailAuthProvider::GetCredential(new_email.c_str(), + kTestPasswordUpdated); + WaitForCompletion(user->Reauthenticate(new_email_cred), "Reauthenticate"); + EXPECT_NE(auth_->current_user(), nullptr); + + WaitForCompletion(user->SendEmailVerification(), "SendEmailVerification"); + DeleteUser(); +} + +TEST_F(FirebaseAuthTest, TestLinkAnonymousUserWithEmailCredential) { + WaitForCompletion(auth_->SignInAnonymously(), "SignInAnonymously"); + ASSERT_NE(auth_->current_user(), nullptr); + firebase::auth::User* user = auth_->current_user(); + std::string email = GenerateEmailAddress(); + firebase::auth::Credential credential = + firebase::auth::EmailAuthProvider::GetCredential(email.c_str(), + kTestPassword); + WaitForCompletion(user->LinkAndRetrieveDataWithCredential(credential), + "LinkAndRetrieveDataWithCredential"); + WaitForCompletion(user->Unlink(credential.provider().c_str()), "Unlink"); + SignOut(); + WaitForCompletion(auth_->SignInAnonymously(), "SignInAnonymously"); + EXPECT_NE(auth_->current_user(), nullptr); + std::string email1 = GenerateEmailAddress(); + firebase::auth::Credential credential1 = + firebase::auth::EmailAuthProvider::GetCredential(email1.c_str(), + kTestPassword); + WaitForCompletion(user->LinkWithCredential(credential1), + "LinkWithCredential 1"); + std::string email2 = GenerateEmailAddress(); + firebase::auth::Credential credential2 = + firebase::auth::EmailAuthProvider::GetCredential(email2.c_str(), + kTestPassword); + WaitForCompletion(user->LinkWithCredential(credential2), + "LinkWithCredential 2", + firebase::auth::kAuthErrorProviderAlreadyLinked); + WaitForCompletion(user->Unlink(credential.provider().c_str()), "Unlink 2"); + DeleteUser(); +} + +TEST_F(FirebaseAuthTest, TestLinkAnonymousUserWithBadCredential) { + WaitForCompletion(auth_->SignInAnonymously(), "SignInAnonymously"); + ASSERT_NE(auth_->current_user(), nullptr); + firebase::auth::User* pre_link_user = auth_->current_user(); + firebase::auth::Credential twitter_cred = + firebase::auth::TwitterAuthProvider::GetCredential(kTestIdTokenBad, + kTestAccessTokenBad); + WaitForCompletion(pre_link_user->LinkWithCredential(twitter_cred), + "LinkWithCredential", + firebase::auth::kAuthErrorInvalidCredential); + // Ensure that user stays the same. + EXPECT_EQ(auth_->current_user(), pre_link_user); + DeleteUser(); +} + +TEST_F(FirebaseAuthTest, TestSignInWithBadEmailFails) { + WaitForCompletion( + auth_->SignInWithEmailAndPassword(kTestEmailBad, kTestPassword), + "SignInWithEmailAndPassword", firebase::auth::kAuthErrorUserNotFound); + EXPECT_EQ(auth_->current_user(), nullptr); +} + +TEST_F(FirebaseAuthTest, TestSignInWithBadPasswordFails) { + std::string email = GenerateEmailAddress(); + WaitForCompletion( + auth_->CreateUserWithEmailAndPassword(email.c_str(), kTestPassword), + "CreateUserWithEmailAndPassword"); + EXPECT_NE(auth_->current_user(), nullptr); + SignOut(); + WaitForCompletion( + auth_->SignInWithEmailAndPassword(email.c_str(), kTestPasswordBad), + "SignInWithEmailAndPassword", firebase::auth::kAuthErrorWrongPassword); + EXPECT_EQ(auth_->current_user(), nullptr); + SignOut(); + // Sign back in and delete the user. + WaitForCompletion( + auth_->SignInWithEmailAndPassword(email.c_str(), kTestPassword), + "SignInWithEmailAndPassword"); + EXPECT_NE(auth_->current_user(), nullptr); + DeleteUser(); +} + +TEST_F(FirebaseAuthTest, TestCreateUserWithExistingEmailFails) { + std::string email = GenerateEmailAddress(); + WaitForCompletion( + auth_->CreateUserWithEmailAndPassword(email.c_str(), kTestPassword), + "CreateUserWithEmailAndPassword 1"); + EXPECT_NE(auth_->current_user(), nullptr); + SignOut(); + WaitForCompletion( + auth_->CreateUserWithEmailAndPassword(email.c_str(), kTestPassword), + "CreateUserWithEmailAndPassword 2", + firebase::auth::kAuthErrorEmailAlreadyInUse); + EXPECT_EQ(auth_->current_user(), nullptr); + SignOut(); + // Try again with a different password. + WaitForCompletion( + auth_->CreateUserWithEmailAndPassword(email.c_str(), kTestPasswordBad), + "CreateUserWithEmailAndPassword 3", + firebase::auth::kAuthErrorEmailAlreadyInUse); + EXPECT_EQ(auth_->current_user(), nullptr); + SignOut(); + WaitForCompletion( + auth_->SignInWithEmailAndPassword(email.c_str(), kTestPassword), + "SignInWithEmailAndPassword"); + EXPECT_NE(auth_->current_user(), nullptr); + DeleteUser(); +} + +TEST_F(FirebaseAuthTest, TestSignInWithBadCredentials) { + // Get an anonymous user first. + WaitForCompletion(auth_->SignInAnonymously(), "SignInAnonymously"); + ASSERT_NE(auth_->current_user(), nullptr); + // Hold on to the existing user, to make sure it is unchanged by bad signins. + firebase::auth::User* existing_user = auth_->current_user(); + // Test signing in with a variety of bad credentials. + WaitForCompletion(auth_->SignInWithCredential( + firebase::auth::FacebookAuthProvider::GetCredential( + kTestAccessTokenBad)), + "SignInWithCredential (Facebook)", + firebase::auth::kAuthErrorInvalidCredential); + // Ensure that failing to sign in with a credential doesn't modify the user. + EXPECT_EQ(auth_->current_user(), existing_user); + WaitForCompletion(auth_->SignInWithCredential( + firebase::auth::TwitterAuthProvider::GetCredential( + kTestIdTokenBad, kTestAccessTokenBad)), + "SignInWithCredential (Twitter)", + firebase::auth::kAuthErrorInvalidCredential); + EXPECT_EQ(auth_->current_user(), existing_user); + WaitForCompletion(auth_->SignInWithCredential( + firebase::auth::GitHubAuthProvider::GetCredential( + kTestAccessTokenBad)), + "SignInWithCredential (GitHub)", + firebase::auth::kAuthErrorInvalidCredential); + EXPECT_EQ(auth_->current_user(), existing_user); + WaitForCompletion(auth_->SignInWithCredential( + firebase::auth::GoogleAuthProvider::GetCredential( + kTestIdTokenBad, kTestAccessTokenBad)), + "SignInWithCredential (Google 1)", + firebase::auth::kAuthErrorInvalidCredential); + EXPECT_EQ(auth_->current_user(), existing_user); + WaitForCompletion(auth_->SignInWithCredential( + firebase::auth::GoogleAuthProvider::GetCredential( + kTestIdTokenBad, nullptr)), + "SignInWithCredential (Google 2)", + firebase::auth::kAuthErrorInvalidCredential); + EXPECT_EQ(auth_->current_user(), existing_user); + WaitForCompletion( + auth_->SignInWithCredential(firebase::auth::OAuthProvider::GetCredential( + kTestIdProviderIdBad, kTestIdTokenBad, kTestAccessTokenBad)), + "SignInWithCredential (OAuth)", firebase::auth::kAuthErrorFailure); + EXPECT_EQ(auth_->current_user(), existing_user); + +#if defined(__ANDROID__) + // Test Play Games sign-in on Android only. + WaitForCompletion(auth_->SignInWithCredential( + firebase::auth::PlayGamesAuthProvider::GetCredential( + kTestServerAuthCodeBad)), + "SignInWithCredential (Play Games)", + firebase::auth::kAuthErrorInvalidCredential); + EXPECT_EQ(auth_->current_user(), existing_user); +#endif // defined(__ANDROID__) + DeleteUser(); +} + +#if TARGET_OS_IPHONE +TEST_F(FirebaseAuthTest, TestGameCenterSignIn) { + // Test Game Center sign-in on iPhone only. + if (!firebase::auth::GameCenterAuthProvider::IsPlayerAuthenticated()) { + LogInfo("Not signed into Game Center, skipping test."); + GTEST_SKIP(); + return; + } + LogDebug("Signed in, testing Game Center authentication."); + firebase::Future credential_future = + firebase::auth::GameCenterAuthProvider::GetCredential(); + FirebaseTest::WaitForCompletion(credential_future, + "GameCenterAuthProvider::GetCredential()"); + + EXPECT_NE(credential_future.result(), nullptr); + if (credential_future.result()) { + WaitForCompletion(auth_->SignInWithCredential(*credential_future.result()), + "SignInWithCredential (Game Center)"); + } + DeleteUser(); +} +#endif // TARGET_OS_IPHONE + +TEST_F(FirebaseAuthTest, TestSendPasswordResetEmail) { + // Test Auth::SendPasswordResetEmail(). + std::string email = GenerateEmailAddress(); + WaitForCompletion( + auth_->CreateUserWithEmailAndPassword(email.c_str(), kTestPassword), + "CreateUserWithEmailAndPassword"); + EXPECT_NE(auth_->current_user(), nullptr); + SignOut(); + // Send to correct email. + WaitForCompletion(auth_->SendPasswordResetEmail(email.c_str()), + "SendPasswordResetEmail (good)"); + // Send to incorrect email. + WaitForCompletion(auth_->SendPasswordResetEmail(kTestEmailBad), + "SendPasswordResetEmail (bad)", + firebase::auth::kAuthErrorUserNotFound); + // Delete user now that we are done with it. + WaitForCompletion( + auth_->SignInWithEmailAndPassword(email.c_str(), kTestPassword), + "SignInWithEmailAndPassword"); + EXPECT_NE(auth_->current_user(), nullptr); + DeleteUser(); +} + +TEST_F(FirebaseAuthTest, TestWithCustomEmailAndPassword) { + if (strlen(kCustomTestEmail) == 0 || strlen(kCustomTestPassword) == 0) { + LogInfo( + "Skipping %s. To enable this test, set " + "kCustomTestEmail and kCustomTestPassword in integration_test.cc.", + test_info_->name()); + GTEST_SKIP(); + return; + } + firebase::Future sign_in_user = + auth_->SignInWithEmailAndPassword(kCustomTestEmail, kCustomTestPassword); + WaitForCompletion(sign_in_user, "SignInWithEmailAndPassword"); + EXPECT_NE(auth_->current_user(), nullptr); +} + +TEST_F(FirebaseAuthTest, TestAuthPersistenceWithAnonymousSignin) { + WaitForCompletion(auth_->SignInAnonymously(), "SignInAnonymously"); + ASSERT_NE(auth_->current_user(), nullptr); + EXPECT_TRUE(auth_->current_user()->is_anonymous()); + Terminate(); + ProcessEvents(2000); + Initialize(); + EXPECT_NE(auth_, nullptr); + ASSERT_NE(auth_->current_user(), nullptr); + EXPECT_TRUE(auth_->current_user()->is_anonymous()); + DeleteUser(); +} + +TEST_F(FirebaseAuthTest, TestAuthPersistenceWithEmailSignin) { + std::string email = GenerateEmailAddress(); + WaitForCompletion( + auth_->CreateUserWithEmailAndPassword(email.c_str(), kTestPassword), + "CreateUserWithEmailAndPassword"); + ASSERT_NE(auth_->current_user(), nullptr); + EXPECT_FALSE(auth_->current_user()->is_anonymous()); + std::string prev_provider_id = auth_->current_user()->provider_id(); + // Save the old provider ID list so we can make sure it's the same once it's + // loaded again. + std::vector prev_provider_data_ids; + for (int i = 0; i < auth_->current_user()->provider_data().size(); i++) { + prev_provider_data_ids.push_back( + auth_->current_user()->provider_data()[i]->provider_id()); + } + Terminate(); + ProcessEvents(2000); + Initialize(); + EXPECT_NE(auth_, nullptr); + ASSERT_NE(auth_->current_user(), nullptr); + EXPECT_FALSE(auth_->current_user()->is_anonymous()); + // Make sure the provider IDs are the same as they were before. + EXPECT_EQ(auth_->current_user()->provider_id(), prev_provider_id); + std::vector loaded_provider_data_ids; + for (int i = 0; i < auth_->current_user()->provider_data().size(); i++) { + loaded_provider_data_ids.push_back( + auth_->current_user()->provider_data()[i]->provider_id()); + } + EXPECT_EQ(loaded_provider_data_ids, prev_provider_data_ids); + + // Cleanup, ensure we are signed in as the user so we can delete it. + WaitForCompletion( + auth_->SignInWithEmailAndPassword(email.c_str(), kTestPassword), + "SignInWithEmailAndPassword"); + EXPECT_NE(auth_->current_user(), nullptr); + DeleteUser(); +} + +class PhoneListener : public firebase::auth::PhoneAuthProvider::Listener { + public: + PhoneListener() + : on_verification_complete_count_(0), + on_verification_failed_count_(0), + on_code_sent_count_(0), + on_code_auto_retrieval_time_out_count_(0) {} + + void OnVerificationCompleted(firebase::auth::Credential credential) override { + LogDebug("PhoneListener: successful automatic verification."); + on_verification_complete_count_++; + credential_ = credential; + } + + void OnVerificationFailed(const std::string& error) override { + LogError("PhoneListener verification failed with error, %s", error.c_str()); + on_verification_failed_count_++; + } + + void OnCodeSent(const std::string& verification_id, + const firebase::auth::PhoneAuthProvider::ForceResendingToken& + force_resending_token) override { + LogDebug("PhoneListener: code sent. verification_id=%s", + verification_id.c_str()); + verification_id_ = verification_id; + force_resending_token_ = force_resending_token; + on_code_sent_count_++; + } + + void OnCodeAutoRetrievalTimeOut(const std::string& verification_id) override { + LogDebug("PhoneListener: auto retrieval timeout. verification_id=%s", + verification_id.c_str()); + verification_id_ = verification_id; + on_code_auto_retrieval_time_out_count_++; + } + + const std::string& verification_id() const { return verification_id_; } + const firebase::auth::PhoneAuthProvider::ForceResendingToken& + force_resending_token() const { + return force_resending_token_; + } + int on_verification_complete_count() const { + return on_verification_complete_count_; + } + int on_verification_failed_count() const { + return on_verification_failed_count_; + } + int on_code_sent_count() const { return on_code_sent_count_; } + int on_code_auto_retrieval_time_out_count() const { + return on_code_auto_retrieval_time_out_count_; + } + + // Helper functions for workflow. + bool waiting_to_send_code() { + return on_verification_complete_count() == 0 && + on_verification_failed_count() == 0 && on_code_sent_count() == 0; + } + + bool waiting_for_verification_id() { + return on_verification_complete_count() == 0 && + on_verification_failed_count() == 0 && + on_code_auto_retrieval_time_out_count() == 0; + } + + firebase::auth::Credential credential() { return credential_; } + + private: + std::string verification_id_; + firebase::auth::PhoneAuthProvider::ForceResendingToken force_resending_token_; + firebase::auth::Credential credential_; + int on_verification_complete_count_; + int on_verification_failed_count_; + int on_code_sent_count_; + int on_code_auto_retrieval_time_out_count_; +}; + +TEST_F(FirebaseAuthTest, TestPhoneAuth) { + SKIP_TEST_ON_DESKTOP; + +#if TARGET_OS_IPHONE + // Note: This test requires interactivity on iOS, as it displays a CAPTCHA. + TEST_REQUIRES_USER_INTERACTION; +#endif // TARGET_OS_IPHONE + { + firebase::auth::PhoneAuthProvider& phone_provider = + firebase::auth::PhoneAuthProvider::GetInstance(auth_); + LogDebug("Creating listener."); + PhoneListener listener; + LogDebug("Calling VerifyPhoneNumber."); + phone_provider.VerifyPhoneNumber(kPhoneAuthTestPhoneNumber, + kPhoneAuthTimeoutMs, nullptr, &listener); + // Wait for OnCodeSent() callback. + int wait_ms = 0; + LogDebug("Waiting for code send."); + while (listener.waiting_to_send_code()) { + if (wait_ms > kPhoneAuthCodeSendWaitMs) break; + ProcessEvents(kWaitIntervalMs); + wait_ms += kWaitIntervalMs; + } + EXPECT_EQ(listener.on_verification_failed_count(), 0); + LogDebug("Waiting for verification ID."); + // Wait for the listener to have a verification ID. + wait_ms = 0; + while (listener.waiting_for_verification_id()) { + if (wait_ms > kPhoneAuthCompletionWaitMs) break; + ProcessEvents(kWaitIntervalMs); + wait_ms += kWaitIntervalMs; + } + if (listener.on_verification_complete_count() > 0) { + LogDebug("Signing in with automatic verification code."); + WaitForCompletion(auth_->SignInWithCredential(listener.credential()), + "SignInWithCredential(PhoneCredential) automatic"); + } else if (listener.on_verification_failed_count() > 0) { + FAIL() << "Automatic verification failed."; + } else { + // Did not automatically verify, submit verification code manually. + EXPECT_GT(listener.on_code_auto_retrieval_time_out_count(), 0); + EXPECT_NE(listener.verification_id(), ""); + LogDebug("Signing in with verification code."); + const firebase::auth::Credential phone_credential = + phone_provider.GetCredential(listener.verification_id().c_str(), + kPhoneAuthTestVerificationCode); + + WaitForCompletion(auth_->SignInWithCredential(phone_credential), + "SignInWithCredential(PhoneCredential)"); + } + } + ProcessEvents(1000); + DeleteUser(); +} + +#if defined(ENABLE_OAUTH_TESTS) +// SignInWithProvider +TEST_F(FirebaseAuthTest, TestSuccessfulSignInFederatedProviderNoScopes) { + SKIP_TEST_ON_DESKTOP; + TEST_REQUIRES_USER_INTERACTION; + + const std::string provider_id = + firebase::auth::GoogleAuthProvider::kProviderId; + firebase::auth::FederatedOAuthProviderData provider_data( + provider_id, /*scopes=*/{}, /*custom_parameters=*/{{"req_id", "1234"}}); + firebase::auth::FederatedOAuthProvider provider(provider_data); + firebase::Future sign_in_future = + auth_->SignInWithProvider(&provider); + WaitForCompletion(sign_in_future, "SignInWithProvider", provider_id); + DeleteUser(); +} + +TEST_F(FirebaseAuthTest, + TestSuccessfulSignInFederatedProviderNoScopesNoCustomParameters) { + SKIP_TEST_ON_DESKTOP; + TEST_REQUIRES_USER_INTERACTION; + + const std::string provider_id = + firebase::auth::GoogleAuthProvider::kProviderId; + firebase::auth::FederatedOAuthProviderData provider_data( + provider_id, /*scopes=*/{}, /*custom_parameters=*/{}); + firebase::auth::FederatedOAuthProvider provider(provider_data); + firebase::Future sign_in_future = + auth_->SignInWithProvider(&provider); + WaitForCompletion(sign_in_future, "SignInWithProvider", provider_id); + DeleteUser(); +} + +TEST_F(FirebaseAuthTest, TestSuccessfulSignInFederatedProvider) { + SKIP_TEST_ON_DESKTOP; + TEST_REQUIRES_USER_INTERACTION; + + const std::string provider_id = + firebase::auth::GoogleAuthProvider::kProviderId; + firebase::auth::FederatedOAuthProviderData provider_data( + provider_id, + /*scopes=*/{"https://www.googleapis.com/auth/fitness.activity.read"}, + /*custom_parameters=*/{{"req_id", "1234"}}); + firebase::auth::FederatedOAuthProvider provider(provider_data); + firebase::Future sign_in_future = + auth_->SignInWithProvider(&provider); + WaitForCompletion(sign_in_future, "SignInWithProvider", provider_id); + DeleteUser(); +} + +TEST_F(FirebaseAuthTest, TestSignInFederatedProviderBadProviderIdFails) { + SKIP_TEST_ON_DESKTOP; + TEST_REQUIRES_USER_INTERACTION; + + firebase::auth::FederatedOAuthProviderData provider_data( + /*provider=*/"MadeUpProvider", + /*scopes=*/{"https://www.googleapis.com/auth/fitness.activity.read"}, + /*custom_parameters=*/{{"req_id", "5321"}}); + firebase::auth::FederatedOAuthProvider provider(provider_data); + firebase::Future sign_in_future = + auth_->SignInWithProvider(&provider); + WaitForCompletion(sign_in_future, "SignInWithProvider", + firebase::auth::kAuthErrorInvalidProviderId); +} + +// ReauthenticateWithProvider +TEST_F(FirebaseAuthTest, TestSuccessfulReauthenticateWithProvider) { + SKIP_TEST_ON_DESKTOP; + TEST_REQUIRES_USER_INTERACTION; + + const std::string provider_id = + firebase::auth::GoogleAuthProvider::kProviderId; + firebase::auth::FederatedOAuthProviderData provider_data( + provider_id, + /*scopes=*/{"https://www.googleapis.com/auth/fitness.activity.read"}, + /*custom_parameters=*/{{"req_id", "1234"}}); + firebase::auth::FederatedOAuthProvider provider(provider_data); + firebase::Future sign_in_future = + auth_->SignInWithProvider(&provider); + if (WaitForCompletion(sign_in_future, "SignInWithProvider", provider_id)) { + WaitForCompletion( + sign_in_future.result()->user->ReauthenticateWithProvider(&provider), + "ReauthenticateWithProvider", provider_id); + } + DeleteUser(); +} + +TEST_F(FirebaseAuthTest, TestSuccessfulReauthenticateWithProviderNoScopes) { + SKIP_TEST_ON_DESKTOP; + TEST_REQUIRES_USER_INTERACTION; + + const std::string provider_id = + firebase::auth::GoogleAuthProvider::kProviderId; + firebase::auth::FederatedOAuthProviderData provider_data( + provider_id, /*scopes=*/{}, /*custom_parameters=*/{{"req_id", "1234"}}); + firebase::auth::FederatedOAuthProvider provider(provider_data); + firebase::Future sign_in_future = + auth_->SignInWithProvider(&provider); + if (WaitForCompletion(sign_in_future, "SignInWithProvider", provider_id)) { + WaitForCompletion( + sign_in_future.result()->user->ReauthenticateWithProvider(&provider), + "ReauthenticateWithProvider", provider_id); + } + DeleteUser(); +} + +TEST_F(FirebaseAuthTest, + TestSuccessfulReauthenticateWithProviderNoScopesNoCustomParameters) { + SKIP_TEST_ON_DESKTOP; + TEST_REQUIRES_USER_INTERACTION; + + const std::string provider_id = + firebase::auth::GoogleAuthProvider::kProviderId; + firebase::auth::FederatedOAuthProviderData provider_data( + provider_id, /*scopes=*/{}, /*custom_parameters=*/{}); + firebase::auth::FederatedOAuthProvider provider(provider_data); + firebase::Future sign_in_future = + auth_->SignInWithProvider(&provider); + if (WaitForCompletion(sign_in_future, "SignInWithProvider", provider_id)) { + WaitForCompletion( + sign_in_future.result()->user->ReauthenticateWithProvider(&provider), + "ReauthenticateWithProvider", provider_id); + } + DeleteUser(); +} + +TEST_F(FirebaseAuthTest, TestReauthenticateWithProviderBadProviderIdFails) { + SKIP_TEST_ON_DESKTOP; + TEST_REQUIRES_USER_INTERACTION; + + const std::string provider_id = + firebase::auth::GoogleAuthProvider::kProviderId; + firebase::auth::FederatedOAuthProviderData provider_data(provider_id); + firebase::auth::FederatedOAuthProvider provider(provider_data); + firebase::Future sign_in_future = + auth_->SignInWithProvider(&provider); + if (WaitForCompletion(sign_in_future, "SignInWithProvider", provider_id)) { + provider_data.provider_id = "MadeUpProvider"; + firebase::auth::FederatedOAuthProvider provider(provider_data); + firebase::Future reauth_future = + auth_->current_user()->ReauthenticateWithProvider(&provider); + WaitForCompletion(reauth_future, "ReauthenticateWithProvider", + firebase::auth::kAuthErrorInvalidProviderId); + } + DeleteUser(); +} + +// LinkWithProvider +TEST_F(FirebaseAuthTest, TestSuccessfulLinkFederatedProviderNoScopes) { + SKIP_TEST_ON_DESKTOP; + TEST_REQUIRES_USER_INTERACTION; + WaitForCompletion(auth_->SignInAnonymously(), "SignInAnonymously"); + ASSERT_NE(auth_->current_user(), nullptr); + const std::string provider_id = + firebase::auth::GoogleAuthProvider::kProviderId; + firebase::auth::FederatedOAuthProviderData provider_data( + provider_id, /*scopes=*/{}, /*custom_parameters=*/{{"req_id", "1234"}}); + firebase::auth::FederatedOAuthProvider provider(provider_data); + firebase::Future sign_in_future = + auth_->current_user()->LinkWithProvider(&provider); + WaitForCompletion(sign_in_future, "LinkWithProvider", provider_id); + DeleteUser(); +} + +TEST_F(FirebaseAuthTest, + TestSuccessfulLinkFederatedProviderNoScopesNoCustomParameters) { + SKIP_TEST_ON_DESKTOP; + TEST_REQUIRES_USER_INTERACTION; + + WaitForCompletion(auth_->SignInAnonymously(), "SignInAnonymously"); + ASSERT_NE(auth_->current_user(), nullptr); + const std::string provider_id = + firebase::auth::GoogleAuthProvider::kProviderId; + firebase::auth::FederatedOAuthProviderData provider_data( + provider_id, /*scopes=*/{}, /*custom_parameters=*/{}); + firebase::auth::FederatedOAuthProvider provider(provider_data); + firebase::Future sign_in_future = + auth_->current_user()->LinkWithProvider(&provider); + WaitForCompletion(sign_in_future, "LinkWithProvider", provider_id); + DeleteUser(); +} + +TEST_F(FirebaseAuthTest, TestSuccessfulLinkFederatedProvider) { + SKIP_TEST_ON_DESKTOP; + TEST_REQUIRES_USER_INTERACTION; + + WaitForCompletion(auth_->SignInAnonymously(), "SignInAnonymously"); + ASSERT_NE(auth_->current_user(), nullptr); + const std::string provider_id = + firebase::auth::GoogleAuthProvider::kProviderId; + firebase::auth::FederatedOAuthProviderData provider_data( + provider_id, + /*scopes=*/{"https://www.googleapis.com/auth/fitness.activity.read"}, + /*custom_parameters=*/{{"req_id", "1234"}}); + firebase::auth::FederatedOAuthProvider provider(provider_data); + firebase::Future sign_in_future = + auth_->current_user()->LinkWithProvider(&provider); + WaitForCompletion(sign_in_future, "LinkWithProvider", provider_id); + DeleteUser(); +} + +TEST_F(FirebaseAuthTest, TestLinkFederatedProviderBadProviderIdFails) { + SKIP_TEST_ON_DESKTOP; + TEST_REQUIRES_USER_INTERACTION; + + WaitForCompletion(auth_->SignInAnonymously(), "SignInAnonymously"); + ASSERT_NE(auth_->current_user(), nullptr); + firebase::auth::FederatedOAuthProviderData provider_data( + /*provider=*/"MadeUpProvider", + /*scopes=*/{"https://www.googleapis.com/auth/fitness.activity.read"}, + /*custom_parameters=*/{{"req_id", "1234"}}); + firebase::auth::FederatedOAuthProvider provider(provider_data); + firebase::Future sign_in_future = + auth_->current_user()->LinkWithProvider(&provider); + WaitForCompletion(sign_in_future, "LinkWithProvider", + firebase::auth::kAuthErrorInvalidProviderId); + DeleteUser(); +} + +#endif // defined(ENABLE_OAUTH_TESTS) + +} // namespace firebase_testapp_automated diff --git a/testing/integration_tests/database/AndroidManifest.xml b/testing/integration_tests/database/AndroidManifest.xml new file mode 100644 index 0000000000..4a470ed5fd --- /dev/null +++ b/testing/integration_tests/database/AndroidManifest.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/testing/integration_tests/database/CMakeLists.txt b/testing/integration_tests/database/CMakeLists.txt new file mode 100644 index 0000000000..f81c859b5d --- /dev/null +++ b/testing/integration_tests/database/CMakeLists.txt @@ -0,0 +1,196 @@ +cmake_minimum_required(VERSION 2.8) + +# User settings for Firebase integration tests. +# Path to Firebase SDK. +# Try to read the path to the Firebase C++ SDK from an environment variable. +if (NOT "$ENV{FIREBASE_CPP_SDK_DIR}" STREQUAL "") + set(DEFAULT_FIREBASE_CPP_SDK_DIR "$ENV{FIREBASE_CPP_SDK_DIR}") +else() + set(DEFAULT_FIREBASE_CPP_SDK_DIR "firebase_cpp_sdk") +endif() +if ("${FIREBASE_CPP_SDK_DIR}" STREQUAL "") + set(FIREBASE_CPP_SDK_DIR ${DEFAULT_FIREBASE_CPP_SDK_DIR}) +endif() +if(NOT EXISTS ${FIREBASE_CPP_SDK_DIR}) + message(FATAL_ERROR "The Firebase C++ SDK directory does not exist: ${FIREBASE_CPP_SDK_DIR}. See the readme.md for more information") +endif() + +# Windows runtime mode, either MD or MT depending on whether you are using +# /MD or /MT. For more information see: +# https://msdn.microsoft.com/en-us/library/2kzt1wy3.aspx +set(MSVC_RUNTIME_MODE MD) + +project(firebase_testapp) + +# Integration test source files. +set(FIREBASE_APP_FRAMEWORK_SRCS + src/app_framework.cc + src/app_framework.h +) + +set(FIREBASE_TEST_FRAMEWORK_SRCS + src/firebase_test_framework.h + src/firebase_test_framework.cc +) + +set(FIREBASE_INTEGRATION_TEST_SRCS + src/integration_test.cc +) + +# The include directory for the testapp. +include_directories(src) + +# Integration test uses some features that require C++ 11, such as lambdas. +set (CMAKE_CXX_STANDARD 11) + +# Download and unpack googletest (and googlemock) at configure time +set(GOOGLETEST_ROOT ${CMAKE_CURRENT_LIST_DIR}/external/googletest) +# Note: Once googletest is downloaded once, it won't be updated or +# downloaded again unless you delete the "external/googletest" +# directory. +if (NOT EXISTS ${GOOGLETEST_ROOT}/src/googletest/src/gtest-all.cc) + configure_file(googletest.cmake + ${CMAKE_CURRENT_LIST_DIR}/external/googletest/CMakeLists.txt COPYONLY) + execute_process(COMMAND ${CMAKE_COMMAND} . + RESULT_VARIABLE result + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/external/googletest ) + if(result) + message(FATAL_ERROR "CMake step for googletest failed: ${result}") + endif() + execute_process(COMMAND ${CMAKE_COMMAND} --build . + RESULT_VARIABLE result + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/external/googletest ) + if(result) + message(FATAL_ERROR "Build step for googletest failed: ${result}") + endif() +endif() + +if(ANDROID) + # Build an Android application. + + # Source files used for the Android build. + set(FIREBASE_APP_FRAMEWORK_ANDROID_SRCS + src/android/android_app_framework.cc + ) + + # Source files used for the Android build. + set(FIREBASE_TEST_FRAMEWORK_ANDROID_SRCS + src/android/android_firebase_test_framework.cc + ) + + # Build native_app_glue as a static lib + add_library(native_app_glue STATIC + ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c) + + # Export ANativeActivity_onCreate(), + # Refer to: https://github.com/android-ndk/ndk/issues/381. + set(CMAKE_SHARED_LINKER_FLAGS + "${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate") + + add_library(gtest STATIC + ${GOOGLETEST_ROOT}/src/googletest/src/gtest-all.cc) + target_include_directories(gtest + PRIVATE ${GOOGLETEST_ROOT}/src/googletest + PUBLIC ${GOOGLETEST_ROOT}/src/googletest/include) + add_library(gmock STATIC + ${GOOGLETEST_ROOT}/src/googlemock/src/gmock-all.cc) + target_include_directories(gmock + PRIVATE ${GOOGLETEST_ROOT}/src/googletest + PRIVATE ${GOOGLETEST_ROOT}/src/googlemock + PUBLIC ${GOOGLETEST_ROOT}/src/googletest/include + PUBLIC ${GOOGLETEST_ROOT}/src/googlemock/include) + + # Define the target as a shared library, as that is what gradle expects. + set(integration_test_target_name "android_integration_test_main") + add_library(${integration_test_target_name} SHARED + ${FIREBASE_APP_FRAMEWORK_SRCS} + ${FIREBASE_APP_FRAMEWORK_ANDROID_SRCS} + ${FIREBASE_INTEGRATION_TEST_SRCS} + ${FIREBASE_TEST_FRAMEWORK_SRCS} + ${FIREBASE_TEST_FRAMEWORK_ANDROID_SRCS} + ) + + target_include_directories(${integration_test_target_name} PRIVATE + ${ANDROID_NDK}/sources/android/native_app_glue) + + set(ADDITIONAL_LIBS log android atomic native_app_glue) +else() + # Build a desktop application. + add_definitions(-D_GLIBCXX_USE_CXX11_ABI=0) + + # Prevent overriding the parent project's compiler/linker + # settings on Windows + set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) + + # Add googletest directly to our build. This defines + # the gtest and gtest_main targets. + add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/external/googletest/src + ${CMAKE_CURRENT_LIST_DIR}/external/googletest/build + EXCLUDE_FROM_ALL) + + # The gtest/gtest_main targets carry header search path + # dependencies automatically when using CMake 2.8.11 or + # later. Otherwise we have to add them here ourselves. + if (CMAKE_VERSION VERSION_LESS 2.8.11) + include_directories("${gtest_SOURCE_DIR}/include") + include_directories("${gmock_SOURCE_DIR}/include") + endif() + + # Windows runtime mode, either MD or MT depending on whether you are using + # /MD or /MT. For more information see: + # https://msdn.microsoft.com/en-us/library/2kzt1wy3.aspx + set(MSVC_RUNTIME_MODE MD) + + # Platform abstraction layer for the desktop integration test. + set(FIREBASE_APP_FRAMEWORK_DESKTOP_SRCS + src/desktop/desktop_app_framework.cc + ) + + set(integration_test_target_name "integration_test") + add_executable(${integration_test_target_name} + ${FIREBASE_APP_FRAMEWORK_SRCS} + ${FIREBASE_APP_FRAMEWORK_DESKTOP_SRCS} + ${FIREBASE_TEST_FRAMEWORK_SRCS} + ${FIREBASE_INTEGRATION_TEST_SRCS} + ) + + if(APPLE) + set(ADDITIONAL_LIBS + gssapi_krb5 + pthread + "-framework CoreFoundation" + "-framework Foundation" + "-framework GSS" + "-framework Security" + ) + elseif(MSVC) + set(ADDITIONAL_LIBS advapi32 ws2_32 crypt32 iphlpapi psapi userenv shell32) + else() + set(ADDITIONAL_LIBS pthread) + endif() + + # If a config file is present, copy it into the binary location so that it's + # possible to create the default Firebase app. + set(FOUND_JSON_FILE FALSE) + foreach(config "google-services-desktop.json" "google-services.json") + if (EXISTS ${config}) + add_custom_command( + TARGET ${integration_test_target_name} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + ${config} $) + set(FOUND_JSON_FILE TRUE) + break() + endif() + endforeach() + if(NOT FOUND_JSON_FILE) + message(WARNING "Failed to find either google-services-desktop.json or google-services.json. See the readme.md for more information.") + endif() +endif() + +# Add the Firebase libraries to the target using the function from the SDK. +add_subdirectory(${FIREBASE_CPP_SDK_DIR} bin/ EXCLUDE_FROM_ALL) +# Note that firebase_app needs to be last in the list. +set(firebase_libs firebase_database firebase_auth firebase_app) +set(gtest_libs gtest gmock) +target_link_libraries(${integration_test_target_name} ${firebase_libs} + ${gtest_libs} ${ADDITIONAL_LIBS}) diff --git a/testing/integration_tests/database/Images.xcassets/AppIcon.appiconset/Contents.json b/testing/integration_tests/database/Images.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000000..d8db8d65fd --- /dev/null +++ b/testing/integration_tests/database/Images.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "83.5x83.5", + "scale" : "2x" + }, + { + "idiom" : "ios-marketing", + "size" : "1024x1024", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/testing/integration_tests/database/Images.xcassets/LaunchImage.launchimage/Contents.json b/testing/integration_tests/database/Images.xcassets/LaunchImage.launchimage/Contents.json new file mode 100644 index 0000000000..6f870a4629 --- /dev/null +++ b/testing/integration_tests/database/Images.xcassets/LaunchImage.launchimage/Contents.json @@ -0,0 +1,51 @@ +{ + "images" : [ + { + "orientation" : "portrait", + "idiom" : "iphone", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "orientation" : "portrait", + "idiom" : "iphone", + "subtype" : "retina4", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "orientation" : "portrait", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "1x" + }, + { + "orientation" : "landscape", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "1x" + }, + { + "orientation" : "portrait", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "orientation" : "landscape", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/testing/integration_tests/database/Info.plist b/testing/integration_tests/database/Info.plist new file mode 100644 index 0000000000..a21836a7b2 --- /dev/null +++ b/testing/integration_tests/database/Info.plist @@ -0,0 +1,40 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + com.google.firebase.cpp.database.testapp + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleURLTypes + + + CFBundleTypeRole + Editor + CFBundleURLName + google + CFBundleURLSchemes + + com.googleusercontent.apps.255980362477-3a1nf8c4nl0c7hlnlnmc98hbtg2mnbue + firebase-game-loop + + + + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + + diff --git a/testing/integration_tests/database/LaunchScreen.storyboard b/testing/integration_tests/database/LaunchScreen.storyboard new file mode 100644 index 0000000000..673e0f7e68 --- /dev/null +++ b/testing/integration_tests/database/LaunchScreen.storyboard @@ -0,0 +1,7 @@ + + + + + + + diff --git a/testing/integration_tests/database/LibraryManifest.xml b/testing/integration_tests/database/LibraryManifest.xml new file mode 100644 index 0000000000..edd412c1b8 --- /dev/null +++ b/testing/integration_tests/database/LibraryManifest.xml @@ -0,0 +1,7 @@ + + + + + + diff --git a/testing/integration_tests/database/Podfile b/testing/integration_tests/database/Podfile new file mode 100644 index 0000000000..ea029c8606 --- /dev/null +++ b/testing/integration_tests/database/Podfile @@ -0,0 +1,14 @@ +source 'sso://cpdc-internal/firebase.git' +source 'https://github.com/CocoaPods/Specs.git' +platform :ios, '8.0' +# Firebase Realtime Database test application. + +target 'integration_test' do + pod 'Firebase/Database', '6.24.0' + pod 'Firebase/Auth', '6.24.0' +end + +post_install do |installer| + system("/usr/bin/python ./download_googletest.py") +end + diff --git a/testing/integration_tests/database/build.gradle b/testing/integration_tests/database/build.gradle new file mode 100644 index 0000000000..464c7d34d4 --- /dev/null +++ b/testing/integration_tests/database/build.gradle @@ -0,0 +1,65 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +buildscript { + repositories { + mavenLocal() + maven { url 'https://maven.google.com' } + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:3.2.1' + classpath 'com.google.gms:google-services:4.0.1' + } +} + +allprojects { + repositories { + mavenLocal() + maven { url 'https://maven.google.com' } + jcenter() + } +} + +apply plugin: 'com.android.application' + +android { + compileSdkVersion 28 + buildToolsVersion '28.0.3' + + sourceSets { + main { + jniLibs.srcDirs = ['libs'] + manifest.srcFile 'AndroidManifest.xml' + java.srcDirs = ['src/android/java'] + res.srcDirs = ['res'] + } + } + + defaultConfig { + applicationId 'com.google.firebase.cpp.database.testapp' + minSdkVersion 16 + targetSdkVersion 28 + versionCode 1 + versionName '1.0' + externalNativeBuild.cmake { + arguments "-DFIREBASE_CPP_SDK_DIR=$gradle.firebase_cpp_sdk_dir" + } + } + externalNativeBuild.cmake { + path 'CMakeLists.txt' + } + buildTypes { + release { + minifyEnabled true + proguardFile getDefaultProguardFile('proguard-android.txt') + proguardFile file('proguard.pro') + } + } +} + +apply from: "$gradle.firebase_cpp_sdk_dir/Android/firebase_dependencies.gradle" +firebaseCpp.dependencies { + auth + database +} + +apply plugin: 'com.google.gms.google-services' diff --git a/testing/integration_tests/database/googletest.cmake b/testing/integration_tests/database/googletest.cmake new file mode 100644 index 0000000000..0eeff685dd --- /dev/null +++ b/testing/integration_tests/database/googletest.cmake @@ -0,0 +1,19 @@ +# Download GoogleTest from GitHub as an external project. +# This CMake file is taken from: +# https://github.com/google/googletest/blob/master/googletest/README.md#incorporating-into-an-existing-cmake-project + +cmake_minimum_required(VERSION 2.8.2) + +project(googletest-download NONE) + +include(ExternalProject) +ExternalProject_Add(googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG master + SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/src" + BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/build" + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + TEST_COMMAND "" +) diff --git a/testing/integration_tests/database/gradle/wrapper/gradle-wrapper.jar b/testing/integration_tests/database/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..8c0fb64a8698b08ecc4158d828ca593c4928e9dd GIT binary patch literal 49896 zcmagFb986H(k`5d^NVfUwr$(C?M#x1ZQHiZiEVpg+jrjgoQrerx!>1o_ul)D>ebz~ zs=Mmxr&>W81QY-S1PKWQ%N-;H^tS;2*XwVA`dej1RRn1z<;3VgfE4~kaG`A%QSPsR z#ovnZe+tS9%1MfeDyz`RirvdjPRK~p(#^q2(^5@O&NM19EHdvN-A&StN>0g6QA^VN z0Gx%Gq#PD$QMRFzmK+utjS^Y1F0e8&u&^=w5K<;4Rz|i3A=o|IKLY+g`iK6vfr9?+ z-`>gmU&i?FGSL5&F?TXFu`&Js6h;15QFkXp2M1H9|Eq~bpov-GU(uz%mH0n55wUl- zv#~ccAz`F5wlQ>e_KlJS3@{)B?^v*EQM=IxLa&76^y51a((wq|2-`qON>+4dLc{Oo z51}}o^Zen(oAjxDK7b++9_Yg`67p$bPo3~BCpGM7uAWmvIhWc5Gi+gQZ|Pwa-Gll@<1xmcPy z|NZmu6m)g5Ftu~BG&Xdxclw7Cij{xbBMBn-LMII#Slp`AElb&2^Hw+w>(3crLH!;I zN+Vk$D+wP1#^!MDCiad@vM>H#6+`Ct#~6VHL4lzmy;lSdk>`z6)=>Wh15Q2)dQtGqvn0vJU@+(B5{MUc*qs4!T+V=q=wy)<6$~ z!G>e_4dN@lGeF_$q9`Ju6Ncb*x?O7=l{anm7Eahuj_6lA{*#Gv*TaJclevPVbbVYu z(NY?5q+xxbO6%g1xF0r@Ix8fJ~u)VRUp`S%&rN$&e!Od`~s+64J z5*)*WSi*i{k%JjMSIN#X;jC{HG$-^iX+5f5BGOIHWAl*%15Z#!xntpk($-EGKCzKa zT7{siZ9;4TICsWQ$pu&wKZQTCvpI$Xvzwxoi+XkkpeE&&kFb!B?h2hi%^YlXt|-@5 zHJ~%AN!g_^tmn1?HSm^|gCE#!GRtK2(L{9pL#hp0xh zME}|DB>(5)`iE7CM)&_+S}-Bslc#@B5W4_+k4Cp$l>iVyg$KP>CN?SVGZ(&02>iZK zB<^HP$g$Lq*L$BWd?2(F?-MUbNWTJVQdW7$#8a|k_30#vHAD1Z{c#p;bETk0VnU5A zBgLe2HFJ3032$G<`m*OB!KM$*sdM20jm)It5OSru@tXpK5LT>#8)N!*skNu1$TpIw zufjjdp#lyH5bZ%|Iuo|iu9vG1HrIVWLH>278xo>aVBkPN3V$~!=KnlXQ4eDqS7%E% zQ!z^$Q$b^6Q)g#cLpwur(|<0gWHo6A6jc;n`t(V9T;LzTAU{IAu*uEQ%Ort1k+Kn+f_N`9|bxYC+~Z1 zCC1UCWv*Orx$_@ydv9mIe(liLfOr7mhbV@tKw{6)q^1DH1nmvZ0cj215R<~&I<4S| zgnr;9Cdjqpz#o8i0CQjtl`}{c*P)aSdH|abxGdrR)-3z+02-eX(k*B)Uqv6~^nh** z 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 \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/testing/integration_tests/database/gradlew.bat b/testing/integration_tests/database/gradlew.bat new file mode 100644 index 0000000000..8a0b282aa6 --- /dev/null +++ b/testing/integration_tests/database/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/testing/integration_tests/database/integration_test.xcodeproj/project.pbxproj b/testing/integration_tests/database/integration_test.xcodeproj/project.pbxproj new file mode 100644 index 0000000000..d1d6b852d1 --- /dev/null +++ b/testing/integration_tests/database/integration_test.xcodeproj/project.pbxproj @@ -0,0 +1,364 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 520BC0391C869159008CFBC3 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 520BC0381C869159008CFBC3 /* GoogleService-Info.plist */; }; + 529226D61C85F68000C89379 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 529226D51C85F68000C89379 /* Foundation.framework */; }; + 529226D81C85F68000C89379 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 529226D71C85F68000C89379 /* CoreGraphics.framework */; }; + 529226DA1C85F68000C89379 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 529226D91C85F68000C89379 /* UIKit.framework */; }; + D61C5F8E22BABA9C00A79141 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D61C5F8C22BABA9B00A79141 /* Images.xcassets */; }; + D61C5F9622BABAD200A79141 /* integration_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = D61C5F9222BABAD100A79141 /* integration_test.cc */; }; + D62CCBC022F367140099BE9F /* gmock-all.cc in Sources */ = {isa = PBXBuildFile; fileRef = D62CCBBF22F367140099BE9F /* gmock-all.cc */; }; + D66B16871CE46E8900E5638A /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D66B16861CE46E8900E5638A /* LaunchScreen.storyboard */; }; + D67D355822BABD2200292C1D /* gtest-all.cc in Sources */ = {isa = PBXBuildFile; fileRef = D67D355622BABD2100292C1D /* gtest-all.cc */; }; + D6C179E922CB322900C2651A /* ios_app_framework.mm in Sources */ = {isa = PBXBuildFile; fileRef = D6C179E722CB322900C2651A /* ios_app_framework.mm */; }; + D6C179EA22CB322900C2651A /* ios_firebase_test_framework.mm in Sources */ = {isa = PBXBuildFile; fileRef = D6C179E822CB322900C2651A /* ios_firebase_test_framework.mm */; }; + D6C179EE22CB323300C2651A /* firebase_test_framework.cc in Sources */ = {isa = PBXBuildFile; fileRef = D6C179EC22CB323300C2651A /* firebase_test_framework.cc */; }; + D6C179F022CB32A000C2651A /* app_framework.cc in Sources */ = {isa = PBXBuildFile; fileRef = D6C179EF22CB32A000C2651A /* app_framework.cc */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 520BC0381C869159008CFBC3 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; + 529226D21C85F68000C89379 /* integration_test.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = integration_test.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 529226D51C85F68000C89379 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + 529226D71C85F68000C89379 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; + 529226D91C85F68000C89379 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; + 529226EE1C85F68000C89379 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; + D61C5F8C22BABA9B00A79141 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; + D61C5F8D22BABA9C00A79141 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + D61C5F9222BABAD100A79141 /* integration_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = integration_test.cc; path = src/integration_test.cc; sourceTree = ""; }; + D62CCBBF22F367140099BE9F /* gmock-all.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "gmock-all.cc"; path = "external/googletest/src/googlemock/src/gmock-all.cc"; sourceTree = ""; }; + D62CCBC122F367320099BE9F /* gmock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = gmock.h; path = external/googletest/src/googlemock/include/gmock/gmock.h; sourceTree = ""; }; + D66B16861CE46E8900E5638A /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = ""; }; + D67D355622BABD2100292C1D /* gtest-all.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "gtest-all.cc"; path = "external/googletest/src/googletest/src/gtest-all.cc"; sourceTree = ""; }; + D67D355722BABD2100292C1D /* gtest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = gtest.h; path = external/googletest/src/googletest/include/gtest/gtest.h; sourceTree = ""; }; + D6C179E722CB322900C2651A /* ios_app_framework.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ios_app_framework.mm; path = src/ios/ios_app_framework.mm; sourceTree = ""; }; + D6C179E822CB322900C2651A /* ios_firebase_test_framework.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ios_firebase_test_framework.mm; path = src/ios/ios_firebase_test_framework.mm; sourceTree = ""; }; + D6C179EB22CB323300C2651A /* firebase_test_framework.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = firebase_test_framework.h; path = src/firebase_test_framework.h; sourceTree = ""; }; + D6C179EC22CB323300C2651A /* firebase_test_framework.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = firebase_test_framework.cc; path = src/firebase_test_framework.cc; sourceTree = ""; }; + D6C179ED22CB323300C2651A /* app_framework.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = app_framework.h; path = src/app_framework.h; sourceTree = ""; }; + D6C179EF22CB32A000C2651A /* app_framework.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = app_framework.cc; path = src/app_framework.cc; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 529226CF1C85F68000C89379 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 529226D81C85F68000C89379 /* CoreGraphics.framework in Frameworks */, + 529226DA1C85F68000C89379 /* UIKit.framework in Frameworks */, + 529226D61C85F68000C89379 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 529226C91C85F68000C89379 = { + isa = PBXGroup; + children = ( + D61C5F8C22BABA9B00A79141 /* Images.xcassets */, + D61C5F8D22BABA9C00A79141 /* Info.plist */, + D66B16861CE46E8900E5638A /* LaunchScreen.storyboard */, + 520BC0381C869159008CFBC3 /* GoogleService-Info.plist */, + 5292271D1C85FB5500C89379 /* src */, + 529226D41C85F68000C89379 /* Frameworks */, + 529226D31C85F68000C89379 /* Products */, + ); + sourceTree = ""; + }; + 529226D31C85F68000C89379 /* Products */ = { + isa = PBXGroup; + children = ( + 529226D21C85F68000C89379 /* integration_test.app */, + ); + name = Products; + sourceTree = ""; + }; + 529226D41C85F68000C89379 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 529226D51C85F68000C89379 /* Foundation.framework */, + 529226D71C85F68000C89379 /* CoreGraphics.framework */, + 529226D91C85F68000C89379 /* UIKit.framework */, + 529226EE1C85F68000C89379 /* XCTest.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 5292271D1C85FB5500C89379 /* src */ = { + isa = PBXGroup; + children = ( + D62CCBC122F367320099BE9F /* gmock.h */, + D62CCBBF22F367140099BE9F /* gmock-all.cc */, + D67D355622BABD2100292C1D /* gtest-all.cc */, + D67D355722BABD2100292C1D /* gtest.h */, + D6C179EF22CB32A000C2651A /* app_framework.cc */, + D6C179ED22CB323300C2651A /* app_framework.h */, + D6C179EC22CB323300C2651A /* firebase_test_framework.cc */, + D6C179EB22CB323300C2651A /* firebase_test_framework.h */, + D61C5F9222BABAD100A79141 /* integration_test.cc */, + 5292271E1C85FB5B00C89379 /* ios */, + ); + name = src; + sourceTree = ""; + }; + 5292271E1C85FB5B00C89379 /* ios */ = { + isa = PBXGroup; + children = ( + D6C179E722CB322900C2651A /* ios_app_framework.mm */, + D6C179E822CB322900C2651A /* ios_firebase_test_framework.mm */, + ); + name = ios; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 529226D11C85F68000C89379 /* integration_test */ = { + isa = PBXNativeTarget; + buildConfigurationList = 529226F91C85F68000C89379 /* Build configuration list for PBXNativeTarget "integration_test" */; + buildPhases = ( + 529226CE1C85F68000C89379 /* Sources */, + 529226CF1C85F68000C89379 /* Frameworks */, + 529226D01C85F68000C89379 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = integration_test; + productName = testapp; + productReference = 529226D21C85F68000C89379 /* integration_test.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 529226CA1C85F68000C89379 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0640; + ORGANIZATIONNAME = Google; + TargetAttributes = { + 529226D11C85F68000C89379 = { + CreatedOnToolsVersion = 6.4; + DevelopmentTeam = EQHXZ8M8AV; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = 529226CD1C85F68000C89379 /* Build configuration list for PBXProject "integration_test" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 529226C91C85F68000C89379; + productRefGroup = 529226D31C85F68000C89379 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 529226D11C85F68000C89379 /* integration_test */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 529226D01C85F68000C89379 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D61C5F8E22BABA9C00A79141 /* Images.xcassets in Resources */, + D66B16871CE46E8900E5638A /* LaunchScreen.storyboard in Resources */, + 520BC0391C869159008CFBC3 /* GoogleService-Info.plist in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 529226CE1C85F68000C89379 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D67D355822BABD2200292C1D /* gtest-all.cc in Sources */, + D62CCBC022F367140099BE9F /* gmock-all.cc in Sources */, + D6C179EA22CB322900C2651A /* ios_firebase_test_framework.mm in Sources */, + D61C5F9622BABAD200A79141 /* integration_test.cc in Sources */, + D6C179E922CB322900C2651A /* ios_app_framework.mm in Sources */, + D6C179F022CB32A000C2651A /* app_framework.cc in Sources */, + D6C179EE22CB323300C2651A /* firebase_test_framework.cc in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 529226F71C85F68000C89379 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.4; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 529226F81C85F68000C89379 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.4; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 529226FA1C85F68000C89379 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ""; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)", + ); + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + "\"$(SRCROOT)/src\"", + "\"$(SRCROOT)/external/googletest/src/googletest/include\"", + "\"$(SRCROOT)/external/googletest/src/googlemock/include\"", + "\"$(SRCROOT)/external/googletest/src/googletest\"", + "\"$(SRCROOT)/external/googletest/src/googlemock\"", + ); + INFOPLIST_FILE = "$(SRCROOT)/Info.plist"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + WRAPPER_EXTENSION = app; + }; + name = Debug; + }; + 529226FB1C85F68000C89379 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ""; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)", + ); + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + "\"$(SRCROOT)/src\"", + "\"$(SRCROOT)/external/googletest/src/googletest/include\"", + "\"$(SRCROOT)/external/googletest/src/googlemock/include\"", + "\"$(SRCROOT)/external/googletest/src/googletest\"", + "\"$(SRCROOT)/external/googletest/src/googlemock\"", + ); + INFOPLIST_FILE = "$(SRCROOT)/Info.plist"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + WRAPPER_EXTENSION = app; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 529226CD1C85F68000C89379 /* Build configuration list for PBXProject "integration_test" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 529226F71C85F68000C89379 /* Debug */, + 529226F81C85F68000C89379 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 529226F91C85F68000C89379 /* Build configuration list for PBXNativeTarget "integration_test" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 529226FA1C85F68000C89379 /* Debug */, + 529226FB1C85F68000C89379 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 529226CA1C85F68000C89379 /* Project object */; +} diff --git a/testing/integration_tests/database/proguard.pro b/testing/integration_tests/database/proguard.pro new file mode 100644 index 0000000000..2d04b8a9a5 --- /dev/null +++ b/testing/integration_tests/database/proguard.pro @@ -0,0 +1,2 @@ +-ignorewarnings +-keep,includedescriptorclasses public class com.google.firebase.example.LoggingUtils { * ; } diff --git a/testing/integration_tests/database/readme.md b/testing/integration_tests/database/readme.md new file mode 100644 index 0000000000..6b9efa6fcd --- /dev/null +++ b/testing/integration_tests/database/readme.md @@ -0,0 +1,215 @@ +Cloud Storage for Firebase Quickstart +======================== + +The Cloud Storage for Firebase Integration Test (integration test) demonstrates +Cloud Storage operations with the Firebase C++ SDK for Cloud Storage. +The application has no user interface and simply logs actions it's performing +to the console. + +The integration test uses the GoogleTest testing framework to perform a variety +of tests on a live Firebase project. Tests include: + - Creating a firebase::App in a platform-specific way. The App holds + platform-specific context that's used by other Firebase APIs, and is a + central point for communication between the Cloud Storage C++ and + Firebase Auth C++ libraries. + - Getting a pointer to firebase::Auth, and signs in anonymously. This allows the + integration test to access a Cloud Storage instance with authentication rules + enabled, which is the default setting in Firebase Console. + - Gets a StorageReference to a test-specific storage node, uses + StorageReference::Child() to create a child with a unique key based on the + current time in microseconds to work in, and gets a reference to that child, + which the integration test will use for the remainder of its actions. + - Uploads some sample files and reads them back to ensure the storage can be + read from and written to. + - Checks the Metadata of the uploaded and downloaded files to ensure they + return the expected values for things like size and date modified. + - Disconnects and then reconnects and verifies it still has access to the + files uploaded. + - Shuts down the Cloud Storage, Firebase Auth, and Firebase App systems, + ensuring the systems can be shut down in any order. + +Introduction +------------ + +- [Read more about Cloud Storage for Firebase](https://firebase.google.com/docs/storage/) + +Building and Running the integration test +----------------------------------------- + +### iOS + - Link your iOS app to the Firebase libraries. + - Get CocoaPods version 1 or later by running, + ``` + sudo gem install cocoapods --pre + ``` + - From the integration_tests/storage directory, install the CocoaPods listed + in the Podfile by running, + ``` + pod install + ``` + - Open the generated Xcode workspace (which now has the CocoaPods), + ``` + open integration_test.xcworkspace + ``` + - For further details please refer to the + [general instructions for setting up an iOS app with Firebase](https://firebase.google.com/docs/ios/setup). + - Register your iOS app with Firebase. + - Create a new app on the [Firebase console](https://firebase.google.com/console/), and attach + your iOS app to it. + - You can use "com.google.firebase.cpp.storage.testapp" as the iOS Bundle + ID while you're testing. You can omit App Store ID while testing. + - Add the GoogleService-Info.plist that you downloaded from Firebase console + to the integration_test/storage directory. This file identifies your iOS + app to the Firebase backend. + - In the Firebase console for your app, select "Auth", then enable + "Anonymous". This will allow the integration test to use anonymous sign-in to + authenticate with Cloud Storage, which requires a signed-in user by + default (an anonymous user will suffice). + - Download the Firebase C++ SDK linked from + [https://firebase.google.com/docs/cpp/setup](https://firebase.google.com/docs/cpp/setup) + and unzip it to a directory of your choice. + - Add the following frameworks from the Firebase C++ SDK to the project: + - frameworks/ios/universal/firebase.framework + - frameworks/ios/universal/firebase_auth.framework + - frameworks/ios/universal/firebase_storage.framework + - You will need to either, + 1. Check "Copy items if needed" when adding the frameworks, or + 2. Add the framework path in "Framework Search Paths" + - e.g. If you downloaded the Firebase C++ SDK to + `/Users/me/firebase_cpp_sdk`, + then you would add the path + `/Users/me/firebase_cpp_sdk/frameworks/ios/universal`. + - To add the path, in XCode, select your project in the project + navigator, then select your target in the main window. + Select the "Build Settings" tab, and click "All" to see all + the build settings. Scroll down to "Search Paths", and add + your path to "Framework Search Paths". + - In XCode, build & run the sample on an iOS device or simulator. + - The integration test has no interativity. The output of the app can be + viewed via the console or on the device's display. In Xcode, select "View + --> Debug Area --> Activate Console" from the menu to view the console. + +### Android + - Register your Android app with Firebase. + - Create a new app on + the [Firebase console](https://firebase.google.com/console/), and attach + your Android app to it. + - You can use "com.google.firebase.cpp.storage.testapp" as the Package + Name while you're testing. + - To + [generate a SHA1](https://developers.google.com/android/guides/client-auth) + run this command on Mac and Linux, + ``` + keytool -exportcert -list -v -alias androiddebugkey -keystore ~/.android/debug.keystore + ``` + or this command on Windows, + ``` + keytool -exportcert -list -v -alias androiddebugkey -keystore %USERPROFILE%\.android\debug.keystore + ``` + - If keytool reports that you do not have a debug.keystore, you can + [create one with](http://developer.android.com/tools/publishing/app-signing.html#signing-manually), + ``` + keytool -genkey -v -keystore ~/.android/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US" + ``` + - Add the `google-services.json` file that you downloaded from Firebase + console to the integration_test/storage directory. This file identifies + your Android app to the Firebase backend. + - In the Firebase console for your app, select "Auth", then enable + "Anonymous". This will allow the integration test to use anonymous sign-in + to authenticate with Cloud Storage, which requires a signed-in user by + default (an anonymous user will suffice). + - For further details please refer to the + [general instructions for setting up an Android app with Firebase](https://firebase.google.com/docs/android/setup). + - Download the Firebase C++ SDK linked from + [https://firebase.google.com/docs/cpp/setup](https://firebase.google.com/docs/cpp/setup) + and unzip it to a directory of your choice. + - Configure the location of the Firebase C++ SDK by setting the + firebase\_cpp\_sdk.dir Gradle property to the SDK install directory. + For example, in the project directory: + ``` + echo "systemProp.firebase\_cpp\_sdk.dir=/User/$USER/firebase\_cpp\_sdk" >> gradle.properties + ``` + - Ensure the Android SDK and NDK locations are set in Android Studio. + - From the Android Studio launch menu, go to `File/Project Structure...` or + `Configure/Project Defaults/Project Structure...` + (Shortcut: Control + Alt + Shift + S on windows, Command + ";" on a mac) + and download the SDK and NDK if the locations are not yet set. + - Open *build.gradle* in Android Studio. + - From the Android Studio launch menu, "Open an existing Android Studio + project", and select `build.gradle`. + - Install the SDK Platforms that Android Studio reports missing. + - Build the integration test and run it on an Android device or emulator. + - Once you've installed the SDKs in Android Studio, you can build the + integration test in Android Studio, or from the command-line by running + `./gradlew build`. + - The integration test has no interactive interface. The output of the app can + be viewed on the device's display, or in the logcat output of Android studio + or by running "adb logcat *:W android_main firebase" from the command line. + +### Desktop + - Register your app with Firebase. + - Create a new app on the [Firebase console](https://firebase.google.com/console/), + following the above instructions for Android or iOS. + - If you have an Android project, add the `google-services.json` file that + you downloaded from the Firebase console to the integration_test/storage + directory. + - If you have an iOS project, and don't wish to use an Android project, + you can use the Python script `generate_xml_from_google_services_json.py --plist`, + located in the Firebase C++ SDK, to convert your `GoogleService-Info.plist` + file into a `google-services-desktop.json` file, which can then be + placed in the integration_test/storage directory . + - Download the Firebase C++ SDK linked from + [https://firebase.google.com/docs/cpp/setup](https://firebase.google.com/docs/cpp/setup) + and unzip it to a directory of your choice. + - Configure the integration test with the location of the Firebase C++ SDK. + This can be done a couple different ways (in highest to lowest priority): + - When invoking cmake, pass in the location with + -DFIREBASE_CPP_SDK_DIR=/path/to/firebase_cpp_sdk. + - Set an environment variable for FIREBASE_CPP_SDK_DIR to the path to use. + - Edit the CMakeLists.txt file, changing the FIREBASE_CPP_SDK_DIR path + to the appropriate location. + - From the integration_test/storage directory, generate the build files by running, + ``` + cmake . + ``` + If you want to use XCode, you can use -G"Xcode" to generate the project. + Similarly, to use Visual Studio, -G"Visual Studio 15 2017". For more + information, see + [CMake generators](https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html). + - Build the integration test, by either opening the generated project file + based on the platform, or running, + ``` + cmake --build . + ``` + - Execute the integration test by running, + ``` + ./integration_test + ``` + Note that the executable might be under another directory, such as Debug. + - The integration test has no user interface, but the output can be viewed via + the console. + +Support +------- + +[https://firebase.google.com/support/](https://firebase.google.com/support/) + +License +------- + +Copyright 2016 Google, Inc. + +Licensed to the Apache Software Foundation (ASF) under one or more contributor +license agreements. See the NOTICE file distributed with this work for +additional information regarding copyright ownership. The ASF licenses this +file to you under the Apache License, Version 2.0 (the "License"); you may not +use this file except in compliance with the License. You may obtain a copy of +the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +License for the specific language governing permissions and limitations under +the License. diff --git a/testing/integration_tests/database/res/layout/main.xml b/testing/integration_tests/database/res/layout/main.xml new file mode 100644 index 0000000000..d3ffb63082 --- /dev/null +++ b/testing/integration_tests/database/res/layout/main.xml @@ -0,0 +1,12 @@ + + + + diff --git a/testing/integration_tests/database/res/values/strings.xml b/testing/integration_tests/database/res/values/strings.xml new file mode 100644 index 0000000000..e848e9c7ba --- /dev/null +++ b/testing/integration_tests/database/res/values/strings.xml @@ -0,0 +1,4 @@ + + + Firebase Database Integration Test + diff --git a/testing/integration_tests/database/settings.gradle b/testing/integration_tests/database/settings.gradle new file mode 100644 index 0000000000..2a543b9351 --- /dev/null +++ b/testing/integration_tests/database/settings.gradle @@ -0,0 +1,36 @@ +// Copyright 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +def firebase_cpp_sdk_dir = System.getProperty('firebase_cpp_sdk.dir') +if (firebase_cpp_sdk_dir == null || firebase_cpp_sdk_dir.isEmpty()) { + firebase_cpp_sdk_dir = System.getenv('FIREBASE_CPP_SDK_DIR') + if (firebase_cpp_sdk_dir == null || firebase_cpp_sdk_dir.isEmpty()) { + if ((new File('firebase_cpp_sdk')).exists()) { + firebase_cpp_sdk_dir = 'firebase_cpp_sdk' + } else { + throw new StopActionException( + 'firebase_cpp_sdk.dir property or the FIREBASE_CPP_SDK_DIR ' + + 'environment variable must be set to reference the Firebase C++ ' + + 'SDK install directory. This is used to configure static library ' + + 'and C/C++ include paths for the SDK.') + } + } +} +if (!(new File(firebase_cpp_sdk_dir)).exists()) { + throw new StopActionException( + sprintf('Firebase C++ SDK directory %s does not exist', + firebase_cpp_sdk_dir)) +} +gradle.ext.firebase_cpp_sdk_dir = "$firebase_cpp_sdk_dir" +includeBuild "$firebase_cpp_sdk_dir" \ No newline at end of file diff --git a/testing/integration_tests/database/src/integration_test.cc b/testing/integration_tests/database/src/integration_test.cc new file mode 100644 index 0000000000..dea3126b95 --- /dev/null +++ b/testing/integration_tests/database/src/integration_test.cc @@ -0,0 +1,1210 @@ +// Copyright 2019 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include +#include +#include +#include +#include +#include + +#include "app_framework.h" // NOLINT +#include "firebase/app.h" +#include "firebase/auth.h" +#include "firebase/database.h" +#include "firebase/internal/platform.h" +#include "firebase/util.h" +#include "firebase_test_framework.h" // NOLINT + +// The TO_STRING macro is useful for command line defined strings as the quotes +// get stripped. +#define TO_STRING_EXPAND(X) #X +#define TO_STRING(X) TO_STRING_EXPAND(X) + +// Path to the Firebase config file to load. +#ifdef FIREBASE_CONFIG +#define FIREBASE_CONFIG_STRING TO_STRING(FIREBASE_CONFIG) +#else +#define FIREBASE_CONFIG_STRING "" +#endif // FIREBASE_CONFIG + +namespace firebase_testapp_automated { + +using app_framework::LogDebug; +using app_framework::LogError; +using app_framework::LogInfo; + +using app_framework::ProcessEvents; +using firebase_test_framework::FirebaseTest; + +using testing::ElementsAre; +using testing::Key; +using testing::Not; +using testing::Pair; +using testing::PrintToString; +using testing::UnorderedElementsAre; + +const char kIntegrationTestRootPath[] = "integration_test_data"; + +// Returns true if the given given timestamp is within 15 minutes of the +// expected timestamp. The value compared against must be a Variant of type +// int64 representing milliseconds since the epoch. +MATCHER_P(TimestampIsNear, expected, + std::string("Timestamp ") + (negation ? "isn't " : "is ") + "near " + + PrintToString(expected)) { + if (!arg.is_int64()) { + *result_listener << "Variant must be of type Int64, but was type " + << firebase::Variant::TypeName(arg.type()); + return false; + } + + // As long as our timestamp is within 15 minutes, it's correct enough + // for our purposes. + const int64_t kAllowedTimeDifferenceMilliseconds = 1000L * 60L * 15L; + + int64_t time_difference = arg.int64_value() - expected; + + return time_difference <= kAllowedTimeDifferenceMilliseconds && + time_difference >= -kAllowedTimeDifferenceMilliseconds; +} + +TEST(TimestampIsNear, Matcher) { + int64_t kOneMinuteInMilliseconds = 1L * 60L * 1000L; + int64_t kTenMinutesInMilliseconds = 10L * 60L * 1000L; + int64_t kTwentyMinutesInMilliseconds = 20L * 60L * 1000L; + + int64_t base_time = 1234567890; + firebase::Variant current_time = base_time; + EXPECT_THAT(current_time, TimestampIsNear(base_time)); + + int64_t one_minute_later = base_time + kOneMinuteInMilliseconds; + EXPECT_THAT(current_time, TimestampIsNear(one_minute_later)); + int64_t one_minutes_earlier = base_time - kOneMinuteInMilliseconds; + EXPECT_THAT(current_time, TimestampIsNear(one_minutes_earlier)); + + int64_t ten_minutes_later = base_time + kTenMinutesInMilliseconds; + EXPECT_THAT(current_time, TimestampIsNear(ten_minutes_later)); + int64_t ten_minutes_earlier = base_time - kTenMinutesInMilliseconds; + EXPECT_THAT(current_time, TimestampIsNear(ten_minutes_earlier)); + + int64_t twenty_minutes_later = base_time + kTwentyMinutesInMilliseconds; + EXPECT_THAT(current_time, Not(TimestampIsNear(twenty_minutes_later))); + int64_t twenty_minutes_earlier = base_time - kTwentyMinutesInMilliseconds; + EXPECT_THAT(current_time, Not(TimestampIsNear(twenty_minutes_earlier))); + + // Wrong types. + EXPECT_THAT(firebase::Variant::Null(), Not(TimestampIsNear(0))); + EXPECT_THAT(firebase::Variant::False(), Not(TimestampIsNear(0))); + EXPECT_THAT(firebase::Variant::EmptyString(), Not(TimestampIsNear(0))); +} + +class FirebaseDatabaseTest : public FirebaseTest { + public: + FirebaseDatabaseTest(); + ~FirebaseDatabaseTest() override; + + void SetUp() override; + void TearDown() override; + + protected: + // Initialize Firebase App, Firebase Auth, and Firebase Database. + void Initialize(); + // Shut down Firebase Database, Firebase Auth, and Firebase App. + void Terminate(); + // Sign in an anonymous user. + void SignIn(); + + firebase::database::DatabaseReference CreateWorkingPath( + bool suppress_cleanup = false); + + static bool first_time_; + + bool initialized_; + firebase::auth::Auth* auth_; + firebase::database::Database* database_; + + std::vector cleanup_paths_; +}; + +bool FirebaseDatabaseTest::first_time_ = true; + +FirebaseDatabaseTest::FirebaseDatabaseTest() + : initialized_(false), auth_(nullptr), database_(nullptr) { + FindFirebaseConfig(FIREBASE_CONFIG_STRING); +} + +FirebaseDatabaseTest::~FirebaseDatabaseTest() { + // Must be cleaned up on exit. + assert(app_ == nullptr); + assert(auth_ == nullptr); + assert(database_ == nullptr); +} + +void FirebaseDatabaseTest::SetUp() { + FirebaseTest::SetUp(); + Initialize(); +} + +void FirebaseDatabaseTest::TearDown() { + // Delete the shared path, if there is one. + if (initialized_) { + if (!cleanup_paths_.empty() && database_ && app_) { + LogDebug("Cleaning up..."); + std::vector> cleanups; + cleanups.reserve(cleanup_paths_.size()); + for (int i = 0; i < cleanup_paths_.size(); ++i) { + cleanups.push_back(cleanup_paths_[i].RemoveValue()); + } + for (int i = 0; i < cleanups.size(); ++i) { + std::string cleanup_name = "Cleanup (" + cleanup_paths_[i].url() + ")"; + WaitForCompletion(cleanups[i], cleanup_name.c_str()); + } + cleanup_paths_.clear(); + } + Terminate(); + } + FirebaseTest::TearDown(); +} + +void FirebaseDatabaseTest::Initialize() { + if (initialized_) return; + + InitializeApp(); + + LogDebug("Initializing Firebase Auth and Firebase Database."); + + // 0th element has a reference to this object, the rest have the initializer + // targets. + void* initialize_targets[] = {&auth_, &database_}; + + const firebase::ModuleInitializer::InitializerFn initializers[] = { + [](::firebase::App* app, void* data) { + void** targets = reinterpret_cast(data); + LogDebug("Attempting to initialize Firebase Auth."); + ::firebase::InitResult result; + *reinterpret_cast<::firebase::auth::Auth**>(targets[0]) = + ::firebase::auth::Auth::GetAuth(app, &result); + return result; + }, + [](::firebase::App* app, void* data) { + void** targets = reinterpret_cast(data); + LogDebug("Attempting to initialize Firebase Database."); + ::firebase::InitResult result; + firebase::database::Database* database = + firebase::database::Database::GetInstance(app, &result); + *reinterpret_cast<::firebase::database::Database**>(targets[1]) = + database; + return result; + }}; + + ::firebase::ModuleInitializer initializer; + initializer.Initialize(app_, initialize_targets, initializers, + sizeof(initializers) / sizeof(initializers[0])); + + WaitForCompletion(initializer.InitializeLastResult(), "Initialize"); + + ASSERT_EQ(initializer.InitializeLastResult().error(), 0) + << initializer.InitializeLastResult().error_message(); + + LogDebug("Successfully initialized Firebase Auth and Firebase Database."); + + // The first time we initialize database, enable persistence on mobile. + // We need to do this here because on iOS you can only enable persistence + // before ANY FIRDatabase instances are used. + if (first_time_) { +#if defined(ANDROID) || (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) + // TODO(b/62282752): Enable persistence on desktop. + database_->set_persistence_enabled(true); +#endif // defined(ANDROID) || (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) + first_time_ = false; + } + + initialized_ = true; +} + +void FirebaseDatabaseTest::Terminate() { + if (!initialized_) return; + + if (database_) { + LogDebug("Shutdown the Database library."); + delete database_; + database_ = nullptr; + } + if (auth_) { + LogDebug("Shutdown the Auth library."); + delete auth_; + auth_ = nullptr; + } + + TerminateApp(); + + initialized_ = false; + + ProcessEvents(100); +} + +void FirebaseDatabaseTest::SignIn() { + LogDebug("Signing in."); + firebase::Future sign_in_future = + auth_->SignInAnonymously(); + WaitForCompletion(sign_in_future, "SignInAnonymously"); + if (sign_in_future.error() != 0) { + FAIL() << "Ensure your application has the Anonymous sign-in provider " + "enabled in Firebase Console."; + } + ProcessEvents(100); +} + +firebase::database::DatabaseReference FirebaseDatabaseTest::CreateWorkingPath( + bool suppress_cleanup) { + auto ref = database_->GetReference(kIntegrationTestRootPath).PushChild(); + if (!suppress_cleanup) { + cleanup_paths_.push_back(ref); + } + return ref; +} + +// Test cases below. + +TEST_F(FirebaseDatabaseTest, TestInitializeAndTerminate) { + // Already tested via SetUp() and TearDown(). +} + +TEST_F(FirebaseDatabaseTest, TestSignIn) { + SignIn(); + EXPECT_NE(auth_->current_user(), nullptr); +} + +TEST_F(FirebaseDatabaseTest, TestCreateWorkingPath) { + SignIn(); + firebase::database::DatabaseReference working_path = CreateWorkingPath(); + LogInfo("Database URL: %s", working_path.url().c_str()); + EXPECT_TRUE(working_path.is_valid()); + EXPECT_FALSE(working_path.url().empty()); + EXPECT_EQ(working_path.url().find(database_->GetReference().url()), 0) + << "Working path URL (" << working_path.url() + << ") does not begin with root URL (" << database_->GetReference().url() + << ")"; +} + +static const char kSimpleString[] = "Some simple string"; +static const int kSimpleInt = 2; +static const int kSimplePriority = 100; +static const double kSimpleDouble = 3.4; +static const bool kSimpleBool = true; + +TEST_F(FirebaseDatabaseTest, TestSetAndGetSimpleValues) { + const char* test_name = test_info_->name(); + SignIn(); + firebase::database::DatabaseReference ref = CreateWorkingPath(); + + { + LogDebug("Setting values."); + firebase::Future f1 = + ref.Child(test_name).Child("String").SetValue(kSimpleString); + firebase::Future f2 = + ref.Child(test_name).Child("Int").SetValue(kSimpleInt); + firebase::Future f3 = + ref.Child(test_name).Child("Double").SetValue(kSimpleDouble); + firebase::Future f4 = + ref.Child(test_name).Child("Bool").SetValue(kSimpleBool); + firebase::Future f5 = + ref.Child(test_name) + .Child("Timestamp") + .SetValue(firebase::database::ServerTimestamp()); + firebase::Future f6 = + ref.Child(test_name) + .Child("IntAndPriority") + .SetValueAndPriority(kSimpleInt, kSimplePriority); + WaitForCompletion(f1, "SetSimpleString"); + WaitForCompletion(f2, "SetSimpleInt"); + WaitForCompletion(f3, "SetSimpleDouble"); + WaitForCompletion(f4, "SetSimpleBool"); + WaitForCompletion(f5, "SetSimpleTimestamp"); + WaitForCompletion(f6, "SetSimpleIntAndPriority"); + } + + // Get the values that we just set, and confirm that they match what we + // set them to. + { + LogDebug("Getting values."); + firebase::Future f1 = + ref.Child(test_name).Child("String").GetValue(); + firebase::Future f2 = + ref.Child(test_name).Child("Int").GetValue(); + firebase::Future f3 = + ref.Child(test_name).Child("Double").GetValue(); + firebase::Future f4 = + ref.Child(test_name).Child("Bool").GetValue(); + firebase::Future f5 = + ref.Child(test_name).Child("Timestamp").GetValue(); + firebase::Future f6 = + ref.Child(test_name).Child("IntAndPriority").GetValue(); + WaitForCompletion(f1, "GetSimpleString"); + WaitForCompletion(f2, "GetSimpleInt"); + WaitForCompletion(f3, "GetSimpleDouble"); + WaitForCompletion(f4, "GetSimpleBool"); + WaitForCompletion(f5, "GetSimpleTimestamp"); + WaitForCompletion(f6, "GetSimpleIntAndPriority"); + + // Get the current time to compare to the Timestamp. + int64_t current_time_milliseconds = + static_cast(time(nullptr)) * 1000L; + + EXPECT_EQ(f1.result()->value().AsString(), kSimpleString); + EXPECT_EQ(f2.result()->value().AsInt64(), kSimpleInt); + EXPECT_EQ(f3.result()->value().AsDouble(), kSimpleDouble); + EXPECT_EQ(f4.result()->value().AsBool(), kSimpleBool); + EXPECT_THAT(f5.result()->value().AsInt64(), + TimestampIsNear(current_time_milliseconds)); + EXPECT_EQ(f6.result()->value().AsInt64(), kSimpleInt); + EXPECT_EQ(f6.result()->priority().AsInt64(), kSimplePriority); + } +} + +// A ValueListener that expects a specific value to be set. +class ExpectValueListener : public firebase::database::ValueListener { + public: + explicit ExpectValueListener(const firebase::Variant& value_to_expect) + : value_to_expect_(value_to_expect), + value_changed_(false), + got_expected_value_(false) {} + void OnValueChanged( + const firebase::database::DataSnapshot& snapshot) override { + value_changed_ = true; + if (snapshot.value().AsString() == value_to_expect_.AsString()) { + got_expected_value_ = true; + } else { + LogError("ExpectValueListener did not receive the expected result."); + } + } + void OnCancelled(const firebase::database::Error& error_code, + const char* error_message) override { + LogError("ExpectValueListener canceled: %d: %s", error_code, error_message); + value_changed_ = true; + got_expected_value_ = false; + } + + bool WaitForExpectedValue() { + const int kWaitForValueSeconds = 10; + + for (int i = 0; i < kWaitForValueSeconds; i++) { + ProcessEvents(1000); + if (value_changed_) { + return got_expected_value_; + } + } + LogError("ExpectValueListener timed out."); + return got_expected_value_; + } + + private: + firebase::Variant value_to_expect_; + bool value_changed_; + bool got_expected_value_; +}; + +TEST_F(FirebaseDatabaseTest, TestReadingFromPersistanceWhileOffline) { + // TODO(b/62282752): Don't skip this test on desktop once desktop supports + // disk persistence. + SKIP_TEST_ON_DESKTOP; + + const char* test_name = test_info_->name(); + + SignIn(); + // database_->set_persistence_enabled(true); // Already set in Initialize(). + + firebase::database::DatabaseReference ref = CreateWorkingPath(true); + std::string working_url = ref.url(); + + // Write a value that we can test for. + const char* kPersistenceString = "Persistence Test!"; + WaitForCompletion(ref.Child(test_name).SetValue(kPersistenceString), + "SetValue (online)"); + + // Shut down the realtime database and restart it, to make sure that + // persistence persists across database object instances. + delete database_; + database_ = ::firebase::database::Database::GetInstance(app_); + + // Offline mode. If persistence works, we should still be able to fetch + // our value even though we're offline. + database_->GoOffline(); + ref = database_->GetReferenceFromUrl(working_url.c_str()); + + { + // Check that we can get the offline value via ValueListener. + ExpectValueListener listener(kPersistenceString); + ref.Child(test_name).AddValueListener(&listener); + ASSERT_TRUE(listener.WaitForExpectedValue()); + ref.Child(test_name).RemoveValueListener(&listener); + } + + // Check that we can get the offline value via GetValue(). + firebase::Future offline_value = + ref.Child(test_name).GetValue(); + WaitForCompletion(offline_value, "GetValue (offline)"); + EXPECT_EQ(offline_value.result()->value(), kPersistenceString); + + // Go back online. + database_->GoOnline(); + SignIn(); + + // Check the online value via GetValue(). + firebase::Future online_value = + ref.Child(test_name).GetValue(); + WaitForCompletion(online_value, "GetValue (online)"); + EXPECT_EQ(online_value.result()->value(), kPersistenceString); + // Clean up manually. + WaitForCompletion(ref.RemoveValue(), "RemoveValue"); +} + +TEST_F(FirebaseDatabaseTest, TestRunTransaction) { + const char* test_name = test_info_->name(); + + SignIn(); + firebase::database::DatabaseReference ref = CreateWorkingPath(); + + // Test running a transaction. This will call RunTransaction and set + // some values, including incrementing the player's score. + firebase::Future transaction_future; + static const int kInitialScore = 500; + // Set an initial score of 500 points. + WaitForCompletion( + ref.Child(test_name).Child("player_score").SetValue(kInitialScore), + "SetInitialScoreValue"); + // The transaction will set the player's item and class, and increment + // their score by 100 points. + int score_delta = 100; + transaction_future = ref.Child(test_name).RunTransaction( + [](firebase::database::MutableData* data, void* score_delta_void) { + LogDebug(" Transaction function executing."); + data->Child("player_item").set_value("Fire sword"); + data->Child("player_class").set_value("Warrior"); + // Increment the current score by 100. + int64_t score = + data->Child("player_score").value().AsInt64().int64_value(); + data->Child("player_score") + .set_value(score + *reinterpret_cast(score_delta_void)); + return firebase::database::kTransactionResultSuccess; + }, + &score_delta); + WaitForCompletion(transaction_future, "RunTransaction"); + + // If the transaction succeeded, let's read back the values that were + // written to confirm they match. + if (transaction_future.error() == firebase::database::kErrorNone) { + firebase::Future read_future = + ref.Child(test_name).GetValue(); + WaitForCompletion(read_future, "ReadTransactionResults"); + + const firebase::database::DataSnapshot& read_result = *read_future.result(); + EXPECT_EQ(read_result.children_count(), 3); + EXPECT_TRUE(read_result.HasChild("player_item")); + EXPECT_EQ(read_result.Child("player_item").value(), "Fire sword"); + EXPECT_TRUE(read_result.HasChild("player_class")); + EXPECT_EQ(read_result.Child("player_class").value(), "Warrior"); + EXPECT_TRUE(read_result.HasChild("player_score")); + EXPECT_EQ(read_result.Child("player_score").value().AsInt64(), + kInitialScore + score_delta); + EXPECT_EQ(read_result.value(), transaction_future.result()->value()); + } +} + +TEST_F(FirebaseDatabaseTest, TestUpdateChildren) { + const char* test_name = test_info_->name(); + + SignIn(); + + firebase::database::DatabaseReference ref = CreateWorkingPath(); + WaitForCompletion( + ref.Child(test_name).SetValue(std::map{ + {"hello", "world"}, {"apples", "oranges"}, {"puppies", "kittens"}}), + "SetValue"); + firebase::Future read_future = + ref.Child(test_name).GetValue(); + WaitForCompletion(read_future, "GetValue 1"); + EXPECT_THAT(read_future.result()->value().map(), + testing::UnorderedElementsAre(Pair("hello", "world"), + Pair("apples", "oranges"), + Pair("puppies", "kittens"))); + firebase::Future update_future = ref.Child(test_name).UpdateChildren( + {{"hello", "galaxy"}, + {"pears", "grapes"}, + {"bunnies", "birbs"}, + {"timestamp", firebase::database::ServerTimestamp()}}); + WaitForCompletion(update_future, "UpdateChildren"); + read_future = ref.Child(test_name).GetValue(); + WaitForCompletion(read_future, "GetValue 2"); + int64_t current_time_milliseconds = + static_cast(time(nullptr)) * 1000L; + EXPECT_THAT( + read_future.result()->value().map(), + UnorderedElementsAre( + Pair("apples", "oranges"), Pair("puppies", "kittens"), + Pair("hello", "galaxy"), Pair("pears", "grapes"), + Pair("bunnies", "birbs"), + Pair("timestamp", TimestampIsNear(current_time_milliseconds)))); +} + +TEST_F(FirebaseDatabaseTest, TestQueryFiltering) { + const char* test_name = test_info_->name(); + + // Set up an initial corpus of data that we can then query filter. + // This test exercises the following methods on Query: + // OrderByChild, OrderByKey, OrderByPriority, OrderByValue + // StartAt, EndAt, EqualTo + // LimitToFirst, LimitToLast + firebase::Variant initial_data = std::map{ + {"apple", 1}, + {"banana", "two"}, + {"durian", + std::map{{"subkey", 3}, + {"value", "delicious"}}}, + {"fig", 3.3}, + {"cranberry", + std::map{{"subkey", 500}, {"value", 9}}}, + {"eggplant", + std::map{{"subkey", 100}, + {"value", "purple"}}}, + {"gooseberry", "honk"}}; + + SignIn(); + + firebase::database::DatabaseReference ref = CreateWorkingPath(); + // On mobile, keep this path synchronized to work around persistence issues. +#if defined(ANDROID) || (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) + ref.SetKeepSynchronized(true); +#endif // defined(ANDROID) || (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) + WaitForCompletion(ref.Child(test_name).SetValue(initial_data), "SetValue"); + WaitForCompletion(ref.Child(test_name).Child("cranberry").SetPriority(100), + "SetPriority 1"); + WaitForCompletion(ref.Child(test_name).Child("banana").SetPriority(-100), + "SetPriority 2"); + WaitForCompletion(ref.Child(test_name).Child("eggplant").SetPriority(200), + "SetPriority 3"); + WaitForCompletion(ref.Child(test_name).Child("gooseberry").SetPriority(300), + "SetPriority 4"); + { + firebase::Future initial_read = + ref.Child(test_name).GetValue(); + WaitForCompletion(initial_read, "GetValue (initial)"); + EXPECT_EQ(initial_read.result()->value(), initial_data); + EXPECT_THAT( + initial_read.result()->value().map(), + UnorderedElementsAre(Key("apple"), Key("banana"), Key("cranberry"), + Key("durian"), Key("eggplant"), Key("fig"), + Key("gooseberry"))); + } + + // First, try ordering by key. Use LimitToFirst/Last to make sure we get the + // first few or last values (even though std::map will always be sorted by + // key). + { + firebase::Future order_by_key = + ref.Child(test_name).OrderByKey().LimitToFirst(3).GetValue(); + WaitForCompletion(order_by_key, "GetValue (OrderByKey)"); + EXPECT_THAT( + order_by_key.result()->value().map(), + UnorderedElementsAre(Key("apple"), Key("banana"), Key("cranberry"))); + } + { + firebase::Future order_by_child = + ref.Child(test_name).OrderByChild("subkey").LimitToLast(3).GetValue(); + WaitForCompletion(order_by_child, "GetValue (OrderByChild)"); + EXPECT_THAT( + order_by_child.result()->value().map(), + UnorderedElementsAre(Key("cranberry"), Key("durian"), Key("eggplant"))); + } + { + firebase::Future order_by_priority = + ref.Child(test_name).OrderByPriority().LimitToLast(3).GetValue(); + WaitForCompletion(order_by_priority, "GetValue (OrderByPriority)"); + EXPECT_THAT(order_by_priority.result()->value().map(), + UnorderedElementsAre(Key("cranberry"), Key("eggplant"), + Key("gooseberry"))); + } + { + firebase::Future order_by_value = + ref.Child(test_name).OrderByValue().LimitToFirst(3).GetValue(); + WaitForCompletion(order_by_value, "GetValue (OrderByValue)"); + EXPECT_THAT( + order_by_value.result()->value().map(), + UnorderedElementsAre(Key("apple"), Key("fig"), Key("gooseberry"))); + } + + // Try StartAt, EndAt, EqualTo. + { + firebase::Future start_at = + ref.Child(test_name).OrderByKey().StartAt("d").GetValue(); + WaitForCompletion(start_at, "GetValue (StartAt)"); + EXPECT_THAT(start_at.result()->value().map(), + UnorderedElementsAre(Key("durian"), Key("eggplant"), Key("fig"), + Key("gooseberry"))); + } + { + firebase::Future end_at = + ref.Child(test_name).OrderByKey().EndAt("f").GetValue(); + WaitForCompletion(end_at, "GetValue (EndAt)"); + EXPECT_THAT( + end_at.result()->value().map(), + UnorderedElementsAre(Key("apple"), Key("banana"), Key("cranberry"), + Key("durian"), Key("eggplant"))); + } + { + firebase::Future start_and_end_at = + ref.Child(test_name).OrderByKey().StartAt("c").EndAt("f").GetValue(); + WaitForCompletion(start_and_end_at, "GetValue (StartAndEndAt)"); + EXPECT_THAT( + start_and_end_at.result()->value().map(), + UnorderedElementsAre(Key("cranberry"), Key("durian"), Key("eggplant"))); + } + { + firebase::Future equal_to = + ref.Child(test_name).OrderByKey().EqualTo("fig").GetValue(); + WaitForCompletion(equal_to, "GetValue (EqualTo)"); + EXPECT_THAT(equal_to.result()->value().map(), + UnorderedElementsAre(Key("fig"))); + } +} + +// A simple ValueListener that logs the values it sees. +class LoggingValueListener : public firebase::database::ValueListener { + public: + LoggingValueListener() : got_error_(false) {} + void OnValueChanged( + const firebase::database::DataSnapshot& snapshot) override { + LogDebug(" ValueListener.OnValueChanged(%s)", + FirebaseTest::VariantToString(snapshot.value()).c_str()); + last_seen_value_ = snapshot.value(); + seen_values_.push_back(snapshot.value()); + } + void OnCancelled(const firebase::database::Error& error_code, + const char* error_message) override { + LogError("ValueListener got error: %d: %s", error_code, error_message); + got_error_ = true; + } + const firebase::Variant& last_seen_value() { return last_seen_value_; } + bool has_seen_value(const firebase::Variant& value) { + return std::find(seen_values_.begin(), seen_values_.end(), value) != + seen_values_.end(); + } + size_t num_seen_values() { return seen_values_.size(); } + + bool got_error() { return got_error_; } + + private: + firebase::Variant last_seen_value_; + std::vector seen_values_; + bool got_error_; +}; + +TEST_F(FirebaseDatabaseTest, TestValueListener) { + const char* test_name = test_info_->name(); + + SignIn(); + + firebase::database::DatabaseReference ref = CreateWorkingPath(); + WaitForCompletion(ref.Child(test_name).SetValue(0), "SetValue"); + LoggingValueListener* listener = new LoggingValueListener(); + + // Attach the listener, then set 3 values, which will trigger the + // listener. + ref.Child(test_name).AddValueListener(listener); + + // The listener's OnChanged callback is triggered once when the listener is + // attached and again every time the data, including children, changes. + // Wait for here for a moment for the initial values to be received. + ProcessEvents(1000); + + WaitForCompletion(ref.Child(test_name).SetValue(1), "SetValue 1"); + WaitForCompletion(ref.Child(test_name).SetValue(2), "SetValue 2"); + WaitForCompletion(ref.Child(test_name).SetValue(3), "SetValue 3"); + + // Wait a moment for the value listener to be triggered. + ProcessEvents(1000); + + ref.Child(test_name).RemoveValueListener(listener); + // Ensure that the listener is not triggered once removed. + WaitForCompletion(ref.Child(test_name).SetValue(4), "SetValue 4"); + + // Wait a moment to ensure the listener is not triggered. + ProcessEvents(1000); + + EXPECT_FALSE(listener->got_error()); + // Ensure that the listener was only triggered 4 times, with the values + // 0 (the initial value), 1, 2, and 3. The value 4 should not be present. + EXPECT_EQ(listener->num_seen_values(), 4); + EXPECT_TRUE(listener->has_seen_value(0)); + EXPECT_TRUE(listener->has_seen_value(1)); + EXPECT_TRUE(listener->has_seen_value(2)); + EXPECT_TRUE(listener->has_seen_value(3)); + EXPECT_FALSE(listener->has_seen_value(4)); + + delete listener; +} + +// An simple ChildListener class that simply logs the events it sees. +class LoggingChildListener : public firebase::database::ChildListener { + public: + LoggingChildListener() : got_error_(false) {} + + void OnChildAdded(const firebase::database::DataSnapshot& snapshot, + const char* previous_sibling) override { + LogDebug(" ChildListener.OnChildAdded(%s)", snapshot.key()); + events_.push_back(std::string("added ") + snapshot.key()); + } + void OnChildChanged(const firebase::database::DataSnapshot& snapshot, + const char* previous_sibling) override { + LogDebug(" ChildListener.OnChildChanged(%s)", snapshot.key()); + events_.push_back(std::string("changed ") + snapshot.key()); + } + void OnChildMoved(const firebase::database::DataSnapshot& snapshot, + const char* previous_sibling) override { + LogDebug(" ChildListener.OnChildMoved(%s)", snapshot.key()); + events_.push_back(std::string("moved ") + snapshot.key()); + } + void OnChildRemoved( + const firebase::database::DataSnapshot& snapshot) override { + LogDebug(" ChildListener.OnChildRemoved(%s)", snapshot.key()); + events_.push_back(std::string("removed ") + snapshot.key()); + } + void OnCancelled(const firebase::database::Error& error_code, + const char* error_message) override { + LogError("ChildListener got error: %d: %s", error_code, error_message); + got_error_ = true; + } + + const std::vector& events() { return events_; } + + // Get the total number of Child events this listener saw. + size_t total_events() { return events_.size(); } + + // Get the number of times this event was seen. + int num_events(const std::string& event) { + int count = 0; + for (int i = 0; i < events_.size(); i++) { + if (events_[i] == event) { + count++; + } + } + return count; + } + bool got_error() { return got_error_; } + + public: + // Vector of strings defining the events we saw, in order. + std::vector events_; + bool got_error_; +}; + +TEST_F(FirebaseDatabaseTest, TestChildListener) { + const char* test_name = test_info_->name(); + + SignIn(); + + firebase::database::DatabaseReference ref = CreateWorkingPath(); + + LoggingChildListener* listener = new LoggingChildListener(); + ref.Child(test_name) + .OrderByChild("entity_type") + .EqualTo("enemy") + .AddChildListener(listener); + + // The listener's OnChanged callback is triggered once when the listener is + // attached and again every time the data, including children, changes. + // Wait for here for a moment for the initial values to be received. + ProcessEvents(1000); + + EXPECT_FALSE(listener->got_error()); + + // Perform a series of operations that we will then verify with the + // ChildListener's event log. + std::map params; + params["entity_name"] = "cobra"; + params["entity_type"] = "enemy"; + WaitForCompletion( + ref.Child(test_name).Child("0").SetValueAndPriority(params, 0), + "SetEntity0"); // added 0 + params["entity_name"] = "warrior"; + params["entity_type"] = "hero"; + WaitForCompletion( + ref.Child(test_name).Child("1").SetValueAndPriority(params, 10), + "SetEntity1"); // no event + params["entity_name"] = "wizard"; + params["entity_type"] = "hero"; + WaitForCompletion( + ref.Child(test_name).Child("2").SetValueAndPriority(params, 20), + "SetEntity2"); // no event + params["entity_name"] = "rat"; + params["entity_type"] = "enemy"; + WaitForCompletion( + ref.Child(test_name).Child("3").SetValueAndPriority(params, 30), + "SetEntity3"); // added 3 + params["entity_name"] = "thief"; + params["entity_type"] = "enemy"; + WaitForCompletion( + ref.Child(test_name).Child("4").SetValueAndPriority(params, 40), + "SetEntity4"); // added 4 + params["entity_name"] = "paladin"; + params["entity_type"] = "hero"; + WaitForCompletion( + ref.Child(test_name).Child("5").SetValueAndPriority(params, 50), + "SetEntity5"); // no event + params["entity_name"] = "ghost"; + params["entity_type"] = "enemy"; + WaitForCompletion( + ref.Child(test_name).Child("6").SetValueAndPriority(params, 60), + "SetEntity6"); // added 6 + params["entity_name"] = "dragon"; + params["entity_type"] = "enemy"; + WaitForCompletion( + ref.Child(test_name).Child("7").SetValueAndPriority(params, 70), + "SetEntity7"); // added 7 + // Now the thief becomes a hero! + WaitForCompletion( + ref.Child(test_name).Child("4").Child("entity_type").SetValue("hero"), + "SetEntity4Type"); + // Now the dragon becomes a super-dragon! + WaitForCompletion(ref.Child(test_name) + .Child("7") + .Child("entity_name") + .SetValue("super-dragon"), + "SetEntity7Name"); // changed 7 + // Now the super-dragon becomes an mega-dragon! + WaitForCompletion(ref.Child(test_name) + .Child("7") + .Child("entity_name") + .SetValue("mega-dragon"), + "SetEntity7NameAgain"); // changed 7 + // And now we change a hero entity, which the Query ignores. + WaitForCompletion(ref.Child(test_name) + .Child("2") + .Child("entity_name") + .SetValue("super-wizard"), + "SetEntity2Value"); // no event + // Now poof, the mega-dragon is gone. + WaitForCompletion(ref.Child(test_name).Child("7").RemoveValue(), + "RemoveEntity7"); // removed 7 + + // Wait a few seconds for the child listener to be triggered. + ProcessEvents(1000); + // Unregister the listener, so it stops triggering. + ref.Child(test_name) + .OrderByChild("entity_type") + .EqualTo("enemy") + .RemoveChildListener(listener); + // Wait a few seconds for the child listener to finish up. + ProcessEvents(1000); + + // Make one more change, to ensure the listener has been removed. + WaitForCompletion(ref.Child(test_name).Child("6").SetPriority(0), + "SetEntity6Priority"); + // We are expecting to have the following events: + EXPECT_THAT(listener->events(), + ElementsAre("added 0", "added 3", "added 4", "added 6", "added 7", + "removed 4", "changed 7", "changed 7", "removed 7")); + delete listener; +} + +TEST_F(FirebaseDatabaseTest, TestOnDisconnect) { + const char* test_name = test_info_->name(); + + SignIn(); + firebase::database::DatabaseReference ref = CreateWorkingPath(); + std::string saved_url = ref.url(); + + // Set up some ondisconnect handlers to set several values. + WaitForCompletion( + ref.Child(test_name).Child("SetValueTo1").OnDisconnect()->SetValue(1), + "OnDisconnectSetValue1"); + WaitForCompletion(ref.Child(test_name) + .Child("SetValue2Priority3") + .OnDisconnect() + ->SetValueAndPriority(2, 3), + "OnDisconnect (SetValue2Priority3)"); + WaitForCompletion(ref.Child(test_name) + .Child("SetValueButThenCancel") + .OnDisconnect() + ->SetValue("Going to cancel this"), + "OnDisconnect (SetValueToCancel)"); + WaitForCompletion(ref.Child(test_name) + .Child("SetValueButThenCancel") + .OnDisconnect() + ->Cancel(), + "OnDisconnect (Cancel)"); + // Set a value that we will then remove on disconnect. + WaitForCompletion( + ref.Child(test_name).Child("RemoveValue").SetValue("Will be removed"), + "SetValue (RemoveValue)"); + WaitForCompletion( + ref.Child(test_name).Child("RemoveValue").OnDisconnect()->RemoveValue(), + "OnDisconnect (RemoveValue)"); + // Set up a map to pass to OnDisconnect()->UpdateChildren(). + std::map children; + children.insert(std::make_pair("one", 1)); + children.insert(std::make_pair("two", 2)); + children.insert(std::make_pair("three", 3)); + WaitForCompletion(ref.Child(test_name) + .Child("UpdateChildren") + .OnDisconnect() + ->UpdateChildren(children), + "OnDisconnect (UpdateChildren)"); + + // Set up a listener to wait for the ondisconnect action to occur. + ExpectValueListener* listener = new ExpectValueListener(1); + ref.Child(test_name).Child("SetValueTo1").AddValueListener(listener); + LogDebug("Disconnecting..."); + database_->GoOffline(); + + listener->WaitForExpectedValue(); + ref.Child(test_name).Child("SetValueTo1").RemoveValueListener(listener); + + // Let go of the reference we already had. + ref = firebase::database::DatabaseReference(); + + delete listener; + listener = nullptr; + + LogDebug("Reconnecting..."); + database_->GoOnline(); + + /// Check that the DisconnectionHandler actions were performed. + /// Get a brand new reference to the location to be sure. + ref = database_->GetReferenceFromUrl(saved_url.c_str()); + firebase::Future future = + ref.Child(test_name).GetValue(); + WaitForCompletion(future, "GetValue (OnDisconnectChanges)"); + const firebase::database::DataSnapshot& result = *future.result(); + EXPECT_TRUE(result.HasChild("SetValueTo1")); + EXPECT_EQ(result.Child("SetValueTo1").value(), 1); + EXPECT_TRUE(result.HasChild("SetValue2Priority3")); + EXPECT_EQ(result.Child("SetValue2Priority3").value(), 2); + EXPECT_EQ(result.Child("SetValue2Priority3").priority().AsInt64(), 3); + EXPECT_FALSE(result.HasChild("RemoveValue")); + EXPECT_FALSE(result.HasChild("SetValueButThenCancel")); + EXPECT_TRUE(result.HasChild("UpdateChildren")); + EXPECT_THAT( + result.Child("UpdateChildren").value().map(), + UnorderedElementsAre(Pair("one", 1), Pair("two", 2), Pair("three", 3))); +} + +TEST_F(FirebaseDatabaseTest, TestInvalidatingReferencesWhenDeletingDatabase) { + SignIn(); + + // Create a file so we can get its metadata and check that it's properly + // invalidated. + firebase::database::DatabaseReference ref = CreateWorkingPath(); + cleanup_paths_.erase(std::find(cleanup_paths_.begin(), cleanup_paths_.end(), + ref)); // We'll remove this path manually. + firebase::database::Query query = ref.LimitToFirst(10); + firebase::Future set_future = + ref.Child("Invalidating").SetValue(kSimpleString); + WaitForCompletion(set_future, "SetValue"); + firebase::Future get_future = + ref.Child("Invalidating").GetValue(); + WaitForCompletion(get_future, "GetValue"); + firebase::database::DataSnapshot snapshot = *get_future.result(); + firebase::Future delete_future = + ref.Child("Invalidating").RemoveValue(); + WaitForCompletion(delete_future, "RemoveValue"); + + EXPECT_TRUE(ref.is_valid()); + EXPECT_TRUE(query.is_valid()); + EXPECT_TRUE(snapshot.is_valid()); + EXPECT_NE(set_future.status(), firebase::kFutureStatusInvalid); + EXPECT_NE(get_future.status(), firebase::kFutureStatusInvalid); + EXPECT_NE(delete_future.status(), firebase::kFutureStatusInvalid); + + delete database_; + database_ = nullptr; + + EXPECT_FALSE(ref.is_valid()); + EXPECT_FALSE(query.is_valid()); + EXPECT_FALSE(snapshot.is_valid()); + EXPECT_EQ(set_future.status(), firebase::kFutureStatusInvalid); + EXPECT_EQ(get_future.status(), firebase::kFutureStatusInvalid); + EXPECT_EQ(delete_future.status(), firebase::kFutureStatusInvalid); +} + +TEST_F(FirebaseDatabaseTest, TestInvalidatingReferencesWhenDeletingApp) { + SignIn(); + + // Create a file so we can get its metadata and check that it's properly + // invalidated. + firebase::database::DatabaseReference ref = CreateWorkingPath(true); + firebase::database::Query query = ref.LimitToFirst(10); + firebase::Future set_future = + ref.Child("Invalidating").SetValue(kSimpleString); + WaitForCompletion(set_future, "SetValue"); + firebase::Future get_future = + ref.Child("Invalidating").GetValue(); + WaitForCompletion(get_future, "GetValue"); + firebase::database::DataSnapshot snapshot = *get_future.result(); + firebase::Future delete_future = + ref.Child("Invalidating").SetValue(firebase::Variant::Null()); + WaitForCompletion(delete_future, "DeleteValue"); + + EXPECT_TRUE(ref.is_valid()); + EXPECT_TRUE(query.is_valid()); + EXPECT_TRUE(snapshot.is_valid()); + EXPECT_NE(set_future.status(), firebase::kFutureStatusInvalid); + EXPECT_NE(get_future.status(), firebase::kFutureStatusInvalid); + EXPECT_NE(delete_future.status(), firebase::kFutureStatusInvalid); + + // Deleting App should invalidate all the objects and Futures, same as + // deleting Database. + delete app_; + app_ = nullptr; + + EXPECT_FALSE(ref.is_valid()); + EXPECT_FALSE(query.is_valid()); + EXPECT_FALSE(snapshot.is_valid()); + EXPECT_EQ(set_future.status(), firebase::kFutureStatusInvalid); + EXPECT_EQ(get_future.status(), firebase::kFutureStatusInvalid); + EXPECT_EQ(delete_future.status(), firebase::kFutureStatusInvalid); +} + +TEST_F(FirebaseDatabaseTest, TestInfoConnected) { + SignIn(); + firebase::database::DatabaseReference ref = CreateWorkingPath(); + // Force getting a value so that we are connected to the database. + WaitForCompletion(ref.GetValue(), "GetValue 1 [ignored]"); + firebase::database::DatabaseReference info = + database_->GetReference(".info").Child("connected"); + { + auto connected = info.GetValue(); + WaitForCompletion(connected, "GetValue 2"); + EXPECT_EQ(connected.result()->value(), true); + } + LogDebug("Disconnecting..."); + database_->GoOffline(); + { + auto disconnected = info.GetValue(); + WaitForCompletion(disconnected, "GetValue 3"); + EXPECT_EQ(disconnected.result()->value(), false); + } + LogDebug("Reconnecting..."); + database_->GoOnline(); + ProcessEvents(1000); + // Force getting a value so that we reconnect to the database. + WaitForCompletion(ref.GetValue(), "GetValue 4 [ignored]"); + { + auto reconnected = info.GetValue(); + WaitForCompletion(reconnected, "GetValue 5"); + EXPECT_EQ(reconnected.result()->value(), true); + } +} + +TEST_F(FirebaseDatabaseTest, TestGetReferenceWillNullArgument) { + EXPECT_FALSE(database_->GetReference(nullptr).is_valid()); +} + +TEST_F(FirebaseDatabaseTest, TestGetReferenceFromUrlWithNullArgument) { + EXPECT_FALSE(database_->GetReferenceFromUrl(nullptr).is_valid()); +} + +TEST_F(FirebaseDatabaseTest, TestDatabaseReferenceChildWithNullArgument) { + firebase::database::DatabaseReference ref = + database_->GetReference("Nothing/will/be/uploaded/here"); + EXPECT_FALSE(ref.Child(nullptr).is_valid()); +} + +TEST_F(FirebaseDatabaseTest, TestDataSnapshotChildWithNullArgument) { + firebase::database::DatabaseReference ref = + database_->GetReference("Nothing/will/be/uploaded/here"); + firebase::Future future = ref.GetValue(); + WaitForCompletion(future, "ref.GetValue()"); + const firebase::database::DataSnapshot* snapshot = future.result(); + EXPECT_FALSE(snapshot->Child(nullptr).is_valid()); +} + +TEST_F(FirebaseDatabaseTest, TestDataSnapshotHasChildWithNullArgument) { + firebase::database::DatabaseReference ref = + database_->GetReference("Nothing/will/be/uploaded/here"); + firebase::Future future = ref.GetValue(); + WaitForCompletion(future, "ref.GetValue()"); + const firebase::database::DataSnapshot* snapshot = future.result(); + EXPECT_FALSE(snapshot->HasChild(nullptr)); +} + +TEST_F(FirebaseDatabaseTest, TestMutableDataChildWithNullArgument) { + firebase::database::DatabaseReference ref = + database_->GetReference("Nothing/will/be/uploaded/here"); + firebase::Future transaction_future; + transaction_future = ref.RunTransaction( + [](firebase::database::MutableData* data, void*) { + // This is the best way we have to check validity of MutableData as we + // don't currently expose an is_valid method. + EXPECT_EQ(data->Child(nullptr).value(), firebase::Variant::Null()); + return firebase::database::kTransactionResultSuccess; + }, + nullptr); + WaitForCompletion(transaction_future, "RunTransaction"); +} + +TEST_F(FirebaseDatabaseTest, TestMutableDataHasChildWithNullArgument) { + firebase::database::DatabaseReference ref = + database_->GetReference("Nothing/will/be/uploaded/here"); + firebase::Future transaction_future; + transaction_future = ref.RunTransaction( + [](firebase::database::MutableData* data, void*) { + EXPECT_FALSE(data->HasChild(nullptr)); + return firebase::database::kTransactionResultSuccess; + }, + nullptr); + WaitForCompletion(transaction_future, "RunTransaction"); +} + +TEST_F(FirebaseDatabaseTest, TestQueryOrderByChildWithNullArgument) { + firebase::database::DatabaseReference ref = + database_->GetReference("Nothing/will/be/uploaded/here"); + EXPECT_FALSE(ref.OrderByChild(nullptr).is_valid()); +} + +TEST_F(FirebaseDatabaseTest, TestQueryStartAtWithNullArgument) { + firebase::database::DatabaseReference ref = + database_->GetReference("Nothing/will/be/uploaded/here"); + EXPECT_FALSE( + ref.StartAt(firebase::Variant("SomeString"), nullptr).is_valid()); +} + +TEST_F(FirebaseDatabaseTest, TestQueryEndAtWithNullArgument) { + firebase::database::DatabaseReference ref = + database_->GetReference("Nothing/will/be/uploaded/here"); + EXPECT_FALSE(ref.EndAt(firebase::Variant("SomeString"), nullptr).is_valid()); +} + +TEST_F(FirebaseDatabaseTest, TestQueryEqualToWithNullArgument) { + firebase::database::DatabaseReference ref = + database_->GetReference("Nothing/will/be/uploaded/here"); + EXPECT_FALSE( + ref.EqualTo(firebase::Variant("SomeString"), nullptr).is_valid()); +} + +TEST_F(FirebaseDatabaseTest, TestValueListenerWithNullArgument) { + firebase::database::DatabaseReference ref = + database_->GetReference("Nothing/will/be/uploaded/here"); + ref.AddValueListener(nullptr); +} + +TEST_F(FirebaseDatabaseTest, TestChildListenerWithNullArgument) { + firebase::database::DatabaseReference ref = + database_->GetReference("Nothing/will/be/uploaded/here"); + ref.AddChildListener(nullptr); +} + +} // namespace firebase_testapp_automated diff --git a/testing/integration_tests/dynamic_links/AndroidManifest.xml b/testing/integration_tests/dynamic_links/AndroidManifest.xml new file mode 100644 index 0000000000..40b00fd352 --- /dev/null +++ b/testing/integration_tests/dynamic_links/AndroidManifest.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/testing/integration_tests/dynamic_links/CMakeLists.txt b/testing/integration_tests/dynamic_links/CMakeLists.txt new file mode 100644 index 0000000000..306672c294 --- /dev/null +++ b/testing/integration_tests/dynamic_links/CMakeLists.txt @@ -0,0 +1,197 @@ +cmake_minimum_required(VERSION 2.8) + +# User settings for Firebase integration tests. +# Path to Firebase SDK. +# Try to read the path to the Firebase C++ SDK from an environment variable. +if (NOT "$ENV{FIREBASE_CPP_SDK_DIR}" STREQUAL "") + set(DEFAULT_FIREBASE_CPP_SDK_DIR "$ENV{FIREBASE_CPP_SDK_DIR}") +else() + set(DEFAULT_FIREBASE_CPP_SDK_DIR "firebase_cpp_sdk") +endif() +if ("${FIREBASE_CPP_SDK_DIR}" STREQUAL "") + set(FIREBASE_CPP_SDK_DIR ${DEFAULT_FIREBASE_CPP_SDK_DIR}) +endif() +if(NOT EXISTS ${FIREBASE_CPP_SDK_DIR}) + message(FATAL_ERROR "The Firebase C++ SDK directory does not exist: ${FIREBASE_CPP_SDK_DIR}. See the readme.md for more information") +endif() + +# Windows runtime mode, either MD or MT depending on whether you are using +# /MD or /MT. For more information see: +# https://msdn.microsoft.com/en-us/library/2kzt1wy3.aspx +set(MSVC_RUNTIME_MODE MD) + +project(firebase_testapp) + +# Integration test source files. +set(FIREBASE_APP_FRAMEWORK_SRCS + src/app_framework.cc + src/app_framework.h +) + +set(FIREBASE_TEST_FRAMEWORK_SRCS + src/firebase_test_framework.h + src/firebase_test_framework.cc +) + +set(FIREBASE_INTEGRATION_TEST_SRCS + src/integration_test.cc +) + +# The include directory for the testapp. +include_directories(src) + +# Integration test uses some features that require C++ 11, such as lambdas. +set (CMAKE_CXX_STANDARD 11) + +# Download and unpack googletest (and googlemock) at configure time +set(GOOGLETEST_ROOT ${CMAKE_CURRENT_LIST_DIR}/external/googletest) +# Note: Once googletest is downloaded once, it won't be updated or +# downloaded again unless you delete the "external/googletest" +# directory. +if (NOT EXISTS ${GOOGLETEST_ROOT}/src/googletest/src/gtest-all.cc) + configure_file(googletest.cmake + ${CMAKE_CURRENT_LIST_DIR}/external/googletest/CMakeLists.txt COPYONLY) + execute_process(COMMAND ${CMAKE_COMMAND} . + RESULT_VARIABLE result + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/external/googletest ) + if(result) + message(FATAL_ERROR "CMake step for googletest failed: ${result}") + endif() + execute_process(COMMAND ${CMAKE_COMMAND} --build . + RESULT_VARIABLE result + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/external/googletest ) + if(result) + message(FATAL_ERROR "Build step for googletest failed: ${result}") + endif() +endif() + +if(ANDROID) + # Build an Android application. + + # Source files used for the Android build. + set(FIREBASE_APP_FRAMEWORK_ANDROID_SRCS + src/android/android_app_framework.cc + ) + + # Source files used for the Android build. + set(FIREBASE_TEST_FRAMEWORK_ANDROID_SRCS + src/android/android_firebase_test_framework.cc + ) + + # Build native_app_glue as a static lib + add_library(native_app_glue STATIC + ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c) + + # Export ANativeActivity_onCreate(), + # Refer to: https://github.com/android-ndk/ndk/issues/381. + set(CMAKE_SHARED_LINKER_FLAGS + "${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate") + + add_library(gtest STATIC + ${GOOGLETEST_ROOT}/src/googletest/src/gtest-all.cc) + target_include_directories(gtest + PRIVATE ${GOOGLETEST_ROOT}/src/googletest + PUBLIC ${GOOGLETEST_ROOT}/src/googletest/include) + add_library(gmock STATIC + ${GOOGLETEST_ROOT}/src/googlemock/src/gmock-all.cc) + target_include_directories(gmock + PRIVATE ${GOOGLETEST_ROOT}/src/googletest + PRIVATE ${GOOGLETEST_ROOT}/src/googlemock + PUBLIC ${GOOGLETEST_ROOT}/src/googletest/include + PUBLIC ${GOOGLETEST_ROOT}/src/googlemock/include) + + # Define the target as a shared library, as that is what gradle expects. + set(integration_test_target_name "android_integration_test_main") + add_library(${integration_test_target_name} SHARED + ${FIREBASE_APP_FRAMEWORK_SRCS} + ${FIREBASE_APP_FRAMEWORK_ANDROID_SRCS} + ${FIREBASE_INTEGRATION_TEST_SRCS} + ${FIREBASE_TEST_FRAMEWORK_SRCS} + ${FIREBASE_TEST_FRAMEWORK_ANDROID_SRCS} + ) + + target_include_directories(${integration_test_target_name} PRIVATE + ${ANDROID_NDK}/sources/android/native_app_glue) + + set(ADDITIONAL_LIBS log android atomic native_app_glue) +else() + # Build a desktop application. + add_definitions(-D_GLIBCXX_USE_CXX11_ABI=0) + + # Prevent overriding the parent project's compiler/linker + # settings on Windows + set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) + + # Add googletest directly to our build. This defines + # the gtest and gtest_main targets. + add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/external/googletest/src + ${CMAKE_CURRENT_LIST_DIR}/external/googletest/build + EXCLUDE_FROM_ALL) + + # The gtest/gtest_main targets carry header search path + # dependencies automatically when using CMake 2.8.11 or + # later. Otherwise we have to add them here ourselves. + if (CMAKE_VERSION VERSION_LESS 2.8.11) + include_directories("${gtest_SOURCE_DIR}/include") + include_directories("${gmock_SOURCE_DIR}/include") + endif() + + # Windows runtime mode, either MD or MT depending on whether you are using + # /MD or /MT. For more information see: + # https://msdn.microsoft.com/en-us/library/2kzt1wy3.aspx + set(MSVC_RUNTIME_MODE MD) + + # Platform abstraction layer for the desktop integration test. + set(FIREBASE_APP_FRAMEWORK_DESKTOP_SRCS + src/desktop/desktop_app_framework.cc + src/desktop/desktop_firebase_test_framework.cc + ) + + set(integration_test_target_name "integration_test") + add_executable(${integration_test_target_name} + ${FIREBASE_APP_FRAMEWORK_SRCS} + ${FIREBASE_APP_FRAMEWORK_DESKTOP_SRCS} + ${FIREBASE_TEST_FRAMEWORK_SRCS} + ${FIREBASE_INTEGRATION_TEST_SRCS} + ) + + if(APPLE) + set(ADDITIONAL_LIBS + gssapi_krb5 + pthread + "-framework CoreFoundation" + "-framework Foundation" + "-framework GSS" + "-framework Security" + ) + elseif(MSVC) + set(ADDITIONAL_LIBS advapi32 ws2_32 crypt32) + else() + set(ADDITIONAL_LIBS pthread) + endif() + + # If a config file is present, copy it into the binary location so that it's + # possible to create the default Firebase app. + set(FOUND_JSON_FILE FALSE) + foreach(config "google-services-desktop.json" "google-services.json") + if (EXISTS ${config}) + add_custom_command( + TARGET ${integration_test_target_name} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + ${config} $) + set(FOUND_JSON_FILE TRUE) + break() + endif() + endforeach() + if(NOT FOUND_JSON_FILE) + message(WARNING "Failed to find either google-services-desktop.json or google-services.json. See the readme.md for more information.") + endif() +endif() + +# Add the Firebase libraries to the target using the function from the SDK. +add_subdirectory(${FIREBASE_CPP_SDK_DIR} bin/ EXCLUDE_FROM_ALL) +# Note that firebase_app needs to be last in the list. +set(firebase_libs firebase_dynamic_links firebase_app) +set(gtest_libs gtest gmock) +target_link_libraries(${integration_test_target_name} ${firebase_libs} + ${gtest_libs} ${ADDITIONAL_LIBS}) diff --git a/testing/integration_tests/dynamic_links/Firebase_Cpp_Dynamic_Links_Test_App_Dev.mobileprovision b/testing/integration_tests/dynamic_links/Firebase_Cpp_Dynamic_Links_Test_App_Dev.mobileprovision new file mode 100644 index 0000000000000000000000000000000000000000..541ff35cfa80e5a90ca00b75dfd13d6a455f7921 GIT binary patch literal 299610 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≫Sg-lME4yX<>zRu)rl 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 H+o}x!!ip-f literal 0 HcmV?d00001 diff --git a/testing/integration_tests/dynamic_links/Images.xcassets/AppIcon.appiconset/Contents.json b/testing/integration_tests/dynamic_links/Images.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000000..d8db8d65fd --- /dev/null +++ b/testing/integration_tests/dynamic_links/Images.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "83.5x83.5", + "scale" : "2x" + }, + { + "idiom" : "ios-marketing", + "size" : "1024x1024", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/testing/integration_tests/dynamic_links/Images.xcassets/LaunchImage.launchimage/Contents.json b/testing/integration_tests/dynamic_links/Images.xcassets/LaunchImage.launchimage/Contents.json new file mode 100644 index 0000000000..6f870a4629 --- /dev/null +++ b/testing/integration_tests/dynamic_links/Images.xcassets/LaunchImage.launchimage/Contents.json @@ -0,0 +1,51 @@ +{ + "images" : [ + { + "orientation" : "portrait", + "idiom" : "iphone", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "orientation" : "portrait", + "idiom" : "iphone", + "subtype" : "retina4", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "orientation" : "portrait", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "1x" + }, + { + "orientation" : "landscape", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "1x" + }, + { + "orientation" : "portrait", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "orientation" : "landscape", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/testing/integration_tests/dynamic_links/Info.plist b/testing/integration_tests/dynamic_links/Info.plist new file mode 100644 index 0000000000..5d64337d39 --- /dev/null +++ b/testing/integration_tests/dynamic_links/Info.plist @@ -0,0 +1,56 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + com.google.FirebaseCppDynamicLinksTestApp.dev + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleURLSchemes + + com.google.FirebaseCppDynamicLinksTestApp.dev + + CFBundleURLTypes + + + CFBundleTypeRole + Editor + CFBundleURLName + com.google.FirebaseCppDynamicLinksTestApp.dev + CFBundleURLSchemes + + com.google.FirebaseCppDynamicLinksTestApp.dev + + + + CFBundleTypeRole + Editor + CFBundleURLName + google + CFBundleURLSchemes + + YOUR_REVERSED_CLIENT_ID + firebase-game-loop + + + + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + NSContactsUsageDescription + Invite others to use the app. + + diff --git a/testing/integration_tests/dynamic_links/LaunchScreen.storyboard b/testing/integration_tests/dynamic_links/LaunchScreen.storyboard new file mode 100644 index 0000000000..673e0f7e68 --- /dev/null +++ b/testing/integration_tests/dynamic_links/LaunchScreen.storyboard @@ -0,0 +1,7 @@ + + + + + + + diff --git a/testing/integration_tests/dynamic_links/LibraryManifest.xml b/testing/integration_tests/dynamic_links/LibraryManifest.xml new file mode 100644 index 0000000000..bfbc445ec9 --- /dev/null +++ b/testing/integration_tests/dynamic_links/LibraryManifest.xml @@ -0,0 +1,7 @@ + + + + + + diff --git a/testing/integration_tests/dynamic_links/Podfile b/testing/integration_tests/dynamic_links/Podfile new file mode 100644 index 0000000000..d04f5ec67f --- /dev/null +++ b/testing/integration_tests/dynamic_links/Podfile @@ -0,0 +1,13 @@ +source 'sso://cpdc-internal/firebase.git' +source 'https://github.com/CocoaPods/Specs.git' +platform :ios, '8.0' +# Firebase Dynamic Links test application. + +target 'integration_test' do + pod 'Firebase/DynamicLinks', '6.24.0' +end + +post_install do |installer| + system("/usr/bin/python ./download_googletest.py") +end + diff --git a/testing/integration_tests/dynamic_links/build.gradle b/testing/integration_tests/dynamic_links/build.gradle new file mode 100644 index 0000000000..c3961d9747 --- /dev/null +++ b/testing/integration_tests/dynamic_links/build.gradle @@ -0,0 +1,64 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +buildscript { + repositories { + mavenLocal() + maven { url 'https://maven.google.com' } + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:3.2.1' + classpath 'com.google.gms:google-services:4.0.1' + } +} + +allprojects { + repositories { + mavenLocal() + maven { url 'https://maven.google.com' } + jcenter() + } +} + +apply plugin: 'com.android.application' + +android { + compileSdkVersion 28 + buildToolsVersion '28.0.3' + + sourceSets { + main { + jniLibs.srcDirs = ['libs'] + manifest.srcFile 'AndroidManifest.xml' + java.srcDirs = ['src/android/java'] + res.srcDirs = ['res'] + } + } + + defaultConfig { + applicationId 'com.google.android.dynamiclinks.testapp' + minSdkVersion 16 + targetSdkVersion 28 + versionCode 1 + versionName '1.0' + externalNativeBuild.cmake { + arguments "-DFIREBASE_CPP_SDK_DIR=$gradle.firebase_cpp_sdk_dir" + } + } + externalNativeBuild.cmake { + path 'CMakeLists.txt' + } + buildTypes { + release { + minifyEnabled true + proguardFile getDefaultProguardFile('proguard-android.txt') + proguardFile file('proguard.pro') + } + } +} + +apply from: "$gradle.firebase_cpp_sdk_dir/Android/firebase_dependencies.gradle" +firebaseCpp.dependencies { + dynamicLinks +} + +apply plugin: 'com.google.gms.google-services' diff --git a/testing/integration_tests/dynamic_links/googletest.cmake b/testing/integration_tests/dynamic_links/googletest.cmake new file mode 100644 index 0000000000..0eeff685dd --- /dev/null +++ b/testing/integration_tests/dynamic_links/googletest.cmake @@ -0,0 +1,19 @@ +# Download GoogleTest from GitHub as an external project. +# This CMake file is taken from: +# https://github.com/google/googletest/blob/master/googletest/README.md#incorporating-into-an-existing-cmake-project + +cmake_minimum_required(VERSION 2.8.2) + +project(googletest-download NONE) + +include(ExternalProject) +ExternalProject_Add(googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG master + SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/src" + BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/build" + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + TEST_COMMAND "" +) diff --git a/testing/integration_tests/dynamic_links/gradle/wrapper/gradle-wrapper.jar b/testing/integration_tests/dynamic_links/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..8c0fb64a8698b08ecc4158d828ca593c4928e9dd GIT binary patch literal 49896 zcmagFb986H(k`5d^NVfUwr$(C?M#x1ZQHiZiEVpg+jrjgoQrerx!>1o_ul)D>ebz~ zs=Mmxr&>W81QY-S1PKWQ%N-;H^tS;2*XwVA`dej1RRn1z<;3VgfE4~kaG`A%QSPsR z#ovnZe+tS9%1MfeDyz`RirvdjPRK~p(#^q2(^5@O&NM19EHdvN-A&StN>0g6QA^VN z0Gx%Gq#PD$QMRFzmK+utjS^Y1F0e8&u&^=w5K<;4Rz|i3A=o|IKLY+g`iK6vfr9?+ z-`>gmU&i?FGSL5&F?TXFu`&Js6h;15QFkXp2M1H9|Eq~bpov-GU(uz%mH0n55wUl- zv#~ccAz`F5wlQ>e_KlJS3@{)B?^v*EQM=IxLa&76^y51a((wq|2-`qON>+4dLc{Oo z51}}o^Zen(oAjxDK7b++9_Yg`67p$bPo3~BCpGM7uAWmvIhWc5Gi+gQZ|Pwa-Gll@<1xmcPy z|NZmu6m)g5Ftu~BG&Xdxclw7Cij{xbBMBn-LMII#Slp`AElb&2^Hw+w>(3crLH!;I zN+Vk$D+wP1#^!MDCiad@vM>H#6+`Ct#~6VHL4lzmy;lSdk>`z6)=>Wh15Q2)dQtGqvn0vJU@+(B5{MUc*qs4!T+V=q=wy)<6$~ z!G>e_4dN@lGeF_$q9`Ju6Ncb*x?O7=l{anm7Eahuj_6lA{*#Gv*TaJclevPVbbVYu z(NY?5q+xxbO6%g1xF0r@Ix8fJ~u)VRUp`S%&rN$&e!Od`~s+64J z5*)*WSi*i{k%JjMSIN#X;jC{HG$-^iX+5f5BGOIHWAl*%15Z#!xntpk($-EGKCzKa zT7{siZ9;4TICsWQ$pu&wKZQTCvpI$Xvzwxoi+XkkpeE&&kFb!B?h2hi%^YlXt|-@5 zHJ~%AN!g_^tmn1?HSm^|gCE#!GRtK2(L{9pL#hp0xh zME}|DB>(5)`iE7CM)&_+S}-Bslc#@B5W4_+k4Cp$l>iVyg$KP>CN?SVGZ(&02>iZK zB<^HP$g$Lq*L$BWd?2(F?-MUbNWTJVQdW7$#8a|k_30#vHAD1Z{c#p;bETk0VnU5A zBgLe2HFJ3032$G<`m*OB!KM$*sdM20jm)It5OSru@tXpK5LT>#8)N!*skNu1$TpIw zufjjdp#lyH5bZ%|Iuo|iu9vG1HrIVWLH>278xo>aVBkPN3V$~!=KnlXQ4eDqS7%E% zQ!z^$Q$b^6Q)g#cLpwur(|<0gWHo6A6jc;n`t(V9T;LzTAU{IAu*uEQ%Ort1k+Kn+f_N`9|bxYC+~Z1 zCC1UCWv*Orx$_@ydv9mIe(liLfOr7mhbV@tKw{6)q^1DH1nmvZ0cj215R<~&I<4S| zgnr;9Cdjqpz#o8i0CQjtl`}{c*P)aSdH|abxGdrR)-3z+02-eX(k*B)Uqv6~^nh** z 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 \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/testing/integration_tests/dynamic_links/gradlew.bat b/testing/integration_tests/dynamic_links/gradlew.bat new file mode 100644 index 0000000000..8a0b282aa6 --- /dev/null +++ b/testing/integration_tests/dynamic_links/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/testing/integration_tests/dynamic_links/integration_test.entitlements b/testing/integration_tests/dynamic_links/integration_test.entitlements new file mode 100644 index 0000000000..3ccfd33539 --- /dev/null +++ b/testing/integration_tests/dynamic_links/integration_test.entitlements @@ -0,0 +1,12 @@ + + + + + application-identifier + $(AppIdentifierPrefix)$(CFBundleIdentifier) + com.apple.developer.associated-domains + + applinks:zx93d.app.goo.gl + + + diff --git a/testing/integration_tests/dynamic_links/integration_test.xcodeproj/project.pbxproj b/testing/integration_tests/dynamic_links/integration_test.xcodeproj/project.pbxproj new file mode 100644 index 0000000000..d1d6b852d1 --- /dev/null +++ b/testing/integration_tests/dynamic_links/integration_test.xcodeproj/project.pbxproj @@ -0,0 +1,364 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 520BC0391C869159008CFBC3 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 520BC0381C869159008CFBC3 /* GoogleService-Info.plist */; }; + 529226D61C85F68000C89379 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 529226D51C85F68000C89379 /* Foundation.framework */; }; + 529226D81C85F68000C89379 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 529226D71C85F68000C89379 /* CoreGraphics.framework */; }; + 529226DA1C85F68000C89379 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 529226D91C85F68000C89379 /* UIKit.framework */; }; + D61C5F8E22BABA9C00A79141 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D61C5F8C22BABA9B00A79141 /* Images.xcassets */; }; + D61C5F9622BABAD200A79141 /* integration_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = D61C5F9222BABAD100A79141 /* integration_test.cc */; }; + D62CCBC022F367140099BE9F /* gmock-all.cc in Sources */ = {isa = PBXBuildFile; fileRef = D62CCBBF22F367140099BE9F /* gmock-all.cc */; }; + D66B16871CE46E8900E5638A /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D66B16861CE46E8900E5638A /* LaunchScreen.storyboard */; }; + D67D355822BABD2200292C1D /* gtest-all.cc in Sources */ = {isa = PBXBuildFile; fileRef = D67D355622BABD2100292C1D /* gtest-all.cc */; }; + D6C179E922CB322900C2651A /* ios_app_framework.mm in Sources */ = {isa = PBXBuildFile; fileRef = D6C179E722CB322900C2651A /* ios_app_framework.mm */; }; + D6C179EA22CB322900C2651A /* ios_firebase_test_framework.mm in Sources */ = {isa = PBXBuildFile; fileRef = D6C179E822CB322900C2651A /* ios_firebase_test_framework.mm */; }; + D6C179EE22CB323300C2651A /* firebase_test_framework.cc in Sources */ = {isa = PBXBuildFile; fileRef = D6C179EC22CB323300C2651A /* firebase_test_framework.cc */; }; + D6C179F022CB32A000C2651A /* app_framework.cc in Sources */ = {isa = PBXBuildFile; fileRef = D6C179EF22CB32A000C2651A /* app_framework.cc */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 520BC0381C869159008CFBC3 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; + 529226D21C85F68000C89379 /* integration_test.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = integration_test.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 529226D51C85F68000C89379 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + 529226D71C85F68000C89379 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; + 529226D91C85F68000C89379 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; + 529226EE1C85F68000C89379 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; + D61C5F8C22BABA9B00A79141 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; + D61C5F8D22BABA9C00A79141 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + D61C5F9222BABAD100A79141 /* integration_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = integration_test.cc; path = src/integration_test.cc; sourceTree = ""; }; + D62CCBBF22F367140099BE9F /* gmock-all.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "gmock-all.cc"; path = "external/googletest/src/googlemock/src/gmock-all.cc"; sourceTree = ""; }; + D62CCBC122F367320099BE9F /* gmock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = gmock.h; path = external/googletest/src/googlemock/include/gmock/gmock.h; sourceTree = ""; }; + D66B16861CE46E8900E5638A /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = ""; }; + D67D355622BABD2100292C1D /* gtest-all.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "gtest-all.cc"; path = "external/googletest/src/googletest/src/gtest-all.cc"; sourceTree = ""; }; + D67D355722BABD2100292C1D /* gtest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = gtest.h; path = external/googletest/src/googletest/include/gtest/gtest.h; sourceTree = ""; }; + D6C179E722CB322900C2651A /* ios_app_framework.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ios_app_framework.mm; path = src/ios/ios_app_framework.mm; sourceTree = ""; }; + D6C179E822CB322900C2651A /* ios_firebase_test_framework.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ios_firebase_test_framework.mm; path = src/ios/ios_firebase_test_framework.mm; sourceTree = ""; }; + D6C179EB22CB323300C2651A /* firebase_test_framework.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = firebase_test_framework.h; path = src/firebase_test_framework.h; sourceTree = ""; }; + D6C179EC22CB323300C2651A /* firebase_test_framework.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = firebase_test_framework.cc; path = src/firebase_test_framework.cc; sourceTree = ""; }; + D6C179ED22CB323300C2651A /* app_framework.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = app_framework.h; path = src/app_framework.h; sourceTree = ""; }; + D6C179EF22CB32A000C2651A /* app_framework.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = app_framework.cc; path = src/app_framework.cc; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 529226CF1C85F68000C89379 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 529226D81C85F68000C89379 /* CoreGraphics.framework in Frameworks */, + 529226DA1C85F68000C89379 /* UIKit.framework in Frameworks */, + 529226D61C85F68000C89379 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 529226C91C85F68000C89379 = { + isa = PBXGroup; + children = ( + D61C5F8C22BABA9B00A79141 /* Images.xcassets */, + D61C5F8D22BABA9C00A79141 /* Info.plist */, + D66B16861CE46E8900E5638A /* LaunchScreen.storyboard */, + 520BC0381C869159008CFBC3 /* GoogleService-Info.plist */, + 5292271D1C85FB5500C89379 /* src */, + 529226D41C85F68000C89379 /* Frameworks */, + 529226D31C85F68000C89379 /* Products */, + ); + sourceTree = ""; + }; + 529226D31C85F68000C89379 /* Products */ = { + isa = PBXGroup; + children = ( + 529226D21C85F68000C89379 /* integration_test.app */, + ); + name = Products; + sourceTree = ""; + }; + 529226D41C85F68000C89379 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 529226D51C85F68000C89379 /* Foundation.framework */, + 529226D71C85F68000C89379 /* CoreGraphics.framework */, + 529226D91C85F68000C89379 /* UIKit.framework */, + 529226EE1C85F68000C89379 /* XCTest.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 5292271D1C85FB5500C89379 /* src */ = { + isa = PBXGroup; + children = ( + D62CCBC122F367320099BE9F /* gmock.h */, + D62CCBBF22F367140099BE9F /* gmock-all.cc */, + D67D355622BABD2100292C1D /* gtest-all.cc */, + D67D355722BABD2100292C1D /* gtest.h */, + D6C179EF22CB32A000C2651A /* app_framework.cc */, + D6C179ED22CB323300C2651A /* app_framework.h */, + D6C179EC22CB323300C2651A /* firebase_test_framework.cc */, + D6C179EB22CB323300C2651A /* firebase_test_framework.h */, + D61C5F9222BABAD100A79141 /* integration_test.cc */, + 5292271E1C85FB5B00C89379 /* ios */, + ); + name = src; + sourceTree = ""; + }; + 5292271E1C85FB5B00C89379 /* ios */ = { + isa = PBXGroup; + children = ( + D6C179E722CB322900C2651A /* ios_app_framework.mm */, + D6C179E822CB322900C2651A /* ios_firebase_test_framework.mm */, + ); + name = ios; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 529226D11C85F68000C89379 /* integration_test */ = { + isa = PBXNativeTarget; + buildConfigurationList = 529226F91C85F68000C89379 /* Build configuration list for PBXNativeTarget "integration_test" */; + buildPhases = ( + 529226CE1C85F68000C89379 /* Sources */, + 529226CF1C85F68000C89379 /* Frameworks */, + 529226D01C85F68000C89379 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = integration_test; + productName = testapp; + productReference = 529226D21C85F68000C89379 /* integration_test.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 529226CA1C85F68000C89379 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0640; + ORGANIZATIONNAME = Google; + TargetAttributes = { + 529226D11C85F68000C89379 = { + CreatedOnToolsVersion = 6.4; + DevelopmentTeam = EQHXZ8M8AV; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = 529226CD1C85F68000C89379 /* Build configuration list for PBXProject "integration_test" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 529226C91C85F68000C89379; + productRefGroup = 529226D31C85F68000C89379 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 529226D11C85F68000C89379 /* integration_test */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 529226D01C85F68000C89379 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D61C5F8E22BABA9C00A79141 /* Images.xcassets in Resources */, + D66B16871CE46E8900E5638A /* LaunchScreen.storyboard in Resources */, + 520BC0391C869159008CFBC3 /* GoogleService-Info.plist in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 529226CE1C85F68000C89379 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D67D355822BABD2200292C1D /* gtest-all.cc in Sources */, + D62CCBC022F367140099BE9F /* gmock-all.cc in Sources */, + D6C179EA22CB322900C2651A /* ios_firebase_test_framework.mm in Sources */, + D61C5F9622BABAD200A79141 /* integration_test.cc in Sources */, + D6C179E922CB322900C2651A /* ios_app_framework.mm in Sources */, + D6C179F022CB32A000C2651A /* app_framework.cc in Sources */, + D6C179EE22CB323300C2651A /* firebase_test_framework.cc in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 529226F71C85F68000C89379 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.4; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 529226F81C85F68000C89379 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.4; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 529226FA1C85F68000C89379 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ""; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)", + ); + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + "\"$(SRCROOT)/src\"", + "\"$(SRCROOT)/external/googletest/src/googletest/include\"", + "\"$(SRCROOT)/external/googletest/src/googlemock/include\"", + "\"$(SRCROOT)/external/googletest/src/googletest\"", + "\"$(SRCROOT)/external/googletest/src/googlemock\"", + ); + INFOPLIST_FILE = "$(SRCROOT)/Info.plist"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + WRAPPER_EXTENSION = app; + }; + name = Debug; + }; + 529226FB1C85F68000C89379 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ""; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)", + ); + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + "\"$(SRCROOT)/src\"", + "\"$(SRCROOT)/external/googletest/src/googletest/include\"", + "\"$(SRCROOT)/external/googletest/src/googlemock/include\"", + "\"$(SRCROOT)/external/googletest/src/googletest\"", + "\"$(SRCROOT)/external/googletest/src/googlemock\"", + ); + INFOPLIST_FILE = "$(SRCROOT)/Info.plist"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + WRAPPER_EXTENSION = app; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 529226CD1C85F68000C89379 /* Build configuration list for PBXProject "integration_test" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 529226F71C85F68000C89379 /* Debug */, + 529226F81C85F68000C89379 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 529226F91C85F68000C89379 /* Build configuration list for PBXNativeTarget "integration_test" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 529226FA1C85F68000C89379 /* Debug */, + 529226FB1C85F68000C89379 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 529226CA1C85F68000C89379 /* Project object */; +} diff --git a/testing/integration_tests/dynamic_links/proguard.pro b/testing/integration_tests/dynamic_links/proguard.pro new file mode 100644 index 0000000000..2d04b8a9a5 --- /dev/null +++ b/testing/integration_tests/dynamic_links/proguard.pro @@ -0,0 +1,2 @@ +-ignorewarnings +-keep,includedescriptorclasses public class com.google.firebase.example.LoggingUtils { * ; } diff --git a/testing/integration_tests/dynamic_links/res/layout/main.xml b/testing/integration_tests/dynamic_links/res/layout/main.xml new file mode 100644 index 0000000000..d3ffb63082 --- /dev/null +++ b/testing/integration_tests/dynamic_links/res/layout/main.xml @@ -0,0 +1,12 @@ + + + + diff --git a/testing/integration_tests/dynamic_links/res/values/strings.xml b/testing/integration_tests/dynamic_links/res/values/strings.xml new file mode 100644 index 0000000000..f8cbe3cc1f --- /dev/null +++ b/testing/integration_tests/dynamic_links/res/values/strings.xml @@ -0,0 +1,4 @@ + + + Firebase Dynamic Links Integration Test + diff --git a/testing/integration_tests/dynamic_links/settings.gradle b/testing/integration_tests/dynamic_links/settings.gradle new file mode 100644 index 0000000000..2a543b9351 --- /dev/null +++ b/testing/integration_tests/dynamic_links/settings.gradle @@ -0,0 +1,36 @@ +// Copyright 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +def firebase_cpp_sdk_dir = System.getProperty('firebase_cpp_sdk.dir') +if (firebase_cpp_sdk_dir == null || firebase_cpp_sdk_dir.isEmpty()) { + firebase_cpp_sdk_dir = System.getenv('FIREBASE_CPP_SDK_DIR') + if (firebase_cpp_sdk_dir == null || firebase_cpp_sdk_dir.isEmpty()) { + if ((new File('firebase_cpp_sdk')).exists()) { + firebase_cpp_sdk_dir = 'firebase_cpp_sdk' + } else { + throw new StopActionException( + 'firebase_cpp_sdk.dir property or the FIREBASE_CPP_SDK_DIR ' + + 'environment variable must be set to reference the Firebase C++ ' + + 'SDK install directory. This is used to configure static library ' + + 'and C/C++ include paths for the SDK.') + } + } +} +if (!(new File(firebase_cpp_sdk_dir)).exists()) { + throw new StopActionException( + sprintf('Firebase C++ SDK directory %s does not exist', + firebase_cpp_sdk_dir)) +} +gradle.ext.firebase_cpp_sdk_dir = "$firebase_cpp_sdk_dir" +includeBuild "$firebase_cpp_sdk_dir" \ No newline at end of file diff --git a/testing/integration_tests/dynamic_links/src/integration_test.cc b/testing/integration_tests/dynamic_links/src/integration_test.cc new file mode 100644 index 0000000000..b3cf61e666 --- /dev/null +++ b/testing/integration_tests/dynamic_links/src/integration_test.cc @@ -0,0 +1,700 @@ +// Copyright 2019 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include +#include +#include +#include +#include + +#include "app_framework.h" // NOLINT +#include "firebase/app.h" +#include "firebase/dynamic_links.h" +#include "firebase/dynamic_links/components.h" +#include "firebase/internal/platform.h" +#include "firebase/log.h" +#include "firebase/util.h" +#include "firebase_test_framework.h" // NOLINT + +// The TO_STRING macro is useful for command line defined strings as the quotes +// get stripped. +#define TO_STRING_EXPAND(X) #X +#define TO_STRING(X) TO_STRING_EXPAND(X) + +// Path to the Firebase config file to load. +#ifdef FIREBASE_CONFIG +#define FIREBASE_CONFIG_STRING TO_STRING(FIREBASE_CONFIG) +#else +#define FIREBASE_CONFIG_STRING "" +#endif // FIREBASE_CONFIG + +namespace firebase_testapp_automated { + +using app_framework::LogDebug; +using app_framework::LogInfo; + +using app_framework::ProcessEvents; +using firebase_test_framework::FirebaseTest; + +// Bundle IDs needed for opening Dynamic Links. +static const char kIOSBundleID[] = + "com.google.FirebaseCppDynamicLinksTestApp.dev"; +static const char kAndroidBundleID[] = + "com.google.android.dynamiclinks.testapp"; +static const char kIOSAppStoreID[] = "2233445566"; // Placeholder ID. + +// Invalid domain, used to make sure the user sets a valid domain. +#define INVALID_DOMAIN_URI_PREFIX "THIS_IS_AN_INVALID_DOMAIN" + +static const char kDomainUriPrefixInvalidError[] = + "kDomainUriPrefix is not valid, link shortening will fail.\n" + "To resolve this:\n" + "* Goto the Firebase console https://firebase.google.com/console/\n" + "* Click on the Dynamic Links tab\n" + "* Copy the URI prefix e.g https://x20yz.app.goo.gl\n" + "* Replace the value of kDomainUriPrefix with the copied URI prefix.\n"; + +// IMPORTANT: You need to set this to a valid URI prefix from the Firebase +// console (see kDomainUriPrefixInvalidError for the details). +static const char* kDomainUriPrefix = "https://zx93d.app.goo.gl"; + +#define TARGET_URL_PREFIX "https://mysite.example.com" + +// When one of the tests tries to open a URL, it suppresses the other tests +// that are attempting to do the same, since only one URL can be opened at a +// time. It does so by setting the "current test" flag to its own test name. +static const char kCurrentTestKey[] = "openurl_current_test"; + +class TestListener; + +class FirebaseDynamicLinksTest : public FirebaseTest { + public: + static void SetUpTestSuite(); + static void TearDownTestSuite(); + + protected: + // Try to claim the "current test" flag, returning true if successful and + // false if not. Because tests run in sequence, this does not actually + // require any mutexes. This returns true if it was already claimed by this + // test, or if no test was claiming it before (in which case, now this test + // is). + bool ClaimCurrentTest(const char* test_name); + // Release the "current test" flag, allowing the next test to run. + void ReleaseCurrentTest(); + + static firebase::App* shared_app_; + static TestListener* shared_listener_; + static bool is_desktop_stub_; + // A list of persistent keys we've saved on the device, to be erased on + // shutdown after all tests are finished. + static std::vector cleanup_persistent_keys_; +}; + +firebase::App* FirebaseDynamicLinksTest::shared_app_ = nullptr; +TestListener* FirebaseDynamicLinksTest::shared_listener_ = nullptr; +bool FirebaseDynamicLinksTest::is_desktop_stub_ = false; +// NOLINTNEXTLINE +std::vector FirebaseDynamicLinksTest::cleanup_persistent_keys_; + +// Handles a received dynamic link. +class TestListener : public firebase::dynamic_links::Listener { + public: + TestListener() : received_link_(false) {} + void OnDynamicLinkReceived( + const firebase::dynamic_links::DynamicLink* dynamic_link) override { + LogInfo("Received dynamic link: %s", dynamic_link->url.c_str()); + link_ = *dynamic_link; + received_link_ = true; + } + + bool WaitForDynamicLink(firebase::dynamic_links::DynamicLink* link_output) { + const int kWaitSeconds = 10; + for (int i = 0; i < kWaitSeconds; i++) { + ProcessEvents(1000); + if (received_link_) { + *link_output = link_; + return true; + } + } + return false; + } + bool received_link_; + firebase::dynamic_links::DynamicLink link_; +}; + +void FirebaseDynamicLinksTest::SetUpTestSuite() { + FindFirebaseConfig(FIREBASE_CONFIG_STRING); + + firebase::SetLogLevel(firebase::kLogLevelDebug); + LogDebug("Initialize Firebase App."); + +#if defined(__ANDROID__) + shared_app_ = ::firebase::App::Create(app_framework::GetJniEnv(), + app_framework::GetActivity()); +#else + shared_app_ = ::firebase::App::Create(); +#endif // defined(__ANDROID__) + + LogDebug("Initializing Firebase Dynamic Links."); + + shared_listener_ = new TestListener(); + + ::firebase::ModuleInitializer initializer; + initializer.Initialize( + shared_app_, shared_listener_, + [](::firebase::App* app, void* listener_void) { + LogDebug("Try to initialize Firebase Dynamic Links"); + firebase::InitResult result; + result = firebase::dynamic_links::Initialize( + *app, reinterpret_cast( + listener_void)); + return result; + }); + + FirebaseTest::WaitForCompletion(initializer.InitializeLastResult(), + "Initialize"); + + ASSERT_EQ(initializer.InitializeLastResult().error(), 0) + << initializer.InitializeLastResult().error_message(); + + is_desktop_stub_ = false; +#if !defined(__ANDROID__) && !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) + is_desktop_stub_ = true; +#endif // !defined(__ANDROID__) && !(defined(TARGET_OS_IPHONE) && + // TARGET_OS_IPHONE) + + LogDebug("Successfully initialized Firebase Dynamic Links."); +} + +void FirebaseDynamicLinksTest::TearDownTestSuite() { + // On teardown, delete all the persistent keys we should clean up, as long as + // there is no longer a current test running. + std::string value; + if (GetPersistentString(kCurrentTestKey, &value) && !value.empty()) { + // Don't clean up the persistent keys yet, not until all the tests are done. + return; + } + LogDebug("Tests finished, cleaning up all persistent keys."); + for (int i = 0; i < cleanup_persistent_keys_.size(); ++i) { + SetPersistentString(cleanup_persistent_keys_[i].c_str(), nullptr); + } + cleanup_persistent_keys_.clear(); + + LogDebug("Shutdown Firebase Dynamic Links."); + firebase::dynamic_links::Terminate(); + + delete shared_listener_; + shared_listener_ = nullptr; + + LogDebug("Shutdown Firebase App."); + delete shared_app_; + shared_app_ = nullptr; + + ProcessEvents(100); +} + +// Test cases below. + +TEST_F(FirebaseDynamicLinksTest, TestInitializeAndTerminate) { + // Already tested via SetUp() and TearDown(). +} + +TEST_F(FirebaseDynamicLinksTest, CheckForDomainUriPrefix) { + ASSERT_NE(strcmp(kDomainUriPrefix, INVALID_DOMAIN_URI_PREFIX), 0) + << kDomainUriPrefixInvalidError; +} + +TEST_F(FirebaseDynamicLinksTest, TestCreateLongLink) { + firebase::dynamic_links::GoogleAnalyticsParameters analytics_parameters; + analytics_parameters.source = "mysource"; + analytics_parameters.medium = "mymedium"; + analytics_parameters.campaign = "mycampaign"; + analytics_parameters.term = "myterm"; + analytics_parameters.content = "mycontent"; + + firebase::dynamic_links::IOSParameters ios_parameters("com.myapp.bundleid"); + ios_parameters.fallback_url = TARGET_URL_PREFIX "/fallback"; + ios_parameters.custom_scheme = "mycustomscheme"; + ios_parameters.minimum_version = "1.2.3"; + ios_parameters.ipad_bundle_id = "com.myapp.bundleid.ipad"; + ios_parameters.ipad_fallback_url = TARGET_URL_PREFIX "/fallbackipad"; + + firebase::dynamic_links::ITunesConnectAnalyticsParameters + app_store_parameters; + app_store_parameters.affiliate_token = "abcdefg"; + app_store_parameters.campaign_token = "hijklmno"; + app_store_parameters.provider_token = "pq-rstuv"; + + firebase::dynamic_links::AndroidParameters android_parameters( + "com.myapp.packageid"); + android_parameters.fallback_url = TARGET_URL_PREFIX "/fallback"; + android_parameters.minimum_version = 12; + + firebase::dynamic_links::SocialMetaTagParameters social_parameters; + social_parameters.title = "My App!"; + social_parameters.description = "My app is awesome!"; + social_parameters.image_url = TARGET_URL_PREFIX "/someimage.jpg"; + + firebase::dynamic_links::DynamicLinkComponents components( + "https://google.com/abc", kDomainUriPrefix); + components.google_analytics_parameters = &analytics_parameters; + components.ios_parameters = &ios_parameters; + components.itunes_connect_analytics_parameters = &app_store_parameters; + components.android_parameters = &android_parameters; + components.social_meta_tag_parameters = &social_parameters; + + firebase::dynamic_links::GeneratedDynamicLink generated_link = + firebase::dynamic_links::GetLongLink(components); + + if (is_desktop_stub_) { + // On desktop, it's enough that we just don't crash. + SUCCEED(); + return; + } + + EXPECT_TRUE(generated_link.error.empty()); + EXPECT_NE(generated_link.url, ""); + EXPECT_EQ(generated_link.url.find(kDomainUriPrefix), 0) + << "Dynamic Link URL (" << generated_link.url + << ") does not begin with Domain URI Prefix (" << kDomainUriPrefix << ")"; + if (!generated_link.warnings.empty()) { + LogDebug("GetLongLink warnings:"); + for (auto it = generated_link.warnings.begin(); + it != generated_link.warnings.end(); ++it) { + LogDebug(" %s", it->c_str()); + } + } +} + +TEST_F(FirebaseDynamicLinksTest, TestGetShortLinkFromComponents) { + firebase::dynamic_links::GoogleAnalyticsParameters analytics_parameters; + analytics_parameters.source = "mysource"; + analytics_parameters.medium = "mymedium"; + analytics_parameters.campaign = "mycampaign"; + analytics_parameters.term = "myterm"; + analytics_parameters.content = "mycontent"; + + firebase::dynamic_links::IOSParameters ios_parameters("com.myapp.bundleid"); + ios_parameters.fallback_url = TARGET_URL_PREFIX "/fallback"; + ios_parameters.custom_scheme = "mycustomscheme"; + ios_parameters.minimum_version = "1.2.3"; + ios_parameters.ipad_bundle_id = "com.myapp.bundleid.ipad"; + ios_parameters.ipad_fallback_url = TARGET_URL_PREFIX "/fallbackipad"; + + firebase::dynamic_links::ITunesConnectAnalyticsParameters + app_store_parameters; + app_store_parameters.affiliate_token = "abcdefg"; + app_store_parameters.campaign_token = "hijklmno"; + app_store_parameters.provider_token = "pq-rstuv"; + + firebase::dynamic_links::AndroidParameters android_parameters( + "com.myapp.packageid"); + android_parameters.fallback_url = TARGET_URL_PREFIX "/fallback"; + android_parameters.minimum_version = 12; + + firebase::dynamic_links::SocialMetaTagParameters social_parameters; + social_parameters.title = "My App!"; + social_parameters.description = "My app is awesome!"; + social_parameters.image_url = TARGET_URL_PREFIX "/someimage.jpg"; + + firebase::dynamic_links::DynamicLinkComponents components( + "https://google.com/def", kDomainUriPrefix); + components.google_analytics_parameters = &analytics_parameters; + components.ios_parameters = &ios_parameters; + components.itunes_connect_analytics_parameters = &app_store_parameters; + components.android_parameters = &android_parameters; + components.social_meta_tag_parameters = &social_parameters; + + firebase::Future future = + firebase::dynamic_links::GetShortLink(components); + WaitForCompletion(future, "GetShortLinkFromComponents"); + + if (is_desktop_stub_) { + // On desktop, it's enough that we just don't crash. + SUCCEED(); + return; + } + + const firebase::dynamic_links::GeneratedDynamicLink& generated_link = + *future.result(); + + EXPECT_TRUE(generated_link.error.empty()); + EXPECT_NE(generated_link.url, ""); + EXPECT_EQ(generated_link.url.find(kDomainUriPrefix), 0) + << "Dynamic Link URL (" << generated_link.url + << ") does not begin with Domain URI Prefix (" << kDomainUriPrefix << ")"; + if (!generated_link.warnings.empty()) { + LogDebug("GetShortLinkFromComponents warnings:"); + for (auto it = generated_link.warnings.begin(); + it != generated_link.warnings.end(); ++it) { + LogDebug(" %s", it->c_str()); + } + } +} + +TEST_F(FirebaseDynamicLinksTest, TestGetShortLinkFromLongLink) { + firebase::dynamic_links::GoogleAnalyticsParameters analytics_parameters; + analytics_parameters.source = "mysource"; + analytics_parameters.medium = "mymedium"; + analytics_parameters.campaign = "mycampaign"; + analytics_parameters.term = "myterm"; + analytics_parameters.content = "mycontent"; + + firebase::dynamic_links::IOSParameters ios_parameters("com.myapp.bundleid"); + ios_parameters.fallback_url = TARGET_URL_PREFIX "/fallback"; + ios_parameters.custom_scheme = "mycustomscheme"; + ios_parameters.minimum_version = "1.2.3"; + ios_parameters.ipad_bundle_id = "com.myapp.bundleid.ipad"; + ios_parameters.ipad_fallback_url = TARGET_URL_PREFIX "/fallbackipad"; + + firebase::dynamic_links::ITunesConnectAnalyticsParameters + app_store_parameters; + app_store_parameters.affiliate_token = "abcdefg"; + app_store_parameters.campaign_token = "hijklmno"; + app_store_parameters.provider_token = "pq-rstuv"; + + firebase::dynamic_links::AndroidParameters android_parameters( + "com.myapp.packageid"); + android_parameters.fallback_url = TARGET_URL_PREFIX "/fallback"; + android_parameters.minimum_version = 12; + + firebase::dynamic_links::SocialMetaTagParameters social_parameters; + social_parameters.title = "My App!"; + social_parameters.description = "My app is awesome!"; + social_parameters.image_url = TARGET_URL_PREFIX "/someimage.jpg"; + + firebase::dynamic_links::DynamicLinkComponents components( + "https://google.com/ghi", kDomainUriPrefix); + components.google_analytics_parameters = &analytics_parameters; + components.ios_parameters = &ios_parameters; + components.itunes_connect_analytics_parameters = &app_store_parameters; + components.android_parameters = &android_parameters; + components.social_meta_tag_parameters = &social_parameters; + + firebase::dynamic_links::GeneratedDynamicLink long_link = + firebase::dynamic_links::GetLongLink(components); + + if (is_desktop_stub_) { + // On desktop, it's enough that we just don't crash. + SUCCEED(); + return; + } + + EXPECT_NE(long_link.url, ""); + + firebase::dynamic_links::DynamicLinkOptions options; + options.path_length = firebase::dynamic_links::kPathLengthShort; + firebase::Future future = + firebase::dynamic_links::GetShortLink(long_link.url.c_str(), options); + WaitForCompletion(future, "GetShortLinkFromLongLink"); + + const firebase::dynamic_links::GeneratedDynamicLink& generated_link = + *future.result(); + + EXPECT_TRUE(generated_link.error.empty()); + EXPECT_NE(generated_link.url, ""); + EXPECT_EQ(generated_link.url.find(kDomainUriPrefix), 0) + << "Dynamic Link URL (" << generated_link.url + << ") does not begin with Domain URI Prefix (" << kDomainUriPrefix << ")"; + if (!generated_link.warnings.empty()) { + LogDebug("GetShortLinkFromLongLink warnings:"); + for (auto it = generated_link.warnings.begin(); + it != generated_link.warnings.end(); ++it) { + LogDebug(" %s", it->c_str()); + } + } +} + +bool FirebaseDynamicLinksTest::ClaimCurrentTest(const char* test_name) { + // Tests using OpenUrlInBrowser must be run one at a time per run of the app. + // The workflow for these tests is: + // + // Run #1: Test A opens its link in browser, tests B & C do nothing. + // Run #2: Test A verifies that its link loaded, test B opens its link in + // browser, test C does nothing. + // Run #3: Test A remembers whether its link had loaded, test B verifies that + // its link loaded, test C opens its link in browser. + // Run #4: Tests A & B remember whether their links had loaded, test C + // verifies that its link loaded. + // + // This is accomplished by setting the value of kCurrentTestKey, which tells + // us which of the tests is currently doing its thing. Each test can also set + // a state variable saying whether they are opening the link in browser (the + // starting state), verifying that the link opened, or previously opened (or + // failed to open) the link. Tests that previously failed to open the link + // will continue to register a FAIL until all the tests are finished. + std::string value; + if (!GetPersistentString(kCurrentTestKey, &value) || value == test_name) { + // If not already set to it, take ownership of the current test. + if (value != test_name) { + SetPersistentString(kCurrentTestKey, test_name); + } + return true; + } + return false; +} + +void FirebaseDynamicLinksTest::ReleaseCurrentTest() { + SetPersistentString(kCurrentTestKey, nullptr); +} + +static firebase::dynamic_links::DynamicLinkComponents GenerateComponentsForTest( + const char* url) { + static firebase::dynamic_links::AndroidParameters android_parameters( + kAndroidBundleID); + static firebase::dynamic_links::IOSParameters ios_parameters(kIOSBundleID); + ios_parameters.app_store_id = kIOSAppStoreID; + static firebase::dynamic_links::SocialMetaTagParameters social_parameters; + static firebase::dynamic_links::ITunesConnectAnalyticsParameters + app_store_parameters; + static firebase::dynamic_links::GoogleAnalyticsParameters + analytics_parameters; + firebase::dynamic_links::DynamicLinkComponents components(url, + kDomainUriPrefix); + components.google_analytics_parameters = &analytics_parameters; + components.ios_parameters = &ios_parameters; + components.itunes_connect_analytics_parameters = &app_store_parameters; + components.android_parameters = &android_parameters; + components.social_meta_tag_parameters = &social_parameters; + return components; +} +static const char kStateSentLink[] = "sentLink"; +static const char kStateReceivedLink[] = "receivedLink"; +static const char kStateReceivedLinkFail[] = "receivedLinkFail"; + +TEST_F(FirebaseDynamicLinksTest, TestOpeningLongLinkInRunningApp) { + // On iOS, the dynamic link landing page requires a click. + // On Android, the first time a dynamic link is clicked on the device, Google + // Play services shows a TOS popup. Either way, this test requires user + // interaction. +#if (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) || defined(__ANDROID__) + TEST_REQUIRES_USER_INTERACTION; +#endif // (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) || + // defined(__ANDROID__) + + // This test uses a persistent key to keep track of how it's running. + const char kUrlToOpen[] = "https://google.com/test_opening_long_link"; + std::string persistent_key_str = test_info_->name(); + const char* persistent_key = persistent_key_str.c_str(); + + bool owns_current_test = ClaimCurrentTest(persistent_key); + + cleanup_persistent_keys_.push_back(persistent_key); + std::string value; + if (owns_current_test && !GetPersistentString(persistent_key, &value)) { + // The first time, create a dynamic link and open it in the browser. + LogDebug("First run, creating and opening long dynamic link..."); + + firebase::dynamic_links::DynamicLinkComponents components = + GenerateComponentsForTest(kUrlToOpen); + firebase::dynamic_links::GeneratedDynamicLink link = + firebase::dynamic_links::GetLongLink(components); + + if (is_desktop_stub_) { + // On desktop, it's enough that we just don't crash. + LogDebug("Succeeded as stub."); + SUCCEED(); + return; + } + EXPECT_TRUE(link.error.empty()); + SetPersistentString(persistent_key, kStateSentLink); + // This will trigger the test to start over. + OpenUrlInBrowser(link.url.c_str()); + exit(0); // Kill the app after opening the URL so it can be restarted + // properly. + } else if (owns_current_test && GetPersistentString(persistent_key, &value) && + value == kStateSentLink) { + // The second time, check that we received the dynamic link. + LogDebug("Second run, checking for dynamic link..."); + firebase::dynamic_links::DynamicLink got_link; + EXPECT_TRUE(shared_listener_->WaitForDynamicLink(&got_link)); + EXPECT_EQ(got_link.url, kUrlToOpen); + if (got_link.url == kUrlToOpen) { + SetPersistentString(persistent_key, kStateReceivedLink); + } else { + SetPersistentString(persistent_key, kStateReceivedLinkFail); + } + ReleaseCurrentTest(); + } else if (GetPersistentString(persistent_key, &value) && + value == kStateReceivedLink) { + // Already verified the link was correct. + LogDebug("Previously verified that dynamic link was received."); + SUCCEED(); + } else if (GetPersistentString(persistent_key, &value) && + value == kStateReceivedLinkFail) { + // Already verified the link failed. + FAIL() << "Previous attempt to get link failed."; + } else { + LogDebug("Skipping this test because another test has taken ownership."); + SUCCEED(); + } +} + +TEST_F(FirebaseDynamicLinksTest, TestOpeningShortLinkFromLongLinkInRunningApp) { + // On iOS, the dynamic link landing page requires a click. + // On Android, the first time a dynamic link is clicked on the device, Google + // Play services shows a TOS popup. Either way, this test requires user + // interaction. +#if (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) || defined(__ANDROID__) + TEST_REQUIRES_USER_INTERACTION; +#endif // (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) || + // defined(__ANDROID__) + + // This test uses a persistent key to keep track of how it's running. + const char kUrlToOpen[] = + "https://google.com/test_opening_short_link_from_long_link"; + std::string persistent_key_str = test_info_->name(); + const char* persistent_key = persistent_key_str.c_str(); + + bool owns_current_test = ClaimCurrentTest(persistent_key); + + cleanup_persistent_keys_.push_back(persistent_key); + std::string value; + if (owns_current_test && !GetPersistentString(persistent_key, &value)) { + // The first time, create a dynamic link and open it in the browser. + LogDebug( + "First run, creating and opening short dynamic link from long link..."); + firebase::dynamic_links::DynamicLinkComponents components = + GenerateComponentsForTest(kUrlToOpen); + firebase::dynamic_links::GeneratedDynamicLink long_link = + firebase::dynamic_links::GetLongLink(components); + // Shorten link. + firebase::dynamic_links::DynamicLinkOptions options; + options.path_length = firebase::dynamic_links::kPathLengthShort; + firebase::Future future = + firebase::dynamic_links::GetShortLink(long_link.url.c_str(), options); + + if (is_desktop_stub_) { + // On desktop, it's enough that we just don't crash. + LogDebug("Succeeded as stub."); + SUCCEED(); + return; + } + + WaitForCompletion(future, "GetShortLinkFromLongLink"); + const firebase::dynamic_links::GeneratedDynamicLink& link = + *future.result(); + + EXPECT_TRUE(link.error.empty()); + SetPersistentString(persistent_key, kStateSentLink); + // This will trigger the test to start over. + OpenUrlInBrowser(link.url.c_str()); + exit(0); // Kill the app after opening the URL so it can be restarted + // properly; + } else if (owns_current_test && GetPersistentString(persistent_key, &value) && + value == kStateSentLink) { + // The second time, check that we received the dynamic link. + LogDebug("Second run, checking for dynamic link..."); + firebase::dynamic_links::DynamicLink got_link; + EXPECT_TRUE(shared_listener_->WaitForDynamicLink(&got_link)); + EXPECT_EQ(got_link.url, kUrlToOpen); + if (got_link.url == kUrlToOpen) { + SetPersistentString(persistent_key, kStateReceivedLink); + } else { + SetPersistentString(persistent_key, kStateReceivedLinkFail); + } + ReleaseCurrentTest(); + } else if (GetPersistentString(persistent_key, &value) && + value == kStateReceivedLink) { + // Already verified the link was correct. + LogDebug("Previously verified that dynamic link was received."); + SUCCEED(); + } else if (GetPersistentString(persistent_key, &value) && + value == kStateReceivedLinkFail) { + // Already verified the link failed. + FAIL() << "Previous attempt to get link failed."; + } else { + LogDebug("Skipping this test because another test has taken ownership."); + SUCCEED(); + } +} + +TEST_F(FirebaseDynamicLinksTest, + TestOpeningShortLinkFromComponentsInRunningApp) { + // On iOS, the dynamic link landing page requires a click. + // On Android, the first time a dynamic link is clicked on the device, Google + // Play services shows a TOS popup. Either way, this test requires user + // interaction. +#if (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) || defined(__ANDROID__) + TEST_REQUIRES_USER_INTERACTION; +#endif // (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) || + // defined(__ANDROID__) + + // This test uses a persistent key to keep track of how it's running. + const char kUrlToOpen[] = + "https://google.com/test_opening_short_link_from_components"; + std::string persistent_key_str = test_info_->name(); + const char* persistent_key = persistent_key_str.c_str(); + + bool owns_current_test = ClaimCurrentTest(persistent_key); + + cleanup_persistent_keys_.push_back(persistent_key); + std::string value; + if (owns_current_test && !GetPersistentString(persistent_key, &value)) { + // The first time, create a dynamic link and open it in the browser. + LogDebug( + "First run, creating and opening short dynamic link from " + "components..."); + firebase::dynamic_links::DynamicLinkComponents components = + GenerateComponentsForTest(kUrlToOpen); + firebase::Future future = + firebase::dynamic_links::GetShortLink(components); + + if (is_desktop_stub_) { + // On desktop, it's enough that we just don't crash. + LogDebug("Succeeded as stub."); + SUCCEED(); + return; + } + + WaitForCompletion(future, "GetShortLinkFromLongLink"); + const firebase::dynamic_links::GeneratedDynamicLink& link = + *future.result(); + + EXPECT_TRUE(link.error.empty()); + SetPersistentString(persistent_key, kStateSentLink); + // This will trigger the test to start over. + OpenUrlInBrowser(link.url.c_str()); + exit(0); // Kill the app after opening the URL so it can be restarted + // properly. + } else if (owns_current_test && GetPersistentString(persistent_key, &value) && + value == kStateSentLink) { + // The second time, check that we received the dynamic link. + LogDebug("Second run, checking for dynamic link..."); + firebase::dynamic_links::DynamicLink got_link; + EXPECT_TRUE(shared_listener_->WaitForDynamicLink(&got_link)); + EXPECT_EQ(got_link.url, kUrlToOpen); + if (got_link.url == kUrlToOpen) { + SetPersistentString(persistent_key, kStateReceivedLink); + } else { + SetPersistentString(persistent_key, kStateReceivedLinkFail); + } + ReleaseCurrentTest(); + } else if (GetPersistentString(persistent_key, &value) && + value == kStateReceivedLink) { + // Already verified the link was correct. + LogDebug("Previously verified that dynamic link was received."); + SUCCEED(); + } else if (GetPersistentString(persistent_key, &value) && + value == kStateReceivedLinkFail) { + // Already verified the link failed. + FAIL() << "Previous attempt to get link failed."; + } else { + LogDebug("Skipping this test because another test has taken ownership."); + SUCCEED(); + } +} +} // namespace firebase_testapp_automated diff --git a/testing/integration_tests/firestore/AndroidManifest.xml b/testing/integration_tests/firestore/AndroidManifest.xml new file mode 100644 index 0000000000..53e48648f8 --- /dev/null +++ b/testing/integration_tests/firestore/AndroidManifest.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/testing/integration_tests/firestore/CMakeLists.txt b/testing/integration_tests/firestore/CMakeLists.txt new file mode 100644 index 0000000000..f9cdc244df --- /dev/null +++ b/testing/integration_tests/firestore/CMakeLists.txt @@ -0,0 +1,197 @@ +cmake_minimum_required(VERSION 2.8) + +# User settings for Firebase integration tests. +# Path to Firebase SDK. +# Try to read the path to the Firebase C++ SDK from an environment variable. +if (NOT "$ENV{FIREBASE_CPP_SDK_DIR}" STREQUAL "") + set(DEFAULT_FIREBASE_CPP_SDK_DIR "$ENV{FIREBASE_CPP_SDK_DIR}") +else() + set(DEFAULT_FIREBASE_CPP_SDK_DIR "firebase_cpp_sdk") +endif() +if ("${FIREBASE_CPP_SDK_DIR}" STREQUAL "") + set(FIREBASE_CPP_SDK_DIR ${DEFAULT_FIREBASE_CPP_SDK_DIR}) +endif() +if(NOT EXISTS ${FIREBASE_CPP_SDK_DIR}) + message(FATAL_ERROR "The Firebase C++ SDK directory does not exist: ${FIREBASE_CPP_SDK_DIR}. See the readme.md for more information") +endif() + +# Windows runtime mode, either MD or MT depending on whether you are using +# /MD or /MT. For more information see: +# https://msdn.microsoft.com/en-us/library/2kzt1wy3.aspx +set(MSVC_RUNTIME_MODE MD) + +project(firebase_testapp) + +# Integration test source files. +set(FIREBASE_APP_FRAMEWORK_SRCS + src/app_framework.cc + src/app_framework.h +) + +set(FIREBASE_TEST_FRAMEWORK_SRCS + src/firebase_test_framework.h + src/firebase_test_framework.cc +) + +set(FIREBASE_INTEGRATION_TEST_SRCS + src/integration_test.cc +) + +# The include directory for the testapp. +include_directories(src) + +# Integration test uses some features that require C++ 11, such as lambdas. +set (CMAKE_CXX_STANDARD 11) + +# Download and unpack googletest (and googlemock) at configure time +set(GOOGLETEST_ROOT ${CMAKE_CURRENT_LIST_DIR}/external/googletest) +# Note: Once googletest is downloaded once, it won't be updated or +# downloaded again unless you delete the "external/googletest" +# directory. +if (NOT EXISTS ${GOOGLETEST_ROOT}/src/googletest/src/gtest-all.cc) + configure_file(googletest.cmake + ${CMAKE_CURRENT_LIST_DIR}/external/googletest/CMakeLists.txt COPYONLY) + execute_process(COMMAND ${CMAKE_COMMAND} . + RESULT_VARIABLE result + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/external/googletest ) + if(result) + message(FATAL_ERROR "CMake step for googletest failed: ${result}") + endif() + execute_process(COMMAND ${CMAKE_COMMAND} --build . + RESULT_VARIABLE result + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/external/googletest ) + if(result) + message(FATAL_ERROR "Build step for googletest failed: ${result}") + endif() +endif() + +if(ANDROID) + # Build an Android application. + + # Source files used for the Android build. + set(FIREBASE_APP_FRAMEWORK_ANDROID_SRCS + src/android/android_app_framework.cc + ) + + # Source files used for the Android build. + set(FIREBASE_TEST_FRAMEWORK_ANDROID_SRCS + src/android/android_firebase_test_framework.cc + ) + + # Build native_app_glue as a static lib + add_library(native_app_glue STATIC + ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c) + + # Export ANativeActivity_onCreate(), + # Refer to: https://github.com/android-ndk/ndk/issues/381. + set(CMAKE_SHARED_LINKER_FLAGS + "${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate") + + add_library(gtest STATIC + ${GOOGLETEST_ROOT}/src/googletest/src/gtest-all.cc) + target_include_directories(gtest + PRIVATE ${GOOGLETEST_ROOT}/src/googletest + PUBLIC ${GOOGLETEST_ROOT}/src/googletest/include) + add_library(gmock STATIC + ${GOOGLETEST_ROOT}/src/googlemock/src/gmock-all.cc) + target_include_directories(gmock + PRIVATE ${GOOGLETEST_ROOT}/src/googletest + PRIVATE ${GOOGLETEST_ROOT}/src/googlemock + PUBLIC ${GOOGLETEST_ROOT}/src/googletest/include + PUBLIC ${GOOGLETEST_ROOT}/src/googlemock/include) + + # Define the target as a shared library, as that is what gradle expects. + set(integration_test_target_name "android_integration_test_main") + add_library(${integration_test_target_name} SHARED + ${FIREBASE_APP_FRAMEWORK_SRCS} + ${FIREBASE_APP_FRAMEWORK_ANDROID_SRCS} + ${FIREBASE_INTEGRATION_TEST_SRCS} + ${FIREBASE_TEST_FRAMEWORK_SRCS} + ${FIREBASE_TEST_FRAMEWORK_ANDROID_SRCS} + ) + + target_include_directories(${integration_test_target_name} PRIVATE + ${ANDROID_NDK}/sources/android/native_app_glue) + + set(ADDITIONAL_LIBS log android atomic native_app_glue) +else() + # Build a desktop application. + add_definitions(-D_GLIBCXX_USE_CXX11_ABI=0) + + # Prevent overriding the parent project's compiler/linker + # settings on Windows + set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) + + # Add googletest directly to our build. This defines + # the gtest and gtest_main targets. + add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/external/googletest/src + ${CMAKE_CURRENT_LIST_DIR}/external/googletest/build + EXCLUDE_FROM_ALL) + + # The gtest/gtest_main targets carry header search path + # dependencies automatically when using CMake 2.8.11 or + # later. Otherwise we have to add them here ourselves. + if (CMAKE_VERSION VERSION_LESS 2.8.11) + include_directories("${gtest_SOURCE_DIR}/include") + include_directories("${gmock_SOURCE_DIR}/include") + endif() + + # Windows runtime mode, either MD or MT depending on whether you are using + # /MD or /MT. For more information see: + # https://msdn.microsoft.com/en-us/library/2kzt1wy3.aspx + set(MSVC_RUNTIME_MODE MD) + + # Platform abstraction layer for the desktop integration test. + set(FIREBASE_APP_FRAMEWORK_DESKTOP_SRCS + src/desktop/desktop_app_framework.cc + ) + + set(integration_test_target_name "integration_test") + add_executable(${integration_test_target_name} + ${FIREBASE_APP_FRAMEWORK_SRCS} + ${FIREBASE_APP_FRAMEWORK_DESKTOP_SRCS} + ${FIREBASE_TEST_FRAMEWORK_SRCS} + ${FIREBASE_INTEGRATION_TEST_SRCS} + ) + + if(APPLE) + set(ADDITIONAL_LIBS + gssapi_krb5 + pthread + "-framework CoreFoundation" + "-framework Foundation" + "-framework GSS" + "-framework Security" + "-framework SystemConfiguration" + ) + elseif(MSVC) + set(ADDITIONAL_LIBS advapi32 ws2_32 crypt32) + else() + set(ADDITIONAL_LIBS pthread) + endif() + + # If a config file is present, copy it into the binary location so that it's + # possible to create the default Firebase app. + set(FOUND_JSON_FILE FALSE) + foreach(config "google-services-desktop.json" "google-services.json") + if (EXISTS ${config}) + add_custom_command( + TARGET ${integration_test_target_name} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + ${config} $) + set(FOUND_JSON_FILE TRUE) + break() + endif() + endforeach() + if(NOT FOUND_JSON_FILE) + message(WARNING "Failed to find either google-services-desktop.json or google-services.json. See the readme.md for more information.") + endif() +endif() + +# Add the Firebase libraries to the target using the function from the SDK. +add_subdirectory(${FIREBASE_CPP_SDK_DIR} bin/ EXCLUDE_FROM_ALL) +# Note that firebase_app needs to be last in the list. +set(firebase_libs firebase_firestore firebase_auth firebase_app) +set(gtest_libs gtest gmock) +target_link_libraries(${integration_test_target_name} ${firebase_libs} + ${gtest_libs} ${ADDITIONAL_LIBS}) diff --git a/testing/integration_tests/firestore/Images.xcassets/AppIcon.appiconset/Contents.json b/testing/integration_tests/firestore/Images.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000000..d8db8d65fd --- /dev/null +++ b/testing/integration_tests/firestore/Images.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "83.5x83.5", + "scale" : "2x" + }, + { + "idiom" : "ios-marketing", + "size" : "1024x1024", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/testing/integration_tests/firestore/Images.xcassets/LaunchImage.launchimage/Contents.json b/testing/integration_tests/firestore/Images.xcassets/LaunchImage.launchimage/Contents.json new file mode 100644 index 0000000000..6f870a4629 --- /dev/null +++ b/testing/integration_tests/firestore/Images.xcassets/LaunchImage.launchimage/Contents.json @@ -0,0 +1,51 @@ +{ + "images" : [ + { + "orientation" : "portrait", + "idiom" : "iphone", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "orientation" : "portrait", + "idiom" : "iphone", + "subtype" : "retina4", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "orientation" : "portrait", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "1x" + }, + { + "orientation" : "landscape", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "1x" + }, + { + "orientation" : "portrait", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "orientation" : "landscape", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/testing/integration_tests/firestore/Info.plist b/testing/integration_tests/firestore/Info.plist new file mode 100644 index 0000000000..c44816f632 --- /dev/null +++ b/testing/integration_tests/firestore/Info.plist @@ -0,0 +1,39 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + com.google.firebase.cpp.firestore.testapp + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleURLTypes + + + CFBundleTypeRole + Editor + CFBundleURLName + google + CFBundleURLSchemes + + com.googleusercontent.apps.80955193299-dpoqtev9j10pffr05pmsikfue7oc2dal + + + + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + + diff --git a/testing/integration_tests/firestore/LaunchScreen.storyboard b/testing/integration_tests/firestore/LaunchScreen.storyboard new file mode 100644 index 0000000000..673e0f7e68 --- /dev/null +++ b/testing/integration_tests/firestore/LaunchScreen.storyboard @@ -0,0 +1,7 @@ + + + + + + + diff --git a/testing/integration_tests/firestore/LibraryManifest.xml b/testing/integration_tests/firestore/LibraryManifest.xml new file mode 100644 index 0000000000..ca63578077 --- /dev/null +++ b/testing/integration_tests/firestore/LibraryManifest.xml @@ -0,0 +1,7 @@ + + + + + + diff --git a/testing/integration_tests/firestore/Podfile b/testing/integration_tests/firestore/Podfile new file mode 100644 index 0000000000..5a6dccd269 --- /dev/null +++ b/testing/integration_tests/firestore/Podfile @@ -0,0 +1,14 @@ +source 'sso://cpdc-internal/firebase.git' +source 'https://github.com/CocoaPods/Specs.git' +platform :ios, '8.0' +# Firebase Realtime Firestore test application. + +target 'integration_test' do + pod 'Firebase/Firestore', '6.24.0' + pod 'Firebase/Auth', '6.24.0' +end + +post_install do |installer| + system("/usr/bin/python ./download_googletest.py") +end + diff --git a/testing/integration_tests/firestore/build.gradle b/testing/integration_tests/firestore/build.gradle new file mode 100644 index 0000000000..946ce6b41f --- /dev/null +++ b/testing/integration_tests/firestore/build.gradle @@ -0,0 +1,66 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +buildscript { + repositories { + mavenLocal() + maven { url 'https://maven.google.com' } + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:3.2.1' + classpath 'com.google.gms:google-services:4.0.1' + } +} + +allprojects { + repositories { + mavenLocal() + maven { url 'https://maven.google.com' } + jcenter() + } +} + +apply plugin: 'com.android.application' + +android { + compileSdkVersion 28 + buildToolsVersion '28.0.3' + + sourceSets { + main { + jniLibs.srcDirs = ['libs'] + manifest.srcFile 'AndroidManifest.xml' + java.srcDirs = ['src/android/java'] + res.srcDirs = ['res'] + } + } + + defaultConfig { + applicationId 'com.google.firebase.cpp.firestore.testapp' + minSdkVersion 16 + targetSdkVersion 28 + versionCode 1 + versionName '1.0' + externalNativeBuild.cmake { + arguments "-DFIREBASE_CPP_SDK_DIR=$gradle.firebase_cpp_sdk_dir" + } + multiDexEnabled true + } + externalNativeBuild.cmake { + path 'CMakeLists.txt' + } + buildTypes { + release { + minifyEnabled true + proguardFile getDefaultProguardFile('proguard-android.txt') + proguardFile file('proguard.pro') + } + } +} + +apply from: "$gradle.firebase_cpp_sdk_dir/Android/firebase_dependencies.gradle" +firebaseCpp.dependencies { + auth + firestore +} + +apply plugin: 'com.google.gms.google-services' diff --git a/testing/integration_tests/firestore/googletest.cmake b/testing/integration_tests/firestore/googletest.cmake new file mode 100644 index 0000000000..0eeff685dd --- /dev/null +++ b/testing/integration_tests/firestore/googletest.cmake @@ -0,0 +1,19 @@ +# Download GoogleTest from GitHub as an external project. +# This CMake file is taken from: +# https://github.com/google/googletest/blob/master/googletest/README.md#incorporating-into-an-existing-cmake-project + +cmake_minimum_required(VERSION 2.8.2) + +project(googletest-download NONE) + +include(ExternalProject) +ExternalProject_Add(googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG master + SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/src" + BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/build" + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + TEST_COMMAND "" +) diff --git a/testing/integration_tests/firestore/gradle/wrapper/gradle-wrapper.jar b/testing/integration_tests/firestore/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..8c0fb64a8698b08ecc4158d828ca593c4928e9dd GIT binary patch literal 49896 zcmagFb986H(k`5d^NVfUwr$(C?M#x1ZQHiZiEVpg+jrjgoQrerx!>1o_ul)D>ebz~ zs=Mmxr&>W81QY-S1PKWQ%N-;H^tS;2*XwVA`dej1RRn1z<;3VgfE4~kaG`A%QSPsR z#ovnZe+tS9%1MfeDyz`RirvdjPRK~p(#^q2(^5@O&NM19EHdvN-A&StN>0g6QA^VN z0Gx%Gq#PD$QMRFzmK+utjS^Y1F0e8&u&^=w5K<;4Rz|i3A=o|IKLY+g`iK6vfr9?+ z-`>gmU&i?FGSL5&F?TXFu`&Js6h;15QFkXp2M1H9|Eq~bpov-GU(uz%mH0n55wUl- zv#~ccAz`F5wlQ>e_KlJS3@{)B?^v*EQM=IxLa&76^y51a((wq|2-`qON>+4dLc{Oo z51}}o^Zen(oAjxDK7b++9_Yg`67p$bPo3~BCpGM7uAWmvIhWc5Gi+gQZ|Pwa-Gll@<1xmcPy z|NZmu6m)g5Ftu~BG&Xdxclw7Cij{xbBMBn-LMII#Slp`AElb&2^Hw+w>(3crLH!;I zN+Vk$D+wP1#^!MDCiad@vM>H#6+`Ct#~6VHL4lzmy;lSdk>`z6)=>Wh15Q2)dQtGqvn0vJU@+(B5{MUc*qs4!T+V=q=wy)<6$~ z!G>e_4dN@lGeF_$q9`Ju6Ncb*x?O7=l{anm7Eahuj_6lA{*#Gv*TaJclevPVbbVYu z(NY?5q+xxbO6%g1xF0r@Ix8fJ~u)VRUp`S%&rN$&e!Od`~s+64J z5*)*WSi*i{k%JjMSIN#X;jC{HG$-^iX+5f5BGOIHWAl*%15Z#!xntpk($-EGKCzKa zT7{siZ9;4TICsWQ$pu&wKZQTCvpI$Xvzwxoi+XkkpeE&&kFb!B?h2hi%^YlXt|-@5 zHJ~%AN!g_^tmn1?HSm^|gCE#!GRtK2(L{9pL#hp0xh zME}|DB>(5)`iE7CM)&_+S}-Bslc#@B5W4_+k4Cp$l>iVyg$KP>CN?SVGZ(&02>iZK zB<^HP$g$Lq*L$BWd?2(F?-MUbNWTJVQdW7$#8a|k_30#vHAD1Z{c#p;bETk0VnU5A zBgLe2HFJ3032$G<`m*OB!KM$*sdM20jm)It5OSru@tXpK5LT>#8)N!*skNu1$TpIw zufjjdp#lyH5bZ%|Iuo|iu9vG1HrIVWLH>278xo>aVBkPN3V$~!=KnlXQ4eDqS7%E% zQ!z^$Q$b^6Q)g#cLpwur(|<0gWHo6A6jc;n`t(V9T;LzTAU{IAu*uEQ%Ort1k+Kn+f_N`9|bxYC+~Z1 zCC1UCWv*Orx$_@ydv9mIe(liLfOr7mhbV@tKw{6)q^1DH1nmvZ0cj215R<~&I<4S| zgnr;9Cdjqpz#o8i0CQjtl`}{c*P)aSdH|abxGdrR)-3z+02-eX(k*B)Uqv6~^nh** z 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 \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/testing/integration_tests/firestore/gradlew.bat b/testing/integration_tests/firestore/gradlew.bat new file mode 100644 index 0000000000..8a0b282aa6 --- /dev/null +++ b/testing/integration_tests/firestore/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/testing/integration_tests/firestore/integration_test.xcodeproj/project.pbxproj b/testing/integration_tests/firestore/integration_test.xcodeproj/project.pbxproj new file mode 100644 index 0000000000..baae0f5067 --- /dev/null +++ b/testing/integration_tests/firestore/integration_test.xcodeproj/project.pbxproj @@ -0,0 +1,372 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 520BC0391C869159008CFBC3 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 520BC0381C869159008CFBC3 /* GoogleService-Info.plist */; }; + 529226D61C85F68000C89379 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 529226D51C85F68000C89379 /* Foundation.framework */; }; + 529226D81C85F68000C89379 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 529226D71C85F68000C89379 /* CoreGraphics.framework */; }; + 529226DA1C85F68000C89379 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 529226D91C85F68000C89379 /* UIKit.framework */; }; + D61C5F8E22BABA9C00A79141 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D61C5F8C22BABA9B00A79141 /* Images.xcassets */; }; + D61C5F9622BABAD200A79141 /* integration_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = D61C5F9222BABAD100A79141 /* integration_test.cc */; }; + D62CCBC022F367140099BE9F /* gmock-all.cc in Sources */ = {isa = PBXBuildFile; fileRef = D62CCBBF22F367140099BE9F /* gmock-all.cc */; }; + D66B16871CE46E8900E5638A /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D66B16861CE46E8900E5638A /* LaunchScreen.storyboard */; }; + D67D355822BABD2200292C1D /* gtest-all.cc in Sources */ = {isa = PBXBuildFile; fileRef = D67D355622BABD2100292C1D /* gtest-all.cc */; }; + D6C179E922CB322900C2651A /* ios_app_framework.mm in Sources */ = {isa = PBXBuildFile; fileRef = D6C179E722CB322900C2651A /* ios_app_framework.mm */; }; + D6C179EA22CB322900C2651A /* ios_firebase_test_framework.mm in Sources */ = {isa = PBXBuildFile; fileRef = D6C179E822CB322900C2651A /* ios_firebase_test_framework.mm */; }; + D6C179EE22CB323300C2651A /* firebase_test_framework.cc in Sources */ = {isa = PBXBuildFile; fileRef = D6C179EC22CB323300C2651A /* firebase_test_framework.cc */; }; + D6C179F022CB32A000C2651A /* app_framework.cc in Sources */ = {isa = PBXBuildFile; fileRef = D6C179EF22CB32A000C2651A /* app_framework.cc */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 520BC0381C869159008CFBC3 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; + 529226D21C85F68000C89379 /* integration_test.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = integration_test.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 529226D51C85F68000C89379 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + 529226D71C85F68000C89379 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; + 529226D91C85F68000C89379 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; + 529226EE1C85F68000C89379 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; + D61C5F8C22BABA9B00A79141 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; + D61C5F8D22BABA9C00A79141 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + D61C5F9222BABAD100A79141 /* integration_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = integration_test.cc; path = src/integration_test.cc; sourceTree = ""; }; + D62CCBBF22F367140099BE9F /* gmock-all.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "gmock-all.cc"; path = "external/googletest/src/googlemock/src/gmock-all.cc"; sourceTree = ""; }; + D62CCBC122F367320099BE9F /* gmock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = gmock.h; path = external/googletest/src/googlemock/include/gmock/gmock.h; sourceTree = ""; }; + D66B16861CE46E8900E5638A /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = ""; }; + D67D355622BABD2100292C1D /* gtest-all.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "gtest-all.cc"; path = "external/googletest/src/googletest/src/gtest-all.cc"; sourceTree = ""; }; + D67D355722BABD2100292C1D /* gtest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = gtest.h; path = external/googletest/src/googletest/include/gtest/gtest.h; sourceTree = ""; }; + D6C179E722CB322900C2651A /* ios_app_framework.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ios_app_framework.mm; path = src/ios/ios_app_framework.mm; sourceTree = ""; }; + D6C179E822CB322900C2651A /* ios_firebase_test_framework.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ios_firebase_test_framework.mm; path = src/ios/ios_firebase_test_framework.mm; sourceTree = ""; }; + D6C179EB22CB323300C2651A /* firebase_test_framework.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = firebase_test_framework.h; path = src/firebase_test_framework.h; sourceTree = ""; }; + D6C179EC22CB323300C2651A /* firebase_test_framework.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = firebase_test_framework.cc; path = src/firebase_test_framework.cc; sourceTree = ""; }; + D6C179ED22CB323300C2651A /* app_framework.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = app_framework.h; path = src/app_framework.h; sourceTree = ""; }; + D6C179EF22CB32A000C2651A /* app_framework.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = app_framework.cc; path = src/app_framework.cc; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 529226CF1C85F68000C89379 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 529226D81C85F68000C89379 /* CoreGraphics.framework in Frameworks */, + 529226DA1C85F68000C89379 /* UIKit.framework in Frameworks */, + 529226D61C85F68000C89379 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 2AD084A34AA54558B7AFA683 /* Pods */ = { + isa = PBXGroup; + children = ( + ); + path = Pods; + sourceTree = ""; + }; + 529226C91C85F68000C89379 = { + isa = PBXGroup; + children = ( + D61C5F8C22BABA9B00A79141 /* Images.xcassets */, + D61C5F8D22BABA9C00A79141 /* Info.plist */, + D66B16861CE46E8900E5638A /* LaunchScreen.storyboard */, + 520BC0381C869159008CFBC3 /* GoogleService-Info.plist */, + 5292271D1C85FB5500C89379 /* src */, + 529226D41C85F68000C89379 /* Frameworks */, + 529226D31C85F68000C89379 /* Products */, + 2AD084A34AA54558B7AFA683 /* Pods */, + ); + sourceTree = ""; + }; + 529226D31C85F68000C89379 /* Products */ = { + isa = PBXGroup; + children = ( + 529226D21C85F68000C89379 /* integration_test.app */, + ); + name = Products; + sourceTree = ""; + }; + 529226D41C85F68000C89379 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 529226D51C85F68000C89379 /* Foundation.framework */, + 529226D71C85F68000C89379 /* CoreGraphics.framework */, + 529226D91C85F68000C89379 /* UIKit.framework */, + 529226EE1C85F68000C89379 /* XCTest.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 5292271D1C85FB5500C89379 /* src */ = { + isa = PBXGroup; + children = ( + D62CCBC122F367320099BE9F /* gmock.h */, + D62CCBBF22F367140099BE9F /* gmock-all.cc */, + D67D355622BABD2100292C1D /* gtest-all.cc */, + D67D355722BABD2100292C1D /* gtest.h */, + D6C179EF22CB32A000C2651A /* app_framework.cc */, + D6C179ED22CB323300C2651A /* app_framework.h */, + D6C179EC22CB323300C2651A /* firebase_test_framework.cc */, + D6C179EB22CB323300C2651A /* firebase_test_framework.h */, + D61C5F9222BABAD100A79141 /* integration_test.cc */, + 5292271E1C85FB5B00C89379 /* ios */, + ); + name = src; + sourceTree = ""; + }; + 5292271E1C85FB5B00C89379 /* ios */ = { + isa = PBXGroup; + children = ( + D6C179E722CB322900C2651A /* ios_app_framework.mm */, + D6C179E822CB322900C2651A /* ios_firebase_test_framework.mm */, + ); + name = ios; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 529226D11C85F68000C89379 /* integration_test */ = { + isa = PBXNativeTarget; + buildConfigurationList = 529226F91C85F68000C89379 /* Build configuration list for PBXNativeTarget "integration_test" */; + buildPhases = ( + 529226CE1C85F68000C89379 /* Sources */, + 529226CF1C85F68000C89379 /* Frameworks */, + 529226D01C85F68000C89379 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = integration_test; + productName = testapp; + productReference = 529226D21C85F68000C89379 /* integration_test.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 529226CA1C85F68000C89379 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0640; + ORGANIZATIONNAME = Google; + TargetAttributes = { + 529226D11C85F68000C89379 = { + CreatedOnToolsVersion = 6.4; + DevelopmentTeam = EQHXZ8M8AV; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = 529226CD1C85F68000C89379 /* Build configuration list for PBXProject "integration_test" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 529226C91C85F68000C89379; + productRefGroup = 529226D31C85F68000C89379 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 529226D11C85F68000C89379 /* integration_test */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 529226D01C85F68000C89379 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D61C5F8E22BABA9C00A79141 /* Images.xcassets in Resources */, + D66B16871CE46E8900E5638A /* LaunchScreen.storyboard in Resources */, + 520BC0391C869159008CFBC3 /* GoogleService-Info.plist in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 529226CE1C85F68000C89379 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D67D355822BABD2200292C1D /* gtest-all.cc in Sources */, + D62CCBC022F367140099BE9F /* gmock-all.cc in Sources */, + D6C179EA22CB322900C2651A /* ios_firebase_test_framework.mm in Sources */, + D61C5F9622BABAD200A79141 /* integration_test.cc in Sources */, + D6C179E922CB322900C2651A /* ios_app_framework.mm in Sources */, + D6C179F022CB32A000C2651A /* app_framework.cc in Sources */, + D6C179EE22CB323300C2651A /* firebase_test_framework.cc in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 529226F71C85F68000C89379 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.4; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 529226F81C85F68000C89379 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.4; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 529226FA1C85F68000C89379 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ""; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)", + ); + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + "\"$(SRCROOT)/src\"", + "\"$(SRCROOT)/external/googletest/src/googletest/include\"", + "\"$(SRCROOT)/external/googletest/src/googlemock/include\"", + "\"$(SRCROOT)/external/googletest/src/googletest\"", + "\"$(SRCROOT)/external/googletest/src/googlemock\"", + ); + INFOPLIST_FILE = "$(SRCROOT)/Info.plist"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + WRAPPER_EXTENSION = app; + }; + name = Debug; + }; + 529226FB1C85F68000C89379 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ""; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)", + ); + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + "\"$(SRCROOT)/src\"", + "\"$(SRCROOT)/external/googletest/src/googletest/include\"", + "\"$(SRCROOT)/external/googletest/src/googlemock/include\"", + "\"$(SRCROOT)/external/googletest/src/googletest\"", + "\"$(SRCROOT)/external/googletest/src/googlemock\"", + ); + INFOPLIST_FILE = "$(SRCROOT)/Info.plist"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + WRAPPER_EXTENSION = app; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 529226CD1C85F68000C89379 /* Build configuration list for PBXProject "integration_test" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 529226F71C85F68000C89379 /* Debug */, + 529226F81C85F68000C89379 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 529226F91C85F68000C89379 /* Build configuration list for PBXNativeTarget "integration_test" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 529226FA1C85F68000C89379 /* Debug */, + 529226FB1C85F68000C89379 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 529226CA1C85F68000C89379 /* Project object */; +} diff --git a/testing/integration_tests/firestore/proguard.pro b/testing/integration_tests/firestore/proguard.pro new file mode 100644 index 0000000000..2d04b8a9a5 --- /dev/null +++ b/testing/integration_tests/firestore/proguard.pro @@ -0,0 +1,2 @@ +-ignorewarnings +-keep,includedescriptorclasses public class com.google.firebase.example.LoggingUtils { * ; } diff --git a/testing/integration_tests/firestore/res/layout/main.xml b/testing/integration_tests/firestore/res/layout/main.xml new file mode 100644 index 0000000000..d3ffb63082 --- /dev/null +++ b/testing/integration_tests/firestore/res/layout/main.xml @@ -0,0 +1,12 @@ + + + + diff --git a/testing/integration_tests/firestore/res/values/strings.xml b/testing/integration_tests/firestore/res/values/strings.xml new file mode 100644 index 0000000000..a91a6c74e9 --- /dev/null +++ b/testing/integration_tests/firestore/res/values/strings.xml @@ -0,0 +1,4 @@ + + + Firebase Firestore Integration Test + diff --git a/testing/integration_tests/firestore/settings.gradle b/testing/integration_tests/firestore/settings.gradle new file mode 100644 index 0000000000..2a543b9351 --- /dev/null +++ b/testing/integration_tests/firestore/settings.gradle @@ -0,0 +1,36 @@ +// Copyright 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +def firebase_cpp_sdk_dir = System.getProperty('firebase_cpp_sdk.dir') +if (firebase_cpp_sdk_dir == null || firebase_cpp_sdk_dir.isEmpty()) { + firebase_cpp_sdk_dir = System.getenv('FIREBASE_CPP_SDK_DIR') + if (firebase_cpp_sdk_dir == null || firebase_cpp_sdk_dir.isEmpty()) { + if ((new File('firebase_cpp_sdk')).exists()) { + firebase_cpp_sdk_dir = 'firebase_cpp_sdk' + } else { + throw new StopActionException( + 'firebase_cpp_sdk.dir property or the FIREBASE_CPP_SDK_DIR ' + + 'environment variable must be set to reference the Firebase C++ ' + + 'SDK install directory. This is used to configure static library ' + + 'and C/C++ include paths for the SDK.') + } + } +} +if (!(new File(firebase_cpp_sdk_dir)).exists()) { + throw new StopActionException( + sprintf('Firebase C++ SDK directory %s does not exist', + firebase_cpp_sdk_dir)) +} +gradle.ext.firebase_cpp_sdk_dir = "$firebase_cpp_sdk_dir" +includeBuild "$firebase_cpp_sdk_dir" \ No newline at end of file diff --git a/testing/integration_tests/firestore/src/integration_test.cc b/testing/integration_tests/firestore/src/integration_test.cc new file mode 100644 index 0000000000..38f9128f0d --- /dev/null +++ b/testing/integration_tests/firestore/src/integration_test.cc @@ -0,0 +1,565 @@ +// Copyright 2019 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "app_framework.h" // NOLINT +#include "firebase/auth.h" +#include "firebase/firestore.h" +#include "firebase_test_framework.h" // NOLINT + +// The TO_STRING macro is useful for command line defined strings as the quotes +// get stripped. +#define TO_STRING_EXPAND(X) #X +#define TO_STRING(X) TO_STRING_EXPAND(X) + +// Path to the Firebase config file to load. +#ifdef FIREBASE_CONFIG +#define FIREBASE_CONFIG_STRING TO_STRING(FIREBASE_CONFIG) +#else +#define FIREBASE_CONFIG_STRING "" +#endif // FIREBASE_CONFIG + +namespace firebase_testapp_automated { + +using app_framework::GetCurrentTimeInMicroseconds; +using app_framework::LogDebug; +using app_framework::ProcessEvents; +using firebase_test_framework::FirebaseTest; +using testing::ElementsAre; +using testing::Pair; +using testing::ResultOf; +using testing::UnorderedElementsAre; + +// Very basic first-level tests for Firestore. More comprehensive integration +// tests are contained in other source files. +class FirebaseFirestoreBasicTest : public FirebaseTest { + public: + FirebaseFirestoreBasicTest(); + ~FirebaseFirestoreBasicTest() override; + + void SetUp() override; + void TearDown() override; + + protected: + // Initialize Firebase App, Firebase Auth, and Firebase Firestore. + void Initialize(); + // Shut down Firebase Firestore, Firebase Auth, and Firebase App. + void Terminate(); + // Sign in an anonymous user. + void SignIn(); + + // Create a custom-named collection to work with for this test. + firebase::firestore::CollectionReference GetTestCollection(); + + // Add the DocumentReference to the cleanup list. At TearDown, all these + // documents will be deleted. + firebase::firestore::DocumentReference Cleanup( + const firebase::firestore::DocumentReference& doc) { + if (find(cleanup_documents_.begin(), cleanup_documents_.end(), doc) == + cleanup_documents_.end()) { + cleanup_documents_.push_back(doc); + } + // Pass through the DocumentReference to simplify test code. + return doc; + } + + firebase::firestore::DocumentReference Doc(const char* suffix = ""); + + bool initialized_ = false; + firebase::auth::Auth* auth_ = nullptr; + firebase::firestore::Firestore* firestore_ = nullptr; + + std::string collection_name_; + std::vector cleanup_documents_; +}; + +FirebaseFirestoreBasicTest::FirebaseFirestoreBasicTest() { + FindFirebaseConfig(FIREBASE_CONFIG_STRING); +} + +FirebaseFirestoreBasicTest::~FirebaseFirestoreBasicTest() { + // Must be cleaned up on exit. + assert(app_ == nullptr); + assert(auth_ == nullptr); + assert(firestore_ == nullptr); +} + +void FirebaseFirestoreBasicTest::SetUp() { + FirebaseTest::SetUp(); + Initialize(); +} + +void FirebaseFirestoreBasicTest::TearDown() { + // Delete the shared path, if there is one. + if (initialized_) { + if (!cleanup_documents_.empty()) { + LogDebug("Cleaning up documents."); + std::vector> cleanups; + cleanups.reserve(cleanup_documents_.size()); + for (int i = 0; i < cleanup_documents_.size(); ++i) { + cleanups.push_back(cleanup_documents_[i].Delete()); + } + for (int i = 0; i < cleanups.size(); ++i) { + WaitForCompletion(cleanups[i], "FirebaseDatabaseTest::TearDown"); + } + cleanup_documents_.clear(); + } + Terminate(); + } + FirebaseTest::TearDown(); +} + +void FirebaseFirestoreBasicTest::Initialize() { + if (initialized_) return; + + InitializeApp(); + + LogDebug("Initializing Firebase Auth and Firebase Firestore."); + + // 0th element has a reference to this object, the rest have the initializer + // targets. + void* initialize_targets[] = {&auth_, &firestore_}; + + const firebase::ModuleInitializer::InitializerFn initializers[] = { + [](::firebase::App* app, void* data) { + void** targets = reinterpret_cast(data); + LogDebug("Attempting to initialize Firebase Auth."); + ::firebase::InitResult result; + *reinterpret_cast<::firebase::auth::Auth**>(targets[0]) = + ::firebase::auth::Auth::GetAuth(app, &result); + return result; + }, + [](::firebase::App* app, void* data) { + void** targets = reinterpret_cast(data); + LogDebug("Attempting to initialize Firebase Firestore."); + ::firebase::InitResult result; + firebase::firestore::Firestore* firestore = + firebase::firestore::Firestore::GetInstance(app, &result); + *reinterpret_cast<::firebase::firestore::Firestore**>(targets[1]) = + firestore; + return result; + }}; + + ::firebase::ModuleInitializer initializer; + initializer.Initialize(app_, initialize_targets, initializers, + sizeof(initializers) / sizeof(initializers[0])); + + WaitForCompletion(initializer.InitializeLastResult(), "Initialize"); + + ASSERT_EQ(initializer.InitializeLastResult().error(), 0) + << initializer.InitializeLastResult().error_message(); + + LogDebug("Successfully initialized Firebase Auth and Firebase Firestore."); + + initialized_ = true; +} + +void FirebaseFirestoreBasicTest::Terminate() { + if (!initialized_) return; + + if (firestore_) { + LogDebug("Shutdown the Firestore library."); + delete firestore_; + firestore_ = nullptr; + } + if (auth_) { + LogDebug("Shutdown the Auth library."); + delete auth_; + auth_ = nullptr; + } + + TerminateApp(); + + initialized_ = false; + + ProcessEvents(100); +} + +void FirebaseFirestoreBasicTest::SignIn() { + LogDebug("Signing in."); + firebase::Future sign_in_future = + auth_->SignInAnonymously(); + WaitForCompletion(sign_in_future, "SignInAnonymously"); + if (sign_in_future.error() != 0) { + FAIL() << "Ensure your application has the Anonymous sign-in provider " + "enabled in Firebase Console."; + } + ProcessEvents(100); +} + +firebase::firestore::CollectionReference +FirebaseFirestoreBasicTest::GetTestCollection() { + if (collection_name_.empty()) { + // Generate a collection for the test data based on the time in + // milliseconds. + int64_t time_in_microseconds = GetCurrentTimeInMicroseconds(); + + char buffer[21] = {0}; + snprintf(buffer, sizeof(buffer), "test%lld", + static_cast(time_in_microseconds)); // NOLINT + collection_name_ = buffer; + } + return firestore_->Collection(collection_name_.c_str()); +} + +firebase::firestore::DocumentReference FirebaseFirestoreBasicTest::Doc( + const char* suffix) { + std::string path = + std::string( + ::testing::UnitTest::GetInstance()->current_test_info()->name()) + + suffix; + return Cleanup(GetTestCollection().Document(path)); +} + +// Test cases below. + +TEST_F(FirebaseFirestoreBasicTest, TestInitializeAndTerminate) { + // Already tested via SetUp() and TearDown(). +} + +TEST_F(FirebaseFirestoreBasicTest, TestSignIn) { + SignIn(); + EXPECT_NE(auth_->current_user(), nullptr); +} + +TEST_F(FirebaseFirestoreBasicTest, TestAppAndSettings) { + EXPECT_EQ(firestore_->app(), app_); + firebase::firestore::Settings settings = firestore_->settings(); + firestore_->set_settings(settings); + // No comparison operator in settings, so just assume it worked if we didn't + // crash. +} + +TEST_F(FirebaseFirestoreBasicTest, TestNonWrappedTypes) { + const firebase::Timestamp timestamp{1, 2}; + EXPECT_EQ(timestamp.seconds(), 1); + EXPECT_EQ(timestamp.nanoseconds(), 2); + const firebase::firestore::SnapshotMetadata metadata{ + /*has_pending_writes*/ false, /*is_from_cache*/ true}; + EXPECT_FALSE(metadata.has_pending_writes()); + EXPECT_TRUE(metadata.is_from_cache()); + const firebase::firestore::GeoPoint point{1.23, 4.56}; + EXPECT_EQ(point.latitude(), 1.23); + EXPECT_EQ(point.longitude(), 4.56); +} + +TEST_F(FirebaseFirestoreBasicTest, TestCollection) { + firebase::firestore::CollectionReference collection = + firestore_->Collection("foo"); + EXPECT_EQ(collection.firestore(), firestore_); + EXPECT_EQ(collection.id(), "foo"); + EXPECT_EQ(collection.Document("bar").path(), "foo/bar"); +} + +TEST_F(FirebaseFirestoreBasicTest, TestDocument) { + firebase::firestore::DocumentReference document = + firestore_->Document("foo/bar"); + EXPECT_EQ(document.firestore(), firestore_); + EXPECT_EQ(document.path(), "foo/bar"); +} + +TEST_F(FirebaseFirestoreBasicTest, TestSetGet) { + SignIn(); + + firebase::firestore::DocumentReference document = Doc(); + + WaitForCompletion( + document.Set(firebase::firestore::MapFieldValue{ + {"str", firebase::firestore::FieldValue::String("foo")}, + {"int", firebase::firestore::FieldValue::Integer(123)}}), + "document.Set"); + firebase::Future future = + document.Get(); + WaitForCompletion(future, "document.Get"); + EXPECT_NE(future.result(), nullptr); + EXPECT_THAT(future.result()->GetData(), + UnorderedElementsAre( + Pair("str", firebase::firestore::FieldValue::String("foo")), + Pair("int", firebase::firestore::FieldValue::Integer(123)))); +} + +TEST_F(FirebaseFirestoreBasicTest, TestSetUpdateGet) { + SignIn(); + + firebase::firestore::DocumentReference document = Doc(); + + WaitForCompletion( + document.Set(firebase::firestore::MapFieldValue{ + {"str", firebase::firestore::FieldValue::String("foo")}, + {"int", firebase::firestore::FieldValue::Integer(123)}}), + "document.Set"); + WaitForCompletion( + document.Update(firebase::firestore::MapFieldValue{ + {"int", firebase::firestore::FieldValue::Integer(321)}}), + "document.Update"); + firebase::Future future = + document.Get(); + WaitForCompletion(future, "document.Get"); + EXPECT_NE(future.result(), nullptr); + EXPECT_THAT(future.result()->GetData(), + UnorderedElementsAre( + Pair("str", firebase::firestore::FieldValue::String("foo")), + Pair("int", firebase::firestore::FieldValue::Integer(321)))); +} + +TEST_F(FirebaseFirestoreBasicTest, TestSetDelete) { + SignIn(); + + firebase::firestore::DocumentReference document = Doc(); + + WaitForCompletion( + document.Set(firebase::firestore::MapFieldValue{ + {"str", firebase::firestore::FieldValue::String("bar")}, + {"int", firebase::firestore::FieldValue::Integer(456)}}), + "document.Set"); + + WaitForCompletion(document.Delete(), "document.Delete"); + firebase::Future future = + document.Get(); + WaitForCompletion(future, "document.Get"); + EXPECT_NE(future.result(), nullptr); + EXPECT_FALSE(future.result()->exists()); + + // TODO(b/139064606): Test error cases (deleting invalid path, etc.) +} + +TEST_F(FirebaseFirestoreBasicTest, TestDocumentListener) { + SKIP_TEST_IF_USING_STLPORT; // STLPort uses EventListener rather than + // std::function. +#if !defined(STLPORT) + SignIn(); + + firebase::firestore::DocumentReference document = Doc(); + + WaitForCompletion( + document.Set(firebase::firestore::MapFieldValue{ + {"val", firebase::firestore::FieldValue::String("start")}}), + "document.Set 0"); + + std::vector document_snapshots; + firebase::firestore::ListenerRegistration registration = + document.AddSnapshotListener( + [&](const firebase::firestore::DocumentSnapshot& result, + firebase::firestore::Error error) { + EXPECT_EQ(error, firebase::firestore::kErrorOk); + document_snapshots.push_back(result.GetData()); + }); + + WaitForCompletion( + document.Set(firebase::firestore::MapFieldValue{ + {"val", firebase::firestore::FieldValue::String("update")}}), + "document.Set 1"); + registration.Remove(); + WaitForCompletion( + document.Set(firebase::firestore::MapFieldValue{ + {"val", firebase::firestore::FieldValue::String("final")}}), + "document.Set 2"); + EXPECT_THAT( + document_snapshots, + ElementsAre( + firebase::firestore::MapFieldValue{ + {"val", firebase::firestore::FieldValue::String("start")}}, + firebase::firestore::MapFieldValue{ + {"val", firebase::firestore::FieldValue::String("update")}})); +#endif // !defined(STLPORT) +} + +TEST_F(FirebaseFirestoreBasicTest, TestBatchWrite) { + SignIn(); + + firebase::firestore::DocumentReference document1 = Doc("1"); + firebase::firestore::DocumentReference document2 = Doc("2"); + + firebase::firestore::WriteBatch batch = firestore_->batch(); + batch.Set(document1, + firebase::firestore::MapFieldValue{ + {"str", firebase::firestore::FieldValue::String("first")}}); + batch.Set(document2, + firebase::firestore::MapFieldValue{ + {"int", firebase::firestore::FieldValue::Integer(2222)}}); + WaitForCompletion(batch.Commit(), "batch.Commit"); + + // Confirm the updated docs are correct. + auto future1 = Doc("1").Get(); + WaitForCompletion(future1, "document.Get 1"); + EXPECT_THAT(future1.result()->GetData(), + ElementsAre(Pair( + "str", firebase::firestore::FieldValue::String("first")))); + + auto future2 = Doc("2").Get(); + WaitForCompletion(future2, "document.Get 2"); + EXPECT_THAT( + future2.result()->GetData(), + ElementsAre(Pair("int", firebase::firestore::FieldValue::Integer(2222)))); +} + +TEST_F(FirebaseFirestoreBasicTest, TestRunTransaction) { + SignIn(); + + WaitForCompletion( + Doc("1").Set(firebase::firestore::MapFieldValue{ + {"str", firebase::firestore::FieldValue::String("foo")}}), + "document.Set 1"); + WaitForCompletion( + Doc("2").Set(firebase::firestore::MapFieldValue{ + {"int", firebase::firestore::FieldValue::Integer(123)}}), + "document.Set 2"); + WaitForCompletion( + Doc("3").Set(firebase::firestore::MapFieldValue{ + {"int", firebase::firestore::FieldValue::Integer(678)}}), + "document.Set 3"); + // Make sure there's no doc 4. + WaitForCompletion(Doc("4").Delete(), "document.Delete 4"); + + auto collection = GetTestCollection(); + + auto transaction_future = firestore_->RunTransaction( + [collection, this](firebase::firestore::Transaction& transaction, + std::string&) -> firebase::firestore::Error { + // Set a default error to ensure that the error is filled in by Get(). + firebase::firestore::Error geterr = + static_cast(-1); + std::string getmsg = "[[uninitialized message]]"; + int64_t prev_int = transaction.Get(Doc("2"), &geterr, &getmsg) + .Get("int") + .integer_value(); + EXPECT_EQ(geterr, firebase::firestore::kErrorOk) << getmsg; + + // Update 1, increment 2, delete 3, add 4. + transaction.Update( + Doc("1"), + firebase::firestore::MapFieldValue{ + {"int", firebase::firestore::FieldValue::Integer(456)}}); + LogDebug("Previous value: %lld", prev_int); + transaction.Update(Doc("2"), + firebase::firestore::MapFieldValue{ + {"int", firebase::firestore::FieldValue::Integer( + prev_int + 100)}}); + transaction.Delete(Doc("3")); + transaction.Set( + Doc("4"), + firebase::firestore::MapFieldValue{ + {"int", firebase::firestore::FieldValue::Integer(789)}}); + return firebase::firestore::kErrorOk; + }); + + WaitForCompletion(transaction_future, "firestore.RunTransaction"); + + (void)Doc("4"); // Add new doc to cleanup list + + // Confirm the updated docs are correct. + // First doc had an additional field added. + auto future1 = Doc("1").Get(); + WaitForCompletion(future1, "document.Get 1"); + EXPECT_THAT(future1.result()->GetData(), + UnorderedElementsAre( + Pair("str", firebase::firestore::FieldValue::String("foo")), + Pair("int", firebase::firestore::FieldValue::Integer(456)))); + + // Second doc was incremented by 100. + auto future2 = Doc("2").Get(); + WaitForCompletion(future2, "document.Get 2"); + EXPECT_THAT( + future2.result()->GetData(), + ElementsAre(Pair("int", firebase::firestore::FieldValue::Integer(223)))); + + // Third doc was deleted. + auto future3 = Doc("3").Get(); + WaitForCompletion(future3, "document.Get 3"); + EXPECT_FALSE(future3.result()->exists()); + + // Fourth doc was newly added. + auto future4 = Doc("4").Get(); + WaitForCompletion(future4, "document.Get 4"); + EXPECT_THAT( + future4.result()->GetData(), + ElementsAre(Pair("int", firebase::firestore::FieldValue::Integer(789)))); +} + +// TODO(b/139064606): Add test for failing transaction. + +TEST_F(FirebaseFirestoreBasicTest, TestQuery) { + SignIn(); + + firebase::firestore::CollectionReference collection = GetTestCollection(); + // { "int" : 99, "int" : 100, "int" : 101, "int": 102, "str": "hello" } + // Query for int > 100 should return only the 101 and 102 entries. + WaitForCompletion(Doc("1").Set(firebase::firestore::MapFieldValue{ + {"int", firebase::firestore::FieldValue::Integer(99)}}), + "document.Set 1"); + WaitForCompletion( + Doc("2").Set(firebase::firestore::MapFieldValue{ + {"int", firebase::firestore::FieldValue::Integer(100)}}), + "document.Set 2"); + WaitForCompletion( + Doc("3").Set(firebase::firestore::MapFieldValue{ + {"int", firebase::firestore::FieldValue::Integer(101)}}), + "document.Set 3"); + WaitForCompletion( + Doc("4").Set(firebase::firestore::MapFieldValue{ + {"int", firebase::firestore::FieldValue::Integer(102)}}), + "document.Set 4"); + WaitForCompletion( + Doc("5").Set(firebase::firestore::MapFieldValue{ + {"str", firebase::firestore::FieldValue::String("hello")}}), + "document.Set 5"); + + firebase::firestore::Query query = collection.WhereGreaterThan( + "int", firebase::firestore::FieldValue::Integer(100)); + auto query_future = query.Get(); + WaitForCompletion(query_future, "query.Get"); + EXPECT_NE(query_future.result(), nullptr); + auto DocumentSnapshot_GetData = + [](const firebase::firestore::DocumentSnapshot& ds) { + return ds.GetData(); + }; + EXPECT_THAT( + query_future.result()->documents(), + UnorderedElementsAre( + ResultOf(DocumentSnapshot_GetData, + ElementsAre(Pair( + "int", firebase::firestore::FieldValue::Integer(102)))), + ResultOf( + DocumentSnapshot_GetData, + ElementsAre(Pair( + "int", firebase::firestore::FieldValue::Integer(101)))))); +} + +TEST_F(FirebaseFirestoreBasicTest, + TestInvalidatingReferencesWhenDeletingFirestore) { + delete firestore_; + firestore_ = nullptr; + // TODO(b/139064606): Ensure existing Firestore objects are invalidated. +} + +TEST_F(FirebaseFirestoreBasicTest, TestInvalidatingReferencesWhenDeletingApp) { + delete app_; + app_ = nullptr; + // TODO(b/139064606): Ensure existing Firestore objects are invalidated. +} + +// TODO(b/139064606): Add test for Auth signout while connected. + +// TODO(b/139064606): Add additional comprehensive tests as needed. + +} // namespace firebase_testapp_automated diff --git a/testing/integration_tests/functions/AndroidManifest.xml b/testing/integration_tests/functions/AndroidManifest.xml new file mode 100644 index 0000000000..329c1e684b --- /dev/null +++ b/testing/integration_tests/functions/AndroidManifest.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/testing/integration_tests/functions/CMakeLists.txt b/testing/integration_tests/functions/CMakeLists.txt new file mode 100644 index 0000000000..4202589125 --- /dev/null +++ b/testing/integration_tests/functions/CMakeLists.txt @@ -0,0 +1,196 @@ +cmake_minimum_required(VERSION 2.8) + +# User settings for Firebase integration tests. +# Path to Firebase SDK. +# Try to read the path to the Firebase C++ SDK from an environment variable. +if (NOT "$ENV{FIREBASE_CPP_SDK_DIR}" STREQUAL "") + set(DEFAULT_FIREBASE_CPP_SDK_DIR "$ENV{FIREBASE_CPP_SDK_DIR}") +else() + set(DEFAULT_FIREBASE_CPP_SDK_DIR "firebase_cpp_sdk") +endif() +if ("${FIREBASE_CPP_SDK_DIR}" STREQUAL "") + set(FIREBASE_CPP_SDK_DIR ${DEFAULT_FIREBASE_CPP_SDK_DIR}) +endif() +if(NOT EXISTS ${FIREBASE_CPP_SDK_DIR}) + message(FATAL_ERROR "The Firebase C++ SDK directory does not exist: ${FIREBASE_CPP_SDK_DIR}. See the readme.md for more information") +endif() + +# Windows runtime mode, either MD or MT depending on whether you are using +# /MD or /MT. For more information see: +# https://msdn.microsoft.com/en-us/library/2kzt1wy3.aspx +set(MSVC_RUNTIME_MODE MD) + +project(firebase_testapp) + +# Integration test source files. +set(FIREBASE_APP_FRAMEWORK_SRCS + src/app_framework.cc + src/app_framework.h +) + +set(FIREBASE_TEST_FRAMEWORK_SRCS + src/firebase_test_framework.h + src/firebase_test_framework.cc +) + +set(FIREBASE_INTEGRATION_TEST_SRCS + src/integration_test.cc +) + +# The include directory for the testapp. +include_directories(src) + +# Integration test uses some features that require C++ 11, such as lambdas. +set (CMAKE_CXX_STANDARD 11) + +# Download and unpack googletest (and googlemock) at configure time +set(GOOGLETEST_ROOT ${CMAKE_CURRENT_LIST_DIR}/external/googletest) +# Note: Once googletest is downloaded once, it won't be updated or +# downloaded again unless you delete the "external/googletest" +# directory. +if (NOT EXISTS ${GOOGLETEST_ROOT}/src/googletest/src/gtest-all.cc) + configure_file(googletest.cmake + ${CMAKE_CURRENT_LIST_DIR}/external/googletest/CMakeLists.txt COPYONLY) + execute_process(COMMAND ${CMAKE_COMMAND} . + RESULT_VARIABLE result + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/external/googletest ) + if(result) + message(FATAL_ERROR "CMake step for googletest failed: ${result}") + endif() + execute_process(COMMAND ${CMAKE_COMMAND} --build . + RESULT_VARIABLE result + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/external/googletest ) + if(result) + message(FATAL_ERROR "Build step for googletest failed: ${result}") + endif() +endif() + +if(ANDROID) + # Build an Android application. + + # Source files used for the Android build. + set(FIREBASE_APP_FRAMEWORK_ANDROID_SRCS + src/android/android_app_framework.cc + ) + + # Source files used for the Android build. + set(FIREBASE_TEST_FRAMEWORK_ANDROID_SRCS + src/android/android_firebase_test_framework.cc + ) + + # Build native_app_glue as a static lib + add_library(native_app_glue STATIC + ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c) + + # Export ANativeActivity_onCreate(), + # Refer to: https://github.com/android-ndk/ndk/issues/381. + set(CMAKE_SHARED_LINKER_FLAGS + "${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate") + + add_library(gtest STATIC + ${GOOGLETEST_ROOT}/src/googletest/src/gtest-all.cc) + target_include_directories(gtest + PRIVATE ${GOOGLETEST_ROOT}/src/googletest + PUBLIC ${GOOGLETEST_ROOT}/src/googletest/include) + add_library(gmock STATIC + ${GOOGLETEST_ROOT}/src/googlemock/src/gmock-all.cc) + target_include_directories(gmock + PRIVATE ${GOOGLETEST_ROOT}/src/googletest + PRIVATE ${GOOGLETEST_ROOT}/src/googlemock + PUBLIC ${GOOGLETEST_ROOT}/src/googletest/include + PUBLIC ${GOOGLETEST_ROOT}/src/googlemock/include) + + # Define the target as a shared library, as that is what gradle expects. + set(integration_test_target_name "android_integration_test_main") + add_library(${integration_test_target_name} SHARED + ${FIREBASE_APP_FRAMEWORK_SRCS} + ${FIREBASE_APP_FRAMEWORK_ANDROID_SRCS} + ${FIREBASE_INTEGRATION_TEST_SRCS} + ${FIREBASE_TEST_FRAMEWORK_SRCS} + ${FIREBASE_TEST_FRAMEWORK_ANDROID_SRCS} + ) + + target_include_directories(${integration_test_target_name} PRIVATE + ${ANDROID_NDK}/sources/android/native_app_glue) + + set(ADDITIONAL_LIBS log android atomic native_app_glue) +else() + # Build a desktop application. + add_definitions(-D_GLIBCXX_USE_CXX11_ABI=0) + + # Prevent overriding the parent project's compiler/linker + # settings on Windows + set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) + + # Add googletest directly to our build. This defines + # the gtest and gtest_main targets. + add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/external/googletest/src + ${CMAKE_CURRENT_LIST_DIR}/external/googletest/build + EXCLUDE_FROM_ALL) + + # The gtest/gtest_main targets carry header search path + # dependencies automatically when using CMake 2.8.11 or + # later. Otherwise we have to add them here ourselves. + if (CMAKE_VERSION VERSION_LESS 2.8.11) + include_directories("${gtest_SOURCE_DIR}/include") + include_directories("${gmock_SOURCE_DIR}/include") + endif() + + # Windows runtime mode, either MD or MT depending on whether you are using + # /MD or /MT. For more information see: + # https://msdn.microsoft.com/en-us/library/2kzt1wy3.aspx + set(MSVC_RUNTIME_MODE MD) + + # Platform abstraction layer for the desktop integration test. + set(FIREBASE_APP_FRAMEWORK_DESKTOP_SRCS + src/desktop/desktop_app_framework.cc + ) + + set(integration_test_target_name "integration_test") + add_executable(${integration_test_target_name} + ${FIREBASE_APP_FRAMEWORK_SRCS} + ${FIREBASE_APP_FRAMEWORK_DESKTOP_SRCS} + ${FIREBASE_TEST_FRAMEWORK_SRCS} + ${FIREBASE_INTEGRATION_TEST_SRCS} + ) + + if(APPLE) + set(ADDITIONAL_LIBS + gssapi_krb5 + pthread + "-framework CoreFoundation" + "-framework Foundation" + "-framework GSS" + "-framework Security" + ) + elseif(MSVC) + set(ADDITIONAL_LIBS advapi32 ws2_32 crypt32) + else() + set(ADDITIONAL_LIBS pthread) + endif() + + # If a config file is present, copy it into the binary location so that it's + # possible to create the default Firebase app. + set(FOUND_JSON_FILE FALSE) + foreach(config "google-services-desktop.json" "google-services.json") + if (EXISTS ${config}) + add_custom_command( + TARGET ${integration_test_target_name} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + ${config} $) + set(FOUND_JSON_FILE TRUE) + break() + endif() + endforeach() + if(NOT FOUND_JSON_FILE) + message(WARNING "Failed to find either google-services-desktop.json or google-services.json. See the readme.md for more information.") + endif() +endif() + +# Add the Firebase libraries to the target using the function from the SDK. +add_subdirectory(${FIREBASE_CPP_SDK_DIR} bin/ EXCLUDE_FROM_ALL) +# Note that firebase_app needs to be last in the list. +set(firebase_libs firebase_functions firebase_auth firebase_app) +set(gtest_libs gtest gmock) +target_link_libraries(${integration_test_target_name} ${firebase_libs} + ${gtest_libs} ${ADDITIONAL_LIBS}) diff --git a/testing/integration_tests/functions/Images.xcassets/AppIcon.appiconset/Contents.json b/testing/integration_tests/functions/Images.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000000..d8db8d65fd --- /dev/null +++ b/testing/integration_tests/functions/Images.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "83.5x83.5", + "scale" : "2x" + }, + { + "idiom" : "ios-marketing", + "size" : "1024x1024", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/testing/integration_tests/functions/Images.xcassets/LaunchImage.launchimage/Contents.json b/testing/integration_tests/functions/Images.xcassets/LaunchImage.launchimage/Contents.json new file mode 100644 index 0000000000..6f870a4629 --- /dev/null +++ b/testing/integration_tests/functions/Images.xcassets/LaunchImage.launchimage/Contents.json @@ -0,0 +1,51 @@ +{ + "images" : [ + { + "orientation" : "portrait", + "idiom" : "iphone", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "orientation" : "portrait", + "idiom" : "iphone", + "subtype" : "retina4", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "orientation" : "portrait", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "1x" + }, + { + "orientation" : "landscape", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "1x" + }, + { + "orientation" : "portrait", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "orientation" : "landscape", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/testing/integration_tests/functions/Info.plist b/testing/integration_tests/functions/Info.plist new file mode 100644 index 0000000000..651edd416c --- /dev/null +++ b/testing/integration_tests/functions/Info.plist @@ -0,0 +1,40 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + com.google.firebase.cpp.functions.testapp + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleURLTypes + + + CFBundleTypeRole + Editor + CFBundleURLName + google + CFBundleURLSchemes + + com.googleusercontent.apps.760619882947-d7uc3pt0rijrudd8mjhc397q5cbm7969 + firebase-game-loop + + + + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + + diff --git a/testing/integration_tests/functions/LaunchScreen.storyboard b/testing/integration_tests/functions/LaunchScreen.storyboard new file mode 100644 index 0000000000..673e0f7e68 --- /dev/null +++ b/testing/integration_tests/functions/LaunchScreen.storyboard @@ -0,0 +1,7 @@ + + + + + + + diff --git a/testing/integration_tests/functions/LibraryManifest.xml b/testing/integration_tests/functions/LibraryManifest.xml new file mode 100644 index 0000000000..2c020252c0 --- /dev/null +++ b/testing/integration_tests/functions/LibraryManifest.xml @@ -0,0 +1,7 @@ + + + + + + diff --git a/testing/integration_tests/functions/Podfile b/testing/integration_tests/functions/Podfile new file mode 100644 index 0000000000..06223ab10b --- /dev/null +++ b/testing/integration_tests/functions/Podfile @@ -0,0 +1,14 @@ +source 'sso://cpdc-internal/firebase.git' +source 'https://github.com/CocoaPods/Specs.git' +platform :ios, '8.0' +# Cloud Functions for Firebase test application. + +target 'integration_test' do + pod 'Firebase/Functions', '6.24.0' + pod 'Firebase/Auth', '6.24.0' +end + +post_install do |installer| + system("/usr/bin/python ./download_googletest.py") +end + diff --git a/testing/integration_tests/functions/build.gradle b/testing/integration_tests/functions/build.gradle new file mode 100644 index 0000000000..28d47fc1f6 --- /dev/null +++ b/testing/integration_tests/functions/build.gradle @@ -0,0 +1,65 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +buildscript { + repositories { + mavenLocal() + maven { url 'https://maven.google.com' } + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:3.2.1' + classpath 'com.google.gms:google-services:4.0.1' + } +} + +allprojects { + repositories { + mavenLocal() + maven { url 'https://maven.google.com' } + jcenter() + } +} + +apply plugin: 'com.android.application' + +android { + compileSdkVersion 28 + buildToolsVersion '28.0.3' + + sourceSets { + main { + jniLibs.srcDirs = ['libs'] + manifest.srcFile 'AndroidManifest.xml' + java.srcDirs = ['src/android/java'] + res.srcDirs = ['res'] + } + } + + defaultConfig { + applicationId 'com.google.firebase.cpp.functions.testapp' + minSdkVersion 16 + targetSdkVersion 28 + versionCode 1 + versionName '1.0' + externalNativeBuild.cmake { + arguments "-DFIREBASE_CPP_SDK_DIR=$gradle.firebase_cpp_sdk_dir" + } + } + externalNativeBuild.cmake { + path 'CMakeLists.txt' + } + buildTypes { + release { + minifyEnabled true + proguardFile getDefaultProguardFile('proguard-android.txt') + proguardFile file('proguard.pro') + } + } +} + +apply from: "$gradle.firebase_cpp_sdk_dir/Android/firebase_dependencies.gradle" +firebaseCpp.dependencies { + auth + functions +} + +apply plugin: 'com.google.gms.google-services' diff --git a/testing/integration_tests/functions/googletest.cmake b/testing/integration_tests/functions/googletest.cmake new file mode 100644 index 0000000000..0eeff685dd --- /dev/null +++ b/testing/integration_tests/functions/googletest.cmake @@ -0,0 +1,19 @@ +# Download GoogleTest from GitHub as an external project. +# This CMake file is taken from: +# https://github.com/google/googletest/blob/master/googletest/README.md#incorporating-into-an-existing-cmake-project + +cmake_minimum_required(VERSION 2.8.2) + +project(googletest-download NONE) + +include(ExternalProject) +ExternalProject_Add(googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG master + SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/src" + BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/build" + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + TEST_COMMAND "" +) diff --git a/testing/integration_tests/functions/gradle/wrapper/gradle-wrapper.jar b/testing/integration_tests/functions/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..8c0fb64a8698b08ecc4158d828ca593c4928e9dd GIT binary patch literal 49896 zcmagFb986H(k`5d^NVfUwr$(C?M#x1ZQHiZiEVpg+jrjgoQrerx!>1o_ul)D>ebz~ zs=Mmxr&>W81QY-S1PKWQ%N-;H^tS;2*XwVA`dej1RRn1z<;3VgfE4~kaG`A%QSPsR z#ovnZe+tS9%1MfeDyz`RirvdjPRK~p(#^q2(^5@O&NM19EHdvN-A&StN>0g6QA^VN z0Gx%Gq#PD$QMRFzmK+utjS^Y1F0e8&u&^=w5K<;4Rz|i3A=o|IKLY+g`iK6vfr9?+ z-`>gmU&i?FGSL5&F?TXFu`&Js6h;15QFkXp2M1H9|Eq~bpov-GU(uz%mH0n55wUl- zv#~ccAz`F5wlQ>e_KlJS3@{)B?^v*EQM=IxLa&76^y51a((wq|2-`qON>+4dLc{Oo z51}}o^Zen(oAjxDK7b++9_Yg`67p$bPo3~BCpGM7uAWmvIhWc5Gi+gQZ|Pwa-Gll@<1xmcPy z|NZmu6m)g5Ftu~BG&Xdxclw7Cij{xbBMBn-LMII#Slp`AElb&2^Hw+w>(3crLH!;I zN+Vk$D+wP1#^!MDCiad@vM>H#6+`Ct#~6VHL4lzmy;lSdk>`z6)=>Wh15Q2)dQtGqvn0vJU@+(B5{MUc*qs4!T+V=q=wy)<6$~ z!G>e_4dN@lGeF_$q9`Ju6Ncb*x?O7=l{anm7Eahuj_6lA{*#Gv*TaJclevPVbbVYu z(NY?5q+xxbO6%g1xF0r@Ix8fJ~u)VRUp`S%&rN$&e!Od`~s+64J z5*)*WSi*i{k%JjMSIN#X;jC{HG$-^iX+5f5BGOIHWAl*%15Z#!xntpk($-EGKCzKa zT7{siZ9;4TICsWQ$pu&wKZQTCvpI$Xvzwxoi+XkkpeE&&kFb!B?h2hi%^YlXt|-@5 zHJ~%AN!g_^tmn1?HSm^|gCE#!GRtK2(L{9pL#hp0xh zME}|DB>(5)`iE7CM)&_+S}-Bslc#@B5W4_+k4Cp$l>iVyg$KP>CN?SVGZ(&02>iZK zB<^HP$g$Lq*L$BWd?2(F?-MUbNWTJVQdW7$#8a|k_30#vHAD1Z{c#p;bETk0VnU5A zBgLe2HFJ3032$G<`m*OB!KM$*sdM20jm)It5OSru@tXpK5LT>#8)N!*skNu1$TpIw zufjjdp#lyH5bZ%|Iuo|iu9vG1HrIVWLH>278xo>aVBkPN3V$~!=KnlXQ4eDqS7%E% zQ!z^$Q$b^6Q)g#cLpwur(|<0gWHo6A6jc;n`t(V9T;LzTAU{IAu*uEQ%Ort1k+Kn+f_N`9|bxYC+~Z1 zCC1UCWv*Orx$_@ydv9mIe(liLfOr7mhbV@tKw{6)q^1DH1nmvZ0cj215R<~&I<4S| zgnr;9Cdjqpz#o8i0CQjtl`}{c*P)aSdH|abxGdrR)-3z+02-eX(k*B)Uqv6~^nh** z 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 \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/testing/integration_tests/functions/gradlew.bat b/testing/integration_tests/functions/gradlew.bat new file mode 100644 index 0000000000..8a0b282aa6 --- /dev/null +++ b/testing/integration_tests/functions/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/testing/integration_tests/functions/integration_test.xcodeproj/project.pbxproj b/testing/integration_tests/functions/integration_test.xcodeproj/project.pbxproj new file mode 100644 index 0000000000..d1d6b852d1 --- /dev/null +++ b/testing/integration_tests/functions/integration_test.xcodeproj/project.pbxproj @@ -0,0 +1,364 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 520BC0391C869159008CFBC3 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 520BC0381C869159008CFBC3 /* GoogleService-Info.plist */; }; + 529226D61C85F68000C89379 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 529226D51C85F68000C89379 /* Foundation.framework */; }; + 529226D81C85F68000C89379 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 529226D71C85F68000C89379 /* CoreGraphics.framework */; }; + 529226DA1C85F68000C89379 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 529226D91C85F68000C89379 /* UIKit.framework */; }; + D61C5F8E22BABA9C00A79141 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D61C5F8C22BABA9B00A79141 /* Images.xcassets */; }; + D61C5F9622BABAD200A79141 /* integration_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = D61C5F9222BABAD100A79141 /* integration_test.cc */; }; + D62CCBC022F367140099BE9F /* gmock-all.cc in Sources */ = {isa = PBXBuildFile; fileRef = D62CCBBF22F367140099BE9F /* gmock-all.cc */; }; + D66B16871CE46E8900E5638A /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D66B16861CE46E8900E5638A /* LaunchScreen.storyboard */; }; + D67D355822BABD2200292C1D /* gtest-all.cc in Sources */ = {isa = PBXBuildFile; fileRef = D67D355622BABD2100292C1D /* gtest-all.cc */; }; + D6C179E922CB322900C2651A /* ios_app_framework.mm in Sources */ = {isa = PBXBuildFile; fileRef = D6C179E722CB322900C2651A /* ios_app_framework.mm */; }; + D6C179EA22CB322900C2651A /* ios_firebase_test_framework.mm in Sources */ = {isa = PBXBuildFile; fileRef = D6C179E822CB322900C2651A /* ios_firebase_test_framework.mm */; }; + D6C179EE22CB323300C2651A /* firebase_test_framework.cc in Sources */ = {isa = PBXBuildFile; fileRef = D6C179EC22CB323300C2651A /* firebase_test_framework.cc */; }; + D6C179F022CB32A000C2651A /* app_framework.cc in Sources */ = {isa = PBXBuildFile; fileRef = D6C179EF22CB32A000C2651A /* app_framework.cc */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 520BC0381C869159008CFBC3 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; + 529226D21C85F68000C89379 /* integration_test.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = integration_test.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 529226D51C85F68000C89379 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + 529226D71C85F68000C89379 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; + 529226D91C85F68000C89379 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; + 529226EE1C85F68000C89379 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; + D61C5F8C22BABA9B00A79141 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; + D61C5F8D22BABA9C00A79141 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + D61C5F9222BABAD100A79141 /* integration_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = integration_test.cc; path = src/integration_test.cc; sourceTree = ""; }; + D62CCBBF22F367140099BE9F /* gmock-all.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "gmock-all.cc"; path = "external/googletest/src/googlemock/src/gmock-all.cc"; sourceTree = ""; }; + D62CCBC122F367320099BE9F /* gmock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = gmock.h; path = external/googletest/src/googlemock/include/gmock/gmock.h; sourceTree = ""; }; + D66B16861CE46E8900E5638A /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = ""; }; + D67D355622BABD2100292C1D /* gtest-all.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "gtest-all.cc"; path = "external/googletest/src/googletest/src/gtest-all.cc"; sourceTree = ""; }; + D67D355722BABD2100292C1D /* gtest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = gtest.h; path = external/googletest/src/googletest/include/gtest/gtest.h; sourceTree = ""; }; + D6C179E722CB322900C2651A /* ios_app_framework.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ios_app_framework.mm; path = src/ios/ios_app_framework.mm; sourceTree = ""; }; + D6C179E822CB322900C2651A /* ios_firebase_test_framework.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ios_firebase_test_framework.mm; path = src/ios/ios_firebase_test_framework.mm; sourceTree = ""; }; + D6C179EB22CB323300C2651A /* firebase_test_framework.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = firebase_test_framework.h; path = src/firebase_test_framework.h; sourceTree = ""; }; + D6C179EC22CB323300C2651A /* firebase_test_framework.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = firebase_test_framework.cc; path = src/firebase_test_framework.cc; sourceTree = ""; }; + D6C179ED22CB323300C2651A /* app_framework.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = app_framework.h; path = src/app_framework.h; sourceTree = ""; }; + D6C179EF22CB32A000C2651A /* app_framework.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = app_framework.cc; path = src/app_framework.cc; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 529226CF1C85F68000C89379 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 529226D81C85F68000C89379 /* CoreGraphics.framework in Frameworks */, + 529226DA1C85F68000C89379 /* UIKit.framework in Frameworks */, + 529226D61C85F68000C89379 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 529226C91C85F68000C89379 = { + isa = PBXGroup; + children = ( + D61C5F8C22BABA9B00A79141 /* Images.xcassets */, + D61C5F8D22BABA9C00A79141 /* Info.plist */, + D66B16861CE46E8900E5638A /* LaunchScreen.storyboard */, + 520BC0381C869159008CFBC3 /* GoogleService-Info.plist */, + 5292271D1C85FB5500C89379 /* src */, + 529226D41C85F68000C89379 /* Frameworks */, + 529226D31C85F68000C89379 /* Products */, + ); + sourceTree = ""; + }; + 529226D31C85F68000C89379 /* Products */ = { + isa = PBXGroup; + children = ( + 529226D21C85F68000C89379 /* integration_test.app */, + ); + name = Products; + sourceTree = ""; + }; + 529226D41C85F68000C89379 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 529226D51C85F68000C89379 /* Foundation.framework */, + 529226D71C85F68000C89379 /* CoreGraphics.framework */, + 529226D91C85F68000C89379 /* UIKit.framework */, + 529226EE1C85F68000C89379 /* XCTest.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 5292271D1C85FB5500C89379 /* src */ = { + isa = PBXGroup; + children = ( + D62CCBC122F367320099BE9F /* gmock.h */, + D62CCBBF22F367140099BE9F /* gmock-all.cc */, + D67D355622BABD2100292C1D /* gtest-all.cc */, + D67D355722BABD2100292C1D /* gtest.h */, + D6C179EF22CB32A000C2651A /* app_framework.cc */, + D6C179ED22CB323300C2651A /* app_framework.h */, + D6C179EC22CB323300C2651A /* firebase_test_framework.cc */, + D6C179EB22CB323300C2651A /* firebase_test_framework.h */, + D61C5F9222BABAD100A79141 /* integration_test.cc */, + 5292271E1C85FB5B00C89379 /* ios */, + ); + name = src; + sourceTree = ""; + }; + 5292271E1C85FB5B00C89379 /* ios */ = { + isa = PBXGroup; + children = ( + D6C179E722CB322900C2651A /* ios_app_framework.mm */, + D6C179E822CB322900C2651A /* ios_firebase_test_framework.mm */, + ); + name = ios; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 529226D11C85F68000C89379 /* integration_test */ = { + isa = PBXNativeTarget; + buildConfigurationList = 529226F91C85F68000C89379 /* Build configuration list for PBXNativeTarget "integration_test" */; + buildPhases = ( + 529226CE1C85F68000C89379 /* Sources */, + 529226CF1C85F68000C89379 /* Frameworks */, + 529226D01C85F68000C89379 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = integration_test; + productName = testapp; + productReference = 529226D21C85F68000C89379 /* integration_test.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 529226CA1C85F68000C89379 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0640; + ORGANIZATIONNAME = Google; + TargetAttributes = { + 529226D11C85F68000C89379 = { + CreatedOnToolsVersion = 6.4; + DevelopmentTeam = EQHXZ8M8AV; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = 529226CD1C85F68000C89379 /* Build configuration list for PBXProject "integration_test" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 529226C91C85F68000C89379; + productRefGroup = 529226D31C85F68000C89379 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 529226D11C85F68000C89379 /* integration_test */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 529226D01C85F68000C89379 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D61C5F8E22BABA9C00A79141 /* Images.xcassets in Resources */, + D66B16871CE46E8900E5638A /* LaunchScreen.storyboard in Resources */, + 520BC0391C869159008CFBC3 /* GoogleService-Info.plist in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 529226CE1C85F68000C89379 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D67D355822BABD2200292C1D /* gtest-all.cc in Sources */, + D62CCBC022F367140099BE9F /* gmock-all.cc in Sources */, + D6C179EA22CB322900C2651A /* ios_firebase_test_framework.mm in Sources */, + D61C5F9622BABAD200A79141 /* integration_test.cc in Sources */, + D6C179E922CB322900C2651A /* ios_app_framework.mm in Sources */, + D6C179F022CB32A000C2651A /* app_framework.cc in Sources */, + D6C179EE22CB323300C2651A /* firebase_test_framework.cc in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 529226F71C85F68000C89379 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.4; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 529226F81C85F68000C89379 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.4; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 529226FA1C85F68000C89379 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ""; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)", + ); + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + "\"$(SRCROOT)/src\"", + "\"$(SRCROOT)/external/googletest/src/googletest/include\"", + "\"$(SRCROOT)/external/googletest/src/googlemock/include\"", + "\"$(SRCROOT)/external/googletest/src/googletest\"", + "\"$(SRCROOT)/external/googletest/src/googlemock\"", + ); + INFOPLIST_FILE = "$(SRCROOT)/Info.plist"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + WRAPPER_EXTENSION = app; + }; + name = Debug; + }; + 529226FB1C85F68000C89379 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ""; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)", + ); + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + "\"$(SRCROOT)/src\"", + "\"$(SRCROOT)/external/googletest/src/googletest/include\"", + "\"$(SRCROOT)/external/googletest/src/googlemock/include\"", + "\"$(SRCROOT)/external/googletest/src/googletest\"", + "\"$(SRCROOT)/external/googletest/src/googlemock\"", + ); + INFOPLIST_FILE = "$(SRCROOT)/Info.plist"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + WRAPPER_EXTENSION = app; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 529226CD1C85F68000C89379 /* Build configuration list for PBXProject "integration_test" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 529226F71C85F68000C89379 /* Debug */, + 529226F81C85F68000C89379 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 529226F91C85F68000C89379 /* Build configuration list for PBXNativeTarget "integration_test" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 529226FA1C85F68000C89379 /* Debug */, + 529226FB1C85F68000C89379 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 529226CA1C85F68000C89379 /* Project object */; +} diff --git a/testing/integration_tests/functions/proguard.pro b/testing/integration_tests/functions/proguard.pro new file mode 100644 index 0000000000..2d04b8a9a5 --- /dev/null +++ b/testing/integration_tests/functions/proguard.pro @@ -0,0 +1,2 @@ +-ignorewarnings +-keep,includedescriptorclasses public class com.google.firebase.example.LoggingUtils { * ; } diff --git a/testing/integration_tests/functions/res/layout/main.xml b/testing/integration_tests/functions/res/layout/main.xml new file mode 100644 index 0000000000..d3ffb63082 --- /dev/null +++ b/testing/integration_tests/functions/res/layout/main.xml @@ -0,0 +1,12 @@ + + + + diff --git a/testing/integration_tests/functions/res/values/strings.xml b/testing/integration_tests/functions/res/values/strings.xml new file mode 100644 index 0000000000..b1d23c931a --- /dev/null +++ b/testing/integration_tests/functions/res/values/strings.xml @@ -0,0 +1,4 @@ + + + Cloud Functions for Firebase Integration Test + diff --git a/testing/integration_tests/functions/settings.gradle b/testing/integration_tests/functions/settings.gradle new file mode 100644 index 0000000000..2a543b9351 --- /dev/null +++ b/testing/integration_tests/functions/settings.gradle @@ -0,0 +1,36 @@ +// Copyright 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +def firebase_cpp_sdk_dir = System.getProperty('firebase_cpp_sdk.dir') +if (firebase_cpp_sdk_dir == null || firebase_cpp_sdk_dir.isEmpty()) { + firebase_cpp_sdk_dir = System.getenv('FIREBASE_CPP_SDK_DIR') + if (firebase_cpp_sdk_dir == null || firebase_cpp_sdk_dir.isEmpty()) { + if ((new File('firebase_cpp_sdk')).exists()) { + firebase_cpp_sdk_dir = 'firebase_cpp_sdk' + } else { + throw new StopActionException( + 'firebase_cpp_sdk.dir property or the FIREBASE_CPP_SDK_DIR ' + + 'environment variable must be set to reference the Firebase C++ ' + + 'SDK install directory. This is used to configure static library ' + + 'and C/C++ include paths for the SDK.') + } + } +} +if (!(new File(firebase_cpp_sdk_dir)).exists()) { + throw new StopActionException( + sprintf('Firebase C++ SDK directory %s does not exist', + firebase_cpp_sdk_dir)) +} +gradle.ext.firebase_cpp_sdk_dir = "$firebase_cpp_sdk_dir" +includeBuild "$firebase_cpp_sdk_dir" \ No newline at end of file diff --git a/testing/integration_tests/functions/src/integration_test.cc b/testing/integration_tests/functions/src/integration_test.cc new file mode 100644 index 0000000000..c405fa29d4 --- /dev/null +++ b/testing/integration_tests/functions/src/integration_test.cc @@ -0,0 +1,326 @@ +// Copyright 2019 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include +#include +#include +#include +#include + +#include "app_framework.h" // NOLINT +#include "firebase/app.h" +#include "firebase/auth.h" +#include "firebase/functions.h" +#include "firebase/util.h" +#include "firebase/variant.h" +#include "firebase_test_framework.h" // NOLINT + +// The TO_STRING macro is useful for command line defined strings as the quotes +// get stripped. +#define TO_STRING_EXPAND(X) #X +#define TO_STRING(X) TO_STRING_EXPAND(X) + +// Path to the Firebase config file to load. +#ifdef FIREBASE_CONFIG +#define FIREBASE_CONFIG_STRING TO_STRING(FIREBASE_CONFIG) +#else +#define FIREBASE_CONFIG_STRING "" +#endif // FIREBASE_CONFIG + +namespace firebase_testapp_automated { + +using app_framework::LogDebug; + +using app_framework::ProcessEvents; +using firebase_test_framework::FirebaseTest; + +const char kIntegrationTestRootPath[] = "integration_test_data"; + +class FirebaseFunctionsTest : public FirebaseTest { + public: + FirebaseFunctionsTest(); + ~FirebaseFunctionsTest() override; + + void SetUp() override; + void TearDown() override; + + protected: + // Initialize Firebase App, Firebase Auth, and Firebase Functions. + void Initialize(); + // Shut down Firebase Functions, Firebase Auth, and Firebase App. + void Terminate(); + // Sign in an anonymous user. + void SignIn(); + + firebase::Future TestFunction( + const char* function_name, const firebase::Variant* const function_data, + const firebase::Variant& expected_result, + firebase::functions::Error expected_error = + firebase::functions::kErrorNone); + + bool initialized_; + firebase::auth::Auth* auth_; + firebase::functions::Functions* functions_; +}; + +FirebaseFunctionsTest::FirebaseFunctionsTest() + : initialized_(false), auth_(nullptr), functions_(nullptr) { + FindFirebaseConfig(FIREBASE_CONFIG_STRING); +} + +FirebaseFunctionsTest::~FirebaseFunctionsTest() { + // Must be cleaned up on exit. + assert(app_ == nullptr); + assert(auth_ == nullptr); + assert(functions_ == nullptr); +} + +void FirebaseFunctionsTest::SetUp() { + FirebaseTest::SetUp(); + Initialize(); +} + +void FirebaseFunctionsTest::TearDown() { + // Delete the shared path, if there is one. + if (initialized_) { + Terminate(); + } + FirebaseTest::TearDown(); +} + +void FirebaseFunctionsTest::Initialize() { + if (initialized_) return; + + InitializeApp(); + + LogDebug("Initializing Firebase Auth and Firebase Functions."); + + // 0th element has a reference to this object, the rest have the initializer + // targets. + void* initialize_targets[] = {&auth_, &functions_}; + + const firebase::ModuleInitializer::InitializerFn initializers[] = { + [](::firebase::App* app, void* data) { + void** targets = reinterpret_cast(data); + LogDebug("Attempting to initialize Firebase Auth."); + ::firebase::InitResult result; + *reinterpret_cast<::firebase::auth::Auth**>(targets[0]) = + ::firebase::auth::Auth::GetAuth(app, &result); + return result; + }, + [](::firebase::App* app, void* data) { + void** targets = reinterpret_cast(data); + LogDebug("Attempting to initialize Firebase Functions."); + ::firebase::InitResult result; + firebase::functions::Functions* functions = + firebase::functions::Functions::GetInstance(app, &result); + *reinterpret_cast<::firebase::functions::Functions**>(targets[1]) = + functions; + return result; + }}; + + ::firebase::ModuleInitializer initializer; + initializer.Initialize(app_, initialize_targets, initializers, + sizeof(initializers) / sizeof(initializers[0])); + + WaitForCompletion(initializer.InitializeLastResult(), "Initialize"); + + ASSERT_EQ(initializer.InitializeLastResult().error(), 0) + << initializer.InitializeLastResult().error_message(); + + LogDebug("Successfully initialized Firebase Auth and Firebase Functions."); + + initialized_ = true; +} + +void FirebaseFunctionsTest::Terminate() { + if (!initialized_) return; + + if (functions_) { + LogDebug("Shutdown the Functions library."); + delete functions_; + functions_ = nullptr; + } + if (auth_) { + LogDebug("Shutdown the Auth library."); + delete auth_; + auth_ = nullptr; + } + + TerminateApp(); + + initialized_ = false; + + ProcessEvents(100); +} + +void FirebaseFunctionsTest::SignIn() { + LogDebug("Signing in."); + firebase::Future sign_in_future = + auth_->SignInAnonymously(); + WaitForCompletion(sign_in_future, "SignInAnonymously"); + if (sign_in_future.error() != 0) { + FAIL() << "Ensure your application has the Anonymous sign-in provider " + "enabled in Firebase Console."; + } + ProcessEvents(100); +} + +// A helper function for calling a Firebase Function and waiting on the result. +firebase::Future +FirebaseFunctionsTest::TestFunction( + const char* function_name, const firebase::Variant* const function_data, + const firebase::Variant& expected_result, + firebase::functions::Error expected_error) { + // Create a callable that we can run our test with. + LogDebug("Calling %s", function_name); + firebase::functions::HttpsCallableReference ref; + ref = functions_->GetHttpsCallable(function_name); + + firebase::Future future; + if (function_data == nullptr) { + future = ref.Call(); + } else { + future = ref.Call(*function_data); + } + WaitForCompletion(future, + (std::string("CallFunction ") + function_name).c_str(), + expected_error); + if (!expected_result.is_null()) { + EXPECT_EQ(VariantToString(expected_result), + VariantToString(future.result()->data())) + << "Unexpected result from calling " << function_name; + } + return future; +} + +TEST_F(FirebaseFunctionsTest, TestInitializeAndTerminate) { + // Already tested via SetUp() and TearDown(). +} + +TEST_F(FirebaseFunctionsTest, TestSignIn) { + SignIn(); + EXPECT_NE(auth_->current_user(), nullptr); +} + +TEST_F(FirebaseFunctionsTest, TestFunction) { + SignIn(); + + // addNumbers(5, 7) = 12 + firebase::Variant data(firebase::Variant::EmptyMap()); + data.map()["firstNumber"] = 5; + data.map()["secondNumber"] = 7; + firebase::Variant result = + TestFunction("addNumbers", &data, firebase::Variant::Null()) + .result() + ->data(); + EXPECT_TRUE(result.is_map()); + EXPECT_EQ(result.map()["operationResult"], 12); +} + +TEST_F(FirebaseFunctionsTest, TestFunctionWithData) { + SignIn(); + + std::map data_map; + data_map["bool"] = firebase::Variant::True(); + data_map["int"] = firebase::Variant(2); + data_map["long"] = firebase::Variant(static_cast(3)); + // TODO(klimt): Add this back when we add Date support. + // 1998-09-04T22:14:15.926Z + // data["date"] = firebase::functions::EncodeDate(904947255926); + data_map["string"] = firebase::Variant("four"); + std::vector array; + array.push_back(5); + array.push_back(6); + data_map["array"] = firebase::Variant(array); + data_map["null"] = firebase::Variant::Null(); + + std::map expected; + expected["message"] = firebase::Variant("stub response"); + expected["code"] = firebase::Variant(42); + expected["long"] = firebase::Variant(420); + std::vector expected_array; + expected_array.push_back(1); + expected_array.push_back(2); + expected_array.push_back(3); + expected["array"] = firebase::Variant(expected_array); + // TODO(klimt): Add this back when we add Date support. + // 2017-09-04T22:14:15.000 + // expected["date"] = + // firebase::Variant(static_cast(1504563255000)); + firebase::Variant data(data_map); + TestFunction("dataTest", &data, firebase::Variant(expected)); +} + +TEST_F(FirebaseFunctionsTest, TestFunctionWithScalar) { + SignIn(); + + // Passing in and returning a scalar value instead of an object. + firebase::Variant data(17); + TestFunction("scalarTest", &data, firebase::Variant(76)); +} + +TEST_F(FirebaseFunctionsTest, TestFunctionWithAuthToken) { + SignIn(); + + // With an auth token. + firebase::Variant data(firebase::Variant::EmptyMap()); + TestFunction("tokenTest", &data, firebase::Variant::EmptyMap()); +} + +// Disabling test temporarily. (b/143598197) +TEST_F(FirebaseFunctionsTest, DISABLED_TestFunctionWithInstanceId) { +#if defined(__ANDROID__) || TARGET_OS_IPHONE + SignIn(); + + // With an instance ID. + firebase::Variant data(firebase::Variant::EmptyMap()); + TestFunction("instanceIdTest", &data, firebase::Variant::EmptyMap()); +#endif // defined(__ANDROID__) || TARGET_OS_IPHONE +} + +TEST_F(FirebaseFunctionsTest, TestFunctionWithNull) { + SignIn(); + + // With an explicit null. + firebase::Variant data(firebase::Variant::Null()); + TestFunction("nullTest", &data, firebase::Variant::Null()); + + // With a void call. + TestFunction("nullTest", nullptr, data); +} + +TEST_F(FirebaseFunctionsTest, TestErrorHandling) { + SignIn(); + + // With the data/result field missing in the response. + TestFunction("missingResultTest", nullptr, firebase::Variant::Null(), + firebase::functions::kErrorInternal); + + // With a response that is not valid JSON. + TestFunction("unhandledErrorTest", nullptr, firebase::Variant::Null(), + firebase::functions::kErrorInternal); + + // With an invalid error code. + TestFunction("unknownErrorTest", nullptr, firebase::Variant::Null(), + firebase::functions::kErrorInternal); + + // With an explicit error code and message. + TestFunction("explicitErrorTest", nullptr, firebase::Variant::Null(), + firebase::functions::kErrorOutOfRange); +} + +} // namespace firebase_testapp_automated diff --git a/testing/integration_tests/instance_id/AndroidManifest.xml b/testing/integration_tests/instance_id/AndroidManifest.xml new file mode 100644 index 0000000000..f62f84f38d --- /dev/null +++ b/testing/integration_tests/instance_id/AndroidManifest.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/testing/integration_tests/instance_id/CMakeLists.txt b/testing/integration_tests/instance_id/CMakeLists.txt new file mode 100644 index 0000000000..39948e6e33 --- /dev/null +++ b/testing/integration_tests/instance_id/CMakeLists.txt @@ -0,0 +1,196 @@ +cmake_minimum_required(VERSION 2.8) + +# User settings for Firebase integration tests. +# Path to Firebase SDK. +# Try to read the path to the Firebase C++ SDK from an environment variable. +if (NOT "$ENV{FIREBASE_CPP_SDK_DIR}" STREQUAL "") + set(DEFAULT_FIREBASE_CPP_SDK_DIR "$ENV{FIREBASE_CPP_SDK_DIR}") +else() + set(DEFAULT_FIREBASE_CPP_SDK_DIR "firebase_cpp_sdk") +endif() +if ("${FIREBASE_CPP_SDK_DIR}" STREQUAL "") + set(FIREBASE_CPP_SDK_DIR ${DEFAULT_FIREBASE_CPP_SDK_DIR}) +endif() +if(NOT EXISTS ${FIREBASE_CPP_SDK_DIR}) + message(FATAL_ERROR "The Firebase C++ SDK directory does not exist: ${FIREBASE_CPP_SDK_DIR}. See the readme.md for more information") +endif() + +# Windows runtime mode, either MD or MT depending on whether you are using +# /MD or /MT. For more information see: +# https://msdn.microsoft.com/en-us/library/2kzt1wy3.aspx +set(MSVC_RUNTIME_MODE MD) + +project(firebase_testapp) + +# Integration test source files. +set(FIREBASE_APP_FRAMEWORK_SRCS + src/app_framework.cc + src/app_framework.h +) + +set(FIREBASE_TEST_FRAMEWORK_SRCS + src/firebase_test_framework.h + src/firebase_test_framework.cc +) + +set(FIREBASE_INTEGRATION_TEST_SRCS + src/integration_test.cc +) + +# The include directory for the testapp. +include_directories(src) + +# Integration test uses some features that require C++ 11, such as lambdas. +set (CMAKE_CXX_STANDARD 11) + +# Download and unpack googletest (and googlemock) at configure time +set(GOOGLETEST_ROOT ${CMAKE_CURRENT_LIST_DIR}/external/googletest) +# Note: Once googletest is downloaded once, it won't be updated or +# downloaded again unless you delete the "external/googletest" +# directory. +if (NOT EXISTS ${GOOGLETEST_ROOT}/src/googletest/src/gtest-all.cc) + configure_file(googletest.cmake + ${CMAKE_CURRENT_LIST_DIR}/external/googletest/CMakeLists.txt COPYONLY) + execute_process(COMMAND ${CMAKE_COMMAND} . + RESULT_VARIABLE result + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/external/googletest ) + if(result) + message(FATAL_ERROR "CMake step for googletest failed: ${result}") + endif() + execute_process(COMMAND ${CMAKE_COMMAND} --build . + RESULT_VARIABLE result + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/external/googletest ) + if(result) + message(FATAL_ERROR "Build step for googletest failed: ${result}") + endif() +endif() + +if(ANDROID) + # Build an Android application. + + # Source files used for the Android build. + set(FIREBASE_APP_FRAMEWORK_ANDROID_SRCS + src/android/android_app_framework.cc + ) + + # Source files used for the Android build. + set(FIREBASE_TEST_FRAMEWORK_ANDROID_SRCS + src/android/android_firebase_test_framework.cc + ) + +# Build native_app_glue as a static lib + add_library(native_app_glue STATIC + ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c) + + # Export ANativeActivity_onCreate(), + # Refer to: https://github.com/android-ndk/ndk/issues/381. + set(CMAKE_SHARED_LINKER_FLAGS + "${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate") + + add_library(gtest STATIC + ${GOOGLETEST_ROOT}/src/googletest/src/gtest-all.cc) + target_include_directories(gtest + PRIVATE ${GOOGLETEST_ROOT}/src/googletest + PUBLIC ${GOOGLETEST_ROOT}/src/googletest/include) + add_library(gmock STATIC + ${GOOGLETEST_ROOT}/src/googlemock/src/gmock-all.cc) + target_include_directories(gmock + PRIVATE ${GOOGLETEST_ROOT}/src/googletest + PRIVATE ${GOOGLETEST_ROOT}/src/googlemock + PUBLIC ${GOOGLETEST_ROOT}/src/googletest/include + PUBLIC ${GOOGLETEST_ROOT}/src/googlemock/include) + + # Define the target as a shared library, as that is what gradle expects. + set(integration_test_target_name "android_integration_test_main") + add_library(${integration_test_target_name} SHARED + ${FIREBASE_APP_FRAMEWORK_SRCS} + ${FIREBASE_APP_FRAMEWORK_ANDROID_SRCS} + ${FIREBASE_INTEGRATION_TEST_SRCS} + ${FIREBASE_TEST_FRAMEWORK_SRCS} + ${FIREBASE_TEST_FRAMEWORK_ANDROID_SRCS} + ) + + target_include_directories(${integration_test_target_name} PRIVATE + ${ANDROID_NDK}/sources/android/native_app_glue) + + set(ADDITIONAL_LIBS log android atomic native_app_glue) +else() + # Build a desktop application. + add_definitions(-D_GLIBCXX_USE_CXX11_ABI=0) + + # Prevent overriding the parent project's compiler/linker + # settings on Windows + set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) + + # Add googletest directly to our build. This defines + # the gtest and gtest_main targets. + add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/external/googletest/src + ${CMAKE_CURRENT_LIST_DIR}/external/googletest/build + EXCLUDE_FROM_ALL) + + # The gtest/gtest_main targets carry header search path + # dependencies automatically when using CMake 2.8.11 or + # later. Otherwise we have to add them here ourselves. + if (CMAKE_VERSION VERSION_LESS 2.8.11) + include_directories("${gtest_SOURCE_DIR}/include") + include_directories("${gmock_SOURCE_DIR}/include") + endif() + + # Windows runtime mode, either MD or MT depending on whether you are using + # /MD or /MT. For more information see: + # https://msdn.microsoft.com/en-us/library/2kzt1wy3.aspx + set(MSVC_RUNTIME_MODE MD) + + # Platform abstraction layer for the desktop integration test. + set(FIREBASE_APP_FRAMEWORK_DESKTOP_SRCS + src/desktop/desktop_app_framework.cc + ) + + set(integration_test_target_name "integration_test") + add_executable(${integration_test_target_name} + ${FIREBASE_APP_FRAMEWORK_SRCS} + ${FIREBASE_APP_FRAMEWORK_DESKTOP_SRCS} + ${FIREBASE_TEST_FRAMEWORK_SRCS} + ${FIREBASE_INTEGRATION_TEST_SRCS} + ) + + if(APPLE) + set(ADDITIONAL_LIBS + gssapi_krb5 + pthread + "-framework CoreFoundation" + "-framework Foundation" + "-framework GSS" + "-framework Security" + ) + elseif(MSVC) + set(ADDITIONAL_LIBS advapi32 ws2_32 crypt32) + else() + set(ADDITIONAL_LIBS pthread) + endif() + + # If a config file is present, copy it into the binary location so that it's + # possible to create the default Firebase app. + set(FOUND_JSON_FILE FALSE) + foreach(config "google-services-desktop.json" "google-services.json") + if (EXISTS ${config}) + add_custom_command( + TARGET ${integration_test_target_name} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + ${config} $) + set(FOUND_JSON_FILE TRUE) + break() + endif() + endforeach() + if(NOT FOUND_JSON_FILE) + message(WARNING "Failed to find either google-services-desktop.json or google-services.json. See the readme.md for more information.") + endif() +endif() + +# Add the Firebase libraries to the target using the function from the SDK. +add_subdirectory(${FIREBASE_CPP_SDK_DIR} bin/ EXCLUDE_FROM_ALL) +# Note that firebase_app needs to be last in the list. +set(firebase_libs firebase_instance_id firebase_app) +set(gtest_libs gtest gmock) +target_link_libraries(${integration_test_target_name} ${firebase_libs} + ${gtest_libs} ${ADDITIONAL_LIBS}) diff --git a/testing/integration_tests/instance_id/Images.xcassets/AppIcon.appiconset/Contents.json b/testing/integration_tests/instance_id/Images.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000000..d8db8d65fd --- /dev/null +++ b/testing/integration_tests/instance_id/Images.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "83.5x83.5", + "scale" : "2x" + }, + { + "idiom" : "ios-marketing", + "size" : "1024x1024", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/testing/integration_tests/instance_id/Images.xcassets/LaunchImage.launchimage/Contents.json b/testing/integration_tests/instance_id/Images.xcassets/LaunchImage.launchimage/Contents.json new file mode 100644 index 0000000000..6f870a4629 --- /dev/null +++ b/testing/integration_tests/instance_id/Images.xcassets/LaunchImage.launchimage/Contents.json @@ -0,0 +1,51 @@ +{ + "images" : [ + { + "orientation" : "portrait", + "idiom" : "iphone", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "orientation" : "portrait", + "idiom" : "iphone", + "subtype" : "retina4", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "orientation" : "portrait", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "1x" + }, + { + "orientation" : "landscape", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "1x" + }, + { + "orientation" : "portrait", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "orientation" : "landscape", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/testing/integration_tests/instance_id/Info.plist b/testing/integration_tests/instance_id/Info.plist new file mode 100644 index 0000000000..7d05423d5d --- /dev/null +++ b/testing/integration_tests/instance_id/Info.plist @@ -0,0 +1,42 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + com.google.firebase.instanceid.testapp + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleURLTypes + + + CFBundleTypeRole + Editor + CFBundleURLName + google + CFBundleURLSchemes + + com.googleusercontent.apps.564468233704-670b1qgtr09mp1g8k9uklkjmchln0950 + firebase-game-loop + + + + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + + diff --git a/testing/integration_tests/instance_id/LaunchScreen.storyboard b/testing/integration_tests/instance_id/LaunchScreen.storyboard new file mode 100644 index 0000000000..673e0f7e68 --- /dev/null +++ b/testing/integration_tests/instance_id/LaunchScreen.storyboard @@ -0,0 +1,7 @@ + + + + + + + diff --git a/testing/integration_tests/instance_id/LibraryManifest.xml b/testing/integration_tests/instance_id/LibraryManifest.xml new file mode 100644 index 0000000000..b61eeb2057 --- /dev/null +++ b/testing/integration_tests/instance_id/LibraryManifest.xml @@ -0,0 +1,7 @@ + + + + + + diff --git a/testing/integration_tests/instance_id/Podfile b/testing/integration_tests/instance_id/Podfile new file mode 100644 index 0000000000..c24638900e --- /dev/null +++ b/testing/integration_tests/instance_id/Podfile @@ -0,0 +1,14 @@ +source 'sso://cpdc-internal/firebase.git' +source 'https://github.com/CocoaPods/Specs.git' +platform :ios, '8.0' +# Firebase Instance ID test application. + +target 'integration_test' do + pod 'Firebase/Analytics', '6.24.0' + pod 'FirebaseInstanceID', '4.3.4' +end + +post_install do |installer| + system("/usr/bin/python ./download_googletest.py") +end + diff --git a/testing/integration_tests/instance_id/build.gradle b/testing/integration_tests/instance_id/build.gradle new file mode 100644 index 0000000000..926821d0e3 --- /dev/null +++ b/testing/integration_tests/instance_id/build.gradle @@ -0,0 +1,64 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +buildscript { + repositories { + mavenLocal() + maven { url 'https://maven.google.com' } + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:3.2.1' + classpath 'com.google.gms:google-services:4.0.1' + } +} + +allprojects { + repositories { + mavenLocal() + maven { url 'https://maven.google.com' } + jcenter() + } +} + +apply plugin: 'com.android.application' + +android { + compileSdkVersion 28 + buildToolsVersion '28.0.3' + + sourceSets { + main { + jniLibs.srcDirs = ['libs'] + manifest.srcFile 'AndroidManifest.xml' + java.srcDirs = ['src/android/java'] + res.srcDirs = ['res'] + } + } + + defaultConfig { + applicationId 'com.google.firebase.instanceid.testapp' + minSdkVersion 16 + targetSdkVersion 28 + versionCode 1 + versionName '1.0' + externalNativeBuild.cmake { + arguments "-DFIREBASE_CPP_SDK_DIR=$gradle.firebase_cpp_sdk_dir" + } + } + externalNativeBuild.cmake { + path 'CMakeLists.txt' + } + buildTypes { + release { + minifyEnabled true + proguardFile getDefaultProguardFile('proguard-android.txt') + proguardFile file('proguard.pro') + } + } +} + +apply from: "$gradle.firebase_cpp_sdk_dir/Android/firebase_dependencies.gradle" +firebaseCpp.dependencies { + instanceId +} + +apply plugin: 'com.google.gms.google-services' diff --git a/testing/integration_tests/instance_id/googletest.cmake b/testing/integration_tests/instance_id/googletest.cmake new file mode 100644 index 0000000000..0eeff685dd --- /dev/null +++ b/testing/integration_tests/instance_id/googletest.cmake @@ -0,0 +1,19 @@ +# Download GoogleTest from GitHub as an external project. +# This CMake file is taken from: +# https://github.com/google/googletest/blob/master/googletest/README.md#incorporating-into-an-existing-cmake-project + +cmake_minimum_required(VERSION 2.8.2) + +project(googletest-download NONE) + +include(ExternalProject) +ExternalProject_Add(googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG master + SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/src" + BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/build" + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + TEST_COMMAND "" +) diff --git a/testing/integration_tests/instance_id/gradle/wrapper/gradle-wrapper.jar b/testing/integration_tests/instance_id/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..8c0fb64a8698b08ecc4158d828ca593c4928e9dd GIT binary patch literal 49896 zcmagFb986H(k`5d^NVfUwr$(C?M#x1ZQHiZiEVpg+jrjgoQrerx!>1o_ul)D>ebz~ zs=Mmxr&>W81QY-S1PKWQ%N-;H^tS;2*XwVA`dej1RRn1z<;3VgfE4~kaG`A%QSPsR z#ovnZe+tS9%1MfeDyz`RirvdjPRK~p(#^q2(^5@O&NM19EHdvN-A&StN>0g6QA^VN z0Gx%Gq#PD$QMRFzmK+utjS^Y1F0e8&u&^=w5K<;4Rz|i3A=o|IKLY+g`iK6vfr9?+ z-`>gmU&i?FGSL5&F?TXFu`&Js6h;15QFkXp2M1H9|Eq~bpov-GU(uz%mH0n55wUl- zv#~ccAz`F5wlQ>e_KlJS3@{)B?^v*EQM=IxLa&76^y51a((wq|2-`qON>+4dLc{Oo z51}}o^Zen(oAjxDK7b++9_Yg`67p$bPo3~BCpGM7uAWmvIhWc5Gi+gQZ|Pwa-Gll@<1xmcPy z|NZmu6m)g5Ftu~BG&Xdxclw7Cij{xbBMBn-LMII#Slp`AElb&2^Hw+w>(3crLH!;I zN+Vk$D+wP1#^!MDCiad@vM>H#6+`Ct#~6VHL4lzmy;lSdk>`z6)=>Wh15Q2)dQtGqvn0vJU@+(B5{MUc*qs4!T+V=q=wy)<6$~ z!G>e_4dN@lGeF_$q9`Ju6Ncb*x?O7=l{anm7Eahuj_6lA{*#Gv*TaJclevPVbbVYu z(NY?5q+xxbO6%g1xF0r@Ix8fJ~u)VRUp`S%&rN$&e!Od`~s+64J z5*)*WSi*i{k%JjMSIN#X;jC{HG$-^iX+5f5BGOIHWAl*%15Z#!xntpk($-EGKCzKa zT7{siZ9;4TICsWQ$pu&wKZQTCvpI$Xvzwxoi+XkkpeE&&kFb!B?h2hi%^YlXt|-@5 zHJ~%AN!g_^tmn1?HSm^|gCE#!GRtK2(L{9pL#hp0xh zME}|DB>(5)`iE7CM)&_+S}-Bslc#@B5W4_+k4Cp$l>iVyg$KP>CN?SVGZ(&02>iZK zB<^HP$g$Lq*L$BWd?2(F?-MUbNWTJVQdW7$#8a|k_30#vHAD1Z{c#p;bETk0VnU5A zBgLe2HFJ3032$G<`m*OB!KM$*sdM20jm)It5OSru@tXpK5LT>#8)N!*skNu1$TpIw zufjjdp#lyH5bZ%|Iuo|iu9vG1HrIVWLH>278xo>aVBkPN3V$~!=KnlXQ4eDqS7%E% zQ!z^$Q$b^6Q)g#cLpwur(|<0gWHo6A6jc;n`t(V9T;LzTAU{IAu*uEQ%Ort1k+Kn+f_N`9|bxYC+~Z1 zCC1UCWv*Orx$_@ydv9mIe(liLfOr7mhbV@tKw{6)q^1DH1nmvZ0cj215R<~&I<4S| zgnr;9Cdjqpz#o8i0CQjtl`}{c*P)aSdH|abxGdrR)-3z+02-eX(k*B)Uqv6~^nh** z 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 \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/testing/integration_tests/instance_id/gradlew.bat b/testing/integration_tests/instance_id/gradlew.bat new file mode 100644 index 0000000000..8a0b282aa6 --- /dev/null +++ b/testing/integration_tests/instance_id/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/testing/integration_tests/instance_id/integration_test.xcodeproj/project.pbxproj b/testing/integration_tests/instance_id/integration_test.xcodeproj/project.pbxproj new file mode 100644 index 0000000000..d1d6b852d1 --- /dev/null +++ b/testing/integration_tests/instance_id/integration_test.xcodeproj/project.pbxproj @@ -0,0 +1,364 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 520BC0391C869159008CFBC3 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 520BC0381C869159008CFBC3 /* GoogleService-Info.plist */; }; + 529226D61C85F68000C89379 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 529226D51C85F68000C89379 /* Foundation.framework */; }; + 529226D81C85F68000C89379 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 529226D71C85F68000C89379 /* CoreGraphics.framework */; }; + 529226DA1C85F68000C89379 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 529226D91C85F68000C89379 /* UIKit.framework */; }; + D61C5F8E22BABA9C00A79141 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D61C5F8C22BABA9B00A79141 /* Images.xcassets */; }; + D61C5F9622BABAD200A79141 /* integration_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = D61C5F9222BABAD100A79141 /* integration_test.cc */; }; + D62CCBC022F367140099BE9F /* gmock-all.cc in Sources */ = {isa = PBXBuildFile; fileRef = D62CCBBF22F367140099BE9F /* gmock-all.cc */; }; + D66B16871CE46E8900E5638A /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D66B16861CE46E8900E5638A /* LaunchScreen.storyboard */; }; + D67D355822BABD2200292C1D /* gtest-all.cc in Sources */ = {isa = PBXBuildFile; fileRef = D67D355622BABD2100292C1D /* gtest-all.cc */; }; + D6C179E922CB322900C2651A /* ios_app_framework.mm in Sources */ = {isa = PBXBuildFile; fileRef = D6C179E722CB322900C2651A /* ios_app_framework.mm */; }; + D6C179EA22CB322900C2651A /* ios_firebase_test_framework.mm in Sources */ = {isa = PBXBuildFile; fileRef = D6C179E822CB322900C2651A /* ios_firebase_test_framework.mm */; }; + D6C179EE22CB323300C2651A /* firebase_test_framework.cc in Sources */ = {isa = PBXBuildFile; fileRef = D6C179EC22CB323300C2651A /* firebase_test_framework.cc */; }; + D6C179F022CB32A000C2651A /* app_framework.cc in Sources */ = {isa = PBXBuildFile; fileRef = D6C179EF22CB32A000C2651A /* app_framework.cc */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 520BC0381C869159008CFBC3 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; + 529226D21C85F68000C89379 /* integration_test.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = integration_test.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 529226D51C85F68000C89379 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + 529226D71C85F68000C89379 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; + 529226D91C85F68000C89379 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; + 529226EE1C85F68000C89379 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; + D61C5F8C22BABA9B00A79141 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; + D61C5F8D22BABA9C00A79141 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + D61C5F9222BABAD100A79141 /* integration_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = integration_test.cc; path = src/integration_test.cc; sourceTree = ""; }; + D62CCBBF22F367140099BE9F /* gmock-all.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "gmock-all.cc"; path = "external/googletest/src/googlemock/src/gmock-all.cc"; sourceTree = ""; }; + D62CCBC122F367320099BE9F /* gmock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = gmock.h; path = external/googletest/src/googlemock/include/gmock/gmock.h; sourceTree = ""; }; + D66B16861CE46E8900E5638A /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = ""; }; + D67D355622BABD2100292C1D /* gtest-all.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "gtest-all.cc"; path = "external/googletest/src/googletest/src/gtest-all.cc"; sourceTree = ""; }; + D67D355722BABD2100292C1D /* gtest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = gtest.h; path = external/googletest/src/googletest/include/gtest/gtest.h; sourceTree = ""; }; + D6C179E722CB322900C2651A /* ios_app_framework.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ios_app_framework.mm; path = src/ios/ios_app_framework.mm; sourceTree = ""; }; + D6C179E822CB322900C2651A /* ios_firebase_test_framework.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ios_firebase_test_framework.mm; path = src/ios/ios_firebase_test_framework.mm; sourceTree = ""; }; + D6C179EB22CB323300C2651A /* firebase_test_framework.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = firebase_test_framework.h; path = src/firebase_test_framework.h; sourceTree = ""; }; + D6C179EC22CB323300C2651A /* firebase_test_framework.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = firebase_test_framework.cc; path = src/firebase_test_framework.cc; sourceTree = ""; }; + D6C179ED22CB323300C2651A /* app_framework.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = app_framework.h; path = src/app_framework.h; sourceTree = ""; }; + D6C179EF22CB32A000C2651A /* app_framework.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = app_framework.cc; path = src/app_framework.cc; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 529226CF1C85F68000C89379 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 529226D81C85F68000C89379 /* CoreGraphics.framework in Frameworks */, + 529226DA1C85F68000C89379 /* UIKit.framework in Frameworks */, + 529226D61C85F68000C89379 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 529226C91C85F68000C89379 = { + isa = PBXGroup; + children = ( + D61C5F8C22BABA9B00A79141 /* Images.xcassets */, + D61C5F8D22BABA9C00A79141 /* Info.plist */, + D66B16861CE46E8900E5638A /* LaunchScreen.storyboard */, + 520BC0381C869159008CFBC3 /* GoogleService-Info.plist */, + 5292271D1C85FB5500C89379 /* src */, + 529226D41C85F68000C89379 /* Frameworks */, + 529226D31C85F68000C89379 /* Products */, + ); + sourceTree = ""; + }; + 529226D31C85F68000C89379 /* Products */ = { + isa = PBXGroup; + children = ( + 529226D21C85F68000C89379 /* integration_test.app */, + ); + name = Products; + sourceTree = ""; + }; + 529226D41C85F68000C89379 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 529226D51C85F68000C89379 /* Foundation.framework */, + 529226D71C85F68000C89379 /* CoreGraphics.framework */, + 529226D91C85F68000C89379 /* UIKit.framework */, + 529226EE1C85F68000C89379 /* XCTest.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 5292271D1C85FB5500C89379 /* src */ = { + isa = PBXGroup; + children = ( + D62CCBC122F367320099BE9F /* gmock.h */, + D62CCBBF22F367140099BE9F /* gmock-all.cc */, + D67D355622BABD2100292C1D /* gtest-all.cc */, + D67D355722BABD2100292C1D /* gtest.h */, + D6C179EF22CB32A000C2651A /* app_framework.cc */, + D6C179ED22CB323300C2651A /* app_framework.h */, + D6C179EC22CB323300C2651A /* firebase_test_framework.cc */, + D6C179EB22CB323300C2651A /* firebase_test_framework.h */, + D61C5F9222BABAD100A79141 /* integration_test.cc */, + 5292271E1C85FB5B00C89379 /* ios */, + ); + name = src; + sourceTree = ""; + }; + 5292271E1C85FB5B00C89379 /* ios */ = { + isa = PBXGroup; + children = ( + D6C179E722CB322900C2651A /* ios_app_framework.mm */, + D6C179E822CB322900C2651A /* ios_firebase_test_framework.mm */, + ); + name = ios; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 529226D11C85F68000C89379 /* integration_test */ = { + isa = PBXNativeTarget; + buildConfigurationList = 529226F91C85F68000C89379 /* Build configuration list for PBXNativeTarget "integration_test" */; + buildPhases = ( + 529226CE1C85F68000C89379 /* Sources */, + 529226CF1C85F68000C89379 /* Frameworks */, + 529226D01C85F68000C89379 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = integration_test; + productName = testapp; + productReference = 529226D21C85F68000C89379 /* integration_test.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 529226CA1C85F68000C89379 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0640; + ORGANIZATIONNAME = Google; + TargetAttributes = { + 529226D11C85F68000C89379 = { + CreatedOnToolsVersion = 6.4; + DevelopmentTeam = EQHXZ8M8AV; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = 529226CD1C85F68000C89379 /* Build configuration list for PBXProject "integration_test" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 529226C91C85F68000C89379; + productRefGroup = 529226D31C85F68000C89379 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 529226D11C85F68000C89379 /* integration_test */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 529226D01C85F68000C89379 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D61C5F8E22BABA9C00A79141 /* Images.xcassets in Resources */, + D66B16871CE46E8900E5638A /* LaunchScreen.storyboard in Resources */, + 520BC0391C869159008CFBC3 /* GoogleService-Info.plist in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 529226CE1C85F68000C89379 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D67D355822BABD2200292C1D /* gtest-all.cc in Sources */, + D62CCBC022F367140099BE9F /* gmock-all.cc in Sources */, + D6C179EA22CB322900C2651A /* ios_firebase_test_framework.mm in Sources */, + D61C5F9622BABAD200A79141 /* integration_test.cc in Sources */, + D6C179E922CB322900C2651A /* ios_app_framework.mm in Sources */, + D6C179F022CB32A000C2651A /* app_framework.cc in Sources */, + D6C179EE22CB323300C2651A /* firebase_test_framework.cc in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 529226F71C85F68000C89379 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.4; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 529226F81C85F68000C89379 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.4; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 529226FA1C85F68000C89379 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ""; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)", + ); + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + "\"$(SRCROOT)/src\"", + "\"$(SRCROOT)/external/googletest/src/googletest/include\"", + "\"$(SRCROOT)/external/googletest/src/googlemock/include\"", + "\"$(SRCROOT)/external/googletest/src/googletest\"", + "\"$(SRCROOT)/external/googletest/src/googlemock\"", + ); + INFOPLIST_FILE = "$(SRCROOT)/Info.plist"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + WRAPPER_EXTENSION = app; + }; + name = Debug; + }; + 529226FB1C85F68000C89379 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ""; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)", + ); + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + "\"$(SRCROOT)/src\"", + "\"$(SRCROOT)/external/googletest/src/googletest/include\"", + "\"$(SRCROOT)/external/googletest/src/googlemock/include\"", + "\"$(SRCROOT)/external/googletest/src/googletest\"", + "\"$(SRCROOT)/external/googletest/src/googlemock\"", + ); + INFOPLIST_FILE = "$(SRCROOT)/Info.plist"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + WRAPPER_EXTENSION = app; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 529226CD1C85F68000C89379 /* Build configuration list for PBXProject "integration_test" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 529226F71C85F68000C89379 /* Debug */, + 529226F81C85F68000C89379 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 529226F91C85F68000C89379 /* Build configuration list for PBXNativeTarget "integration_test" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 529226FA1C85F68000C89379 /* Debug */, + 529226FB1C85F68000C89379 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 529226CA1C85F68000C89379 /* Project object */; +} diff --git a/testing/integration_tests/instance_id/proguard.pro b/testing/integration_tests/instance_id/proguard.pro new file mode 100644 index 0000000000..2d04b8a9a5 --- /dev/null +++ b/testing/integration_tests/instance_id/proguard.pro @@ -0,0 +1,2 @@ +-ignorewarnings +-keep,includedescriptorclasses public class com.google.firebase.example.LoggingUtils { * ; } diff --git a/testing/integration_tests/instance_id/res/layout/main.xml b/testing/integration_tests/instance_id/res/layout/main.xml new file mode 100644 index 0000000000..d3ffb63082 --- /dev/null +++ b/testing/integration_tests/instance_id/res/layout/main.xml @@ -0,0 +1,12 @@ + + + + diff --git a/testing/integration_tests/instance_id/res/values/strings.xml b/testing/integration_tests/instance_id/res/values/strings.xml new file mode 100644 index 0000000000..0202809a5f --- /dev/null +++ b/testing/integration_tests/instance_id/res/values/strings.xml @@ -0,0 +1,4 @@ + + + Firebase Instance ID Integration Test + diff --git a/testing/integration_tests/instance_id/settings.gradle b/testing/integration_tests/instance_id/settings.gradle new file mode 100644 index 0000000000..2a543b9351 --- /dev/null +++ b/testing/integration_tests/instance_id/settings.gradle @@ -0,0 +1,36 @@ +// Copyright 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +def firebase_cpp_sdk_dir = System.getProperty('firebase_cpp_sdk.dir') +if (firebase_cpp_sdk_dir == null || firebase_cpp_sdk_dir.isEmpty()) { + firebase_cpp_sdk_dir = System.getenv('FIREBASE_CPP_SDK_DIR') + if (firebase_cpp_sdk_dir == null || firebase_cpp_sdk_dir.isEmpty()) { + if ((new File('firebase_cpp_sdk')).exists()) { + firebase_cpp_sdk_dir = 'firebase_cpp_sdk' + } else { + throw new StopActionException( + 'firebase_cpp_sdk.dir property or the FIREBASE_CPP_SDK_DIR ' + + 'environment variable must be set to reference the Firebase C++ ' + + 'SDK install directory. This is used to configure static library ' + + 'and C/C++ include paths for the SDK.') + } + } +} +if (!(new File(firebase_cpp_sdk_dir)).exists()) { + throw new StopActionException( + sprintf('Firebase C++ SDK directory %s does not exist', + firebase_cpp_sdk_dir)) +} +gradle.ext.firebase_cpp_sdk_dir = "$firebase_cpp_sdk_dir" +includeBuild "$firebase_cpp_sdk_dir" \ No newline at end of file diff --git a/testing/integration_tests/instance_id/src/integration_test.cc b/testing/integration_tests/instance_id/src/integration_test.cc new file mode 100644 index 0000000000..034fceaf55 --- /dev/null +++ b/testing/integration_tests/instance_id/src/integration_test.cc @@ -0,0 +1,224 @@ +// Copyright 2019 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include +#include +#include +#include +#include + +#include "app_framework.h" // NOLINT +#include "firebase/app.h" +#include "firebase/instance_id.h" +#include "firebase/util.h" +#include "firebase_test_framework.h" // NOLINT + +// The TO_STRING macro is useful for command line defined strings as the quotes +// get stripped. +#define TO_STRING_EXPAND(X) #X +#define TO_STRING(X) TO_STRING_EXPAND(X) + +// Path to the Firebase config file to load. +#ifdef FIREBASE_CONFIG +#define FIREBASE_CONFIG_STRING TO_STRING(FIREBASE_CONFIG) +#else +#define FIREBASE_CONFIG_STRING "" +#endif // FIREBASE_CONFIG + +namespace firebase_testapp_automated { + +using app_framework::LogDebug; + +using app_framework::ProcessEvents; +using firebase_test_framework::FirebaseTest; + +class FirebaseInstanceIdTest : public FirebaseTest { + public: + FirebaseInstanceIdTest(); + ~FirebaseInstanceIdTest() override; + + void SetUp() override; + void TearDown() override; + + protected: + // Initialize Firebase App and Firebase IID. + void Initialize(); + // Shut down Firebase IID and Firebase App. + void Terminate(); + + bool initialized_; + firebase::instance_id::InstanceId* instance_id_; +}; + +FirebaseInstanceIdTest::FirebaseInstanceIdTest() + : initialized_(false), instance_id_(nullptr) { + FindFirebaseConfig(FIREBASE_CONFIG_STRING); +} + +FirebaseInstanceIdTest::~FirebaseInstanceIdTest() { + // Must be cleaned up on exit. + assert(app_ == nullptr); + assert(instance_id_ == nullptr); +} + +void FirebaseInstanceIdTest::SetUp() { + FirebaseTest::SetUp(); + Initialize(); +} + +void FirebaseInstanceIdTest::TearDown() { + // Delete the shared path, if there is one. + if (initialized_) { + Terminate(); + } + FirebaseTest::TearDown(); +} + +void FirebaseInstanceIdTest::Initialize() { + if (initialized_) return; + + InitializeApp(); + + LogDebug("Initializing Firebase Instance ID."); + + ::firebase::ModuleInitializer initializer; + initializer.Initialize( + app_, &instance_id_, [](::firebase::App* app, void* target) { + LogDebug("Try to initialize Firebase Instance ID"); + firebase::InitResult result; + firebase::instance_id::InstanceId** iid_ptr = + reinterpret_cast(target); + *iid_ptr = + firebase::instance_id::InstanceId::GetInstanceId(app, &result); + return result; + }); + + FirebaseTest::WaitForCompletion(initializer.InitializeLastResult(), + "Initialize"); + + ASSERT_EQ(initializer.InitializeLastResult().error(), 0) + << initializer.InitializeLastResult().error_message(); + + LogDebug("Successfully initialized Firebase Instance ID."); + + initialized_ = true; +} + +void FirebaseInstanceIdTest::Terminate() { + if (!initialized_) return; + + if (instance_id_) { + LogDebug("Shutdown the Instance ID library."); + delete instance_id_; + instance_id_ = nullptr; + } + + TerminateApp(); + + initialized_ = false; + + ProcessEvents(100); +} + +TEST_F(FirebaseInstanceIdTest, TestInitializeAndTerminate) { + // Already tested via SetUp() and TearDown(). +} + +TEST_F(FirebaseInstanceIdTest, TestCanGetId) { + firebase::Future id = instance_id_->GetId(); + WaitForCompletion(id, "GetId"); + EXPECT_NE(*id.result(), ""); +} + +TEST_F(FirebaseInstanceIdTest, TestGettingIdTwiceMatches) { + firebase::Future id = instance_id_->GetId(); + WaitForCompletion(id, "GetId"); + EXPECT_NE(*id.result(), ""); + std::string first_id = *id.result(); + id = instance_id_->GetId(); + WaitForCompletion(id, "GetId 2"); + EXPECT_EQ(*id.result(), first_id); +} + +TEST_F(FirebaseInstanceIdTest, TestDeleteIdGivesNewIdNextTime) { + firebase::Future id = instance_id_->GetId(); + WaitForCompletion(id, "GetId"); + EXPECT_NE(*id.result(), ""); + std::string first_id = *id.result(); + + firebase::Future del = instance_id_->DeleteId(); + WaitForCompletion(del, "DeleteId"); + + // Ensure that we now get a different IID. + id = instance_id_->GetId(); + WaitForCompletion(id, "GetId 2"); + EXPECT_NE(*id.result(), ""); +#if defined(__ANDROID__) || (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) + // Desktop is a stub and returns the same ID, but on mobile it should + // return a new ID. + EXPECT_NE(*id.result(), first_id); +#endif // defined(__ANDROID__) || (defined(TARGET_OS_IPHONE) && + // TARGET_OS_IPHONE) +} + +TEST_F(FirebaseInstanceIdTest, TestCanGetToken) { + firebase::Future token = instance_id_->GetToken(); + WaitForCompletion(token, "GetToken"); + EXPECT_NE(*token.result(), ""); +} + +TEST_F(FirebaseInstanceIdTest, TestGettingTokenTwiceMatches) { + firebase::Future token = instance_id_->GetToken(); + WaitForCompletion(token, "GetToken"); + EXPECT_NE(*token.result(), ""); + std::string first_token = *token.result(); + token = instance_id_->GetToken(); + WaitForCompletion(token, "GetToken 2"); + EXPECT_EQ(*token.result(), first_token); +} + +// Test disabled due to flakiness (b/143697451). +TEST_F(FirebaseInstanceIdTest, DISABLED_TestDeleteTokenGivesNewTokenNextTime) { + firebase::Future token = instance_id_->GetToken(); + WaitForCompletion(token, "GetToken"); + EXPECT_NE(*token.result(), ""); + std::string first_token = *token.result(); + + firebase::Future del = instance_id_->DeleteToken(); + WaitForCompletion(del, "DeleteToken"); + + // Ensure that we now get a different IID. + token = instance_id_->GetToken(); + WaitForCompletion(token, "GetToken 2"); + EXPECT_NE(*token.result(), ""); +#if defined(__ANDROID__) || (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) + // Desktop is a stub and returns the same token, but on mobile it should + // return a new token. + EXPECT_NE(*token.result(), first_token); +#endif // defined(__ANDROID__) || (defined(TARGET_OS_IPHONE) && + // TARGET_OS_IPHONE) +} + +TEST_F(FirebaseInstanceIdTest, TestCanGetIdAndTokenTogether) { + firebase::Future id = instance_id_->GetId(); + firebase::Future token = instance_id_->GetToken(); + WaitForCompletion(token, "GetToken"); + WaitForCompletion(id, "GetId"); + EXPECT_NE(*id.result(), ""); + EXPECT_NE(*token.result(), ""); +} + +} // namespace firebase_testapp_automated diff --git a/testing/integration_tests/messaging/AndroidManifest.xml b/testing/integration_tests/messaging/AndroidManifest.xml new file mode 100644 index 0000000000..08ec978150 --- /dev/null +++ b/testing/integration_tests/messaging/AndroidManifest.xml @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/testing/integration_tests/messaging/CMakeLists.txt b/testing/integration_tests/messaging/CMakeLists.txt new file mode 100644 index 0000000000..632718e0d4 --- /dev/null +++ b/testing/integration_tests/messaging/CMakeLists.txt @@ -0,0 +1,198 @@ +cmake_minimum_required(VERSION 2.8) + +# User settings for Firebase integration tests. +# Path to Firebase SDK. +# Try to read the path to the Firebase C++ SDK from an environment variable. +if (NOT "$ENV{FIREBASE_CPP_SDK_DIR}" STREQUAL "") + set(DEFAULT_FIREBASE_CPP_SDK_DIR "$ENV{FIREBASE_CPP_SDK_DIR}") +else() + set(DEFAULT_FIREBASE_CPP_SDK_DIR "firebase_cpp_sdk") +endif() +if ("${FIREBASE_CPP_SDK_DIR}" STREQUAL "") + set(FIREBASE_CPP_SDK_DIR ${DEFAULT_FIREBASE_CPP_SDK_DIR}) +endif() +if(NOT EXISTS ${FIREBASE_CPP_SDK_DIR}) + message(FATAL_ERROR "The Firebase C++ SDK directory does not exist: ${FIREBASE_CPP_SDK_DIR}. See the readme.md for more information") +endif() + +# Windows runtime mode, either MD or MT depending on whether you are using +# /MD or /MT. For more information see: +# https://msdn.microsoft.com/en-us/library/2kzt1wy3.aspx +set(MSVC_RUNTIME_MODE MD) + +project(firebase_testapp) + +# Integration test source files. +set(FIREBASE_APP_FRAMEWORK_SRCS + src/app_framework.cc + src/app_framework.h +) + +set(FIREBASE_TEST_FRAMEWORK_SRCS + src/firebase_test_framework.h + src/firebase_test_framework.cc +) + +set(FIREBASE_INTEGRATION_TEST_SRCS + src/integration_test.cc +) + +# The include directory for the testapp. +include_directories(src) + +# Integration test uses some features that require C++ 11, such as lambdas. +set (CMAKE_CXX_STANDARD 11) + +# Download and unpack googletest (and googlemock) at configure time +set(GOOGLETEST_ROOT ${CMAKE_CURRENT_LIST_DIR}/external/googletest) +# Note: Once googletest is downloaded once, it won't be updated or +# downloaded again unless you delete the "external/googletest" +# directory. +if (NOT EXISTS ${GOOGLETEST_ROOT}/src/googletest/src/gtest-all.cc) + configure_file(googletest.cmake + ${CMAKE_CURRENT_LIST_DIR}/external/googletest/CMakeLists.txt COPYONLY) + execute_process(COMMAND ${CMAKE_COMMAND} . + RESULT_VARIABLE result + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/external/googletest ) + if(result) + message(FATAL_ERROR "CMake step for googletest failed: ${result}") + endif() + execute_process(COMMAND ${CMAKE_COMMAND} --build . + RESULT_VARIABLE result + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/external/googletest ) + if(result) + message(FATAL_ERROR "Build step for googletest failed: ${result}") + endif() +endif() + +if(ANDROID) + # Build an Android application. + + # Source files used for the Android build. + set(FIREBASE_APP_FRAMEWORK_ANDROID_SRCS + src/android/android_app_framework.cc + src/android/android_firebase_test_framework.cc + ) + + # Source files used for the Android build. + set(FIREBASE_TEST_FRAMEWORK_ANDROID_SRCS + src/android/android_firebase_test_framework.cc + ) + + # Build native_app_glue as a static lib + add_library(native_app_glue STATIC + ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c) + + # Export ANativeActivity_onCreate(), + # Refer to: https://github.com/android-ndk/ndk/issues/381. + set(CMAKE_SHARED_LINKER_FLAGS + "${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate") + + add_library(gtest STATIC + ${GOOGLETEST_ROOT}/src/googletest/src/gtest-all.cc) + target_include_directories(gtest + PRIVATE ${GOOGLETEST_ROOT}/src/googletest + PUBLIC ${GOOGLETEST_ROOT}/src/googletest/include) + add_library(gmock STATIC + ${GOOGLETEST_ROOT}/src/googlemock/src/gmock-all.cc) + target_include_directories(gmock + PRIVATE ${GOOGLETEST_ROOT}/src/googletest + PRIVATE ${GOOGLETEST_ROOT}/src/googlemock + PUBLIC ${GOOGLETEST_ROOT}/src/googletest/include + PUBLIC ${GOOGLETEST_ROOT}/src/googlemock/include) + + # Define the target as a shared library, as that is what gradle expects. + set(integration_test_target_name "android_integration_test_main") + add_library(${integration_test_target_name} SHARED + ${FIREBASE_APP_FRAMEWORK_SRCS} + ${FIREBASE_APP_FRAMEWORK_ANDROID_SRCS} + ${FIREBASE_INTEGRATION_TEST_SRCS} + ${FIREBASE_TEST_FRAMEWORK_SRCS} + ${FIREBASE_TEST_FRAMEWORK_ANDROID_SRCS} + ) + + target_include_directories(${integration_test_target_name} PRIVATE + ${ANDROID_NDK}/sources/android/native_app_glue) + + set(ADDITIONAL_LIBS log android atomic native_app_glue) +else() + # Build a desktop application. + add_definitions(-D_GLIBCXX_USE_CXX11_ABI=0) + + # Prevent overriding the parent project's compiler/linker + # settings on Windows + set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) + + # Add googletest directly to our build. This defines + # the gtest and gtest_main targets. + add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/external/googletest/src + ${CMAKE_CURRENT_LIST_DIR}/external/googletest/build + EXCLUDE_FROM_ALL) + + # The gtest/gtest_main targets carry header search path + # dependencies automatically when using CMake 2.8.11 or + # later. Otherwise we have to add them here ourselves. + if (CMAKE_VERSION VERSION_LESS 2.8.11) + include_directories("${gtest_SOURCE_DIR}/include") + include_directories("${gmock_SOURCE_DIR}/include") + endif() + + # Windows runtime mode, either MD or MT depending on whether you are using + # /MD or /MT. For more information see: + # https://msdn.microsoft.com/en-us/library/2kzt1wy3.aspx + set(MSVC_RUNTIME_MODE MD) + + # Platform abstraction layer for the desktop integration test. + set(FIREBASE_APP_FRAMEWORK_DESKTOP_SRCS + src/desktop/desktop_app_framework.cc + src/desktop/desktop_firebase_test_framework.cc + ) + + set(integration_test_target_name "integration_test") + add_executable(${integration_test_target_name} + ${FIREBASE_APP_FRAMEWORK_SRCS} + ${FIREBASE_APP_FRAMEWORK_DESKTOP_SRCS} + ${FIREBASE_TEST_FRAMEWORK_SRCS} + ${FIREBASE_INTEGRATION_TEST_SRCS} + ) + + if(APPLE) + set(ADDITIONAL_LIBS + gssapi_krb5 + pthread + "-framework CoreFoundation" + "-framework Foundation" + "-framework GSS" + "-framework Security" + ) + elseif(MSVC) + set(ADDITIONAL_LIBS advapi32 ws2_32 crypt32) + else() + set(ADDITIONAL_LIBS pthread) + endif() + + # If a config file is present, copy it into the binary location so that it's + # possible to create the default Firebase app. + set(FOUND_JSON_FILE FALSE) + foreach(config "google-services-desktop.json" "google-services.json") + if (EXISTS ${config}) + add_custom_command( + TARGET ${integration_test_target_name} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + ${config} $) + set(FOUND_JSON_FILE TRUE) + break() + endif() + endforeach() + if(NOT FOUND_JSON_FILE) + message(WARNING "Failed to find either google-services-desktop.json or google-services.json. See the readme.md for more information.") + endif() +endif() + +# Add the Firebase libraries to the target using the function from the SDK. +add_subdirectory(${FIREBASE_CPP_SDK_DIR} bin/ EXCLUDE_FROM_ALL) +# Note that firebase_app needs to be last in the list. +set(firebase_libs firebase_messaging firebase_app) +set(gtest_libs gtest gmock) +target_link_libraries(${integration_test_target_name} ${firebase_libs} + ${gtest_libs} ${ADDITIONAL_LIBS}) diff --git a/testing/integration_tests/messaging/Firebase_Cpp_Messaging_Test_App_Dev.mobileprovision b/testing/integration_tests/messaging/Firebase_Cpp_Messaging_Test_App_Dev.mobileprovision new file mode 100644 index 0000000000000000000000000000000000000000..d2e2a24c51a1538285d357d351a48868d5409ddf GIT binary patch literal 290126 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≷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 literal 0 HcmV?d00001 diff --git a/testing/integration_tests/messaging/Images.xcassets/AppIcon.appiconset/Contents.json b/testing/integration_tests/messaging/Images.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000000..d8db8d65fd --- /dev/null +++ b/testing/integration_tests/messaging/Images.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "83.5x83.5", + "scale" : "2x" + }, + { + "idiom" : "ios-marketing", + "size" : "1024x1024", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/testing/integration_tests/messaging/Images.xcassets/LaunchImage.launchimage/Contents.json b/testing/integration_tests/messaging/Images.xcassets/LaunchImage.launchimage/Contents.json new file mode 100644 index 0000000000..6f870a4629 --- /dev/null +++ b/testing/integration_tests/messaging/Images.xcassets/LaunchImage.launchimage/Contents.json @@ -0,0 +1,51 @@ +{ + "images" : [ + { + "orientation" : "portrait", + "idiom" : "iphone", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "orientation" : "portrait", + "idiom" : "iphone", + "subtype" : "retina4", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "orientation" : "portrait", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "1x" + }, + { + "orientation" : "landscape", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "1x" + }, + { + "orientation" : "portrait", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "orientation" : "landscape", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/testing/integration_tests/messaging/Info.plist b/testing/integration_tests/messaging/Info.plist new file mode 100644 index 0000000000..319f56a445 --- /dev/null +++ b/testing/integration_tests/messaging/Info.plist @@ -0,0 +1,44 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + com.google.FirebaseCppMessagingTestApp.dev + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + UIBackgroundModes + + remote-notification + + CFBundleURLTypes + + + CFBundleTypeRole + Editor + CFBundleURLName + google + CFBundleURLSchemes + + com.googleusercontent.apps.255980362477-3a1nf8c4nl0c7hlnlnmc98hbtg2mnbue + firebase-game-loop + + + + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + + diff --git a/testing/integration_tests/messaging/LaunchScreen.storyboard b/testing/integration_tests/messaging/LaunchScreen.storyboard new file mode 100644 index 0000000000..673e0f7e68 --- /dev/null +++ b/testing/integration_tests/messaging/LaunchScreen.storyboard @@ -0,0 +1,7 @@ + + + + + + + diff --git a/testing/integration_tests/messaging/LibraryManifest.xml b/testing/integration_tests/messaging/LibraryManifest.xml new file mode 100644 index 0000000000..90c5fb93d7 --- /dev/null +++ b/testing/integration_tests/messaging/LibraryManifest.xml @@ -0,0 +1,7 @@ + + + + + + diff --git a/testing/integration_tests/messaging/Podfile b/testing/integration_tests/messaging/Podfile new file mode 100644 index 0000000000..7de3086fbf --- /dev/null +++ b/testing/integration_tests/messaging/Podfile @@ -0,0 +1,13 @@ +source 'sso://cpdc-internal/firebase.git' +source 'https://github.com/CocoaPods/Specs.git' +platform :ios, '8.0' +# Firebase Cloud Messaging test application. + +target 'integration_test' do + pod 'Firebase/Messaging', '6.24.0' +end + +post_install do |installer| + system("/usr/bin/python ./download_googletest.py") +end + diff --git a/testing/integration_tests/messaging/build.gradle b/testing/integration_tests/messaging/build.gradle new file mode 100644 index 0000000000..8cc3faef76 --- /dev/null +++ b/testing/integration_tests/messaging/build.gradle @@ -0,0 +1,64 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +buildscript { + repositories { + mavenLocal() + maven { url 'https://maven.google.com' } + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:3.2.1' + classpath 'com.google.gms:google-services:4.0.1' + } +} + +allprojects { + repositories { + mavenLocal() + maven { url 'https://maven.google.com' } + jcenter() + } +} + +apply plugin: 'com.android.application' + +android { + compileSdkVersion 28 + buildToolsVersion '28.0.3' + + sourceSets { + main { + jniLibs.srcDirs = ['libs'] + manifest.srcFile 'AndroidManifest.xml' + java.srcDirs = ['src/android/java'] + res.srcDirs = ['res'] + } + } + + defaultConfig { + applicationId 'com.google.firebase.cpp.messaging.testapp' + minSdkVersion 16 + targetSdkVersion 28 + versionCode 1 + versionName '1.0' + externalNativeBuild.cmake { + arguments "-DFIREBASE_CPP_SDK_DIR=$gradle.firebase_cpp_sdk_dir" + } + } + externalNativeBuild.cmake { + path 'CMakeLists.txt' + } + buildTypes { + release { + minifyEnabled true + proguardFile getDefaultProguardFile('proguard-android.txt') + proguardFile file('proguard.pro') + } + } +} + +apply from: "$gradle.firebase_cpp_sdk_dir/Android/firebase_dependencies.gradle" +firebaseCpp.dependencies { + messaging +} + +apply plugin: 'com.google.gms.google-services' diff --git a/testing/integration_tests/messaging/googletest.cmake b/testing/integration_tests/messaging/googletest.cmake new file mode 100644 index 0000000000..0eeff685dd --- /dev/null +++ b/testing/integration_tests/messaging/googletest.cmake @@ -0,0 +1,19 @@ +# Download GoogleTest from GitHub as an external project. +# This CMake file is taken from: +# https://github.com/google/googletest/blob/master/googletest/README.md#incorporating-into-an-existing-cmake-project + +cmake_minimum_required(VERSION 2.8.2) + +project(googletest-download NONE) + +include(ExternalProject) +ExternalProject_Add(googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG master + SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/src" + BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/build" + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + TEST_COMMAND "" +) diff --git a/testing/integration_tests/messaging/gradle/wrapper/gradle-wrapper.jar b/testing/integration_tests/messaging/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..8c0fb64a8698b08ecc4158d828ca593c4928e9dd GIT binary patch literal 49896 zcmagFb986H(k`5d^NVfUwr$(C?M#x1ZQHiZiEVpg+jrjgoQrerx!>1o_ul)D>ebz~ zs=Mmxr&>W81QY-S1PKWQ%N-;H^tS;2*XwVA`dej1RRn1z<;3VgfE4~kaG`A%QSPsR z#ovnZe+tS9%1MfeDyz`RirvdjPRK~p(#^q2(^5@O&NM19EHdvN-A&StN>0g6QA^VN z0Gx%Gq#PD$QMRFzmK+utjS^Y1F0e8&u&^=w5K<;4Rz|i3A=o|IKLY+g`iK6vfr9?+ z-`>gmU&i?FGSL5&F?TXFu`&Js6h;15QFkXp2M1H9|Eq~bpov-GU(uz%mH0n55wUl- zv#~ccAz`F5wlQ>e_KlJS3@{)B?^v*EQM=IxLa&76^y51a((wq|2-`qON>+4dLc{Oo z51}}o^Zen(oAjxDK7b++9_Yg`67p$bPo3~BCpGM7uAWmvIhWc5Gi+gQZ|Pwa-Gll@<1xmcPy z|NZmu6m)g5Ftu~BG&Xdxclw7Cij{xbBMBn-LMII#Slp`AElb&2^Hw+w>(3crLH!;I zN+Vk$D+wP1#^!MDCiad@vM>H#6+`Ct#~6VHL4lzmy;lSdk>`z6)=>Wh15Q2)dQtGqvn0vJU@+(B5{MUc*qs4!T+V=q=wy)<6$~ z!G>e_4dN@lGeF_$q9`Ju6Ncb*x?O7=l{anm7Eahuj_6lA{*#Gv*TaJclevPVbbVYu z(NY?5q+xxbO6%g1xF0r@Ix8fJ~u)VRUp`S%&rN$&e!Od`~s+64J z5*)*WSi*i{k%JjMSIN#X;jC{HG$-^iX+5f5BGOIHWAl*%15Z#!xntpk($-EGKCzKa zT7{siZ9;4TICsWQ$pu&wKZQTCvpI$Xvzwxoi+XkkpeE&&kFb!B?h2hi%^YlXt|-@5 zHJ~%AN!g_^tmn1?HSm^|gCE#!GRtK2(L{9pL#hp0xh zME}|DB>(5)`iE7CM)&_+S}-Bslc#@B5W4_+k4Cp$l>iVyg$KP>CN?SVGZ(&02>iZK zB<^HP$g$Lq*L$BWd?2(F?-MUbNWTJVQdW7$#8a|k_30#vHAD1Z{c#p;bETk0VnU5A zBgLe2HFJ3032$G<`m*OB!KM$*sdM20jm)It5OSru@tXpK5LT>#8)N!*skNu1$TpIw zufjjdp#lyH5bZ%|Iuo|iu9vG1HrIVWLH>278xo>aVBkPN3V$~!=KnlXQ4eDqS7%E% zQ!z^$Q$b^6Q)g#cLpwur(|<0gWHo6A6jc;n`t(V9T;LzTAU{IAu*uEQ%Ort1k+Kn+f_N`9|bxYC+~Z1 zCC1UCWv*Orx$_@ydv9mIe(liLfOr7mhbV@tKw{6)q^1DH1nmvZ0cj215R<~&I<4S| zgnr;9Cdjqpz#o8i0CQjtl`}{c*P)aSdH|abxGdrR)-3z+02-eX(k*B)Uqv6~^nh** z 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 \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/testing/integration_tests/messaging/gradlew.bat b/testing/integration_tests/messaging/gradlew.bat new file mode 100644 index 0000000000..8a0b282aa6 --- /dev/null +++ b/testing/integration_tests/messaging/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/testing/integration_tests/messaging/integration_test.entitlements b/testing/integration_tests/messaging/integration_test.entitlements new file mode 100644 index 0000000000..e38c46b835 --- /dev/null +++ b/testing/integration_tests/messaging/integration_test.entitlements @@ -0,0 +1,10 @@ + + + + + application-identifier + $(AppIdentifierPrefix)$(CFBundleIdentifier) + aps-environment + development + + diff --git a/testing/integration_tests/messaging/integration_test.xcodeproj/project.pbxproj b/testing/integration_tests/messaging/integration_test.xcodeproj/project.pbxproj new file mode 100644 index 0000000000..d1d6b852d1 --- /dev/null +++ b/testing/integration_tests/messaging/integration_test.xcodeproj/project.pbxproj @@ -0,0 +1,364 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 520BC0391C869159008CFBC3 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 520BC0381C869159008CFBC3 /* GoogleService-Info.plist */; }; + 529226D61C85F68000C89379 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 529226D51C85F68000C89379 /* Foundation.framework */; }; + 529226D81C85F68000C89379 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 529226D71C85F68000C89379 /* CoreGraphics.framework */; }; + 529226DA1C85F68000C89379 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 529226D91C85F68000C89379 /* UIKit.framework */; }; + D61C5F8E22BABA9C00A79141 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D61C5F8C22BABA9B00A79141 /* Images.xcassets */; }; + D61C5F9622BABAD200A79141 /* integration_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = D61C5F9222BABAD100A79141 /* integration_test.cc */; }; + D62CCBC022F367140099BE9F /* gmock-all.cc in Sources */ = {isa = PBXBuildFile; fileRef = D62CCBBF22F367140099BE9F /* gmock-all.cc */; }; + D66B16871CE46E8900E5638A /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D66B16861CE46E8900E5638A /* LaunchScreen.storyboard */; }; + D67D355822BABD2200292C1D /* gtest-all.cc in Sources */ = {isa = PBXBuildFile; fileRef = D67D355622BABD2100292C1D /* gtest-all.cc */; }; + D6C179E922CB322900C2651A /* ios_app_framework.mm in Sources */ = {isa = PBXBuildFile; fileRef = D6C179E722CB322900C2651A /* ios_app_framework.mm */; }; + D6C179EA22CB322900C2651A /* ios_firebase_test_framework.mm in Sources */ = {isa = PBXBuildFile; fileRef = D6C179E822CB322900C2651A /* ios_firebase_test_framework.mm */; }; + D6C179EE22CB323300C2651A /* firebase_test_framework.cc in Sources */ = {isa = PBXBuildFile; fileRef = D6C179EC22CB323300C2651A /* firebase_test_framework.cc */; }; + D6C179F022CB32A000C2651A /* app_framework.cc in Sources */ = {isa = PBXBuildFile; fileRef = D6C179EF22CB32A000C2651A /* app_framework.cc */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 520BC0381C869159008CFBC3 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; + 529226D21C85F68000C89379 /* integration_test.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = integration_test.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 529226D51C85F68000C89379 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + 529226D71C85F68000C89379 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; + 529226D91C85F68000C89379 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; + 529226EE1C85F68000C89379 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; + D61C5F8C22BABA9B00A79141 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; + D61C5F8D22BABA9C00A79141 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + D61C5F9222BABAD100A79141 /* integration_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = integration_test.cc; path = src/integration_test.cc; sourceTree = ""; }; + D62CCBBF22F367140099BE9F /* gmock-all.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "gmock-all.cc"; path = "external/googletest/src/googlemock/src/gmock-all.cc"; sourceTree = ""; }; + D62CCBC122F367320099BE9F /* gmock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = gmock.h; path = external/googletest/src/googlemock/include/gmock/gmock.h; sourceTree = ""; }; + D66B16861CE46E8900E5638A /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = ""; }; + D67D355622BABD2100292C1D /* gtest-all.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "gtest-all.cc"; path = "external/googletest/src/googletest/src/gtest-all.cc"; sourceTree = ""; }; + D67D355722BABD2100292C1D /* gtest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = gtest.h; path = external/googletest/src/googletest/include/gtest/gtest.h; sourceTree = ""; }; + D6C179E722CB322900C2651A /* ios_app_framework.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ios_app_framework.mm; path = src/ios/ios_app_framework.mm; sourceTree = ""; }; + D6C179E822CB322900C2651A /* ios_firebase_test_framework.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ios_firebase_test_framework.mm; path = src/ios/ios_firebase_test_framework.mm; sourceTree = ""; }; + D6C179EB22CB323300C2651A /* firebase_test_framework.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = firebase_test_framework.h; path = src/firebase_test_framework.h; sourceTree = ""; }; + D6C179EC22CB323300C2651A /* firebase_test_framework.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = firebase_test_framework.cc; path = src/firebase_test_framework.cc; sourceTree = ""; }; + D6C179ED22CB323300C2651A /* app_framework.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = app_framework.h; path = src/app_framework.h; sourceTree = ""; }; + D6C179EF22CB32A000C2651A /* app_framework.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = app_framework.cc; path = src/app_framework.cc; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 529226CF1C85F68000C89379 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 529226D81C85F68000C89379 /* CoreGraphics.framework in Frameworks */, + 529226DA1C85F68000C89379 /* UIKit.framework in Frameworks */, + 529226D61C85F68000C89379 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 529226C91C85F68000C89379 = { + isa = PBXGroup; + children = ( + D61C5F8C22BABA9B00A79141 /* Images.xcassets */, + D61C5F8D22BABA9C00A79141 /* Info.plist */, + D66B16861CE46E8900E5638A /* LaunchScreen.storyboard */, + 520BC0381C869159008CFBC3 /* GoogleService-Info.plist */, + 5292271D1C85FB5500C89379 /* src */, + 529226D41C85F68000C89379 /* Frameworks */, + 529226D31C85F68000C89379 /* Products */, + ); + sourceTree = ""; + }; + 529226D31C85F68000C89379 /* Products */ = { + isa = PBXGroup; + children = ( + 529226D21C85F68000C89379 /* integration_test.app */, + ); + name = Products; + sourceTree = ""; + }; + 529226D41C85F68000C89379 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 529226D51C85F68000C89379 /* Foundation.framework */, + 529226D71C85F68000C89379 /* CoreGraphics.framework */, + 529226D91C85F68000C89379 /* UIKit.framework */, + 529226EE1C85F68000C89379 /* XCTest.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 5292271D1C85FB5500C89379 /* src */ = { + isa = PBXGroup; + children = ( + D62CCBC122F367320099BE9F /* gmock.h */, + D62CCBBF22F367140099BE9F /* gmock-all.cc */, + D67D355622BABD2100292C1D /* gtest-all.cc */, + D67D355722BABD2100292C1D /* gtest.h */, + D6C179EF22CB32A000C2651A /* app_framework.cc */, + D6C179ED22CB323300C2651A /* app_framework.h */, + D6C179EC22CB323300C2651A /* firebase_test_framework.cc */, + D6C179EB22CB323300C2651A /* firebase_test_framework.h */, + D61C5F9222BABAD100A79141 /* integration_test.cc */, + 5292271E1C85FB5B00C89379 /* ios */, + ); + name = src; + sourceTree = ""; + }; + 5292271E1C85FB5B00C89379 /* ios */ = { + isa = PBXGroup; + children = ( + D6C179E722CB322900C2651A /* ios_app_framework.mm */, + D6C179E822CB322900C2651A /* ios_firebase_test_framework.mm */, + ); + name = ios; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 529226D11C85F68000C89379 /* integration_test */ = { + isa = PBXNativeTarget; + buildConfigurationList = 529226F91C85F68000C89379 /* Build configuration list for PBXNativeTarget "integration_test" */; + buildPhases = ( + 529226CE1C85F68000C89379 /* Sources */, + 529226CF1C85F68000C89379 /* Frameworks */, + 529226D01C85F68000C89379 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = integration_test; + productName = testapp; + productReference = 529226D21C85F68000C89379 /* integration_test.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 529226CA1C85F68000C89379 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0640; + ORGANIZATIONNAME = Google; + TargetAttributes = { + 529226D11C85F68000C89379 = { + CreatedOnToolsVersion = 6.4; + DevelopmentTeam = EQHXZ8M8AV; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = 529226CD1C85F68000C89379 /* Build configuration list for PBXProject "integration_test" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 529226C91C85F68000C89379; + productRefGroup = 529226D31C85F68000C89379 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 529226D11C85F68000C89379 /* integration_test */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 529226D01C85F68000C89379 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D61C5F8E22BABA9C00A79141 /* Images.xcassets in Resources */, + D66B16871CE46E8900E5638A /* LaunchScreen.storyboard in Resources */, + 520BC0391C869159008CFBC3 /* GoogleService-Info.plist in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 529226CE1C85F68000C89379 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D67D355822BABD2200292C1D /* gtest-all.cc in Sources */, + D62CCBC022F367140099BE9F /* gmock-all.cc in Sources */, + D6C179EA22CB322900C2651A /* ios_firebase_test_framework.mm in Sources */, + D61C5F9622BABAD200A79141 /* integration_test.cc in Sources */, + D6C179E922CB322900C2651A /* ios_app_framework.mm in Sources */, + D6C179F022CB32A000C2651A /* app_framework.cc in Sources */, + D6C179EE22CB323300C2651A /* firebase_test_framework.cc in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 529226F71C85F68000C89379 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.4; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 529226F81C85F68000C89379 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.4; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 529226FA1C85F68000C89379 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ""; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)", + ); + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + "\"$(SRCROOT)/src\"", + "\"$(SRCROOT)/external/googletest/src/googletest/include\"", + "\"$(SRCROOT)/external/googletest/src/googlemock/include\"", + "\"$(SRCROOT)/external/googletest/src/googletest\"", + "\"$(SRCROOT)/external/googletest/src/googlemock\"", + ); + INFOPLIST_FILE = "$(SRCROOT)/Info.plist"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + WRAPPER_EXTENSION = app; + }; + name = Debug; + }; + 529226FB1C85F68000C89379 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ""; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)", + ); + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + "\"$(SRCROOT)/src\"", + "\"$(SRCROOT)/external/googletest/src/googletest/include\"", + "\"$(SRCROOT)/external/googletest/src/googlemock/include\"", + "\"$(SRCROOT)/external/googletest/src/googletest\"", + "\"$(SRCROOT)/external/googletest/src/googlemock\"", + ); + INFOPLIST_FILE = "$(SRCROOT)/Info.plist"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + WRAPPER_EXTENSION = app; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 529226CD1C85F68000C89379 /* Build configuration list for PBXProject "integration_test" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 529226F71C85F68000C89379 /* Debug */, + 529226F81C85F68000C89379 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 529226F91C85F68000C89379 /* Build configuration list for PBXNativeTarget "integration_test" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 529226FA1C85F68000C89379 /* Debug */, + 529226FB1C85F68000C89379 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 529226CA1C85F68000C89379 /* Project object */; +} diff --git a/testing/integration_tests/messaging/proguard.pro b/testing/integration_tests/messaging/proguard.pro new file mode 100644 index 0000000000..16b4cf1f7a --- /dev/null +++ b/testing/integration_tests/messaging/proguard.pro @@ -0,0 +1,3 @@ +-ignorewarnings +-keep,includedescriptorclasses public class com.google.firebase.example.LoggingUtils { * ; } +-keep,includedescriptorclasses public class com.google.firebase.example.SampleNativeActivity { * ; } diff --git a/testing/integration_tests/messaging/readme.md b/testing/integration_tests/messaging/readme.md new file mode 100644 index 0000000000..ea47af7387 --- /dev/null +++ b/testing/integration_tests/messaging/readme.md @@ -0,0 +1,258 @@ +Firebase Cloud Messaging Quickstart +=================================== + +The Firebase Cloud Messaging Test Application (testapp) demonstrates receiving +Firebase Cloud Messages using the Firebase Cloud Messaging C++ SDK. This +application has no user interface and simply logs actions it's performing to the +console. + +Introduction +------------ + +- [Read more about Firebase Cloud Messaging](https://firebase.google.com/docs/cloud-messaging/) + +Building and Running the testapp +-------------------------------- +### iOS + - Link your iOS app to the Firebase libraries. + - Get CocoaPods version 1 or later by running, + + ``` + sudo gem install cocoapods --pre + ``` + - From the testapp directory, install the CocoaPods listed in the Podfile by + running, + ``` + pod install + ``` + - Open the generated Xcode workspace (which now has the CocoaPods), + + ``` + open testapp.xcworkspace + ``` + - For further details please refer to the + [general instructions for setting up an iOS app with Firebase](https://firebase.google.com/docs/ios/setup). + + - Register your iOS app with Firebase. + - Create a new app on + [firebase.google.com/console](https://firebase.google.com/console/) + , and attach your iOS app to it. + - For Messaging, you will need an App Store ID. Use something random such + as 12345678." + - You can use "com.google.FirebaseCppMessagingTestApp.dev" as the iOS Bundle ID + while you're testing. + - Add the GoogleService-Info.plist that you downloaded from Firebase + console to the testapp root directory. This file identifies your iOS app + to the Firebase backend. + - Download the Firebase C++ SDK linked from + [https://firebase.google.com/docs/cpp/setup](https://firebase.google.com/docs/cpp/setup) + and unzip it to a directory of your choice. + - Add the following frameworks from the Firebase C++ SDK to the project: + - frameworks/ios/universal/firebase.framework + - frameworks/ios/universal/firebase_messaging.framework + - You will need to either, + 1. Check "Copy items if needed" when adding the frameworks, or + 2. Add the framework path in "Framework Search Paths" + - e.g. If you downloaded the Firebase C++ SDK to + `/Users/me/firebase_cpp_sdk`, + then you would add the path + `/Users/me/firebase_cpp_sdk/frameworks/ios/universal`. + - To add the path, in XCode, select your project in the project + navigator, then select your target in the main window. + Select the "Build Settings" tab, and click "All" to see all + the build settings. Scroll down to "Search Paths", and add + your path to "Framework Search Paths". + - You need a valid + [APNs](https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Chapters/ApplePushService.html) + certificate. If you don't already have one, see + [Provisioning APNs SSL Certificates.](https://firebase.google.com/docs/cloud-messaging/ios/certs) + - Configure the Xcode project for push messaging. + - Select the `testapp` project from the `Navigator area`. + - Select the `testapp` target from the `Editor area`. + - Select the `General` tab from the `Editor area`. + - Scroll down to `Linked Frameworks and Libraries` and click the `+` + button to add a framework. + - In the window that appears, scroll to `UserNotifications.framework` + and click on that entry, then click on `Add`. This framework will only + appear in Xcode version 8 and higher, required by this library. + - Select the `Capabilities` tab from the `Editor area`. + - Switch `Push Notifications` to `On`. + - Scroll down to `Background Modes` and switch it to `On`. + - Tick the `Remote notifications` box under `Background Modes`. + - In XCode, build & run the sample on an iOS device or simulator. + - The testapp has no user interface. The output of the app can be viewed + via the console. In Xcode, select + `View --> Debug Area --> Activate Console` from the menu. + +### Android +**Register your Android app with Firebase.** + +- Create a new app on +[firebase.google.com/console](https://firebase.google.com/console/) +, and attach your Android app to it. +- You can use "com.google.android.messaging.testapp" as the Package Name +while you're testing. +- To [generate a SHA1](https://developers.google.com/android/guides/client-auth) +run this command (_Note: the default password is 'android'_): + * Mac and Linux: + + ``` + keytool -exportcert -list -v -alias androiddebugkey -keystore ~/.android/debug.keystore + ``` + * Windows: + + ``` + keytool -exportcert -list -v -alias androiddebugkey -keystore %USERPROFILE%\.android\debug.keystore + ``` +- If keytool reports that you do not have a debug.keystore, you can +[create one](http://developer.android.com/tools/publishing/app-signing.html#signing-manually) +with: + + ``` + keytool -genkey -v -keystore ~/.android/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US" + ``` +- Add the `google-services.json` file that you downloaded from Firebase + console to the root directory of testapp. This file identifies your + Android app to the Firebase backend. +- For further details please refer to the +[general instructions for setting up an Android app with Firebase](https://firebase.google.com/docs/android/setup). + +- Download the Firebase C++ SDK linked from + [https://firebase.google.com/docs/cpp/setup](https://firebase.google.com/docs/cpp/setup) + and unzip it to a directory of your choice. + +**Configure your SDK paths** + +- Configure the location of the Firebase C++ SDK by setting the +`firebase_cpp_sdk.dir` Gradle property to the SDK install directory. + * Run this command in the project directory, and modify the path to match your + local installation path: + + ``` + echo "systemProp.firebase_cpp_sdk.dir=/User/$USER/firebase_cpp_sdk" >> gradle.properties + ``` +- Ensure the Android SDK and NDK locations are set in Android Studio. + * From the Android Studio launch menu, go to `File/Project Structure...` or + `Configure/Project Defaults/Project Structure...` + (Shortcut: Control + Alt + Shift + S on windows, Command + ";" on a mac) + and download the SDK and NDK if the locations are not yet set. + * Android Studio will write these paths to `local.properties`. + +**Optional: Configure your deep link URL** + +- To enable your app to receive deep links via FCM, you will need to add an + intent filter to your AndroidManifest.xml in the native activity that + associates your domain with your app. + +``` + + + + + + + +``` + +**Build & Run** + +- Open `build.gradle` in Android Studio. + - From the Android Studio launch menu, "Open an existing Android Studio + project", and select `build.gradle`. +- Install the SDK Platforms that Android Studio reports missing. +- Build the testapp and run it on an Android device or emulator. +- See [below](#using_the_test_app) for usage instructions. + +### Desktop + - Register your app with Firebase. + - Create a new app on the [Firebase console](https://firebase.google.com/console/), + following the above instructions for Android or iOS. + - If you have an Android project, add the `google-services.json` file that + you downloaded from the Firebase console to the root directory of the + testapp. + - If you have an iOS project, and don't wish to use an Android project, + you can use the Python script `generate_xml_from_google_services_json.py --plist`, + located in the Firebase C++ SDK, to convert your `GoogleService-Info.plist` + file into a `google-services-desktop.json` file, which can then be + placed in the root directory of the testapp. + - Download the Firebase C++ SDK linked from + [https://firebase.google.com/docs/cpp/setup](https://firebase.google.com/docs/cpp/setup) + and unzip it to a directory of your choice. + - Configure the testapp with the location of the Firebase C++ SDK. + This can be done a couple different ways (in highest to lowest priority): + - When invoking cmake, pass in the location with + -DFIREBASE_CPP_SDK_DIR=/path/to/firebase_cpp_sdk. + - Set an environment variable for FIREBASE_CPP_SDK_DIR to the path to use. + - Edit the CMakeLists.txt file, changing the FIREBASE_CPP_SDK_DIR path + to the appropriate location. + - From the testapp directory, generate the build files by running, + ``` + cmake . + ``` + If you want to use XCode, you can use -G"Xcode" to generate the project. + Similarly, to use Visual Studio, -G"Visual Studio 15 2017". For more + information, see + [CMake generators](https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html). + - Build the testapp, by either opening the generated project file based on + the platform, or running, + ``` + cmake --build . + ``` + - Execute the testapp by running, + ``` + ./desktop_testapp + ``` + Note that the executable might be under another directory, such as Debug. + - The testapp has no user interface, but the output can be viewed via the + console. Note that Messaging uses a stubbed implementation on desktop, + so functionality is not expected. + +Using the Test App +------------------ + +- Install and run the test app on your iOS or Android device or emulator. +- The application has minimal user interface. The output of the app can be +viewed via the console: + * **iOS**: Open select "View --> Debug Area --> Activate Console" from the + menu in Xcode. + * **Android**: View the logcat output in Android studio or by running + "adb logcat" from the command line. +- When you first run the app, it will print: +`Received Registration Token: `. Copy this code to a text editor. +- Copy the ServerKey from the firebase console: + - Open your project in the + [Firebase Console](https://firebase.google.com/console/). + - Click the gear icon then `Project settings` in the menu on the left + - Select the `Cloud Messaging` tab. + - Copy the `Server Key` +- Replace `` and `` in this command and run it +from the command line. +``` +curl --header "Authorization: key=" --header "Content-Type: application/json" https://android.googleapis.com/gcm/send -d '{"notification":{"title":"Hi","body":"Hello from the Cloud"},"data":{"score":"lots"},"to":""}' +``` +- Observe the command received in the app, via the console output. + +Support +------- + +[https://firebase.google.com/support/](https://firebase.google.com/support/) + +License +------- + +Copyright 2016 Google, Inc. + +Licensed to the Apache Software Foundation (ASF) under one or more contributor +license agreements. See the NOTICE file distributed with this work for +additional information regarding copyright ownership. The ASF licenses this +file to you under the Apache License, Version 2.0 (the "License"); you may not +use this file except in compliance with the License. You may obtain a copy of +the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +License for the specific language governing permissions and limitations under +the License. diff --git a/testing/integration_tests/messaging/res/layout/main.xml b/testing/integration_tests/messaging/res/layout/main.xml new file mode 100644 index 0000000000..d3ffb63082 --- /dev/null +++ b/testing/integration_tests/messaging/res/layout/main.xml @@ -0,0 +1,12 @@ + + + + diff --git a/testing/integration_tests/messaging/res/values/strings.xml b/testing/integration_tests/messaging/res/values/strings.xml new file mode 100644 index 0000000000..0b0c595859 --- /dev/null +++ b/testing/integration_tests/messaging/res/values/strings.xml @@ -0,0 +1,4 @@ + + + Firebase Cloud Messaging Integration Test + diff --git a/testing/integration_tests/messaging/settings.gradle b/testing/integration_tests/messaging/settings.gradle new file mode 100644 index 0000000000..2a543b9351 --- /dev/null +++ b/testing/integration_tests/messaging/settings.gradle @@ -0,0 +1,36 @@ +// Copyright 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +def firebase_cpp_sdk_dir = System.getProperty('firebase_cpp_sdk.dir') +if (firebase_cpp_sdk_dir == null || firebase_cpp_sdk_dir.isEmpty()) { + firebase_cpp_sdk_dir = System.getenv('FIREBASE_CPP_SDK_DIR') + if (firebase_cpp_sdk_dir == null || firebase_cpp_sdk_dir.isEmpty()) { + if ((new File('firebase_cpp_sdk')).exists()) { + firebase_cpp_sdk_dir = 'firebase_cpp_sdk' + } else { + throw new StopActionException( + 'firebase_cpp_sdk.dir property or the FIREBASE_CPP_SDK_DIR ' + + 'environment variable must be set to reference the Firebase C++ ' + + 'SDK install directory. This is used to configure static library ' + + 'and C/C++ include paths for the SDK.') + } + } +} +if (!(new File(firebase_cpp_sdk_dir)).exists()) { + throw new StopActionException( + sprintf('Firebase C++ SDK directory %s does not exist', + firebase_cpp_sdk_dir)) +} +gradle.ext.firebase_cpp_sdk_dir = "$firebase_cpp_sdk_dir" +includeBuild "$firebase_cpp_sdk_dir" \ No newline at end of file diff --git a/testing/integration_tests/messaging/src/android/java/com/google/firebase/example/SampleNativeActivity.java b/testing/integration_tests/messaging/src/android/java/com/google/firebase/example/SampleNativeActivity.java new file mode 100644 index 0000000000..3706c6acad --- /dev/null +++ b/testing/integration_tests/messaging/src/android/java/com/google/firebase/example/SampleNativeActivity.java @@ -0,0 +1,73 @@ +// Copyright 2016 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.firebase.example; + +import android.app.NativeActivity; +import android.content.Intent; +import android.os.Bundle; +import com.google.firebase.messaging.MessageForwardingService; + +/** + * SampleNativeActivity is a NativeActivity that updates its intent when new intents are sent to + * it. + * + * This is a workaround for a known issue that prevents the native C++ library from responding to + * data payloads when both a data and notification payload are sent to the app while it is in the + * background. + */ +public class SampleNativeActivity extends NativeActivity { + // The key in the intent's extras that maps to the incoming message's message ID. Only sent by + // the server, GmsCore sends EXTRA_MESSAGE_ID_KEY below. Server can't send that as it would get + // stripped by the client. + private static final String EXTRA_MESSAGE_ID_KEY_SERVER = "message_id"; + + // An alternate key value in the intent's extras that also maps to the incoming message's message + // ID. Used by upstream, and set by GmsCore. + private static final String EXTRA_MESSAGE_ID_KEY = "google.message_id"; + + // The key in the intent's extras that maps to the incoming message's sender value. + private static final String EXTRA_FROM = "google.message_id"; + + /** + * Workaround for when a message is sent containing both a Data and Notification payload. + * + * When the app is in the foreground all data payloads are sent to the method + * `::firebase::messaging::Listener::OnMessage`. However, when the app is in the background, if a + * message with both a data and notification payload is receieved the data payload is stored on + * the notification Intent. NativeActivity does not provide native callbacks for onNewIntent, so + * it cannot route the data payload that is stored in the Intent to the C++ function OnMessage. As + * a workaround, we override onNewIntent so that it forwards the intent to the C++ library's + * service which in turn forwards the data to the native C++ messaging library. + */ + @Override + protected void onNewIntent(Intent intent) { + // If we do not have a 'from' field this intent was not a message and should not be handled. It + // probably means this intent was fired by tapping on the app icon. + Bundle extras = intent.getExtras(); + String from = extras.getString(EXTRA_FROM); + String messageId = extras.getString(EXTRA_MESSAGE_ID_KEY); + if (messageId == null) { + messageId = extras.getString(EXTRA_MESSAGE_ID_KEY_SERVER); + } + if (from != null && messageId != null) { + Intent message = new Intent(this, MessageForwardingService.class); + message.setAction(MessageForwardingService.ACTION_REMOTE_INTENT); + message.putExtras(intent); + message.setData(intent.getData()); + startService(message); + } + setIntent(intent); + } +} diff --git a/testing/integration_tests/messaging/src/integration_test.cc b/testing/integration_tests/messaging/src/integration_test.cc new file mode 100644 index 0000000000..2a5c244220 --- /dev/null +++ b/testing/integration_tests/messaging/src/integration_test.cc @@ -0,0 +1,596 @@ +// Copyright 2019 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include +#include +#include +#include +#include + +#include "app_framework.h" // NOLINT +#include "firebase/app.h" +#include "firebase/messaging.h" +#include "firebase/util.h" +#include "firebase_test_framework.h" // NOLINT + +// The TO_STRING macro is useful for command line defined strings as the quotes +// get stripped. +#define TO_STRING_EXPAND(X) #X +#define TO_STRING(X) TO_STRING_EXPAND(X) + +// Path to the Firebase config file to load. +#ifdef FIREBASE_CONFIG +#define FIREBASE_CONFIG_STRING TO_STRING(FIREBASE_CONFIG) +#else +#define FIREBASE_CONFIG_STRING "" +#endif // FIREBASE_CONFIG + +namespace firebase_testapp_automated { + +// Your Firebase project's Server Key for Cloud Messaging goes here. +// You can get this from Firebase Console, in your Project settings under Cloud +// Messaging. +const char kFcmServerKey[] = + "AAAAM2ROZHA:APA91bHVTMuAdnIw014jKETAUYiy7qLvIo1zMHGMOszbZyZJaf_" + "cEEd6MF9ad8qwS_DbgjYo36FeELhtTf-dsJCsLt6hurDnbRPIGcxMPtNMNSYC1Krh-" + "KSn9GURceEwEA-sr-i3HTDU"; + +const char kRestEndpoint[] = "https://fcm.googleapis.com/fcm/send"; + +const char kNotificationLinkKey[] = "gcm.n.link"; +const char kTestLink[] = "https://this-is-a-test-link/"; + +// Give each operation approximately 60 seconds before failing. +const int kTimeoutSeconds = 60; +const char kTestingNotificationKey[] = "fcm_testing_notification"; + +using app_framework::LogDebug; +using app_framework::LogInfo; + +using app_framework::GetCurrentTimeInMicroseconds; +using app_framework::PathForResource; +using app_framework::ProcessEvents; +using firebase_test_framework::FirebaseTest; + +class FirebaseMessagingTest : public FirebaseTest { + public: + FirebaseMessagingTest(); + ~FirebaseMessagingTest() override; + + static void SetUpTestSuite(); + static void TearDownTestSuite(); + + void SetUp() override; + void TearDown() override; + + // Create a request and heads for a test message (returning false if unable to + // do so). send_to can be a FCM token or a topic subscription. + bool CreateTestMessage( + const char* send_to, const char* notification_title, + const char* notification_body, + const std::map& message_fields, + std::string* request_out, + std::map* headers_out); + // Send a message previously created by CreateTestMessage. + void SendTestMessage(const std::string& request, + const std::map& headers); + // Convenience method combining the above. + void SendTestMessage( + const char* send_to, const char* notification_title, + const char* notification_body, + const std::map& message_fields); + + protected: + // Get a unique message ID so we can confirm the correct message is being + // received. + std::string GetUniqueMessageId(); + + // Wait to receive a token. Returns true if a token was received, and places + // it in token_. + static bool WaitForToken(int timeout = kTimeoutSeconds); + // Request messaging permissions from the user. Returns true if permission was + // granted. + bool RequestPermission(); + // Wait to receive a message. Returns true if a message was received and + // returns it in the message_out parameter. + bool WaitForMessage(firebase::messaging::Message* message_out, + int timeout = kTimeoutSeconds); + + static firebase::App* shared_app_; + static firebase::messaging::PollableListener* shared_listener_; + static std::string* shared_token_; + static bool is_desktop_stub_; +}; + +const char kObtainedPermissionKey[] = "messaging_got_permission"; + +std::string* FirebaseMessagingTest::shared_token_ = nullptr; +firebase::App* FirebaseMessagingTest::shared_app_ = nullptr; +firebase::messaging::PollableListener* FirebaseMessagingTest::shared_listener_ = + nullptr; +bool FirebaseMessagingTest::is_desktop_stub_; + +void FirebaseMessagingTest::SetUpTestSuite() { + LogDebug("Initialize Firebase App."); + +#if defined(__ANDROID__) + shared_app_ = ::firebase::App::Create(app_framework::GetJniEnv(), + app_framework::GetActivity()); +#else + shared_app_ = ::firebase::App::Create(); +#endif // defined(__ANDROID__) + + LogDebug("Initializing Firebase Cloud Messaging."); + shared_token_ = new std::string(); + + ::firebase::ModuleInitializer initializer; + initializer.Initialize( + shared_app_, &shared_listener_, [](::firebase::App* app, void* userdata) { + LogDebug("Try to initialize Firebase Messaging"); + firebase::messaging::PollableListener** listener = + reinterpret_cast(userdata); + *listener = new firebase::messaging::PollableListener(); + firebase::messaging::MessagingOptions options; + // Prevent the app from requesting permission to show notifications + // immediately upon starting up for the first time. Since it the prompt + // is being suppressed, we must manually display it with a call to + // RequestPermission() elsewhere. + // + // After the first time we've done this, we no longer will suppress the + // permission prompt just for ease of initialization. + std::string value; + if (!GetPersistentString(kObtainedPermissionKey, &value) || + value.empty()) { + // The first time, suppress the notification prompt so that + // RequestPermission will be called. + options.suppress_notification_permission_prompt = true; + } + + return ::firebase::messaging::Initialize(*app, *listener, options); + }); + + WaitForCompletion(initializer.InitializeLastResult(), "Initialize"); + + ASSERT_EQ(initializer.InitializeLastResult().error(), 0) + << initializer.InitializeLastResult().error_message(); + + LogDebug("Successfully initialized Firebase Cloud Messaging."); + is_desktop_stub_ = false; +#if !defined(ANDROID) && !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) + is_desktop_stub_ = true; +#endif // !defined(ANDROID) && !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) +} + +void FirebaseMessagingTest::TearDownTestSuite() { + LogDebug("All tests finished, cleaning up."); + firebase::messaging::SetListener(nullptr); + delete shared_listener_; + shared_listener_ = nullptr; + delete shared_token_; + shared_token_ = nullptr; + + LogDebug("Shutdown Firebase Cloud Messaging."); + firebase::messaging::Terminate(); + LogDebug("Shutdown Firebase App."); + delete shared_app_; + shared_app_ = nullptr; +} + +FirebaseMessagingTest::FirebaseMessagingTest() { + FindFirebaseConfig(FIREBASE_CONFIG_STRING); +} + +FirebaseMessagingTest::~FirebaseMessagingTest() {} + +void FirebaseMessagingTest::SetUp() { FirebaseTest::SetUp(); } + +void FirebaseMessagingTest::TearDown() { FirebaseTest::TearDown(); } + +std::string FirebaseMessagingTest::GetUniqueMessageId() { + int64_t time_in_microseconds = GetCurrentTimeInMicroseconds(); + char buffer[21] = {0}; + snprintf(buffer, sizeof(buffer), "%lld", + static_cast(time_in_microseconds)); // NOLINT + return std::string(buffer); +} + +// send_to can be a FCM token or a topic subscription. +bool FirebaseMessagingTest::CreateTestMessage( + const char* send_to, const char* notification_title, + const char* notification_body, + const std::map& message_fields, + std::string* request_out, std::map* headers_out) { + if (is_desktop_stub_) { + // Don't send HTTP requests in stub mode. + return false; + } + std::map headers; + headers.insert(std::make_pair("Content-type", "application/json")); + headers.insert( + std::make_pair("Authorization", std::string("key=") + kFcmServerKey)); + std::string request; // Build a JSON request. + request += "{\"notification\":{\"title\":\""; + request += notification_title ? notification_title : ""; + request += "\",\"body\":\""; + request += notification_body ? notification_body : ""; + request += "\"},\"data\":{"; + for (auto i = message_fields.begin(); i != message_fields.end(); ++i) { + if (i != message_fields.begin()) request += ","; + request += "\""; + request += i->first; + request += "\":\""; + request += i->second; + request += "\""; + } + request += "}, \"to\":\""; + request += send_to; + // Messages will expire after 5 minutes, so if there are stale/leftover + // messages from a previous run, just wait 5 minutes and try again. + request += "\", \"time_to_live\":300}"; + *request_out = request; + *headers_out = headers; + return true; +} + +void FirebaseMessagingTest::SendTestMessage( + const char* send_to, const char* notification_title, + const char* notification_body, + const std::map& message_fields) { + std::string request; + std::map headers; + EXPECT_TRUE(CreateTestMessage(send_to, notification_title, notification_body, + message_fields, &request, &headers)); + SendTestMessage(request, headers); +} + +void FirebaseMessagingTest::SendTestMessage( + const std::string& request, + const std::map& headers) { + LogDebug("Request: %s", request.c_str()); + LogDebug("Triggering FCM message from server..."); + EXPECT_TRUE( + SendHttpPostRequest(kRestEndpoint, headers, request, nullptr, nullptr)); +} + +bool FirebaseMessagingTest::WaitForToken(int timeout) { + if (!shared_token_->empty()) return true; + + if (is_desktop_stub_) { + // On desktop, just set a stub token. + *shared_token_ = "FcmDesktopStubToken"; + return true; + } + + std::string new_token; + // No new or old token immediately, so wait for a new token. + int seconds = 0; + while (seconds <= timeout) { + if (shared_listener_->PollRegistrationToken(&new_token)) { + if (!new_token.empty()) { + *shared_token_ = new_token; + LogInfo("Got token: %s", shared_token_->c_str()); + return true; + } + } + seconds++; + ProcessEvents(1000); + } + // Failed to get a token. + *shared_token_ = ""; + return false; +} + +bool FirebaseMessagingTest::WaitForMessage( + firebase::messaging::Message* message_out, int timeout) { + int seconds = 0; + while (seconds <= timeout) { + if (shared_listener_->PollMessage(message_out)) { + LogDebug("Received a message."); + return true; + } + seconds++; + ProcessEvents(1000); + } + LogDebug("Did not receive a message."); + *message_out = firebase::messaging::Message(); + return false; +} + +bool FirebaseMessagingTest::RequestPermission() { + std::string value; + if (GetPersistentString(kObtainedPermissionKey, &value) && value == "1") { + return true; // Already got permission. + } + + bool b = WaitForCompletion(firebase::messaging::RequestPermission(), + "RequestPermission"); + if (b) { +#if TARGET_OS_IPHONE + // We only need to pause for permission on iOS. + LogDebug("Pausing so user can grant permission (if needed)."); + ProcessEvents(10000); +#endif // TARGET_OS_IPHONE + SetPersistentString(kObtainedPermissionKey, "1"); + } + return b; +} + +// Test cases below. + +TEST_F(FirebaseMessagingTest, TestRequestPermission) { + TEST_REQUIRES_USER_INTERACTION_ON_IOS; + + // This test may request a permission from the user; if so, the user must + // respond affirmatively. + EXPECT_TRUE(RequestPermission()); +} + +TEST_F(FirebaseMessagingTest, TestReceiveToken) { + TEST_REQUIRES_USER_INTERACTION_ON_IOS; + + EXPECT_TRUE(RequestPermission()); + EXPECT_TRUE(WaitForToken()); + EXPECT_NE(*shared_token_, ""); +} + +TEST_F(FirebaseMessagingTest, TestSubscribeAndUnsubscribe) { + TEST_REQUIRES_USER_INTERACTION_ON_IOS; + + EXPECT_TRUE(RequestPermission()); + EXPECT_TRUE(WaitForToken()); + EXPECT_TRUE(WaitForCompletion(firebase::messaging::Subscribe("SubscribeTest"), + "Subscribe")); + EXPECT_TRUE(WaitForCompletion( + firebase::messaging::Unsubscribe("SubscribeTest"), "Unsubscribe")); +} + +static std::string ConstructHtmlToSendMessage( + const std::string& request, + const std::map& headers, int delay_seconds) { + // Generate some simple HTML/Javascript to pause a few seconds, then send the + // POST request via XMLHttpRequest. + std::string h; + h += ""; + return h; +} + +TEST_F(FirebaseMessagingTest, TestNotification) { + TEST_REQUIRES_USER_INTERACTION; + SKIP_TEST_ON_DESKTOP; + + EXPECT_TRUE(RequestPermission()); + EXPECT_TRUE(WaitForToken()); + + // To test notifications, this test app must be running in the background. To + // accomplish this, switch over to the device's web browser, loading an HTML + // page that will, after a short delay, send the FCM message request to the + // app in the background. This will produce the system notification that you + // can then click on to go back into the app and continue the test. + + std::string unique_id = GetUniqueMessageId(); + std::string token = *shared_token_; + const char kNotificationTitle[] = "FCM Integration Test"; + const char kNotificationBody[] = "Test notification, open to resume testing."; + std::string value; + if (!GetPersistentString(kTestingNotificationKey, &value) || value.empty()) { + // If the notification test is already in progress, just go straight to the + // waiting part. This can happen if you wait too long to click on the + // notification and the app is no longer running in the background. + std::string request; + std::map headers; + std::map message_fields = { + {"message", "This is a notification."}, + {"unique_id", unique_id}, +#if defined(__ANDROID__) + // Duplicate notification.title and notification.body here, see + // below for why. + {"notification_title", kNotificationTitle}, + {"notification_body", kNotificationBody}, +#endif // defined(__ANDROID__) + }; + EXPECT_TRUE(CreateTestMessage(shared_token_->c_str(), kNotificationTitle, + kNotificationBody, message_fields, &request, + &headers)); + std::string html = ConstructHtmlToSendMessage(request, headers, 5); + // We now have some HTML/Javascript to send the message request. Embed it in + // a data: url so we can try receiving a message with the app in the + // background. + // Encode the HTML into base64. + std::string html_encoded; + EXPECT_TRUE(Base64Encode(html, &html_encoded)); + std::string url = std::string("data:text/html;base64,") + html_encoded; + LogInfo("Opening browser to trigger FCM message."); + if (OpenUrlInBrowser(url.c_str())) { + SetPersistentString(kTestingNotificationKey, "1"); + } else { + FAIL() << "Failed to open URL in browser."; + } + } + SetPersistentString(kTestingNotificationKey, nullptr); + LogDebug("Waiting for message."); + firebase::messaging::Message message; + EXPECT_TRUE(WaitForMessage(&message, 120)); + EXPECT_EQ(message.data["unique_id"], unique_id); + EXPECT_TRUE(message.notification_opened); + +#if defined(__ANDROID__) + // On Android, if the app is running in the background, FCM does not deliver + // both the "notification" and the "data". So for our purposes, duplicate the + // notification fields we are checking into the data fields so we can still + // check that it's correct. + EXPECT_EQ(message.notification, nullptr); + EXPECT_EQ(message.data["notification_title"], kNotificationTitle); + EXPECT_EQ(message.data["notification_body"], kNotificationBody); +#else + // On iOS, we do get the notification. + EXPECT_NE(message.notification, nullptr); + if (message.notification) { + EXPECT_EQ(message.notification->title, kNotificationTitle); + EXPECT_EQ(message.notification->body, kNotificationBody); + } +#endif // defined(__ANDROID__) +} + +TEST_F(FirebaseMessagingTest, TestSendMessageToToken) { + TEST_REQUIRES_USER_INTERACTION_ON_IOS; + SKIP_TEST_ON_DESKTOP; + + EXPECT_TRUE(RequestPermission()); + EXPECT_TRUE(WaitForToken()); + std::string unique_id = GetUniqueMessageId(); + const char kNotificationTitle[] = "Token Test"; + const char kNotificationBody[] = "Token Test notification body"; + SendTestMessage(shared_token_->c_str(), kNotificationTitle, kNotificationBody, + {{"message", "Hello, world!"}, + {"unique_id", unique_id}, + {kNotificationLinkKey, kTestLink}}); + LogDebug("Waiting for message."); + firebase::messaging::Message message; + EXPECT_TRUE(WaitForMessage(&message)); + EXPECT_EQ(message.data["unique_id"], unique_id); + EXPECT_NE(message.notification, nullptr); + if (message.notification) { + EXPECT_EQ(message.notification->title, kNotificationTitle); + EXPECT_EQ(message.notification->body, kNotificationBody); + } + EXPECT_EQ(message.link, kTestLink); +} + +TEST_F(FirebaseMessagingTest, TestSendMessageToTopic) { + TEST_REQUIRES_USER_INTERACTION_ON_IOS; + SKIP_TEST_ON_DESKTOP; + + EXPECT_TRUE(RequestPermission()); + EXPECT_TRUE(WaitForToken()); + std::string unique_id = GetUniqueMessageId(); + const char kNotificationTitle[] = "Topic Test"; + const char kNotificationBody[] = "Topic Test notification body"; + // Create a somewhat unique topic name using 2 digits near the end of + // unique_id (but not the LAST 2 digits, due to timestamp resolution on some + // platforms). + std::string unique_id_tag = + (unique_id.length() >= 7 ? unique_id.substr(unique_id.length() - 5, 2) + : "00"); + std::string topic = "FCMTestTopic" + unique_id_tag; + EXPECT_TRUE(WaitForCompletion(firebase::messaging::Subscribe(topic.c_str()), + "Subscribe")); + SendTestMessage(("/topics/" + topic).c_str(), kNotificationTitle, + kNotificationBody, + {{"message", "Hello, world!"}, {"unique_id", unique_id}}); + firebase::messaging::Message message; + EXPECT_TRUE(WaitForMessage(&message)); + EXPECT_EQ(message.data["unique_id"], unique_id); + if (message.notification) { + EXPECT_EQ(message.notification->title, kNotificationTitle); + EXPECT_EQ(message.notification->body, kNotificationBody); + } + + EXPECT_TRUE(WaitForCompletion(firebase::messaging::Unsubscribe(topic.c_str()), + "Unsubscribe")); + + // Ensure that we *don't* receive a message now. + unique_id = GetUniqueMessageId(); + SendTestMessage(("/topics/" + topic).c_str(), "Topic Title 2", "Topic Body 2", + {{"message", "Hello, world!"}, {"unique_id", unique_id}}); + EXPECT_FALSE(WaitForMessage(&message, 5)); +} + +TEST_F(FirebaseMessagingTest, TestChangingListener) { + TEST_REQUIRES_USER_INTERACTION_ON_IOS; + SKIP_TEST_ON_DESKTOP; + + EXPECT_TRUE(RequestPermission()); + EXPECT_TRUE(WaitForToken()); + + // Back up the previous listener object and create a new one. + firebase::messaging::PollableListener* old_listener_ = shared_listener_; + // WaitForMessage() uses whatever shared_listener_ is set to. + shared_listener_ = new firebase::messaging::PollableListener(); + firebase::messaging::SetListener(shared_listener_); + + std::string unique_id = GetUniqueMessageId(); + const char kNotificationTitle[] = "New Listener Test"; + const char kNotificationBody[] = "New Listener Test notification body"; + SendTestMessage(shared_token_->c_str(), kNotificationTitle, kNotificationBody, + {{"message", "Hello, world!"}, {"unique_id", unique_id}}); + LogDebug("Waiting for message."); + firebase::messaging::Message message; + EXPECT_TRUE(WaitForMessage(&message)); + EXPECT_EQ(message.data["unique_id"], unique_id); + if (message.notification) { + EXPECT_EQ(message.notification->title, kNotificationTitle); + EXPECT_EQ(message.notification->body, kNotificationBody); + } + + // Set back to the previous listener. + firebase::messaging::SetListener(old_listener_); + delete shared_listener_; + shared_listener_ = old_listener_; +} + +TEST_F(FirebaseMessagingTest, DeliverMetricsToBigQuery) { + // These setters and getters are not implemented on all platforms, so we run + // them here to make sure they don't crash, and then validate the values + // received below only on the platforms they are implemented on. + + bool initial_value = + firebase::messaging::DeliveryMetricsExportToBigQueryEnabled(); + // This one should always default to false unless it has been set. + EXPECT_FALSE(initial_value); + + firebase::messaging::SetDeliveryMetricsExportToBigQuery(true); + bool result_after_setting = + firebase::messaging::DeliveryMetricsExportToBigQueryEnabled(); + + firebase::messaging::SetDeliveryMetricsExportToBigQuery(false); + bool result_after_clearing = + firebase::messaging::DeliveryMetricsExportToBigQueryEnabled(); + +#if defined(ANDROID) + EXPECT_TRUE(result_after_setting); + EXPECT_FALSE(result_after_clearing); +#else + (void)result_after_setting; + (void)result_after_clearing; +#endif +} + +} // namespace firebase_testapp_automated diff --git a/testing/integration_tests/remote_config/AndroidManifest.xml b/testing/integration_tests/remote_config/AndroidManifest.xml new file mode 100644 index 0000000000..1ce2e9b6e8 --- /dev/null +++ b/testing/integration_tests/remote_config/AndroidManifest.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/testing/integration_tests/remote_config/CMakeLists.txt b/testing/integration_tests/remote_config/CMakeLists.txt new file mode 100644 index 0000000000..06369a69a5 --- /dev/null +++ b/testing/integration_tests/remote_config/CMakeLists.txt @@ -0,0 +1,196 @@ +cmake_minimum_required(VERSION 2.8) + +# User settings for Firebase integration tests. +# Path to Firebase SDK. +# Try to read the path to the Firebase C++ SDK from an environment variable. +if (NOT "$ENV{FIREBASE_CPP_SDK_DIR}" STREQUAL "") + set(DEFAULT_FIREBASE_CPP_SDK_DIR "$ENV{FIREBASE_CPP_SDK_DIR}") +else() + set(DEFAULT_FIREBASE_CPP_SDK_DIR "firebase_cpp_sdk") +endif() +if ("${FIREBASE_CPP_SDK_DIR}" STREQUAL "") + set(FIREBASE_CPP_SDK_DIR ${DEFAULT_FIREBASE_CPP_SDK_DIR}) +endif() +if(NOT EXISTS ${FIREBASE_CPP_SDK_DIR}) + message(FATAL_ERROR "The Firebase C++ SDK directory does not exist: ${FIREBASE_CPP_SDK_DIR}. See the readme.md for more information") +endif() + +# Windows runtime mode, either MD or MT depending on whether you are using +# /MD or /MT. For more information see: +# https://msdn.microsoft.com/en-us/library/2kzt1wy3.aspx +set(MSVC_RUNTIME_MODE MD) + +project(firebase_testapp) + +# Integration test source files. +set(FIREBASE_APP_FRAMEWORK_SRCS + src/app_framework.cc + src/app_framework.h +) + +set(FIREBASE_TEST_FRAMEWORK_SRCS + src/firebase_test_framework.h + src/firebase_test_framework.cc +) + +set(FIREBASE_INTEGRATION_TEST_SRCS + src/integration_test.cc +) + +# The include directory for the testapp. +include_directories(src) + +# Integration test uses some features that require C++ 11, such as lambdas. +set (CMAKE_CXX_STANDARD 11) + +# Download and unpack googletest (and googlemock) at configure time +set(GOOGLETEST_ROOT ${CMAKE_CURRENT_LIST_DIR}/external/googletest) +# Note: Once googletest is downloaded once, it won't be updated or +# downloaded again unless you delete the "external/googletest" +# directory. +if (NOT EXISTS ${GOOGLETEST_ROOT}/src/googletest/src/gtest-all.cc) + configure_file(googletest.cmake + ${CMAKE_CURRENT_LIST_DIR}/external/googletest/CMakeLists.txt COPYONLY) + execute_process(COMMAND ${CMAKE_COMMAND} . + RESULT_VARIABLE result + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/external/googletest ) + if(result) + message(FATAL_ERROR "CMake step for googletest failed: ${result}") + endif() + execute_process(COMMAND ${CMAKE_COMMAND} --build . + RESULT_VARIABLE result + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/external/googletest ) + if(result) + message(FATAL_ERROR "Build step for googletest failed: ${result}") + endif() +endif() + +if(ANDROID) + # Build an Android application. + + # Source files used for the Android build. + set(FIREBASE_APP_FRAMEWORK_ANDROID_SRCS + src/android/android_app_framework.cc + ) + + # Source files used for the Android build. + set(FIREBASE_TEST_FRAMEWORK_ANDROID_SRCS + src/android/android_firebase_test_framework.cc + ) + + # Build native_app_glue as a static lib + add_library(native_app_glue STATIC + ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c) + + # Export ANativeActivity_onCreate(), + # Refer to: https://github.com/android-ndk/ndk/issues/381. + set(CMAKE_SHARED_LINKER_FLAGS + "${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate") + + add_library(gtest STATIC + ${GOOGLETEST_ROOT}/src/googletest/src/gtest-all.cc) + target_include_directories(gtest + PRIVATE ${GOOGLETEST_ROOT}/src/googletest + PUBLIC ${GOOGLETEST_ROOT}/src/googletest/include) + add_library(gmock STATIC + ${GOOGLETEST_ROOT}/src/googlemock/src/gmock-all.cc) + target_include_directories(gmock + PRIVATE ${GOOGLETEST_ROOT}/src/googletest + PRIVATE ${GOOGLETEST_ROOT}/src/googlemock + PUBLIC ${GOOGLETEST_ROOT}/src/googletest/include + PUBLIC ${GOOGLETEST_ROOT}/src/googlemock/include) + + # Define the target as a shared library, as that is what gradle expects. + set(integration_test_target_name "android_integration_test_main") + add_library(${integration_test_target_name} SHARED + ${FIREBASE_APP_FRAMEWORK_SRCS} + ${FIREBASE_APP_FRAMEWORK_ANDROID_SRCS} + ${FIREBASE_INTEGRATION_TEST_SRCS} + ${FIREBASE_TEST_FRAMEWORK_SRCS} + ${FIREBASE_TEST_FRAMEWORK_ANDROID_SRCS} + ) + + target_include_directories(${integration_test_target_name} PRIVATE + ${ANDROID_NDK}/sources/android/native_app_glue) + + set(ADDITIONAL_LIBS log android atomic native_app_glue) +else() + # Build a desktop application. + add_definitions(-D_GLIBCXX_USE_CXX11_ABI=0) + + # Prevent overriding the parent project's compiler/linker + # settings on Windows + set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) + + # Add googletest directly to our build. This defines + # the gtest and gtest_main targets. + add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/external/googletest/src + ${CMAKE_CURRENT_LIST_DIR}/external/googletest/build + EXCLUDE_FROM_ALL) + + # The gtest/gtest_main targets carry header search path + # dependencies automatically when using CMake 2.8.11 or + # later. Otherwise we have to add them here ourselves. + if (CMAKE_VERSION VERSION_LESS 2.8.11) + include_directories("${gtest_SOURCE_DIR}/include") + include_directories("${gmock_SOURCE_DIR}/include") + endif() + + # Windows runtime mode, either MD or MT depending on whether you are using + # /MD or /MT. For more information see: + # https://msdn.microsoft.com/en-us/library/2kzt1wy3.aspx + set(MSVC_RUNTIME_MODE MD) + + # Platform abstraction layer for the desktop integration test. + set(FIREBASE_APP_FRAMEWORK_DESKTOP_SRCS + src/desktop/desktop_app_framework.cc + ) + + set(integration_test_target_name "integration_test") + add_executable(${integration_test_target_name} + ${FIREBASE_APP_FRAMEWORK_SRCS} + ${FIREBASE_APP_FRAMEWORK_DESKTOP_SRCS} + ${FIREBASE_TEST_FRAMEWORK_SRCS} + ${FIREBASE_INTEGRATION_TEST_SRCS} + ) + + if(APPLE) + set(ADDITIONAL_LIBS + gssapi_krb5 + pthread + "-framework CoreFoundation" + "-framework Foundation" + "-framework GSS" + "-framework Security" + ) + elseif(MSVC) + set(ADDITIONAL_LIBS advapi32 ws2_32 crypt32) + else() + set(ADDITIONAL_LIBS pthread) + endif() + + # If a config file is present, copy it into the binary location so that it's + # possible to create the default Firebase app. + set(FOUND_JSON_FILE FALSE) + foreach(config "google-services-desktop.json" "google-services.json") + if (EXISTS ${config}) + add_custom_command( + TARGET ${integration_test_target_name} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + ${config} $) + set(FOUND_JSON_FILE TRUE) + break() + endif() + endforeach() + if(NOT FOUND_JSON_FILE) + message(WARNING "Failed to find either google-services-desktop.json or google-services.json. See the readme.md for more information.") + endif() +endif() + +# Add the Firebase libraries to the target using the function from the SDK. +add_subdirectory(${FIREBASE_CPP_SDK_DIR} bin/ EXCLUDE_FROM_ALL) +# Note that firebase_app needs to be last in the list. +set(firebase_libs firebase_remote_config firebase_app) +set(gtest_libs gtest gmock) +target_link_libraries(${integration_test_target_name} ${firebase_libs} + ${gtest_libs} ${ADDITIONAL_LIBS}) diff --git a/testing/integration_tests/remote_config/Images.xcassets/AppIcon.appiconset/Contents.json b/testing/integration_tests/remote_config/Images.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000000..d8db8d65fd --- /dev/null +++ b/testing/integration_tests/remote_config/Images.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "83.5x83.5", + "scale" : "2x" + }, + { + "idiom" : "ios-marketing", + "size" : "1024x1024", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/testing/integration_tests/remote_config/Images.xcassets/LaunchImage.launchimage/Contents.json b/testing/integration_tests/remote_config/Images.xcassets/LaunchImage.launchimage/Contents.json new file mode 100644 index 0000000000..6f870a4629 --- /dev/null +++ b/testing/integration_tests/remote_config/Images.xcassets/LaunchImage.launchimage/Contents.json @@ -0,0 +1,51 @@ +{ + "images" : [ + { + "orientation" : "portrait", + "idiom" : "iphone", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "orientation" : "portrait", + "idiom" : "iphone", + "subtype" : "retina4", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "orientation" : "portrait", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "1x" + }, + { + "orientation" : "landscape", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "1x" + }, + { + "orientation" : "portrait", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "orientation" : "landscape", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/testing/integration_tests/remote_config/Info.plist b/testing/integration_tests/remote_config/Info.plist new file mode 100644 index 0000000000..e8bfc83986 --- /dev/null +++ b/testing/integration_tests/remote_config/Info.plist @@ -0,0 +1,40 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + com.google.ios.remoteconfig.testapp + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleURLTypes + + + CFBundleTypeRole + Editor + CFBundleURLName + google + CFBundleURLSchemes + + com.googleusercontent.apps.255980362477-3a1nf8c4nl0c7hlnlnmc98hbtg2mnbue + firebase-game-loop + + + + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + + diff --git a/testing/integration_tests/remote_config/LaunchScreen.storyboard b/testing/integration_tests/remote_config/LaunchScreen.storyboard new file mode 100644 index 0000000000..673e0f7e68 --- /dev/null +++ b/testing/integration_tests/remote_config/LaunchScreen.storyboard @@ -0,0 +1,7 @@ + + + + + + + diff --git a/testing/integration_tests/remote_config/LibraryManifest.xml b/testing/integration_tests/remote_config/LibraryManifest.xml new file mode 100644 index 0000000000..0f9554e694 --- /dev/null +++ b/testing/integration_tests/remote_config/LibraryManifest.xml @@ -0,0 +1,7 @@ + + + + + + diff --git a/testing/integration_tests/remote_config/Podfile b/testing/integration_tests/remote_config/Podfile new file mode 100644 index 0000000000..7de5edc486 --- /dev/null +++ b/testing/integration_tests/remote_config/Podfile @@ -0,0 +1,13 @@ +source 'sso://cpdc-internal/firebase.git' +source 'https://github.com/CocoaPods/Specs.git' +platform :ios, '8.0' +# Firebase Remote Config test application. + +target 'integration_test' do + pod 'Firebase/RemoteConfig', '6.24.0' +end + +post_install do |installer| + system("/usr/bin/python ./download_googletest.py") +end + diff --git a/testing/integration_tests/remote_config/build.gradle b/testing/integration_tests/remote_config/build.gradle new file mode 100644 index 0000000000..650a11af08 --- /dev/null +++ b/testing/integration_tests/remote_config/build.gradle @@ -0,0 +1,64 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +buildscript { + repositories { + mavenLocal() + maven { url 'https://maven.google.com' } + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:3.2.1' + classpath 'com.google.gms:google-services:4.0.1' + } +} + +allprojects { + repositories { + mavenLocal() + maven { url 'https://maven.google.com' } + jcenter() + } +} + +apply plugin: 'com.android.application' + +android { + compileSdkVersion 28 + buildToolsVersion '28.0.3' + + sourceSets { + main { + jniLibs.srcDirs = ['libs'] + manifest.srcFile 'AndroidManifest.xml' + java.srcDirs = ['src/android/java'] + res.srcDirs = ['res'] + } + } + + defaultConfig { + applicationId 'com.google.android.remoteconfig.testapp' + minSdkVersion 16 + targetSdkVersion 28 + versionCode 1 + versionName '1.0' + externalNativeBuild.cmake { + arguments "-DFIREBASE_CPP_SDK_DIR=$gradle.firebase_cpp_sdk_dir" + } + } + externalNativeBuild.cmake { + path 'CMakeLists.txt' + } + buildTypes { + release { + minifyEnabled true + proguardFile getDefaultProguardFile('proguard-android.txt') + proguardFile file('proguard.pro') + } + } +} + +apply from: "$gradle.firebase_cpp_sdk_dir/Android/firebase_dependencies.gradle" +firebaseCpp.dependencies { + remoteConfig +} + +apply plugin: 'com.google.gms.google-services' diff --git a/testing/integration_tests/remote_config/googletest.cmake b/testing/integration_tests/remote_config/googletest.cmake new file mode 100644 index 0000000000..0eeff685dd --- /dev/null +++ b/testing/integration_tests/remote_config/googletest.cmake @@ -0,0 +1,19 @@ +# Download GoogleTest from GitHub as an external project. +# This CMake file is taken from: +# https://github.com/google/googletest/blob/master/googletest/README.md#incorporating-into-an-existing-cmake-project + +cmake_minimum_required(VERSION 2.8.2) + +project(googletest-download NONE) + +include(ExternalProject) +ExternalProject_Add(googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG master + SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/src" + BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/build" + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + TEST_COMMAND "" +) diff --git a/testing/integration_tests/remote_config/gradle/wrapper/gradle-wrapper.jar b/testing/integration_tests/remote_config/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..8c0fb64a8698b08ecc4158d828ca593c4928e9dd GIT binary patch literal 49896 zcmagFb986H(k`5d^NVfUwr$(C?M#x1ZQHiZiEVpg+jrjgoQrerx!>1o_ul)D>ebz~ zs=Mmxr&>W81QY-S1PKWQ%N-;H^tS;2*XwVA`dej1RRn1z<;3VgfE4~kaG`A%QSPsR z#ovnZe+tS9%1MfeDyz`RirvdjPRK~p(#^q2(^5@O&NM19EHdvN-A&StN>0g6QA^VN z0Gx%Gq#PD$QMRFzmK+utjS^Y1F0e8&u&^=w5K<;4Rz|i3A=o|IKLY+g`iK6vfr9?+ z-`>gmU&i?FGSL5&F?TXFu`&Js6h;15QFkXp2M1H9|Eq~bpov-GU(uz%mH0n55wUl- zv#~ccAz`F5wlQ>e_KlJS3@{)B?^v*EQM=IxLa&76^y51a((wq|2-`qON>+4dLc{Oo z51}}o^Zen(oAjxDK7b++9_Yg`67p$bPo3~BCpGM7uAWmvIhWc5Gi+gQZ|Pwa-Gll@<1xmcPy z|NZmu6m)g5Ftu~BG&Xdxclw7Cij{xbBMBn-LMII#Slp`AElb&2^Hw+w>(3crLH!;I zN+Vk$D+wP1#^!MDCiad@vM>H#6+`Ct#~6VHL4lzmy;lSdk>`z6)=>Wh15Q2)dQtGqvn0vJU@+(B5{MUc*qs4!T+V=q=wy)<6$~ z!G>e_4dN@lGeF_$q9`Ju6Ncb*x?O7=l{anm7Eahuj_6lA{*#Gv*TaJclevPVbbVYu z(NY?5q+xxbO6%g1xF0r@Ix8fJ~u)VRUp`S%&rN$&e!Od`~s+64J z5*)*WSi*i{k%JjMSIN#X;jC{HG$-^iX+5f5BGOIHWAl*%15Z#!xntpk($-EGKCzKa zT7{siZ9;4TICsWQ$pu&wKZQTCvpI$Xvzwxoi+XkkpeE&&kFb!B?h2hi%^YlXt|-@5 zHJ~%AN!g_^tmn1?HSm^|gCE#!GRtK2(L{9pL#hp0xh zME}|DB>(5)`iE7CM)&_+S}-Bslc#@B5W4_+k4Cp$l>iVyg$KP>CN?SVGZ(&02>iZK zB<^HP$g$Lq*L$BWd?2(F?-MUbNWTJVQdW7$#8a|k_30#vHAD1Z{c#p;bETk0VnU5A zBgLe2HFJ3032$G<`m*OB!KM$*sdM20jm)It5OSru@tXpK5LT>#8)N!*skNu1$TpIw zufjjdp#lyH5bZ%|Iuo|iu9vG1HrIVWLH>278xo>aVBkPN3V$~!=KnlXQ4eDqS7%E% zQ!z^$Q$b^6Q)g#cLpwur(|<0gWHo6A6jc;n`t(V9T;LzTAU{IAu*uEQ%Ort1k+Kn+f_N`9|bxYC+~Z1 zCC1UCWv*Orx$_@ydv9mIe(liLfOr7mhbV@tKw{6)q^1DH1nmvZ0cj215R<~&I<4S| zgnr;9Cdjqpz#o8i0CQjtl`}{c*P)aSdH|abxGdrR)-3z+02-eX(k*B)Uqv6~^nh** z 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 \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/testing/integration_tests/remote_config/gradlew.bat b/testing/integration_tests/remote_config/gradlew.bat new file mode 100644 index 0000000000..8a0b282aa6 --- /dev/null +++ b/testing/integration_tests/remote_config/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/testing/integration_tests/remote_config/integration_test.xcodeproj/project.pbxproj b/testing/integration_tests/remote_config/integration_test.xcodeproj/project.pbxproj new file mode 100644 index 0000000000..d1d6b852d1 --- /dev/null +++ b/testing/integration_tests/remote_config/integration_test.xcodeproj/project.pbxproj @@ -0,0 +1,364 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 520BC0391C869159008CFBC3 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 520BC0381C869159008CFBC3 /* GoogleService-Info.plist */; }; + 529226D61C85F68000C89379 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 529226D51C85F68000C89379 /* Foundation.framework */; }; + 529226D81C85F68000C89379 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 529226D71C85F68000C89379 /* CoreGraphics.framework */; }; + 529226DA1C85F68000C89379 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 529226D91C85F68000C89379 /* UIKit.framework */; }; + D61C5F8E22BABA9C00A79141 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D61C5F8C22BABA9B00A79141 /* Images.xcassets */; }; + D61C5F9622BABAD200A79141 /* integration_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = D61C5F9222BABAD100A79141 /* integration_test.cc */; }; + D62CCBC022F367140099BE9F /* gmock-all.cc in Sources */ = {isa = PBXBuildFile; fileRef = D62CCBBF22F367140099BE9F /* gmock-all.cc */; }; + D66B16871CE46E8900E5638A /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D66B16861CE46E8900E5638A /* LaunchScreen.storyboard */; }; + D67D355822BABD2200292C1D /* gtest-all.cc in Sources */ = {isa = PBXBuildFile; fileRef = D67D355622BABD2100292C1D /* gtest-all.cc */; }; + D6C179E922CB322900C2651A /* ios_app_framework.mm in Sources */ = {isa = PBXBuildFile; fileRef = D6C179E722CB322900C2651A /* ios_app_framework.mm */; }; + D6C179EA22CB322900C2651A /* ios_firebase_test_framework.mm in Sources */ = {isa = PBXBuildFile; fileRef = D6C179E822CB322900C2651A /* ios_firebase_test_framework.mm */; }; + D6C179EE22CB323300C2651A /* firebase_test_framework.cc in Sources */ = {isa = PBXBuildFile; fileRef = D6C179EC22CB323300C2651A /* firebase_test_framework.cc */; }; + D6C179F022CB32A000C2651A /* app_framework.cc in Sources */ = {isa = PBXBuildFile; fileRef = D6C179EF22CB32A000C2651A /* app_framework.cc */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 520BC0381C869159008CFBC3 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; + 529226D21C85F68000C89379 /* integration_test.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = integration_test.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 529226D51C85F68000C89379 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + 529226D71C85F68000C89379 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; + 529226D91C85F68000C89379 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; + 529226EE1C85F68000C89379 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; + D61C5F8C22BABA9B00A79141 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; + D61C5F8D22BABA9C00A79141 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + D61C5F9222BABAD100A79141 /* integration_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = integration_test.cc; path = src/integration_test.cc; sourceTree = ""; }; + D62CCBBF22F367140099BE9F /* gmock-all.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "gmock-all.cc"; path = "external/googletest/src/googlemock/src/gmock-all.cc"; sourceTree = ""; }; + D62CCBC122F367320099BE9F /* gmock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = gmock.h; path = external/googletest/src/googlemock/include/gmock/gmock.h; sourceTree = ""; }; + D66B16861CE46E8900E5638A /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = ""; }; + D67D355622BABD2100292C1D /* gtest-all.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "gtest-all.cc"; path = "external/googletest/src/googletest/src/gtest-all.cc"; sourceTree = ""; }; + D67D355722BABD2100292C1D /* gtest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = gtest.h; path = external/googletest/src/googletest/include/gtest/gtest.h; sourceTree = ""; }; + D6C179E722CB322900C2651A /* ios_app_framework.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ios_app_framework.mm; path = src/ios/ios_app_framework.mm; sourceTree = ""; }; + D6C179E822CB322900C2651A /* ios_firebase_test_framework.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ios_firebase_test_framework.mm; path = src/ios/ios_firebase_test_framework.mm; sourceTree = ""; }; + D6C179EB22CB323300C2651A /* firebase_test_framework.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = firebase_test_framework.h; path = src/firebase_test_framework.h; sourceTree = ""; }; + D6C179EC22CB323300C2651A /* firebase_test_framework.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = firebase_test_framework.cc; path = src/firebase_test_framework.cc; sourceTree = ""; }; + D6C179ED22CB323300C2651A /* app_framework.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = app_framework.h; path = src/app_framework.h; sourceTree = ""; }; + D6C179EF22CB32A000C2651A /* app_framework.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = app_framework.cc; path = src/app_framework.cc; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 529226CF1C85F68000C89379 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 529226D81C85F68000C89379 /* CoreGraphics.framework in Frameworks */, + 529226DA1C85F68000C89379 /* UIKit.framework in Frameworks */, + 529226D61C85F68000C89379 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 529226C91C85F68000C89379 = { + isa = PBXGroup; + children = ( + D61C5F8C22BABA9B00A79141 /* Images.xcassets */, + D61C5F8D22BABA9C00A79141 /* Info.plist */, + D66B16861CE46E8900E5638A /* LaunchScreen.storyboard */, + 520BC0381C869159008CFBC3 /* GoogleService-Info.plist */, + 5292271D1C85FB5500C89379 /* src */, + 529226D41C85F68000C89379 /* Frameworks */, + 529226D31C85F68000C89379 /* Products */, + ); + sourceTree = ""; + }; + 529226D31C85F68000C89379 /* Products */ = { + isa = PBXGroup; + children = ( + 529226D21C85F68000C89379 /* integration_test.app */, + ); + name = Products; + sourceTree = ""; + }; + 529226D41C85F68000C89379 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 529226D51C85F68000C89379 /* Foundation.framework */, + 529226D71C85F68000C89379 /* CoreGraphics.framework */, + 529226D91C85F68000C89379 /* UIKit.framework */, + 529226EE1C85F68000C89379 /* XCTest.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 5292271D1C85FB5500C89379 /* src */ = { + isa = PBXGroup; + children = ( + D62CCBC122F367320099BE9F /* gmock.h */, + D62CCBBF22F367140099BE9F /* gmock-all.cc */, + D67D355622BABD2100292C1D /* gtest-all.cc */, + D67D355722BABD2100292C1D /* gtest.h */, + D6C179EF22CB32A000C2651A /* app_framework.cc */, + D6C179ED22CB323300C2651A /* app_framework.h */, + D6C179EC22CB323300C2651A /* firebase_test_framework.cc */, + D6C179EB22CB323300C2651A /* firebase_test_framework.h */, + D61C5F9222BABAD100A79141 /* integration_test.cc */, + 5292271E1C85FB5B00C89379 /* ios */, + ); + name = src; + sourceTree = ""; + }; + 5292271E1C85FB5B00C89379 /* ios */ = { + isa = PBXGroup; + children = ( + D6C179E722CB322900C2651A /* ios_app_framework.mm */, + D6C179E822CB322900C2651A /* ios_firebase_test_framework.mm */, + ); + name = ios; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 529226D11C85F68000C89379 /* integration_test */ = { + isa = PBXNativeTarget; + buildConfigurationList = 529226F91C85F68000C89379 /* Build configuration list for PBXNativeTarget "integration_test" */; + buildPhases = ( + 529226CE1C85F68000C89379 /* Sources */, + 529226CF1C85F68000C89379 /* Frameworks */, + 529226D01C85F68000C89379 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = integration_test; + productName = testapp; + productReference = 529226D21C85F68000C89379 /* integration_test.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 529226CA1C85F68000C89379 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0640; + ORGANIZATIONNAME = Google; + TargetAttributes = { + 529226D11C85F68000C89379 = { + CreatedOnToolsVersion = 6.4; + DevelopmentTeam = EQHXZ8M8AV; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = 529226CD1C85F68000C89379 /* Build configuration list for PBXProject "integration_test" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 529226C91C85F68000C89379; + productRefGroup = 529226D31C85F68000C89379 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 529226D11C85F68000C89379 /* integration_test */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 529226D01C85F68000C89379 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D61C5F8E22BABA9C00A79141 /* Images.xcassets in Resources */, + D66B16871CE46E8900E5638A /* LaunchScreen.storyboard in Resources */, + 520BC0391C869159008CFBC3 /* GoogleService-Info.plist in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 529226CE1C85F68000C89379 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D67D355822BABD2200292C1D /* gtest-all.cc in Sources */, + D62CCBC022F367140099BE9F /* gmock-all.cc in Sources */, + D6C179EA22CB322900C2651A /* ios_firebase_test_framework.mm in Sources */, + D61C5F9622BABAD200A79141 /* integration_test.cc in Sources */, + D6C179E922CB322900C2651A /* ios_app_framework.mm in Sources */, + D6C179F022CB32A000C2651A /* app_framework.cc in Sources */, + D6C179EE22CB323300C2651A /* firebase_test_framework.cc in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 529226F71C85F68000C89379 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.4; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 529226F81C85F68000C89379 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.4; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 529226FA1C85F68000C89379 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ""; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)", + ); + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + "\"$(SRCROOT)/src\"", + "\"$(SRCROOT)/external/googletest/src/googletest/include\"", + "\"$(SRCROOT)/external/googletest/src/googlemock/include\"", + "\"$(SRCROOT)/external/googletest/src/googletest\"", + "\"$(SRCROOT)/external/googletest/src/googlemock\"", + ); + INFOPLIST_FILE = "$(SRCROOT)/Info.plist"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + WRAPPER_EXTENSION = app; + }; + name = Debug; + }; + 529226FB1C85F68000C89379 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ""; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)", + ); + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + "\"$(SRCROOT)/src\"", + "\"$(SRCROOT)/external/googletest/src/googletest/include\"", + "\"$(SRCROOT)/external/googletest/src/googlemock/include\"", + "\"$(SRCROOT)/external/googletest/src/googletest\"", + "\"$(SRCROOT)/external/googletest/src/googlemock\"", + ); + INFOPLIST_FILE = "$(SRCROOT)/Info.plist"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + WRAPPER_EXTENSION = app; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 529226CD1C85F68000C89379 /* Build configuration list for PBXProject "integration_test" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 529226F71C85F68000C89379 /* Debug */, + 529226F81C85F68000C89379 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 529226F91C85F68000C89379 /* Build configuration list for PBXNativeTarget "integration_test" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 529226FA1C85F68000C89379 /* Debug */, + 529226FB1C85F68000C89379 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 529226CA1C85F68000C89379 /* Project object */; +} diff --git a/testing/integration_tests/remote_config/proguard.pro b/testing/integration_tests/remote_config/proguard.pro new file mode 100644 index 0000000000..2d04b8a9a5 --- /dev/null +++ b/testing/integration_tests/remote_config/proguard.pro @@ -0,0 +1,2 @@ +-ignorewarnings +-keep,includedescriptorclasses public class com.google.firebase.example.LoggingUtils { * ; } diff --git a/testing/integration_tests/remote_config/res/layout/main.xml b/testing/integration_tests/remote_config/res/layout/main.xml new file mode 100644 index 0000000000..d3ffb63082 --- /dev/null +++ b/testing/integration_tests/remote_config/res/layout/main.xml @@ -0,0 +1,12 @@ + + + + diff --git a/testing/integration_tests/remote_config/res/values/strings.xml b/testing/integration_tests/remote_config/res/values/strings.xml new file mode 100644 index 0000000000..fd7b9680c8 --- /dev/null +++ b/testing/integration_tests/remote_config/res/values/strings.xml @@ -0,0 +1,4 @@ + + + Firebase Remote Config Integration Test + diff --git a/testing/integration_tests/remote_config/settings.gradle b/testing/integration_tests/remote_config/settings.gradle new file mode 100644 index 0000000000..2a543b9351 --- /dev/null +++ b/testing/integration_tests/remote_config/settings.gradle @@ -0,0 +1,36 @@ +// Copyright 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +def firebase_cpp_sdk_dir = System.getProperty('firebase_cpp_sdk.dir') +if (firebase_cpp_sdk_dir == null || firebase_cpp_sdk_dir.isEmpty()) { + firebase_cpp_sdk_dir = System.getenv('FIREBASE_CPP_SDK_DIR') + if (firebase_cpp_sdk_dir == null || firebase_cpp_sdk_dir.isEmpty()) { + if ((new File('firebase_cpp_sdk')).exists()) { + firebase_cpp_sdk_dir = 'firebase_cpp_sdk' + } else { + throw new StopActionException( + 'firebase_cpp_sdk.dir property or the FIREBASE_CPP_SDK_DIR ' + + 'environment variable must be set to reference the Firebase C++ ' + + 'SDK install directory. This is used to configure static library ' + + 'and C/C++ include paths for the SDK.') + } + } +} +if (!(new File(firebase_cpp_sdk_dir)).exists()) { + throw new StopActionException( + sprintf('Firebase C++ SDK directory %s does not exist', + firebase_cpp_sdk_dir)) +} +gradle.ext.firebase_cpp_sdk_dir = "$firebase_cpp_sdk_dir" +includeBuild "$firebase_cpp_sdk_dir" \ No newline at end of file diff --git a/testing/integration_tests/remote_config/src/integration_test.cc b/testing/integration_tests/remote_config/src/integration_test.cc new file mode 100644 index 0000000000..aff4a866d2 --- /dev/null +++ b/testing/integration_tests/remote_config/src/integration_test.cc @@ -0,0 +1,430 @@ +// Copyright 2019 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include +#include +#include +#include +#include + +#include "app_framework.h" // NOLINT +#include "firebase/app.h" +#include "firebase/remote_config.h" +#include "firebase/util.h" +#include "firebase_test_framework.h" // NOLINT + +// The TO_STRING macro is useful for command line defined strings as the quotes +// get stripped. +#define TO_STRING_EXPAND(X) #X +#define TO_STRING(X) TO_STRING_EXPAND(X) + +// Path to the Firebase config file to load. +#ifdef FIREBASE_CONFIG +#define FIREBASE_CONFIG_STRING TO_STRING(FIREBASE_CONFIG) +#else +#define FIREBASE_CONFIG_STRING "" +#endif // FIREBASE_CONFIG + +namespace firebase_testapp_automated { + +using app_framework::LogDebug; +using app_framework::LogWarning; +using app_framework::ProcessEvents; +using firebase_test_framework::FirebaseTest; +#ifdef FIREBASE_EARLY_ACCESS_PREVIEW +using firebase::Future; +using firebase::remote_config::RemoteConfig; +#endif // FIREBASE_EARLY_ACCESS_PREVIEW + +using testing::UnorderedElementsAre; + +class FirebaseRemoteConfigTest : public FirebaseTest { + public: + FirebaseRemoteConfigTest(); + ~FirebaseRemoteConfigTest() override; + + void SetUp() override; + void TearDown() override; + + protected: + // Initialize Firebase App and Firebase Remote Config. + void Initialize(); + // Shut down Firebase Remote Config and Firebase App. + void Terminate(); + + bool initialized_; +#ifdef FIREBASE_EARLY_ACCESS_PREVIEW + RemoteConfig* rc_; +#endif // FIREBASE_EARLY_ACCESS_PREVIEW +}; + +FirebaseRemoteConfigTest::FirebaseRemoteConfigTest() : initialized_(false) { + FindFirebaseConfig(FIREBASE_CONFIG_STRING); +} + +FirebaseRemoteConfigTest::~FirebaseRemoteConfigTest() { + // Must be cleaned up on exit. + assert(app_ == nullptr); +#ifdef FIREBASE_EARLY_ACCESS_PREVIEW + assert(rc_ == nullptr); +#endif // FIREBASE_EARLY_ACCESS_PREVIEW +} + +void FirebaseRemoteConfigTest::SetUp() { + FirebaseTest::SetUp(); + Initialize(); +} + +void FirebaseRemoteConfigTest::TearDown() { + // Delete the shared path, if there is one. + if (initialized_) { + Terminate(); + } + FirebaseTest::TearDown(); +} + +void FirebaseRemoteConfigTest::Initialize() { + if (initialized_) return; + SetLogLevel(app_framework::kDebug); + + InitializeApp(); + + LogDebug("Initializing Firebase Remote Config."); + + ::firebase::ModuleInitializer initializer; + + void* ptr = nullptr; +#ifdef FIREBASE_EARLY_ACCESS_PREVIEW + ptr = &rc_; +#endif + initializer.Initialize(app_, ptr, [](::firebase::App* app, void* target) { + LogDebug("Try to initialize Firebase RemoteConfig"); +#ifdef FIREBASE_EARLY_ACCESS_PREVIEW + RemoteConfig** rc_ptr = reinterpret_cast(target); + *rc_ptr = RemoteConfig::GetInstance(app); +#endif + return firebase::remote_config::Initialize(*app); + }); + + WaitForCompletion(initializer.InitializeLastResult(), "Initialize"); + + ASSERT_EQ(initializer.InitializeLastResult().error(), 0) + << initializer.InitializeLastResult().error_message(); + + LogDebug("Successfully initialized Firebase RemoteConfig."); + + initialized_ = true; +} + +void FirebaseRemoteConfigTest::Terminate() { + if (!initialized_) return; + + LogDebug("Shutdown the Remote Config library."); +#ifdef FIREBASE_EARLY_ACCESS_PREVIEW + if (rc_) { + delete rc_; + rc_ = nullptr; + } +#endif // FIREBASE_EARLY_ACCESS_PREVIEW + LogDebug("DTerminating."); + firebase::remote_config::Terminate(); + TerminateApp(); + + initialized_ = false; + + ProcessEvents(100); +} + +static const char* ValueSourceToString( + firebase::remote_config::ValueSource source) { + static const char* kSourceToString[] = { + "Static", // kValueSourceStaticValue + "Remote", // kValueSourceRemoteValue + "Default", // kValueSourceDefaultValue + }; + return kSourceToString[source]; +} + +static const unsigned char kBinaryDefaults[] = {6, 0, 0, 6, 7, 3}; +// NOLINTNEXTLINE +static const firebase::remote_config::ConfigKeyValueVariant defaults[] = { + {"TestBoolean", false}, + {"TestLong", 42}, + {"TestDouble", 3.14}, + {"TestString", "Hello World"}, + {"TestData", firebase::Variant::FromStaticBlob(kBinaryDefaults, + sizeof(kBinaryDefaults))}, + {"TestDefaultOnly", "Default value that won't be overridden"}}; + +// TestData 4321 +// TestDouble 625.63 +// TestLong 119 +// TestBoolean true +// TestString This is a string +// NOLINTNEXTLINE +static const firebase::remote_config::ConfigKeyValueVariant kServerValue[] = { + {"TestBoolean", true}, + {"TestLong", 119}, + {"TestDouble", 625.63}, + {"TestString", firebase::Variant::FromMutableString("This is a string")}, + {"TestData", 4321}, + {"TestDefaultOnly", firebase::Variant::FromMutableString( + "Default value that won't be overridden")}}; + +static void SetDefaults() { + size_t default_count = FIREBASE_ARRAYSIZE(defaults); + firebase::remote_config::SetDefaults(defaults, default_count); +} + +#ifdef FIREBASE_EARLY_ACCESS_PREVIEW +static Future SetDefaultsV2(RemoteConfig* rc) { + size_t default_count = FIREBASE_ARRAYSIZE(defaults); + return rc->SetDefaults(defaults, default_count); +} +#endif // FIREBASE_EARLY_ACCESS_PREVIEW + +// Test cases below. + +TEST_F(FirebaseRemoteConfigTest, TestInitializeAndTerminate) { + // Already tested via SetUp() and TearDown(). +} + +TEST_F(FirebaseRemoteConfigTest, TestSetDefaults) { + SetDefaults(); + + bool validated_defaults = true; + firebase::remote_config::ValueInfo value_info; + bool bool_value = + firebase::remote_config::GetBoolean("TestBoolean", &value_info); + if (value_info.source == firebase::remote_config::kValueSourceDefaultValue) { + EXPECT_FALSE(bool_value); + } else { + validated_defaults = false; + } + int64_t int64_value = + firebase::remote_config::GetLong("TestLong", &value_info); + if (value_info.source == firebase::remote_config::kValueSourceDefaultValue) { + EXPECT_EQ(int64_value, 42); + } else { + validated_defaults = false; + } + double double_value = + firebase::remote_config::GetDouble("TestDouble", &value_info); + if (value_info.source == firebase::remote_config::kValueSourceDefaultValue) { + EXPECT_NEAR(double_value, 3.14, 0.0001); + } else { + validated_defaults = false; + } + std::string string_value = + firebase::remote_config::GetString("TestString", &value_info); + if (value_info.source == firebase::remote_config::kValueSourceDefaultValue) { + EXPECT_EQ(string_value, "Hello World"); + } else { + validated_defaults = false; + } + std::vector blob_value = + firebase::remote_config::GetData("TestData"); //, &value_info); + if (value_info.source == firebase::remote_config::kValueSourceDefaultValue) { + EXPECT_THAT(blob_value, testing::ElementsAreArray(kBinaryDefaults, + sizeof(kBinaryDefaults))); + } else { + validated_defaults = false; + } + + if (!validated_defaults) { + LogWarning( + "Can't validate defaults, they've been overridden by server values.\n" +#if defined(__ANDROID__) + "Delete the app's data and run this test again to test SetDefaults:\n" + " adb shell pm clear [bundle ID]" +#elif defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE + "Uninstall and re-install the app and run this again to test " + "SetDefaults." +#else // Desktop + "Delete the Remote Config cache and run this test again to test " + "SetDefaults:\n" +#if defined(_WIN32) + " del remote_config_data" +#else + " rm remote_config_data" +#endif // defined(_WIN32) +#endif // defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE + ); + } +} + +/* The following test expects that you have your server values set to: + TestData 4321 + TestDouble 625.63 + TestLong 119 + TestBoolean true + TestString This is a string + */ + +TEST_F(FirebaseRemoteConfigTest, TestFetchAndActivate) { + SetDefaults(); + + WaitForCompletion(firebase::remote_config::Fetch(0), "Fetch"); + EXPECT_TRUE(firebase::remote_config::ActivateFetched()); + const firebase::remote_config::ConfigInfo& info = + firebase::remote_config::GetInfo(); + LogDebug("Fetch time: %lld", info.fetch_time); + firebase::remote_config::ValueInfo value_info; + bool bool_value = + firebase::remote_config::GetBoolean("TestBoolean", &value_info); + EXPECT_EQ(value_info.source, firebase::remote_config::kValueSourceRemoteValue) + << "TestBoolean source is " << ValueSourceToString(value_info.source) + << ", expected Remote"; + EXPECT_TRUE(bool_value); + + int64_t int64_value = + firebase::remote_config::GetLong("TestLong", &value_info); + EXPECT_EQ(value_info.source, firebase::remote_config::kValueSourceRemoteValue) + << "TestLong source is " << ValueSourceToString(value_info.source) + << ", expected Remote"; + EXPECT_EQ(int64_value, 119); + + double double_value = + firebase::remote_config::GetDouble("TestDouble", &value_info); + EXPECT_EQ(value_info.source, firebase::remote_config::kValueSourceRemoteValue) + << "TestDouble source is " << ValueSourceToString(value_info.source) + << ", expected Remote"; + EXPECT_NEAR(double_value, 625.63, 0.0001); + + std::string string_value = + firebase::remote_config::GetString("TestString", &value_info); + EXPECT_EQ(value_info.source, firebase::remote_config::kValueSourceRemoteValue) + << "TestString source is " << ValueSourceToString(value_info.source) + << ", expected Remote"; + EXPECT_EQ(string_value, "This is a string"); + + std::vector blob_value = + firebase::remote_config::GetData("TestData"); //, &value_info); + EXPECT_EQ(value_info.source, firebase::remote_config::kValueSourceRemoteValue) + << "TestData source is " << ValueSourceToString(value_info.source) + << ", expected Remote"; + + const unsigned char kExpectedBlobServerValue[] = {'4', '3', '2', '1'}; + EXPECT_THAT(blob_value, + testing::ElementsAreArray(kExpectedBlobServerValue, + sizeof(kExpectedBlobServerValue))); +} + +TEST_F(FirebaseRemoteConfigTest, TestGetKeys) { + SetDefaults(); + + std::vector keys = firebase::remote_config::GetKeys(); + EXPECT_THAT( + keys, UnorderedElementsAre("TestBoolean", "TestLong", "TestDouble", + "TestString", "TestData", "TestDefaultOnly")); + std::vector keys_subset = + firebase::remote_config::GetKeysByPrefix("TestD"); + EXPECT_THAT(keys_subset, UnorderedElementsAre("TestDouble", "TestData", + "TestDefaultOnly")); +} + +#if defined(FIREBASE_EARLY_ACCESS_PREVIEW) && \ + (defined(__ANDROID__) || (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE)) +TEST_F(FirebaseRemoteConfigTest, TestSetDefaultsV2) { + EXPECT_TRUE(WaitForCompletion(SetDefaultsV2(rc_), "SetDefaultsV2")); + + bool validated_defaults = true; + firebase::remote_config::ValueInfo value_info; + bool bool_value = rc_->GetBoolean("TestBoolean", &value_info); + if (value_info.source == firebase::remote_config::kValueSourceDefaultValue) { + EXPECT_FALSE(bool_value); + } else { + validated_defaults = false; + } + int64_t int64_value = rc_->GetLong("TestLong", &value_info); + if (value_info.source == firebase::remote_config::kValueSourceDefaultValue) { + EXPECT_EQ(int64_value, 42); + } else { + validated_defaults = false; + } + double double_value = rc_->GetDouble("TestDouble", &value_info); + if (value_info.source == firebase::remote_config::kValueSourceDefaultValue) { + EXPECT_NEAR(double_value, 3.14, 0.0001); + } else { + validated_defaults = false; + } + std::string string_value = rc_->GetString("TestString", &value_info); + if (value_info.source == firebase::remote_config::kValueSourceDefaultValue) { + EXPECT_EQ(string_value, "Hello World"); + } else { + validated_defaults = false; + } + std::vector blob_value = + rc_->GetData("TestData"); //, &value_info); + if (value_info.source == firebase::remote_config::kValueSourceDefaultValue) { + EXPECT_THAT(blob_value, testing::ElementsAreArray(kBinaryDefaults, + sizeof(kBinaryDefaults))); + } else { + validated_defaults = false; + } + + if (!validated_defaults) { + LogWarning( + "Can't validate defaults, they've been overridden by server values.\n" +#if defined(__ANDROID__) + "Delete the app's data and run this test again to test SetDefaults:\n" + " adb shell pm clear [bundle ID]" +#elif defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE + "Uninstall and re-install the app and run this again to test " + "SetDefaults." +#else // Desktop + "Delete the Remote Config cache and run this test again to test " + "SetDefaults:\n" +#if defined(_WIN32) + " del remote_config_data" +#else + " rm remote_config_data" +#endif // defined(_WIN32) +#endif // defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE + ); + } +} + +TEST_F(FirebaseRemoteConfigTest, TestGetKeysV2) { + EXPECT_TRUE(WaitForCompletion(SetDefaultsV2(rc_), "SetDefaultsV2")); + + std::vector keys = rc_->GetKeys(); + EXPECT_THAT( + keys, UnorderedElementsAre("TestBoolean", "TestLong", "TestDouble", + "TestString", "TestData", "TestDefaultOnly")); + std::vector keys_subset = + firebase::remote_config::GetKeysByPrefix("TestD"); + EXPECT_THAT(keys_subset, UnorderedElementsAre("TestDouble", "TestData", + "TestDefaultOnly")); +} + +TEST_F(FirebaseRemoteConfigTest, TestGetAll) { + EXPECT_TRUE(WaitForCompletion(SetDefaultsV2(rc_), "SetDefaultsV2")); + + std::map key_values = rc_->GetAll(); + EXPECT_EQ(key_values.size(), 6); + + for (auto key_valur_pair : kServerValue) { + firebase::Variant k_value = key_valur_pair.value; + firebase::Variant fetched_value = key_values[key_valur_pair.key]; + EXPECT_EQ(k_value.type(), fetched_value.type()); + EXPECT_EQ(k_value, fetched_value); + } +} + +#endif // FIREBASE_EARLY_ACCESS_PREVIEW + +} // namespace firebase_testapp_automated diff --git a/testing/integration_tests/storage/AndroidManifest.xml b/testing/integration_tests/storage/AndroidManifest.xml new file mode 100644 index 0000000000..2f834feea2 --- /dev/null +++ b/testing/integration_tests/storage/AndroidManifest.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/testing/integration_tests/storage/CMakeLists.txt b/testing/integration_tests/storage/CMakeLists.txt new file mode 100644 index 0000000000..41052e7d44 --- /dev/null +++ b/testing/integration_tests/storage/CMakeLists.txt @@ -0,0 +1,196 @@ +cmake_minimum_required(VERSION 2.8) + +# User settings for Firebase integration tests. +# Path to Firebase SDK. +# Try to read the path to the Firebase C++ SDK from an environment variable. +if (NOT "$ENV{FIREBASE_CPP_SDK_DIR}" STREQUAL "") + set(DEFAULT_FIREBASE_CPP_SDK_DIR "$ENV{FIREBASE_CPP_SDK_DIR}") +else() + set(DEFAULT_FIREBASE_CPP_SDK_DIR "firebase_cpp_sdk") +endif() +if ("${FIREBASE_CPP_SDK_DIR}" STREQUAL "") + set(FIREBASE_CPP_SDK_DIR ${DEFAULT_FIREBASE_CPP_SDK_DIR}) +endif() +if(NOT EXISTS ${FIREBASE_CPP_SDK_DIR}) + message(FATAL_ERROR "The Firebase C++ SDK directory does not exist: ${FIREBASE_CPP_SDK_DIR}. See the readme.md for more information") +endif() + +# Windows runtime mode, either MD or MT depending on whether you are using +# /MD or /MT. For more information see: +# https://msdn.microsoft.com/en-us/library/2kzt1wy3.aspx +set(MSVC_RUNTIME_MODE MD) + +project(firebase_testapp) + +# Integration test source files. +set(FIREBASE_APP_FRAMEWORK_SRCS + src/app_framework.cc + src/app_framework.h +) + +set(FIREBASE_TEST_FRAMEWORK_SRCS + src/firebase_test_framework.h + src/firebase_test_framework.cc +) + +set(FIREBASE_INTEGRATION_TEST_SRCS + src/integration_test.cc +) + +# The include directory for the testapp. +include_directories(src) + +# Integration test uses some features that require C++ 11, such as lambdas. +set (CMAKE_CXX_STANDARD 11) + +# Download and unpack googletest (and googlemock) at configure time +set(GOOGLETEST_ROOT ${CMAKE_CURRENT_LIST_DIR}/external/googletest) +# Note: Once googletest is downloaded once, it won't be updated or +# downloaded again unless you delete the "external/googletest" +# directory. +if (NOT EXISTS ${GOOGLETEST_ROOT}/src/googletest/src/gtest-all.cc) + configure_file(googletest.cmake + ${CMAKE_CURRENT_LIST_DIR}/external/googletest/CMakeLists.txt COPYONLY) + execute_process(COMMAND ${CMAKE_COMMAND} . + RESULT_VARIABLE result + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/external/googletest ) + if(result) + message(FATAL_ERROR "CMake step for googletest failed: ${result}") + endif() + execute_process(COMMAND ${CMAKE_COMMAND} --build . + RESULT_VARIABLE result + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/external/googletest ) + if(result) + message(FATAL_ERROR "Build step for googletest failed: ${result}") + endif() +endif() + +if(ANDROID) + # Build an Android application. + + # Source files used for the Android build. + set(FIREBASE_APP_FRAMEWORK_ANDROID_SRCS + src/android/android_app_framework.cc + ) + + # Source files used for the Android build. + set(FIREBASE_TEST_FRAMEWORK_ANDROID_SRCS + src/android/android_firebase_test_framework.cc + ) + +# Build native_app_glue as a static lib + add_library(native_app_glue STATIC + ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c) + + # Export ANativeActivity_onCreate(), + # Refer to: https://github.com/android-ndk/ndk/issues/381. + set(CMAKE_SHARED_LINKER_FLAGS + "${CMAKE_SHARED_LINKER_FLAGS} -u ANativeActivity_onCreate") + + add_library(gtest STATIC + ${GOOGLETEST_ROOT}/src/googletest/src/gtest-all.cc) + target_include_directories(gtest + PRIVATE ${GOOGLETEST_ROOT}/src/googletest + PUBLIC ${GOOGLETEST_ROOT}/src/googletest/include) + add_library(gmock STATIC + ${GOOGLETEST_ROOT}/src/googlemock/src/gmock-all.cc) + target_include_directories(gmock + PRIVATE ${GOOGLETEST_ROOT}/src/googletest + PRIVATE ${GOOGLETEST_ROOT}/src/googlemock + PUBLIC ${GOOGLETEST_ROOT}/src/googletest/include + PUBLIC ${GOOGLETEST_ROOT}/src/googlemock/include) + + # Define the target as a shared library, as that is what gradle expects. + set(integration_test_target_name "android_integration_test_main") + add_library(${integration_test_target_name} SHARED + ${FIREBASE_APP_FRAMEWORK_SRCS} + ${FIREBASE_APP_FRAMEWORK_ANDROID_SRCS} + ${FIREBASE_INTEGRATION_TEST_SRCS} + ${FIREBASE_TEST_FRAMEWORK_SRCS} + ${FIREBASE_TEST_FRAMEWORK_ANDROID_SRCS} + ) + + target_include_directories(${integration_test_target_name} PRIVATE + ${ANDROID_NDK}/sources/android/native_app_glue) + + set(ADDITIONAL_LIBS log android atomic native_app_glue) +else() + # Build a desktop application. + add_definitions(-D_GLIBCXX_USE_CXX11_ABI=0) + + # Prevent overriding the parent project's compiler/linker + # settings on Windows + set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) + + # Add googletest directly to our build. This defines + # the gtest and gtest_main targets. + add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/external/googletest/src + ${CMAKE_CURRENT_LIST_DIR}/external/googletest/build + EXCLUDE_FROM_ALL) + + # The gtest/gtest_main targets carry header search path + # dependencies automatically when using CMake 2.8.11 or + # later. Otherwise we have to add them here ourselves. + if (CMAKE_VERSION VERSION_LESS 2.8.11) + include_directories("${gtest_SOURCE_DIR}/include") + include_directories("${gmock_SOURCE_DIR}/include") + endif() + + # Windows runtime mode, either MD or MT depending on whether you are using + # /MD or /MT. For more information see: + # https://msdn.microsoft.com/en-us/library/2kzt1wy3.aspx + set(MSVC_RUNTIME_MODE MD) + + # Platform abstraction layer for the desktop integration test. + set(FIREBASE_APP_FRAMEWORK_DESKTOP_SRCS + src/desktop/desktop_app_framework.cc + ) + + set(integration_test_target_name "integration_test") + add_executable(${integration_test_target_name} + ${FIREBASE_APP_FRAMEWORK_SRCS} + ${FIREBASE_APP_FRAMEWORK_DESKTOP_SRCS} + ${FIREBASE_TEST_FRAMEWORK_SRCS} + ${FIREBASE_INTEGRATION_TEST_SRCS} + ) + + if(APPLE) + set(ADDITIONAL_LIBS + gssapi_krb5 + pthread + "-framework CoreFoundation" + "-framework Foundation" + "-framework GSS" + "-framework Security" + ) + elseif(MSVC) + set(ADDITIONAL_LIBS advapi32 ws2_32 crypt32) + else() + set(ADDITIONAL_LIBS pthread) + endif() + + # If a config file is present, copy it into the binary location so that it's + # possible to create the default Firebase app. + set(FOUND_JSON_FILE FALSE) + foreach(config "google-services-desktop.json" "google-services.json") + if (EXISTS ${config}) + add_custom_command( + TARGET ${integration_test_target_name} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + ${config} $) + set(FOUND_JSON_FILE TRUE) + break() + endif() + endforeach() + if(NOT FOUND_JSON_FILE) + message(WARNING "Failed to find either google-services-desktop.json or google-services.json. See the readme.md for more information.") + endif() +endif() + +# Add the Firebase libraries to the target using the function from the SDK. +add_subdirectory(${FIREBASE_CPP_SDK_DIR} bin/ EXCLUDE_FROM_ALL) +# Note that firebase_app needs to be last in the list. +set(firebase_libs firebase_storage firebase_auth firebase_app) +set(gtest_libs gtest gmock) +target_link_libraries(${integration_test_target_name} ${firebase_libs} + ${gtest_libs} ${ADDITIONAL_LIBS}) diff --git a/testing/integration_tests/storage/Images.xcassets/AppIcon.appiconset/Contents.json b/testing/integration_tests/storage/Images.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000000..d8db8d65fd --- /dev/null +++ b/testing/integration_tests/storage/Images.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "83.5x83.5", + "scale" : "2x" + }, + { + "idiom" : "ios-marketing", + "size" : "1024x1024", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/testing/integration_tests/storage/Images.xcassets/LaunchImage.launchimage/Contents.json b/testing/integration_tests/storage/Images.xcassets/LaunchImage.launchimage/Contents.json new file mode 100644 index 0000000000..6f870a4629 --- /dev/null +++ b/testing/integration_tests/storage/Images.xcassets/LaunchImage.launchimage/Contents.json @@ -0,0 +1,51 @@ +{ + "images" : [ + { + "orientation" : "portrait", + "idiom" : "iphone", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "orientation" : "portrait", + "idiom" : "iphone", + "subtype" : "retina4", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "orientation" : "portrait", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "1x" + }, + { + "orientation" : "landscape", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "1x" + }, + { + "orientation" : "portrait", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "orientation" : "landscape", + "idiom" : "ipad", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/testing/integration_tests/storage/Info.plist b/testing/integration_tests/storage/Info.plist new file mode 100644 index 0000000000..c112525414 --- /dev/null +++ b/testing/integration_tests/storage/Info.plist @@ -0,0 +1,40 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + com.google.firebase.cpp.storage.testapp + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleURLTypes + + + CFBundleTypeRole + Editor + CFBundleURLName + google + CFBundleURLSchemes + + com.googleusercontent.apps.255980362477-3a1nf8c4nl0c7hlnlnmc98hbtg2mnbue + firebase-game-loop + + + + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + + diff --git a/testing/integration_tests/storage/LaunchScreen.storyboard b/testing/integration_tests/storage/LaunchScreen.storyboard new file mode 100644 index 0000000000..673e0f7e68 --- /dev/null +++ b/testing/integration_tests/storage/LaunchScreen.storyboard @@ -0,0 +1,7 @@ + + + + + + + diff --git a/testing/integration_tests/storage/LibraryManifest.xml b/testing/integration_tests/storage/LibraryManifest.xml new file mode 100644 index 0000000000..18192be20a --- /dev/null +++ b/testing/integration_tests/storage/LibraryManifest.xml @@ -0,0 +1,7 @@ + + + + + + diff --git a/testing/integration_tests/storage/Podfile b/testing/integration_tests/storage/Podfile new file mode 100644 index 0000000000..87df276a25 --- /dev/null +++ b/testing/integration_tests/storage/Podfile @@ -0,0 +1,14 @@ +source 'sso://cpdc-internal/firebase.git' +source 'https://github.com/CocoaPods/Specs.git' +platform :ios, '8.0' +# Cloud Storage for Firebase test application. + +target 'integration_test' do + pod 'Firebase/Storage', '6.24.0' + pod 'Firebase/Auth', '6.24.0' +end + +post_install do |installer| + system("/usr/bin/python ./download_googletest.py") +end + diff --git a/testing/integration_tests/storage/build.gradle b/testing/integration_tests/storage/build.gradle new file mode 100644 index 0000000000..264b2a34b0 --- /dev/null +++ b/testing/integration_tests/storage/build.gradle @@ -0,0 +1,65 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +buildscript { + repositories { + mavenLocal() + maven { url 'https://maven.google.com' } + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:3.2.1' + classpath 'com.google.gms:google-services:4.0.1' + } +} + +allprojects { + repositories { + mavenLocal() + maven { url 'https://maven.google.com' } + jcenter() + } +} + +apply plugin: 'com.android.application' + +android { + compileSdkVersion 28 + buildToolsVersion '28.0.3' + + sourceSets { + main { + jniLibs.srcDirs = ['libs'] + manifest.srcFile 'AndroidManifest.xml' + java.srcDirs = ['src/android/java'] + res.srcDirs = ['res'] + } + } + + defaultConfig { + applicationId 'com.google.firebase.cpp.storage.testapp' + minSdkVersion 16 + targetSdkVersion 28 + versionCode 1 + versionName '1.0' + externalNativeBuild.cmake { + arguments "-DFIREBASE_CPP_SDK_DIR=$gradle.firebase_cpp_sdk_dir" + } + } + externalNativeBuild.cmake { + path 'CMakeLists.txt' + } + buildTypes { + release { + minifyEnabled true + proguardFile getDefaultProguardFile('proguard-android.txt') + proguardFile file('proguard.pro') + } + } +} + +apply from: "$gradle.firebase_cpp_sdk_dir/Android/firebase_dependencies.gradle" +firebaseCpp.dependencies { + auth + storage +} + +apply plugin: 'com.google.gms.google-services' diff --git a/testing/integration_tests/storage/googletest.cmake b/testing/integration_tests/storage/googletest.cmake new file mode 100644 index 0000000000..0eeff685dd --- /dev/null +++ b/testing/integration_tests/storage/googletest.cmake @@ -0,0 +1,19 @@ +# Download GoogleTest from GitHub as an external project. +# This CMake file is taken from: +# https://github.com/google/googletest/blob/master/googletest/README.md#incorporating-into-an-existing-cmake-project + +cmake_minimum_required(VERSION 2.8.2) + +project(googletest-download NONE) + +include(ExternalProject) +ExternalProject_Add(googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG master + SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/src" + BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/build" + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + TEST_COMMAND "" +) diff --git a/testing/integration_tests/storage/gradle/wrapper/gradle-wrapper.jar b/testing/integration_tests/storage/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..8c0fb64a8698b08ecc4158d828ca593c4928e9dd GIT binary patch literal 49896 zcmagFb986H(k`5d^NVfUwr$(C?M#x1ZQHiZiEVpg+jrjgoQrerx!>1o_ul)D>ebz~ zs=Mmxr&>W81QY-S1PKWQ%N-;H^tS;2*XwVA`dej1RRn1z<;3VgfE4~kaG`A%QSPsR z#ovnZe+tS9%1MfeDyz`RirvdjPRK~p(#^q2(^5@O&NM19EHdvN-A&StN>0g6QA^VN z0Gx%Gq#PD$QMRFzmK+utjS^Y1F0e8&u&^=w5K<;4Rz|i3A=o|IKLY+g`iK6vfr9?+ z-`>gmU&i?FGSL5&F?TXFu`&Js6h;15QFkXp2M1H9|Eq~bpov-GU(uz%mH0n55wUl- zv#~ccAz`F5wlQ>e_KlJS3@{)B?^v*EQM=IxLa&76^y51a((wq|2-`qON>+4dLc{Oo z51}}o^Zen(oAjxDK7b++9_Yg`67p$bPo3~BCpGM7uAWmvIhWc5Gi+gQZ|Pwa-Gll@<1xmcPy z|NZmu6m)g5Ftu~BG&Xdxclw7Cij{xbBMBn-LMII#Slp`AElb&2^Hw+w>(3crLH!;I zN+Vk$D+wP1#^!MDCiad@vM>H#6+`Ct#~6VHL4lzmy;lSdk>`z6)=>Wh15Q2)dQtGqvn0vJU@+(B5{MUc*qs4!T+V=q=wy)<6$~ z!G>e_4dN@lGeF_$q9`Ju6Ncb*x?O7=l{anm7Eahuj_6lA{*#Gv*TaJclevPVbbVYu z(NY?5q+xxbO6%g1xF0r@Ix8fJ~u)VRUp`S%&rN$&e!Od`~s+64J z5*)*WSi*i{k%JjMSIN#X;jC{HG$-^iX+5f5BGOIHWAl*%15Z#!xntpk($-EGKCzKa zT7{siZ9;4TICsWQ$pu&wKZQTCvpI$Xvzwxoi+XkkpeE&&kFb!B?h2hi%^YlXt|-@5 zHJ~%AN!g_^tmn1?HSm^|gCE#!GRtK2(L{9pL#hp0xh zME}|DB>(5)`iE7CM)&_+S}-Bslc#@B5W4_+k4Cp$l>iVyg$KP>CN?SVGZ(&02>iZK zB<^HP$g$Lq*L$BWd?2(F?-MUbNWTJVQdW7$#8a|k_30#vHAD1Z{c#p;bETk0VnU5A zBgLe2HFJ3032$G<`m*OB!KM$*sdM20jm)It5OSru@tXpK5LT>#8)N!*skNu1$TpIw zufjjdp#lyH5bZ%|Iuo|iu9vG1HrIVWLH>278xo>aVBkPN3V$~!=KnlXQ4eDqS7%E% zQ!z^$Q$b^6Q)g#cLpwur(|<0gWHo6A6jc;n`t(V9T;LzTAU{IAu*uEQ%Ort1k+Kn+f_N`9|bxYC+~Z1 zCC1UCWv*Orx$_@ydv9mIe(liLfOr7mhbV@tKw{6)q^1DH1nmvZ0cj215R<~&I<4S| zgnr;9Cdjqpz#o8i0CQjtl`}{c*P)aSdH|abxGdrR)-3z+02-eX(k*B)Uqv6~^nh** z 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 \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/testing/integration_tests/storage/gradlew.bat b/testing/integration_tests/storage/gradlew.bat new file mode 100644 index 0000000000..8a0b282aa6 --- /dev/null +++ b/testing/integration_tests/storage/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/testing/integration_tests/storage/integration_test.xcodeproj/project.pbxproj b/testing/integration_tests/storage/integration_test.xcodeproj/project.pbxproj new file mode 100644 index 0000000000..d1d6b852d1 --- /dev/null +++ b/testing/integration_tests/storage/integration_test.xcodeproj/project.pbxproj @@ -0,0 +1,364 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 520BC0391C869159008CFBC3 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 520BC0381C869159008CFBC3 /* GoogleService-Info.plist */; }; + 529226D61C85F68000C89379 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 529226D51C85F68000C89379 /* Foundation.framework */; }; + 529226D81C85F68000C89379 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 529226D71C85F68000C89379 /* CoreGraphics.framework */; }; + 529226DA1C85F68000C89379 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 529226D91C85F68000C89379 /* UIKit.framework */; }; + D61C5F8E22BABA9C00A79141 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D61C5F8C22BABA9B00A79141 /* Images.xcassets */; }; + D61C5F9622BABAD200A79141 /* integration_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = D61C5F9222BABAD100A79141 /* integration_test.cc */; }; + D62CCBC022F367140099BE9F /* gmock-all.cc in Sources */ = {isa = PBXBuildFile; fileRef = D62CCBBF22F367140099BE9F /* gmock-all.cc */; }; + D66B16871CE46E8900E5638A /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D66B16861CE46E8900E5638A /* LaunchScreen.storyboard */; }; + D67D355822BABD2200292C1D /* gtest-all.cc in Sources */ = {isa = PBXBuildFile; fileRef = D67D355622BABD2100292C1D /* gtest-all.cc */; }; + D6C179E922CB322900C2651A /* ios_app_framework.mm in Sources */ = {isa = PBXBuildFile; fileRef = D6C179E722CB322900C2651A /* ios_app_framework.mm */; }; + D6C179EA22CB322900C2651A /* ios_firebase_test_framework.mm in Sources */ = {isa = PBXBuildFile; fileRef = D6C179E822CB322900C2651A /* ios_firebase_test_framework.mm */; }; + D6C179EE22CB323300C2651A /* firebase_test_framework.cc in Sources */ = {isa = PBXBuildFile; fileRef = D6C179EC22CB323300C2651A /* firebase_test_framework.cc */; }; + D6C179F022CB32A000C2651A /* app_framework.cc in Sources */ = {isa = PBXBuildFile; fileRef = D6C179EF22CB32A000C2651A /* app_framework.cc */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 520BC0381C869159008CFBC3 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; + 529226D21C85F68000C89379 /* integration_test.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = integration_test.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 529226D51C85F68000C89379 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + 529226D71C85F68000C89379 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; + 529226D91C85F68000C89379 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; + 529226EE1C85F68000C89379 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; + D61C5F8C22BABA9B00A79141 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; + D61C5F8D22BABA9C00A79141 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + D61C5F9222BABAD100A79141 /* integration_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = integration_test.cc; path = src/integration_test.cc; sourceTree = ""; }; + D62CCBBF22F367140099BE9F /* gmock-all.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "gmock-all.cc"; path = "external/googletest/src/googlemock/src/gmock-all.cc"; sourceTree = ""; }; + D62CCBC122F367320099BE9F /* gmock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = gmock.h; path = external/googletest/src/googlemock/include/gmock/gmock.h; sourceTree = ""; }; + D66B16861CE46E8900E5638A /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = ""; }; + D67D355622BABD2100292C1D /* gtest-all.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "gtest-all.cc"; path = "external/googletest/src/googletest/src/gtest-all.cc"; sourceTree = ""; }; + D67D355722BABD2100292C1D /* gtest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = gtest.h; path = external/googletest/src/googletest/include/gtest/gtest.h; sourceTree = ""; }; + D6C179E722CB322900C2651A /* ios_app_framework.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ios_app_framework.mm; path = src/ios/ios_app_framework.mm; sourceTree = ""; }; + D6C179E822CB322900C2651A /* ios_firebase_test_framework.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ios_firebase_test_framework.mm; path = src/ios/ios_firebase_test_framework.mm; sourceTree = ""; }; + D6C179EB22CB323300C2651A /* firebase_test_framework.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = firebase_test_framework.h; path = src/firebase_test_framework.h; sourceTree = ""; }; + D6C179EC22CB323300C2651A /* firebase_test_framework.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = firebase_test_framework.cc; path = src/firebase_test_framework.cc; sourceTree = ""; }; + D6C179ED22CB323300C2651A /* app_framework.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = app_framework.h; path = src/app_framework.h; sourceTree = ""; }; + D6C179EF22CB32A000C2651A /* app_framework.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = app_framework.cc; path = src/app_framework.cc; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 529226CF1C85F68000C89379 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 529226D81C85F68000C89379 /* CoreGraphics.framework in Frameworks */, + 529226DA1C85F68000C89379 /* UIKit.framework in Frameworks */, + 529226D61C85F68000C89379 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 529226C91C85F68000C89379 = { + isa = PBXGroup; + children = ( + D61C5F8C22BABA9B00A79141 /* Images.xcassets */, + D61C5F8D22BABA9C00A79141 /* Info.plist */, + D66B16861CE46E8900E5638A /* LaunchScreen.storyboard */, + 520BC0381C869159008CFBC3 /* GoogleService-Info.plist */, + 5292271D1C85FB5500C89379 /* src */, + 529226D41C85F68000C89379 /* Frameworks */, + 529226D31C85F68000C89379 /* Products */, + ); + sourceTree = ""; + }; + 529226D31C85F68000C89379 /* Products */ = { + isa = PBXGroup; + children = ( + 529226D21C85F68000C89379 /* integration_test.app */, + ); + name = Products; + sourceTree = ""; + }; + 529226D41C85F68000C89379 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 529226D51C85F68000C89379 /* Foundation.framework */, + 529226D71C85F68000C89379 /* CoreGraphics.framework */, + 529226D91C85F68000C89379 /* UIKit.framework */, + 529226EE1C85F68000C89379 /* XCTest.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 5292271D1C85FB5500C89379 /* src */ = { + isa = PBXGroup; + children = ( + D62CCBC122F367320099BE9F /* gmock.h */, + D62CCBBF22F367140099BE9F /* gmock-all.cc */, + D67D355622BABD2100292C1D /* gtest-all.cc */, + D67D355722BABD2100292C1D /* gtest.h */, + D6C179EF22CB32A000C2651A /* app_framework.cc */, + D6C179ED22CB323300C2651A /* app_framework.h */, + D6C179EC22CB323300C2651A /* firebase_test_framework.cc */, + D6C179EB22CB323300C2651A /* firebase_test_framework.h */, + D61C5F9222BABAD100A79141 /* integration_test.cc */, + 5292271E1C85FB5B00C89379 /* ios */, + ); + name = src; + sourceTree = ""; + }; + 5292271E1C85FB5B00C89379 /* ios */ = { + isa = PBXGroup; + children = ( + D6C179E722CB322900C2651A /* ios_app_framework.mm */, + D6C179E822CB322900C2651A /* ios_firebase_test_framework.mm */, + ); + name = ios; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 529226D11C85F68000C89379 /* integration_test */ = { + isa = PBXNativeTarget; + buildConfigurationList = 529226F91C85F68000C89379 /* Build configuration list for PBXNativeTarget "integration_test" */; + buildPhases = ( + 529226CE1C85F68000C89379 /* Sources */, + 529226CF1C85F68000C89379 /* Frameworks */, + 529226D01C85F68000C89379 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = integration_test; + productName = testapp; + productReference = 529226D21C85F68000C89379 /* integration_test.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 529226CA1C85F68000C89379 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0640; + ORGANIZATIONNAME = Google; + TargetAttributes = { + 529226D11C85F68000C89379 = { + CreatedOnToolsVersion = 6.4; + DevelopmentTeam = EQHXZ8M8AV; + ProvisioningStyle = Automatic; + }; + }; + }; + buildConfigurationList = 529226CD1C85F68000C89379 /* Build configuration list for PBXProject "integration_test" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 529226C91C85F68000C89379; + productRefGroup = 529226D31C85F68000C89379 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 529226D11C85F68000C89379 /* integration_test */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 529226D01C85F68000C89379 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D61C5F8E22BABA9C00A79141 /* Images.xcassets in Resources */, + D66B16871CE46E8900E5638A /* LaunchScreen.storyboard in Resources */, + 520BC0391C869159008CFBC3 /* GoogleService-Info.plist in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 529226CE1C85F68000C89379 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D67D355822BABD2200292C1D /* gtest-all.cc in Sources */, + D62CCBC022F367140099BE9F /* gmock-all.cc in Sources */, + D6C179EA22CB322900C2651A /* ios_firebase_test_framework.mm in Sources */, + D61C5F9622BABAD200A79141 /* integration_test.cc in Sources */, + D6C179E922CB322900C2651A /* ios_app_framework.mm in Sources */, + D6C179F022CB32A000C2651A /* app_framework.cc in Sources */, + D6C179EE22CB323300C2651A /* firebase_test_framework.cc in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 529226F71C85F68000C89379 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.4; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 529226F81C85F68000C89379 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.4; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 529226FA1C85F68000C89379 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ""; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)", + ); + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + "\"$(SRCROOT)/src\"", + "\"$(SRCROOT)/external/googletest/src/googletest/include\"", + "\"$(SRCROOT)/external/googletest/src/googlemock/include\"", + "\"$(SRCROOT)/external/googletest/src/googletest\"", + "\"$(SRCROOT)/external/googletest/src/googlemock\"", + ); + INFOPLIST_FILE = "$(SRCROOT)/Info.plist"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + WRAPPER_EXTENSION = app; + }; + name = Debug; + }; + 529226FB1C85F68000C89379 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ""; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)", + ); + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + "\"$(SRCROOT)/src\"", + "\"$(SRCROOT)/external/googletest/src/googletest/include\"", + "\"$(SRCROOT)/external/googletest/src/googlemock/include\"", + "\"$(SRCROOT)/external/googletest/src/googletest\"", + "\"$(SRCROOT)/external/googletest/src/googlemock\"", + ); + INFOPLIST_FILE = "$(SRCROOT)/Info.plist"; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + WRAPPER_EXTENSION = app; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 529226CD1C85F68000C89379 /* Build configuration list for PBXProject "integration_test" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 529226F71C85F68000C89379 /* Debug */, + 529226F81C85F68000C89379 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 529226F91C85F68000C89379 /* Build configuration list for PBXNativeTarget "integration_test" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 529226FA1C85F68000C89379 /* Debug */, + 529226FB1C85F68000C89379 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 529226CA1C85F68000C89379 /* Project object */; +} diff --git a/testing/integration_tests/storage/proguard.pro b/testing/integration_tests/storage/proguard.pro new file mode 100644 index 0000000000..2d04b8a9a5 --- /dev/null +++ b/testing/integration_tests/storage/proguard.pro @@ -0,0 +1,2 @@ +-ignorewarnings +-keep,includedescriptorclasses public class com.google.firebase.example.LoggingUtils { * ; } diff --git a/testing/integration_tests/storage/readme.md b/testing/integration_tests/storage/readme.md new file mode 100644 index 0000000000..6b9efa6fcd --- /dev/null +++ b/testing/integration_tests/storage/readme.md @@ -0,0 +1,215 @@ +Cloud Storage for Firebase Quickstart +======================== + +The Cloud Storage for Firebase Integration Test (integration test) demonstrates +Cloud Storage operations with the Firebase C++ SDK for Cloud Storage. +The application has no user interface and simply logs actions it's performing +to the console. + +The integration test uses the GoogleTest testing framework to perform a variety +of tests on a live Firebase project. Tests include: + - Creating a firebase::App in a platform-specific way. The App holds + platform-specific context that's used by other Firebase APIs, and is a + central point for communication between the Cloud Storage C++ and + Firebase Auth C++ libraries. + - Getting a pointer to firebase::Auth, and signs in anonymously. This allows the + integration test to access a Cloud Storage instance with authentication rules + enabled, which is the default setting in Firebase Console. + - Gets a StorageReference to a test-specific storage node, uses + StorageReference::Child() to create a child with a unique key based on the + current time in microseconds to work in, and gets a reference to that child, + which the integration test will use for the remainder of its actions. + - Uploads some sample files and reads them back to ensure the storage can be + read from and written to. + - Checks the Metadata of the uploaded and downloaded files to ensure they + return the expected values for things like size and date modified. + - Disconnects and then reconnects and verifies it still has access to the + files uploaded. + - Shuts down the Cloud Storage, Firebase Auth, and Firebase App systems, + ensuring the systems can be shut down in any order. + +Introduction +------------ + +- [Read more about Cloud Storage for Firebase](https://firebase.google.com/docs/storage/) + +Building and Running the integration test +----------------------------------------- + +### iOS + - Link your iOS app to the Firebase libraries. + - Get CocoaPods version 1 or later by running, + ``` + sudo gem install cocoapods --pre + ``` + - From the integration_tests/storage directory, install the CocoaPods listed + in the Podfile by running, + ``` + pod install + ``` + - Open the generated Xcode workspace (which now has the CocoaPods), + ``` + open integration_test.xcworkspace + ``` + - For further details please refer to the + [general instructions for setting up an iOS app with Firebase](https://firebase.google.com/docs/ios/setup). + - Register your iOS app with Firebase. + - Create a new app on the [Firebase console](https://firebase.google.com/console/), and attach + your iOS app to it. + - You can use "com.google.firebase.cpp.storage.testapp" as the iOS Bundle + ID while you're testing. You can omit App Store ID while testing. + - Add the GoogleService-Info.plist that you downloaded from Firebase console + to the integration_test/storage directory. This file identifies your iOS + app to the Firebase backend. + - In the Firebase console for your app, select "Auth", then enable + "Anonymous". This will allow the integration test to use anonymous sign-in to + authenticate with Cloud Storage, which requires a signed-in user by + default (an anonymous user will suffice). + - Download the Firebase C++ SDK linked from + [https://firebase.google.com/docs/cpp/setup](https://firebase.google.com/docs/cpp/setup) + and unzip it to a directory of your choice. + - Add the following frameworks from the Firebase C++ SDK to the project: + - frameworks/ios/universal/firebase.framework + - frameworks/ios/universal/firebase_auth.framework + - frameworks/ios/universal/firebase_storage.framework + - You will need to either, + 1. Check "Copy items if needed" when adding the frameworks, or + 2. Add the framework path in "Framework Search Paths" + - e.g. If you downloaded the Firebase C++ SDK to + `/Users/me/firebase_cpp_sdk`, + then you would add the path + `/Users/me/firebase_cpp_sdk/frameworks/ios/universal`. + - To add the path, in XCode, select your project in the project + navigator, then select your target in the main window. + Select the "Build Settings" tab, and click "All" to see all + the build settings. Scroll down to "Search Paths", and add + your path to "Framework Search Paths". + - In XCode, build & run the sample on an iOS device or simulator. + - The integration test has no interativity. The output of the app can be + viewed via the console or on the device's display. In Xcode, select "View + --> Debug Area --> Activate Console" from the menu to view the console. + +### Android + - Register your Android app with Firebase. + - Create a new app on + the [Firebase console](https://firebase.google.com/console/), and attach + your Android app to it. + - You can use "com.google.firebase.cpp.storage.testapp" as the Package + Name while you're testing. + - To + [generate a SHA1](https://developers.google.com/android/guides/client-auth) + run this command on Mac and Linux, + ``` + keytool -exportcert -list -v -alias androiddebugkey -keystore ~/.android/debug.keystore + ``` + or this command on Windows, + ``` + keytool -exportcert -list -v -alias androiddebugkey -keystore %USERPROFILE%\.android\debug.keystore + ``` + - If keytool reports that you do not have a debug.keystore, you can + [create one with](http://developer.android.com/tools/publishing/app-signing.html#signing-manually), + ``` + keytool -genkey -v -keystore ~/.android/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US" + ``` + - Add the `google-services.json` file that you downloaded from Firebase + console to the integration_test/storage directory. This file identifies + your Android app to the Firebase backend. + - In the Firebase console for your app, select "Auth", then enable + "Anonymous". This will allow the integration test to use anonymous sign-in + to authenticate with Cloud Storage, which requires a signed-in user by + default (an anonymous user will suffice). + - For further details please refer to the + [general instructions for setting up an Android app with Firebase](https://firebase.google.com/docs/android/setup). + - Download the Firebase C++ SDK linked from + [https://firebase.google.com/docs/cpp/setup](https://firebase.google.com/docs/cpp/setup) + and unzip it to a directory of your choice. + - Configure the location of the Firebase C++ SDK by setting the + firebase\_cpp\_sdk.dir Gradle property to the SDK install directory. + For example, in the project directory: + ``` + echo "systemProp.firebase\_cpp\_sdk.dir=/User/$USER/firebase\_cpp\_sdk" >> gradle.properties + ``` + - Ensure the Android SDK and NDK locations are set in Android Studio. + - From the Android Studio launch menu, go to `File/Project Structure...` or + `Configure/Project Defaults/Project Structure...` + (Shortcut: Control + Alt + Shift + S on windows, Command + ";" on a mac) + and download the SDK and NDK if the locations are not yet set. + - Open *build.gradle* in Android Studio. + - From the Android Studio launch menu, "Open an existing Android Studio + project", and select `build.gradle`. + - Install the SDK Platforms that Android Studio reports missing. + - Build the integration test and run it on an Android device or emulator. + - Once you've installed the SDKs in Android Studio, you can build the + integration test in Android Studio, or from the command-line by running + `./gradlew build`. + - The integration test has no interactive interface. The output of the app can + be viewed on the device's display, or in the logcat output of Android studio + or by running "adb logcat *:W android_main firebase" from the command line. + +### Desktop + - Register your app with Firebase. + - Create a new app on the [Firebase console](https://firebase.google.com/console/), + following the above instructions for Android or iOS. + - If you have an Android project, add the `google-services.json` file that + you downloaded from the Firebase console to the integration_test/storage + directory. + - If you have an iOS project, and don't wish to use an Android project, + you can use the Python script `generate_xml_from_google_services_json.py --plist`, + located in the Firebase C++ SDK, to convert your `GoogleService-Info.plist` + file into a `google-services-desktop.json` file, which can then be + placed in the integration_test/storage directory . + - Download the Firebase C++ SDK linked from + [https://firebase.google.com/docs/cpp/setup](https://firebase.google.com/docs/cpp/setup) + and unzip it to a directory of your choice. + - Configure the integration test with the location of the Firebase C++ SDK. + This can be done a couple different ways (in highest to lowest priority): + - When invoking cmake, pass in the location with + -DFIREBASE_CPP_SDK_DIR=/path/to/firebase_cpp_sdk. + - Set an environment variable for FIREBASE_CPP_SDK_DIR to the path to use. + - Edit the CMakeLists.txt file, changing the FIREBASE_CPP_SDK_DIR path + to the appropriate location. + - From the integration_test/storage directory, generate the build files by running, + ``` + cmake . + ``` + If you want to use XCode, you can use -G"Xcode" to generate the project. + Similarly, to use Visual Studio, -G"Visual Studio 15 2017". For more + information, see + [CMake generators](https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html). + - Build the integration test, by either opening the generated project file + based on the platform, or running, + ``` + cmake --build . + ``` + - Execute the integration test by running, + ``` + ./integration_test + ``` + Note that the executable might be under another directory, such as Debug. + - The integration test has no user interface, but the output can be viewed via + the console. + +Support +------- + +[https://firebase.google.com/support/](https://firebase.google.com/support/) + +License +------- + +Copyright 2016 Google, Inc. + +Licensed to the Apache Software Foundation (ASF) under one or more contributor +license agreements. See the NOTICE file distributed with this work for +additional information regarding copyright ownership. The ASF licenses this +file to you under the Apache License, Version 2.0 (the "License"); you may not +use this file except in compliance with the License. You may obtain a copy of +the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +License for the specific language governing permissions and limitations under +the License. diff --git a/testing/integration_tests/storage/res/layout/main.xml b/testing/integration_tests/storage/res/layout/main.xml new file mode 100644 index 0000000000..d3ffb63082 --- /dev/null +++ b/testing/integration_tests/storage/res/layout/main.xml @@ -0,0 +1,12 @@ + + + + diff --git a/testing/integration_tests/storage/res/values/strings.xml b/testing/integration_tests/storage/res/values/strings.xml new file mode 100644 index 0000000000..6d72216f3b --- /dev/null +++ b/testing/integration_tests/storage/res/values/strings.xml @@ -0,0 +1,4 @@ + + + Cloud Storage for Firebase Integration Test + diff --git a/testing/integration_tests/storage/settings.gradle b/testing/integration_tests/storage/settings.gradle new file mode 100644 index 0000000000..2a543b9351 --- /dev/null +++ b/testing/integration_tests/storage/settings.gradle @@ -0,0 +1,36 @@ +// Copyright 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +def firebase_cpp_sdk_dir = System.getProperty('firebase_cpp_sdk.dir') +if (firebase_cpp_sdk_dir == null || firebase_cpp_sdk_dir.isEmpty()) { + firebase_cpp_sdk_dir = System.getenv('FIREBASE_CPP_SDK_DIR') + if (firebase_cpp_sdk_dir == null || firebase_cpp_sdk_dir.isEmpty()) { + if ((new File('firebase_cpp_sdk')).exists()) { + firebase_cpp_sdk_dir = 'firebase_cpp_sdk' + } else { + throw new StopActionException( + 'firebase_cpp_sdk.dir property or the FIREBASE_CPP_SDK_DIR ' + + 'environment variable must be set to reference the Firebase C++ ' + + 'SDK install directory. This is used to configure static library ' + + 'and C/C++ include paths for the SDK.') + } + } +} +if (!(new File(firebase_cpp_sdk_dir)).exists()) { + throw new StopActionException( + sprintf('Firebase C++ SDK directory %s does not exist', + firebase_cpp_sdk_dir)) +} +gradle.ext.firebase_cpp_sdk_dir = "$firebase_cpp_sdk_dir" +includeBuild "$firebase_cpp_sdk_dir" \ No newline at end of file diff --git a/testing/integration_tests/storage/src/integration_test.cc b/testing/integration_tests/storage/src/integration_test.cc new file mode 100644 index 0000000000..80f193cce2 --- /dev/null +++ b/testing/integration_tests/storage/src/integration_test.cc @@ -0,0 +1,776 @@ +// Copyright 2019 Google Inc. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include +#include +#include +#include +#include + +#include "app_framework.h" // NOLINT +#include "firebase/app.h" +#include "firebase/auth.h" +#include "firebase/internal/platform.h" +#include "firebase/storage.h" +#include "firebase/util.h" +#include "firebase_test_framework.h" // NOLINT + +// The TO_STRING macro is useful for command line defined strings as the quotes +// get stripped. +#define TO_STRING_EXPAND(X) #X +#define TO_STRING(X) TO_STRING_EXPAND(X) + +// Path to the Firebase config file to load. +#ifdef FIREBASE_CONFIG +#define FIREBASE_CONFIG_STRING TO_STRING(FIREBASE_CONFIG) +#else +#define FIREBASE_CONFIG_STRING "" +#endif // FIREBASE_CONFIG + +namespace firebase_testapp_automated { + +using app_framework::LogDebug; + +// You can customize the Storage URL here. +const char* kStorageUrl = nullptr; + +#if FIREBASE_PLATFORM_DESKTOP +// Use a larger file on desktop... +const int kLargeFileMegabytes = 25; +#else +// ...and a smaller file on mobile. +const int kLargeFileMegabytes = 10; +#endif + +const char kRootNodeName[] = "integration_test_data"; + +using app_framework::GetCurrentTimeInMicroseconds; +using app_framework::PathForResource; +using app_framework::ProcessEvents; +using firebase_test_framework::FirebaseTest; +using testing::ElementsAreArray; + +class FirebaseStorageTest : public FirebaseTest { + public: + FirebaseStorageTest(); + ~FirebaseStorageTest() override; + + void SetUp() override; + void TearDown() override; + + protected: + // Initialize Firebase App, Firebase Auth, and Firebase Storage. + void Initialize(); + // Shut down Firebase Storage, Firebase Auth, and Firebase App. + void Terminate(); + // Sign in an anonymous user. + void SignIn(); + // Create a unique working folder and return a reference to it. + firebase::storage::StorageReference CreateFolder(); + + bool initialized_; + firebase::auth::Auth* auth_; + firebase::storage::Storage* storage_; + // File references that we need to delete on test exit. + std::vector cleanup_files_; + std::string saved_url_; +}; + +FirebaseStorageTest::FirebaseStorageTest() + : initialized_(false), auth_(nullptr), storage_(nullptr) { + FindFirebaseConfig(FIREBASE_CONFIG_STRING); +} + +FirebaseStorageTest::~FirebaseStorageTest() { + // Must be cleaned up on exit. + assert(app_ == nullptr); + assert(auth_ == nullptr); + assert(storage_ == nullptr); +} + +void FirebaseStorageTest::SetUp() { + FirebaseTest::SetUp(); + Initialize(); +} + +void FirebaseStorageTest::TearDown() { + if (initialized_) { + Terminate(); + } + FirebaseTest::TearDown(); +} + +void FirebaseStorageTest::Initialize() { + if (initialized_) return; + + InitializeApp(); + + LogDebug("Initializing Firebase Auth and Cloud Storage."); + + // 0th element has a reference to this object, the rest have the initializer + // targets. + void* initialize_targets[] = {&auth_, &storage_}; + + const firebase::ModuleInitializer::InitializerFn initializers[] = { + [](::firebase::App* app, void* data) { + void** targets = reinterpret_cast(data); + LogDebug("Attempting to initialize Firebase Auth."); + ::firebase::InitResult result; + *reinterpret_cast<::firebase::auth::Auth**>(targets[0]) = + ::firebase::auth::Auth::GetAuth(app, &result); + return result; + }, + [](::firebase::App* app, void* data) { + void** targets = reinterpret_cast(data); + LogDebug("Attempting to initialize Cloud Storage."); + ::firebase::InitResult result; + firebase::storage::Storage* storage = + firebase::storage::Storage::GetInstance(app, kStorageUrl, &result); + *reinterpret_cast<::firebase::storage::Storage**>(targets[1]) = storage; + return result; + }}; + + ::firebase::ModuleInitializer initializer; + initializer.Initialize(app_, initialize_targets, initializers, + sizeof(initializers) / sizeof(initializers[0])); + + WaitForCompletion(initializer.InitializeLastResult(), "Initialize"); + + ASSERT_EQ(initializer.InitializeLastResult().error(), 0) + << initializer.InitializeLastResult().error_message(); + + LogDebug("Successfully initialized Firebase Auth and Cloud Storage."); + + initialized_ = true; +} + +void FirebaseStorageTest::Terminate() { + if (!initialized_) return; + + if (storage_) { + LogDebug("Cleaning up files."); + std::vector> cleanups; + cleanups.reserve(cleanup_files_.size()); + for (int i = 0; i < cleanup_files_.size(); ++i) { + cleanups.push_back(cleanup_files_[i].Delete()); + } + for (int i = 0; i < cleanups.size(); ++i) { + WaitForCompletion(cleanups[i], "FirebaseStorageTest::TearDown"); + } + cleanup_files_.clear(); + + LogDebug("Shutdown the Storage library."); + delete storage_; + storage_ = nullptr; + } + if (auth_) { + LogDebug("Shutdown the Auth library."); + delete auth_; + auth_ = nullptr; + } + + TerminateApp(); + + initialized_ = false; + + ProcessEvents(100); +} + +void FirebaseStorageTest::SignIn() { + LogDebug("Signing in."); + firebase::Future sign_in_future = + auth_->SignInAnonymously(); + WaitForCompletion(sign_in_future, "SignInAnonymously"); + if (sign_in_future.error() != 0) { + FAIL() << "Ensure your application has the Anonymous sign-in provider " + "enabled in Firebase Console."; + } + ProcessEvents(100); +} + +firebase::storage::StorageReference FirebaseStorageTest::CreateFolder() { + // Generate a folder for the test data based on the time in milliseconds. + int64_t time_in_microseconds = GetCurrentTimeInMicroseconds(); + + char buffer[21] = {0}; + snprintf(buffer, sizeof(buffer), "%lld", + static_cast(time_in_microseconds)); // NOLINT + saved_url_ = buffer; + return storage_->GetReference(kRootNodeName).Child(saved_url_); +} + +// Test cases below. + +TEST_F(FirebaseStorageTest, TestInitializeAndTerminate) { + // Already tested via SetUp() and TearDown(). +} + +TEST_F(FirebaseStorageTest, TestSignIn) { + SignIn(); + EXPECT_NE(auth_->current_user(), nullptr); +} + +TEST_F(FirebaseStorageTest, TestCreateWorkingFolder) { + SignIn(); + // Create a unique child in the storage that we can run our tests in. + firebase::storage::StorageReference ref = CreateFolder(); + EXPECT_NE(saved_url_, ""); + + LogDebug("Storage URL: gs://%s%s", ref.bucket().c_str(), + ref.full_path().c_str()); + // Create the same reference in a few different manners and ensure they're + // equivalent. + // NOLINTNEXTLINE intentional redundant string conversion + { + firebase::storage::StorageReference ref_from_str = + storage_->GetReference(std::string(kRootNodeName)).Child(saved_url_); + EXPECT_EQ(ref.bucket(), ref_from_str.bucket()); + EXPECT_EQ(ref.full_path(), ref_from_str.full_path()); + } + std::string url = "gs://" + ref.bucket() + "/" + kRootNodeName; + LogDebug("Calling GetReferenceFromUrl(%s)", url.c_str()); + firebase::storage::StorageReference ref_from_url = + storage_->GetReferenceFromUrl(url.c_str()).Child(saved_url_); + EXPECT_TRUE(ref_from_url.is_valid()); + EXPECT_EQ(ref.bucket(), ref_from_url.bucket()); + EXPECT_EQ(ref.full_path(), ref_from_url.full_path()); + firebase::storage::StorageReference ref_from_url_str = + storage_->GetReferenceFromUrl(url).Child(saved_url_); + EXPECT_TRUE(ref_from_url_str.is_valid()); + EXPECT_EQ(ref.bucket(), ref_from_url_str.bucket()); + EXPECT_EQ(ref.full_path(), ref_from_url_str.full_path()); +} + +TEST_F(FirebaseStorageTest, TestStorageUrl) { + SignIn(); + // Confirm that creating a storage instance with a URL returns a url(), and + // creating a storage instance with a null URL returns a blank url(). + std::string default_url = + std::string("gs://") + storage_->GetReference().bucket(); + + // Check whether the Storage instance we already have is handled correctly. + EXPECT_EQ(storage_->url(), kStorageUrl ? kStorageUrl : ""); + delete storage_; + storage_ = nullptr; + { + firebase::storage::Storage* storage_explicit = + firebase::storage::Storage::GetInstance(app_, default_url.c_str(), + nullptr); + ASSERT_NE(storage_explicit, nullptr); + EXPECT_EQ(storage_explicit->url(), default_url); + delete storage_explicit; + } + { + firebase::storage::Storage* storage_implicit = + firebase::storage::Storage::GetInstance(app_, nullptr, nullptr); + ASSERT_NE(storage_implicit, nullptr); + EXPECT_EQ(storage_implicit->url(), ""); + delete storage_implicit; + } +} + +// NOLINTNEXTLINE +const std::string kSimpleTestFile = + "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do " + "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim " + "ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut " + "aliquip ex ea commodo consequat. Duis aute irure dolor in " + "reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla " + "pariatur. Excepteur sint occaecat cupidatat non proident, sunt in " + "culpa qui officia deserunt mollit anim id est laborum."; + +TEST_F(FirebaseStorageTest, TestWriteAndReadByteBuffer) { + SignIn(); + + firebase::storage::StorageReference ref = + CreateFolder().Child("TestFile.txt"); + LogDebug("Storage URL: gs://%s%s", ref.bucket().c_str(), + ref.full_path().c_str()); + cleanup_files_.push_back(ref); + // Write to a simple file. + { + LogDebug("Upload sample file from memory."); + firebase::Future future = + ref.PutBytes(&kSimpleTestFile[0], kSimpleTestFile.size()); + WaitForCompletion(future, "PutBytes"); + auto metadata = future.result(); + EXPECT_EQ(metadata->size_bytes(), kSimpleTestFile.size()); + } + + // Now read back the file. + { + LogDebug("Download sample file to memory."); + const size_t kBufferSize = 1024; + char buffer[kBufferSize]; + memset(buffer, 0, sizeof(buffer)); + + firebase::Future future = ref.GetBytes(buffer, kBufferSize); + WaitForCompletion(future, "GetBytes"); + ASSERT_NE(future.result(), nullptr); + size_t file_size = *future.result(); + EXPECT_EQ(file_size, kSimpleTestFile.size()); + EXPECT_THAT(kSimpleTestFile, ElementsAreArray(buffer, file_size)) + << "Download failed, file contents did not match."; + } +} + +TEST_F(FirebaseStorageTest, TestWriteAndReadFileWithCustomMetadata) { + SignIn(); + + firebase::storage::StorageReference ref = + CreateFolder().Child("TestFile-CustomMetadata.txt"); + LogDebug("Storage URL: gs://%s%s", ref.bucket().c_str(), + ref.full_path().c_str()); + cleanup_files_.push_back(ref); + std::string content_type = "text/plain"; + std::string custom_metadata_key = "specialkey"; + std::string custom_metadata_value = "secret value"; + // Write to a simple file. + { + LogDebug("Write a sample file with custom metadata from byte buffer."); + firebase::storage::Metadata metadata; + metadata.set_content_type(content_type.c_str()); + (*metadata.custom_metadata())[custom_metadata_key] = custom_metadata_value; + firebase::Future future = + ref.PutBytes(&kSimpleTestFile[0], kSimpleTestFile.size(), metadata); + WaitForCompletion(future, "PutBytes"); + const firebase::storage::Metadata* metadata_written = future.result(); + ASSERT_NE(metadata_written, nullptr); + EXPECT_EQ(metadata_written->size_bytes(), kSimpleTestFile.size()); + EXPECT_EQ(metadata_written->content_type(), content_type); + auto& custom_metadata = *metadata_written->custom_metadata(); + EXPECT_EQ(custom_metadata[custom_metadata_key], custom_metadata_value); + } + // Now read back the file. + { + LogDebug("Download sample file with custom metadata to memory."); + const size_t kBufferSize = 1024; + char buffer[kBufferSize]; + memset(buffer, 0, sizeof(buffer)); + + firebase::Future future = ref.GetBytes(buffer, kBufferSize); + WaitForCompletion(future, "GetBytes"); + ASSERT_NE(future.result(), nullptr); + size_t file_size = *future.result(); + EXPECT_EQ(file_size, kSimpleTestFile.size()); + EXPECT_THAT(kSimpleTestFile, ElementsAreArray(buffer, file_size)) + << "Download failed, file contents did not match."; + } + // And read the custom metadata. + { + LogDebug("Read custom metadata."); + firebase::Future future = ref.GetMetadata(); + WaitForCompletion(future, "GetFileMetadata"); + const firebase::storage::Metadata* metadata = future.result(); + ASSERT_NE(metadata, nullptr); + + // Get the current time to compare to the Timestamp. + int64_t current_time_seconds = static_cast(time(nullptr)); + int64_t updated_time_milliseconds = metadata->updated_time(); + int64_t updated_time_seconds = updated_time_milliseconds / 1000; + int64_t time_difference_seconds = + updated_time_seconds - current_time_seconds; + // As long as our timestamp is within a day, it's correct enough for + // our purposes. + const int64_t kAllowedTimeDifferenceSeconds = 60L * 60L * 24L; + EXPECT_TRUE(time_difference_seconds < kAllowedTimeDifferenceSeconds && + time_difference_seconds > -kAllowedTimeDifferenceSeconds) + << "Bad timestamp in metadata."; + EXPECT_EQ(metadata->size_bytes(), kSimpleTestFile.size()); + EXPECT_EQ(metadata->content_type(), content_type); + ASSERT_NE(metadata->custom_metadata(), nullptr); + auto& custom_metadata = *metadata->custom_metadata(); + EXPECT_EQ(custom_metadata[custom_metadata_key], custom_metadata_value); + } +} + +const char kPutFileTestFile[] = "PutFileTest.txt"; +const char kGetFileTestFile[] = "GetFileTest.txt"; +const char kFileUriScheme[] = "file://"; + +TEST_F(FirebaseStorageTest, TestPutFileAndGetFile) { + SignIn(); + firebase::storage::StorageReference ref = + CreateFolder().Child("TestFile-FileIO.txt"); + cleanup_files_.push_back(ref); + + // Upload a file. + { + // Write file that we're going to upload. + std::string path = PathForResource() + kPutFileTestFile; + // Cloud Storage expects a URI, so add file:// in front of local paths. + std::string file_path = kFileUriScheme + path; + + LogDebug("Creating local file: %s", path.c_str()); + + FILE* file = fopen(path.c_str(), "wb"); + std::fwrite(kSimpleTestFile.c_str(), 1, kSimpleTestFile.size(), file); + fclose(file); + + firebase::storage::Metadata new_metadata; + std::string content_type = "text/plain"; + new_metadata.set_content_type(content_type); + + LogDebug("Uploading sample file from disk."); + firebase::Future future = + ref.PutFile(file_path.c_str(), new_metadata); + WaitForCompletion(future, "PutFile"); + ASSERT_NE(future.result(), nullptr); + const firebase::storage::Metadata* metadata = future.result(); + EXPECT_EQ(metadata->size_bytes(), kSimpleTestFile.size()); + EXPECT_EQ(metadata->content_type(), content_type); + } + + // Use GetBytes to ensure the file uploaded correctly. + { + LogDebug("Downloading file to disk."); + const size_t kBufferSize = 1024; + char buffer[kBufferSize]; + memset(buffer, 0, sizeof(buffer)); + + firebase::Future future = ref.GetBytes(buffer, kBufferSize); + WaitForCompletion(future, "GetBytes"); + ASSERT_NE(future.result(), nullptr); + size_t file_size = *future.result(); + EXPECT_EQ(file_size, kSimpleTestFile.size()); + EXPECT_THAT(kSimpleTestFile, ElementsAreArray(buffer, file_size)) + << "Read file to byte buffer failed, file contents did not match."; + } + // Test GetFile to ensure we can download to a file. + { + std::string path = PathForResource() + kGetFileTestFile; + // Cloud Storage expects a URI, so add file:// in front of local paths. + std::string file_path = kFileUriScheme + path; + + LogDebug("Saving to local file: %s", path.c_str()); + + firebase::Future future = ref.GetFile(file_path.c_str()); + WaitForCompletion(future, "GetFile"); + ASSERT_NE(future.result(), nullptr); + EXPECT_EQ(*future.result(), kSimpleTestFile.size()); + + std::vector buffer(kSimpleTestFile.size()); + FILE* file = fopen(path.c_str(), "rb"); + ASSERT_NE(file, nullptr); + std::fread(&buffer[0], 1, kSimpleTestFile.size(), file); + fclose(file); + EXPECT_THAT(kSimpleTestFile, ElementsAreArray(&buffer[0], buffer.size())) + << "Download to disk failed, file contents did not match."; + } +} + +TEST_F(FirebaseStorageTest, TestDownloadUrl) { + SignIn(); + + const char kTestFileName[] = "TestFile-DownloadUrl.txt"; + firebase::storage::StorageReference ref = CreateFolder().Child(kTestFileName); + cleanup_files_.push_back(ref); + + LogDebug("Uploading file."); + WaitForCompletion(ref.PutBytes(&kSimpleTestFile[0], kSimpleTestFile.size()), + "PutBytes"); + + LogDebug("Getting download URL."); + firebase::Future future = ref.GetDownloadUrl(); + WaitForCompletion(future, "GetDownloadUrl"); + ASSERT_NE(future.result(), nullptr); + LogDebug("Got download URL: %s", future.result()->c_str()); + // Check for a somewhat well-formed URL, i.e. it starts with "https://" and + // has "TestFile-DownloadUrl" in the name. + EXPECT_TRUE(future.result()->find("https://") == 0) + << "Download Url doesn't start with https://"; + EXPECT_TRUE(future.result()->find(kTestFileName) != std::string::npos) + << "Download Url doesn't contain the filename " << kTestFileName; +} + +TEST_F(FirebaseStorageTest, TestDeleteFile) { + SignIn(); + + firebase::storage::StorageReference ref = + CreateFolder().Child("TestFile-Delete.txt"); + // Don't add to cleanup_files_ because we are going to delete it anyway. + + LogDebug("Uploading file."); + WaitForCompletion(ref.PutBytes(&kSimpleTestFile[0], kSimpleTestFile.size()), + "PutBytes"); + + LogDebug("Deleting file."); + WaitForCompletion(ref.Delete(), "Delete"); + + // Need a placeholder buffer. + const size_t kBufferSize = 1024; + char buffer[kBufferSize]; + // Ensure the file was deleted. + LogDebug("Ensuring file was deleted."); + firebase::Future future = ref.GetBytes(buffer, kBufferSize); + WaitForCompletion(future, "GetBytes", + firebase::storage::kErrorObjectNotFound); +} + +class StorageListener : public firebase::storage::Listener { + public: + StorageListener() + : on_paused_was_called_(false), on_progress_was_called_(false) {} + + // Tracks whether OnPaused was ever called and resumes the transfer. + void OnPaused(firebase::storage::Controller* controller) override { + on_paused_was_called_ = true; + controller->Resume(); + } + + void OnProgress(firebase::storage::Controller* controller) override { + LogDebug("Transferred %lld of %lld", controller->bytes_transferred(), + controller->total_byte_count()); + on_progress_was_called_ = true; + } + + bool on_paused_was_called() const { return on_paused_was_called_; } + bool on_progress_was_called() const { return on_progress_was_called_; } + + public: + bool on_paused_was_called_; + bool on_progress_was_called_; +}; + +// Contents of a large file, "X" will be replaced with a different character +// each line. +const char kLargeFileString[] = + "X: This is a large file with multiple lines and even some \xB1nary " + "char\xAC\ters.\n"; + +std::string CreateDataForLargeFile(size_t size_bytes) { + std::string large_test_file; + const std::string kLine = kLargeFileString; + + large_test_file.reserve(size_bytes + kLine.size()); + char replacement[2] = "a"; + while (large_test_file.size() < size_bytes) { + std::string line = kLine; + line.replace(kLine.find("X"), 1, replacement); + large_test_file += line; + replacement[0] = (replacement[0] - 'a' + 1) % 26 + 'a'; + } + large_test_file.resize(size_bytes); + return large_test_file; +} + +TEST_F(FirebaseStorageTest, TestLargeFilePauseResumeAndDownloadCancel) { + SignIn(); + + firebase::storage::StorageReference ref = + CreateFolder().Child("TestFile-LargeFile.txt"); + cleanup_files_.push_back(ref); + + const size_t kLargeFileSize = kLargeFileMegabytes * 1024 * 1024; + const std::string kLargeTestFile = CreateDataForLargeFile(kLargeFileSize); + + { + LogDebug("Uploading large file with pause/resume."); + StorageListener listener; + firebase::storage::Controller controller; + firebase::Future future = ref.PutBytes( + kLargeTestFile.c_str(), kLargeFileSize, &listener, &controller); + + // Ensure the Controller is valid now that we have associated it with an + // operation. + ASSERT_TRUE(controller.is_valid()); + + ProcessEvents(500); + // After waiting a moment for the operation to start, pause the operation + // and verify it was successfully paused. Note that pause might not take + // effect immediately, so we give it a few moments to pause before + // failing. + LogDebug("Pausing upload."); + EXPECT_TRUE(controller.Pause()) << "Upload pause"; + + // The StorageListener's OnPaused will call Resume(). + + LogDebug("Waiting for future."); + WaitForCompletion(future, "WriteLargeFile"); + LogDebug("Upload complete."); + + // Ensure the various callbacks were called. + EXPECT_TRUE(listener.on_paused_was_called()); + EXPECT_TRUE(listener.on_progress_was_called()); + + ASSERT_EQ(future.error(), firebase::storage::kErrorNone); + auto metadata = future.result(); + ASSERT_EQ(metadata->size_bytes(), kLargeFileSize) + << "Metadata reports incorrect size, file failed to upload."; + } + + // Download the file and confirm it's correct. + std::vector buffer(kLargeFileSize); + { + memset(&buffer[0], 0, kLargeFileSize); + LogDebug("Downloading large file for comparison."); + StorageListener listener; + firebase::Future future = + ref.GetBytes(&buffer[0], kLargeFileSize, &listener); + WaitForCompletion(future, "GetBytes"); + ASSERT_NE(future.result(), nullptr); + size_t file_size = *future.result(); + EXPECT_EQ(file_size, kLargeFileSize) << "Read size did not match"; + EXPECT_TRUE(memcmp(kLargeTestFile.c_str(), &buffer[0], kLargeFileSize) == 0) + << "Read large file failed, contents did not match."; + } +#if FIREBASE_PLATFORM_DESKTOP + { + // Test pausing/resuming while downloading (desktop only). + memset(&buffer[0], 0, kLargeFileSize); + LogDebug("Downloading large file with pausing/resuming."); + StorageListener listener; + firebase::storage::Controller controller; + firebase::Future future = + ref.GetBytes(&buffer[0], kLargeFileSize, &listener, &controller); + ASSERT_TRUE(controller.is_valid()); + + ProcessEvents(500); + LogDebug("Pausing download."); + EXPECT_TRUE(controller.Pause()) << "Download pause"; + + WaitForCompletion(future, "GetBytes"); + LogDebug("Download complete."); + + // Ensure the progress and pause callbacks were called. + EXPECT_TRUE(listener.on_progress_was_called()); + EXPECT_TRUE(listener.on_paused_was_called()); + + ASSERT_NE(future.result(), nullptr); + size_t file_size = *future.result(); + EXPECT_EQ(file_size, kLargeFileSize) + << "Read size with pause/resume did not match"; + EXPECT_TRUE(memcmp(kLargeTestFile.c_str(), &buffer[0], kLargeFileSize) == 0) + << "Read large file failed, contents did not match."; + } +#else + { + // Test downloading large file (mobile only), without pausing, as mobile + // does not support pause during file download, only upload. + memset(&buffer[0], 0, kLargeFileSize); + LogDebug("Downloading large file."); + StorageListener listener; + firebase::storage::Controller controller; + firebase::Future future = + ref.GetBytes(&buffer[0], kLargeFileSize, &listener, &controller); + ASSERT_TRUE(controller.is_valid()); + + WaitForCompletion(future, "GetBytes"); + LogDebug("Download complete."); + + // Ensure the progress callback was called. + EXPECT_TRUE(listener.on_progress_was_called()); + EXPECT_FALSE(listener.on_paused_was_called()); + + ASSERT_NE(future.result(), nullptr); + size_t file_size = *future.result(); + EXPECT_EQ(file_size, kLargeFileSize) << "Read size did not match"; + EXPECT_TRUE(memcmp(kLargeTestFile.c_str(), &buffer[0], kLargeFileSize) == 0) + << "Read large file failed, contents did not match."; + } +#endif // FIREBASE_PLATFORM_DESKTOP + + // Try canceling while downloading. + { + LogDebug("Downloading large file with cancellation."); + StorageListener listener; + firebase::storage::Controller controller; + firebase::Future future = + ref.GetBytes(&buffer[0], kLargeFileSize, &listener, &controller); + ASSERT_TRUE(controller.is_valid()); + ProcessEvents(500); + LogDebug("Cancelling download."); + EXPECT_TRUE(controller.Cancel()); + WaitForCompletion(future, "GetBytes", firebase::storage::kErrorCancelled); + } +} + +TEST_F(FirebaseStorageTest, TestLargeFileCancelUpload) { + SignIn(); + + firebase::storage::StorageReference ref = + CreateFolder().Child("TestFile-LargeFileCancel.txt"); + + const size_t kLargeFileSize = kLargeFileMegabytes * 1024 * 1024; + const std::string kLargeTestFile = CreateDataForLargeFile(kLargeFileSize); + { + LogDebug("Write a large file and cancel mid-way."); + StorageListener listener; + firebase::storage::Controller controller; + firebase::Future future = ref.PutBytes( + kLargeTestFile.c_str(), kLargeFileSize, &listener, &controller); + + // Ensure the Controller is valid now that we have associated it with an + // operation. + ASSERT_TRUE(controller.is_valid()); + + ProcessEvents(500); + + LogDebug("Cancelling upload."); + // Cancel the operation and verify it was successfully canceled. + EXPECT_TRUE(controller.Cancel()); + + WaitForCompletion(future, "PutBytes", firebase::storage::kErrorCancelled); + } +} + +TEST_F(FirebaseStorageTest, TestInvalidatingReferencesWhenDeletingStorage) { + SignIn(); + + // Create a file so we can get its metadata and check that it's properly + // invalidated. + firebase::storage::StorageReference ref = + CreateFolder().Child("TestFile-InvalidateReferencesDeletingStorage.txt"); + // Don't clean up, will be manually deleted. + + WaitForCompletion(ref.PutBytes(&kSimpleTestFile[0], kSimpleTestFile.size()), + "PutBytes"); + ASSERT_NE(ref.PutBytesLastResult().result(), nullptr); + firebase::storage::Metadata metadata = *ref.PutBytesLastResult().result(); + WaitForCompletion(ref.Delete(), "Delete"); + + ASSERT_TRUE(ref.is_valid()); + ASSERT_TRUE(metadata.is_valid()); + delete storage_; + storage_ = nullptr; + EXPECT_FALSE(ref.is_valid()); + EXPECT_FALSE(metadata.is_valid()); +} + +TEST_F(FirebaseStorageTest, TestInvalidatingReferencesWhenDeletingApp) { + SignIn(); + + // Create a file so we can get its metadata and check that it's properly + // invalidated. + firebase::storage::StorageReference ref = + CreateFolder().Child("TestFile-InvalidateReferencesDeletingApp.txt"); + // Don't clean up, will be manually deleted. + + WaitForCompletion(ref.PutBytes(&kSimpleTestFile[0], kSimpleTestFile.size()), + "PutBytes"); + ASSERT_NE(ref.PutBytesLastResult().result(), nullptr); + firebase::storage::Metadata metadata = *ref.PutBytesLastResult().result(); + WaitForCompletion(ref.Delete(), "Delete"); + + ASSERT_TRUE(ref.is_valid()); + ASSERT_TRUE(metadata.is_valid()); + delete app_; + app_ = nullptr; + EXPECT_FALSE(ref.is_valid()); + EXPECT_FALSE(metadata.is_valid()); +} + +} // namespace firebase_testapp_automated