<commit_before>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/file_util.h"
#include "base/path_service.h"
#include "base/test/test_timeouts.h"
#include "build/build_config.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/test/automation/tab_proxy.h"
#include "chrome/test/ui/ui_test.h"
#include "net/base/net_util.h"
#include "net/test/test_server.h"
#include "webkit/plugins/plugin_switches.h"

namespace {

// Platform-specific filename relative to the chrome executable.
#if defined(OS_WIN)
const wchar_t library_name[] = L"ppapi_tests.dll";
#elif defined(OS_MACOSX)
const char library_name[] = "ppapi_tests.plugin";
#elif defined(OS_POSIX)
const char library_name[] = "libppapi_tests.so";
#endif

}  // namespace

// In-process plugin test runner.  See OutOfProcessPPAPITest below for the
// out-of-process version.
class PPAPITest : public UITest {
 public:
  PPAPITest() {
    // Append the switch to register the pepper plugin.
    // library name = <out dir>/<test_name>.<library_extension>
    // MIME type = application/x-ppapi-<test_name>
    FilePath plugin_dir;
    PathService::Get(base::DIR_EXE, &plugin_dir);

    FilePath plugin_lib = plugin_dir.Append(library_name);
    EXPECT_TRUE(file_util::PathExists(plugin_lib));
    FilePath::StringType pepper_plugin = plugin_lib.value();
    pepper_plugin.append(FILE_PATH_LITERAL(";application/x-ppapi-tests"));
    launch_arguments_.AppendSwitchNative(switches::kRegisterPepperPlugins,
                                         pepper_plugin);

    // The test sends us the result via a cookie.
    launch_arguments_.AppendSwitch(switches::kEnableFileCookies);

    // Some stuff is hung off of the testing interface which is not enabled
    // by default.
    launch_arguments_.AppendSwitch(switches::kEnablePepperTesting);

    // Give unlimited quota for files to Pepper tests.
    // TODO(dumi): remove this switch once we have a quota management
    // system in place.
    launch_arguments_.AppendSwitch(switches::kUnlimitedQuotaForFiles);
  }

  void RunTest(const std::string& test_case) {
    FilePath test_path;
    PathService::Get(base::DIR_SOURCE_ROOT, &test_path);
    test_path = test_path.Append(FILE_PATH_LITERAL("ppapi"));
    test_path = test_path.Append(FILE_PATH_LITERAL("tests"));
    test_path = test_path.Append(FILE_PATH_LITERAL("test_case.html"));

    // Sanity check the file name.
    EXPECT_TRUE(file_util::PathExists(test_path));

    GURL::Replacements replacements;
    std::string query("testcase=");
    query += test_case;
    replacements.SetQuery(query.c_str(), url_parse::Component(0, query.size()));
    GURL test_url = net::FilePathToFileURL(test_path);
    RunTestURL(test_url.ReplaceComponents(replacements));
  }

  void RunTestViaHTTP(const std::string& test_case) {
    net::TestServer test_server(
        net::TestServer::TYPE_HTTP,
        FilePath(FILE_PATH_LITERAL("ppapi/tests")));
    ASSERT_TRUE(test_server.Start());
    RunTestURL(
        test_server.GetURL("files/test_case.html?testcase=" + test_case));
  }

 private:
  void RunTestURL(const GURL& test_url) {
    scoped_refptr<TabProxy> tab(GetActiveTab());
    ASSERT_TRUE(tab.get());
    ASSERT_TRUE(tab->NavigateToURL(test_url));

    // First wait for the "starting" signal. This cookie is set at the start
    // of every test. Waiting for this separately allows us to avoid a single
    // long timeout. Instead, we can have two timeouts which allow startup +
    // test execution time to take a while on a loaded computer, while also
    // making sure we're making forward progress.
    std::string startup_cookie =
        WaitUntilCookieNonEmpty(tab.get(), test_url,
            "STARTUP_COOKIE", TestTimeouts::action_max_timeout_ms());

    // If this fails, the plugin couldn't be loaded in the given amount of
    // time. This may mean the plugin was not found or possibly the system
    // can't load it due to missing symbols, etc.
    ASSERT_STREQ("STARTED", startup_cookie.c_str())
        << "Plugin couldn't be loaded. Make sure the PPAPI test plugin is "
        << "built, in the right place, and doesn't have any missing symbols.";

    std::string escaped_value =
        WaitUntilCookieNonEmpty(tab.get(), test_url,
            "COMPLETION_COOKIE", TestTimeouts::large_test_timeout_ms());
    EXPECT_STREQ("PASS", escaped_value.c_str());
  }
};

// Variant of PPAPITest that runs plugins out-of-process to test proxy
// codepaths.
class OutOfProcessPPAPITest : public PPAPITest {
 public:
  OutOfProcessPPAPITest() {
    // Run PPAPI out-of-process to exercise proxy implementations.
    launch_arguments_.AppendSwitch(switches::kPpapiOutOfProcess);
  }
};

// Use these macros to run the tests for a specific interface.
// Most interfaces should be tested with both macros.
#define TEST_PPAPI_IN_PROCESS(test_name) \
    TEST_F(PPAPITest, test_name) { \
      RunTest(#test_name); \
    }
#define TEST_PPAPI_OUT_OF_PROCESS(test_name) \
    TEST_F(OutOfProcessPPAPITest, test_name) { \
      RunTest(#test_name); \
    }

// Similar macros that test over HTTP.
#define TEST_PPAPI_IN_PROCESS_VIA_HTTP(test_name) \
    TEST_F(PPAPITest, test_name) { \
      RunTestViaHTTP(#test_name); \
    }
#define TEST_PPAPI_OUT_OF_PROCESS_VIA_HTTP(test_name) \
    TEST_F(OutOfProcessPPAPITest, test_name) { \
      RunTestViaHTTP(#test_name); \
    }

//
// Interface tests.
//

TEST_PPAPI_IN_PROCESS(Broker)
TEST_PPAPI_OUT_OF_PROCESS(Broker)

TEST_PPAPI_IN_PROCESS(Core)
TEST_PPAPI_OUT_OF_PROCESS(Core)

TEST_PPAPI_IN_PROCESS(CursorControl)
TEST_PPAPI_OUT_OF_PROCESS(CursorControl)

TEST_PPAPI_IN_PROCESS(Instance)
TEST_PPAPI_OUT_OF_PROCESS(Instance)

TEST_PPAPI_IN_PROCESS(Graphics2D)
// Disabled because it times out: http://crbug.com/89961
//TEST_PPAPI_OUT_OF_PROCESS(Graphics2D)

TEST_PPAPI_IN_PROCESS(ImageData)
TEST_PPAPI_OUT_OF_PROCESS(ImageData)

TEST_PPAPI_IN_PROCESS(Buffer)
TEST_PPAPI_OUT_OF_PROCESS(Buffer)

TEST_PPAPI_IN_PROCESS_VIA_HTTP(URLLoader)
// http://crbug.com/89961
TEST_F(OutOfProcessPPAPITest, FAILS_URLLoader) {
  RunTestViaHTTP("URLLoader");
}

TEST_PPAPI_IN_PROCESS(PaintAggregator)
TEST_PPAPI_OUT_OF_PROCESS(PaintAggregator)

TEST_PPAPI_IN_PROCESS(Scrollbar)
// http://crbug.com/89961
TEST_F(OutOfProcessPPAPITest, FAILS_Scrollbar) {
  RunTest("Scrollbar");
}

TEST_PPAPI_IN_PROCESS(URLUtil)
TEST_PPAPI_OUT_OF_PROCESS(URLUtil)

TEST_PPAPI_IN_PROCESS(CharSet)
TEST_PPAPI_OUT_OF_PROCESS(CharSet)

TEST_PPAPI_IN_PROCESS(Var)
// http://crbug.com/89961
TEST_F(OutOfProcessPPAPITest, FAILS_Var) {
  RunTest("Var");
}

TEST_PPAPI_IN_PROCESS(VarDeprecated)
// Disabled because it times out: http://crbug.com/89961
//TEST_PPAPI_OUT_OF_PROCESS(VarDeprecated)

// Windows defines 'PostMessage', so we have to undef it.
#ifdef PostMessage
#undef PostMessage
#endif
TEST_PPAPI_IN_PROCESS(PostMessage)
TEST_PPAPI_OUT_OF_PROCESS(PostMessage)

TEST_PPAPI_IN_PROCESS(Memory)
TEST_PPAPI_OUT_OF_PROCESS(Memory)

TEST_PPAPI_IN_PROCESS(QueryPolicy)
//TEST_PPAPI_OUT_OF_PROCESS(QueryPolicy)

// http://crbug.com/90039 and http://crbug.com/83443 (Mac)
TEST_F(PPAPITest, FAILS_FileIO) {
  RunTestViaHTTP("FileIO");
}
TEST_F(OutOfProcessPPAPITest, FAILS_FileIO) {
  RunTestViaHTTP("FileIO");
}

TEST_PPAPI_IN_PROCESS_VIA_HTTP(FileRef)
// Disabled because it times out: http://crbug.com/89961
//TEST_PPAPI_OUT_OF_PROCESS_VIA_HTTP(FileRef)

TEST_PPAPI_IN_PROCESS_VIA_HTTP(FileSystem)
// http://crbug.com/90040
TEST_F(OutOfProcessPPAPITest, FLAKY_FileSystem) {
  RunTestViaHTTP("FileSystem");
}

#if defined(OS_POSIX)
#define MAYBE_DirectoryReader FLAKY_DirectoryReader
#else
#define MAYBE_DirectoryReader DirectoryReader
#endif

// Flaky on Mac + Linux, maybe http://codereview.chromium.org/7094008
TEST_F(PPAPITest, MAYBE_DirectoryReader) {
  RunTestViaHTTP("DirectoryReader");
}
// http://crbug.com/89961
TEST_F(OutOfProcessPPAPITest, FAILS_DirectoryReader) {
  RunTestViaHTTP("DirectoryReader");
}

#if defined(ENABLE_P2P_APIS)
// Flaky. http://crbug.com/84295
TEST_F(PPAPITest, FLAKY_Transport) {
  RunTest("Transport");
}
// http://crbug.com/89961
TEST_F(OutOfProcessPPAPITest, FAILS_Transport) {
  RunTestViaHTTP("Transport");
}
#endif // ENABLE_P2P_APIS

TEST_PPAPI_IN_PROCESS(UMA)
// There is no proxy.
TEST_F(OutOfProcessPPAPITest, FAILS_UMA) {
  RunTest("UMA");
}

TEST_PPAPI_IN_PROCESS(VideoDecoder)
// There is no proxy yet (vrk is adding it).
TEST_F(OutOfProcessPPAPITest, FAILS_VideoDecoder) {
  RunTest("VideoDecoder");
}
<commit_msg>Marked OutOfProcessPPAPITest.Instance flaky.<commit_after>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/file_util.h"
#include "base/path_service.h"
#include "base/test/test_timeouts.h"
#include "build/build_config.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/test/automation/tab_proxy.h"
#include "chrome/test/ui/ui_test.h"
#include "net/base/net_util.h"
#include "net/test/test_server.h"
#include "webkit/plugins/plugin_switches.h"

namespace {

// Platform-specific filename relative to the chrome executable.
#if defined(OS_WIN)
const wchar_t library_name[] = L"ppapi_tests.dll";
#elif defined(OS_MACOSX)
const char library_name[] = "ppapi_tests.plugin";
#elif defined(OS_POSIX)
const char library_name[] = "libppapi_tests.so";
#endif

}  // namespace

// In-process plugin test runner.  See OutOfProcessPPAPITest below for the
// out-of-process version.
class PPAPITest : public UITest {
 public:
  PPAPITest() {
    // Append the switch to register the pepper plugin.
    // library name = <out dir>/<test_name>.<library_extension>
    // MIME type = application/x-ppapi-<test_name>
    FilePath plugin_dir;
    PathService::Get(base::DIR_EXE, &plugin_dir);

    FilePath plugin_lib = plugin_dir.Append(library_name);
    EXPECT_TRUE(file_util::PathExists(plugin_lib));
    FilePath::StringType pepper_plugin = plugin_lib.value();
    pepper_plugin.append(FILE_PATH_LITERAL(";application/x-ppapi-tests"));
    launch_arguments_.AppendSwitchNative(switches::kRegisterPepperPlugins,
                                         pepper_plugin);

    // The test sends us the result via a cookie.
    launch_arguments_.AppendSwitch(switches::kEnableFileCookies);

    // Some stuff is hung off of the testing interface which is not enabled
    // by default.
    launch_arguments_.AppendSwitch(switches::kEnablePepperTesting);

    // Give unlimited quota for files to Pepper tests.
    // TODO(dumi): remove this switch once we have a quota management
    // system in place.
    launch_arguments_.AppendSwitch(switches::kUnlimitedQuotaForFiles);
  }

  void RunTest(const std::string& test_case) {
    FilePath test_path;
    PathService::Get(base::DIR_SOURCE_ROOT, &test_path);
    test_path = test_path.Append(FILE_PATH_LITERAL("ppapi"));
    test_path = test_path.Append(FILE_PATH_LITERAL("tests"));
    test_path = test_path.Append(FILE_PATH_LITERAL("test_case.html"));

    // Sanity check the file name.
    EXPECT_TRUE(file_util::PathExists(test_path));

    GURL::Replacements replacements;
    std::string query("testcase=");
    query += test_case;
    replacements.SetQuery(query.c_str(), url_parse::Component(0, query.size()));
    GURL test_url = net::FilePathToFileURL(test_path);
    RunTestURL(test_url.ReplaceComponents(replacements));
  }

  void RunTestViaHTTP(const std::string& test_case) {
    net::TestServer test_server(
        net::TestServer::TYPE_HTTP,
        FilePath(FILE_PATH_LITERAL("ppapi/tests")));
    ASSERT_TRUE(test_server.Start());
    RunTestURL(
        test_server.GetURL("files/test_case.html?testcase=" + test_case));
  }

 private:
  void RunTestURL(const GURL& test_url) {
    scoped_refptr<TabProxy> tab(GetActiveTab());
    ASSERT_TRUE(tab.get());
    ASSERT_TRUE(tab->NavigateToURL(test_url));

    // First wait for the "starting" signal. This cookie is set at the start
    // of every test. Waiting for this separately allows us to avoid a single
    // long timeout. Instead, we can have two timeouts which allow startup +
    // test execution time to take a while on a loaded computer, while also
    // making sure we're making forward progress.
    std::string startup_cookie =
        WaitUntilCookieNonEmpty(tab.get(), test_url,
            "STARTUP_COOKIE", TestTimeouts::action_max_timeout_ms());

    // If this fails, the plugin couldn't be loaded in the given amount of
    // time. This may mean the plugin was not found or possibly the system
    // can't load it due to missing symbols, etc.
    ASSERT_STREQ("STARTED", startup_cookie.c_str())
        << "Plugin couldn't be loaded. Make sure the PPAPI test plugin is "
        << "built, in the right place, and doesn't have any missing symbols.";

    std::string escaped_value =
        WaitUntilCookieNonEmpty(tab.get(), test_url,
            "COMPLETION_COOKIE", TestTimeouts::large_test_timeout_ms());
    EXPECT_STREQ("PASS", escaped_value.c_str());
  }
};

// Variant of PPAPITest that runs plugins out-of-process to test proxy
// codepaths.
class OutOfProcessPPAPITest : public PPAPITest {
 public:
  OutOfProcessPPAPITest() {
    // Run PPAPI out-of-process to exercise proxy implementations.
    launch_arguments_.AppendSwitch(switches::kPpapiOutOfProcess);
  }
};

// Use these macros to run the tests for a specific interface.
// Most interfaces should be tested with both macros.
#define TEST_PPAPI_IN_PROCESS(test_name) \
    TEST_F(PPAPITest, test_name) { \
      RunTest(#test_name); \
    }
#define TEST_PPAPI_OUT_OF_PROCESS(test_name) \
    TEST_F(OutOfProcessPPAPITest, test_name) { \
      RunTest(#test_name); \
    }

// Similar macros that test over HTTP.
#define TEST_PPAPI_IN_PROCESS_VIA_HTTP(test_name) \
    TEST_F(PPAPITest, test_name) { \
      RunTestViaHTTP(#test_name); \
    }
#define TEST_PPAPI_OUT_OF_PROCESS_VIA_HTTP(test_name) \
    TEST_F(OutOfProcessPPAPITest, test_name) { \
      RunTestViaHTTP(#test_name); \
    }

//
// Interface tests.
//

TEST_PPAPI_IN_PROCESS(Broker)
TEST_PPAPI_OUT_OF_PROCESS(Broker)

TEST_PPAPI_IN_PROCESS(Core)
TEST_PPAPI_OUT_OF_PROCESS(Core)

TEST_PPAPI_IN_PROCESS(CursorControl)
TEST_PPAPI_OUT_OF_PROCESS(CursorControl)

TEST_PPAPI_IN_PROCESS(Instance)

// http://crbug.com/91729
#if defined(OS_LINUX)
#define MAYBE_Instance FLAKY_Instance
#else
#define MAYBE_Instance Instance
#endif

TEST_PPAPI_OUT_OF_PROCESS(MAYBE_Instance)

TEST_PPAPI_IN_PROCESS(Graphics2D)
// Disabled because it times out: http://crbug.com/89961
//TEST_PPAPI_OUT_OF_PROCESS(Graphics2D)

TEST_PPAPI_IN_PROCESS(ImageData)
TEST_PPAPI_OUT_OF_PROCESS(ImageData)

TEST_PPAPI_IN_PROCESS(Buffer)
TEST_PPAPI_OUT_OF_PROCESS(Buffer)

TEST_PPAPI_IN_PROCESS_VIA_HTTP(URLLoader)
// http://crbug.com/89961
TEST_F(OutOfProcessPPAPITest, FAILS_URLLoader) {
  RunTestViaHTTP("URLLoader");
}

TEST_PPAPI_IN_PROCESS(PaintAggregator)
TEST_PPAPI_OUT_OF_PROCESS(PaintAggregator)

TEST_PPAPI_IN_PROCESS(Scrollbar)
// http://crbug.com/89961
TEST_F(OutOfProcessPPAPITest, FAILS_Scrollbar) {
  RunTest("Scrollbar");
}

TEST_PPAPI_IN_PROCESS(URLUtil)
TEST_PPAPI_OUT_OF_PROCESS(URLUtil)

TEST_PPAPI_IN_PROCESS(CharSet)
TEST_PPAPI_OUT_OF_PROCESS(CharSet)

TEST_PPAPI_IN_PROCESS(Var)
// http://crbug.com/89961
TEST_F(OutOfProcessPPAPITest, FAILS_Var) {
  RunTest("Var");
}

TEST_PPAPI_IN_PROCESS(VarDeprecated)
// Disabled because it times out: http://crbug.com/89961
//TEST_PPAPI_OUT_OF_PROCESS(VarDeprecated)

// Windows defines 'PostMessage', so we have to undef it.
#ifdef PostMessage
#undef PostMessage
#endif
TEST_PPAPI_IN_PROCESS(PostMessage)
TEST_PPAPI_OUT_OF_PROCESS(PostMessage)

TEST_PPAPI_IN_PROCESS(Memory)
TEST_PPAPI_OUT_OF_PROCESS(Memory)

TEST_PPAPI_IN_PROCESS(QueryPolicy)
//TEST_PPAPI_OUT_OF_PROCESS(QueryPolicy)

// http://crbug.com/90039 and http://crbug.com/83443 (Mac)
TEST_F(PPAPITest, FAILS_FileIO) {
  RunTestViaHTTP("FileIO");
}
TEST_F(OutOfProcessPPAPITest, FAILS_FileIO) {
  RunTestViaHTTP("FileIO");
}

TEST_PPAPI_IN_PROCESS_VIA_HTTP(FileRef)
// Disabled because it times out: http://crbug.com/89961
//TEST_PPAPI_OUT_OF_PROCESS_VIA_HTTP(FileRef)

TEST_PPAPI_IN_PROCESS_VIA_HTTP(FileSystem)
// http://crbug.com/90040
TEST_F(OutOfProcessPPAPITest, FLAKY_FileSystem) {
  RunTestViaHTTP("FileSystem");
}

#if defined(OS_POSIX)
#define MAYBE_DirectoryReader FLAKY_DirectoryReader
#else
#define MAYBE_DirectoryReader DirectoryReader
#endif

// Flaky on Mac + Linux, maybe http://codereview.chromium.org/7094008
TEST_F(PPAPITest, MAYBE_DirectoryReader) {
  RunTestViaHTTP("DirectoryReader");
}
// http://crbug.com/89961
TEST_F(OutOfProcessPPAPITest, FAILS_DirectoryReader) {
  RunTestViaHTTP("DirectoryReader");
}

#if defined(ENABLE_P2P_APIS)
// Flaky. http://crbug.com/84295
TEST_F(PPAPITest, FLAKY_Transport) {
  RunTest("Transport");
}
// http://crbug.com/89961
TEST_F(OutOfProcessPPAPITest, FAILS_Transport) {
  RunTestViaHTTP("Transport");
}
#endif // ENABLE_P2P_APIS

TEST_PPAPI_IN_PROCESS(UMA)
// There is no proxy.
TEST_F(OutOfProcessPPAPITest, FAILS_UMA) {
  RunTest("UMA");
}

TEST_PPAPI_IN_PROCESS(VideoDecoder)
// There is no proxy yet (vrk is adding it).
TEST_F(OutOfProcessPPAPITest, FAILS_VideoDecoder) {
  RunTest("VideoDecoder");
}
<|endoftext|>


<commit_before>/*
    __ _____ _____ _____
 __|  |   __|     |   | |  JSON for Modern C++ (test suite)
|  |  |__   |  |  | | | |  version 3.7.3
|_____|_____|_____|_|___|  https://github.com/nlohmann/json

Licensed under the MIT License <http://opensource.org/licenses/MIT>.
SPDX-License-Identifier: MIT
Copyright (c) 2013-2019 Niels Lohmann <http://nlohmann.me>.

Permission is hereby  granted, free of charge, to any  person obtaining a copy
of this software and associated  documentation files (the "Software"), to deal
in the Software  without restriction, including without  limitation the rights
to  use, copy,  modify, merge,  publish, distribute,  sublicense, and/or  sell
copies  of  the Software,  and  to  permit persons  to  whom  the Software  is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE  IS PROVIDED "AS  IS", WITHOUT WARRANTY  OF ANY KIND,  EXPRESS OR
IMPLIED,  INCLUDING BUT  NOT  LIMITED TO  THE  WARRANTIES OF  MERCHANTABILITY,
FITNESS FOR  A PARTICULAR PURPOSE AND  NONINFRINGEMENT. IN NO EVENT  SHALL THE
AUTHORS  OR COPYRIGHT  HOLDERS  BE  LIABLE FOR  ANY  CLAIM,  DAMAGES OR  OTHER
LIABILITY, WHETHER IN AN ACTION OF  CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE  OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

#include "doctest_compatibility.h"

#define private public
#include <nlohmann/json.hpp>
using nlohmann::json;
#undef private

namespace
{
// special test case to check if memory is leaked if constructor throws
template<class T>
struct bad_allocator : std::allocator<T>
{
    template<class... Args>
    void construct(T*, Args&& ...)
    {
        throw std::bad_alloc();
    }
};
}

TEST_CASE("bad_alloc")
{
    SECTION("bad_alloc")
    {
        // create JSON type using the throwing allocator
        using bad_json = nlohmann::basic_json<std::map,
              std::vector,
              std::string,
              bool,
              std::int64_t,
              std::uint64_t,
              double,
              bad_allocator>;

        // creating an object should throw
        CHECK_THROWS_AS(bad_json(bad_json::value_t::object), std::bad_alloc&);
    }
}

namespace
{
bool next_construct_fails = false;
bool next_destroy_fails = false;
bool next_deallocate_fails = false;

template<class T>
struct my_allocator : std::allocator<T>
{
    using std::allocator<T>::allocator;

    template<class... Args>
    void construct(T* p, Args&& ... args)
    {
        if (next_construct_fails)
        {
            next_construct_fails = false;
            throw std::bad_alloc();
        }
        else
        {
            ::new (reinterpret_cast<void*>(p)) T(std::forward<Args>(args)...);
        }
    }

    void deallocate(T* p, std::size_t n)
    {
        if (next_deallocate_fails)
        {
            next_deallocate_fails = false;
            throw std::bad_alloc();
        }
        else
        {
            std::allocator<T>::deallocate(p, n);
        }
    }

    void destroy(T* p)
    {
        if (next_destroy_fails)
        {
            next_destroy_fails = false;
            throw std::bad_alloc();
        }
        else
        {
            p->~T();
        }
    }

    template <class U>
    struct rebind
    {
        using other = my_allocator<U>;
    };
};

// allows deletion of raw pointer, usually hold by json_value
template<class T>
void my_allocator_clean_up(T* p)
{
    assert(p != nullptr);
    my_allocator<T> alloc;
    alloc.destroy(p);
    alloc.deallocate(p, 1);
}
}

TEST_CASE("controlled bad_alloc")
{
    // create JSON type using the throwing allocator
    using my_json = nlohmann::basic_json<std::map,
          std::vector,
          std::string,
          bool,
          std::int64_t,
          std::uint64_t,
          double,
          my_allocator>;

    SECTION("class json_value")
    {
        SECTION("json_value(value_t)")
        {
            SECTION("object")
            {
                next_construct_fails = false;
                auto t = my_json::value_t::object;
                CHECK_NOTHROW(my_allocator_clean_up(my_json::json_value(t).object));
                next_construct_fails = true;
                CHECK_THROWS_AS(my_json::json_value(t), std::bad_alloc&);
                next_construct_fails = false;
            }
            SECTION("array")
            {
                next_construct_fails = false;
                auto t = my_json::value_t::array;
                CHECK_NOTHROW(my_allocator_clean_up(my_json::json_value(t).array));
                next_construct_fails = true;
                CHECK_THROWS_AS(my_json::json_value(t), std::bad_alloc&);
                next_construct_fails = false;
            }
            SECTION("string")
            {
                next_construct_fails = false;
                auto t = my_json::value_t::string;
                CHECK_NOTHROW(my_allocator_clean_up(my_json::json_value(t).string));
                next_construct_fails = true;
                CHECK_THROWS_AS(my_json::json_value(t), std::bad_alloc&);
                next_construct_fails = false;
            }
        }

        SECTION("json_value(const string_t&)")
        {
            next_construct_fails = false;
            my_json::string_t v("foo");
            CHECK_NOTHROW(my_allocator_clean_up(my_json::json_value(v).string));
            next_construct_fails = true;
            CHECK_THROWS_AS(my_json::json_value(v), std::bad_alloc&);
            next_construct_fails = false;
        }
    }

    SECTION("class basic_json")
    {
        SECTION("basic_json(const CompatibleObjectType&)")
        {
            next_construct_fails = false;
            std::map<std::string, std::string> v {{"foo", "bar"}};
            CHECK_NOTHROW(my_json(v));
            next_construct_fails = true;
            CHECK_THROWS_AS(my_json(v), std::bad_alloc&);
            next_construct_fails = false;
        }

        SECTION("basic_json(const CompatibleArrayType&)")
        {
            next_construct_fails = false;
            std::vector<std::string> v {"foo", "bar", "baz"};
            CHECK_NOTHROW(my_json(v));
            next_construct_fails = true;
            CHECK_THROWS_AS(my_json(v), std::bad_alloc&);
            next_construct_fails = false;
        }

        SECTION("basic_json(const typename string_t::value_type*)")
        {
            next_construct_fails = false;
            CHECK_NOTHROW(my_json("foo"));
            next_construct_fails = true;
            CHECK_THROWS_AS(my_json("foo"), std::bad_alloc&);
            next_construct_fails = false;
        }

        SECTION("basic_json(const typename string_t::value_type*)")
        {
            next_construct_fails = false;
            std::string s("foo");
            CHECK_NOTHROW(my_json(s));
            next_construct_fails = true;
            CHECK_THROWS_AS(my_json(s), std::bad_alloc&);
            next_construct_fails = false;
        }
    }
}

namespace
{
template<class T>
struct allocator_no_forward : std::allocator<T>
{
    allocator_no_forward() {}
    template <class U>
    allocator_no_forward(allocator_no_forward<U>) {}

    template <class U>
    struct rebind {
        using other =  allocator_no_forward<U>;
    };

    template <class... Args>
    void construct(T* p, const Args&... args)
    {
        // force copy even if move is available
        ::new (static_cast<void*>(p)) T(args...);
    }
};
}

TEST_CASE("bad my_allocator::construct")
{
    SECTION("my_allocator::construct doesn't forward")
    {
        using bad_alloc_json = nlohmann::basic_json<std::map,
                                                    std::vector,
                                                    std::string,
                                                    bool,
                                                    std::int64_t,
                                                    std::uint64_t,
                                                    double,
                                                    allocator_no_forward>;

      bad_alloc_json json;
      json["test"] = bad_alloc_json::array_t();
      json["test"].push_back("should not leak");
    }
}
<commit_msg>:art: fix indentation<commit_after>/*
    __ _____ _____ _____
 __|  |   __|     |   | |  JSON for Modern C++ (test suite)
|  |  |__   |  |  | | | |  version 3.7.3
|_____|_____|_____|_|___|  https://github.com/nlohmann/json

Licensed under the MIT License <http://opensource.org/licenses/MIT>.
SPDX-License-Identifier: MIT
Copyright (c) 2013-2019 Niels Lohmann <http://nlohmann.me>.

Permission is hereby  granted, free of charge, to any  person obtaining a copy
of this software and associated  documentation files (the "Software"), to deal
in the Software  without restriction, including without  limitation the rights
to  use, copy,  modify, merge,  publish, distribute,  sublicense, and/or  sell
copies  of  the Software,  and  to  permit persons  to  whom  the Software  is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE  IS PROVIDED "AS  IS", WITHOUT WARRANTY  OF ANY KIND,  EXPRESS OR
IMPLIED,  INCLUDING BUT  NOT  LIMITED TO  THE  WARRANTIES OF  MERCHANTABILITY,
FITNESS FOR  A PARTICULAR PURPOSE AND  NONINFRINGEMENT. IN NO EVENT  SHALL THE
AUTHORS  OR COPYRIGHT  HOLDERS  BE  LIABLE FOR  ANY  CLAIM,  DAMAGES OR  OTHER
LIABILITY, WHETHER IN AN ACTION OF  CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE  OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

#include "doctest_compatibility.h"

#define private public
#include <nlohmann/json.hpp>
using nlohmann::json;
#undef private

namespace
{
// special test case to check if memory is leaked if constructor throws
template<class T>
struct bad_allocator : std::allocator<T>
{
    template<class... Args>
    void construct(T*, Args&& ...)
    {
        throw std::bad_alloc();
    }
};
}

TEST_CASE("bad_alloc")
{
    SECTION("bad_alloc")
    {
        // create JSON type using the throwing allocator
        using bad_json = nlohmann::basic_json<std::map,
              std::vector,
              std::string,
              bool,
              std::int64_t,
              std::uint64_t,
              double,
              bad_allocator>;

        // creating an object should throw
        CHECK_THROWS_AS(bad_json(bad_json::value_t::object), std::bad_alloc&);
    }
}

namespace
{
bool next_construct_fails = false;
bool next_destroy_fails = false;
bool next_deallocate_fails = false;

template<class T>
struct my_allocator : std::allocator<T>
{
    using std::allocator<T>::allocator;

    template<class... Args>
    void construct(T* p, Args&& ... args)
    {
        if (next_construct_fails)
        {
            next_construct_fails = false;
            throw std::bad_alloc();
        }
        else
        {
            ::new (reinterpret_cast<void*>(p)) T(std::forward<Args>(args)...);
        }
    }

    void deallocate(T* p, std::size_t n)
    {
        if (next_deallocate_fails)
        {
            next_deallocate_fails = false;
            throw std::bad_alloc();
        }
        else
        {
            std::allocator<T>::deallocate(p, n);
        }
    }

    void destroy(T* p)
    {
        if (next_destroy_fails)
        {
            next_destroy_fails = false;
            throw std::bad_alloc();
        }
        else
        {
            p->~T();
        }
    }

    template <class U>
    struct rebind
    {
        using other = my_allocator<U>;
    };
};

// allows deletion of raw pointer, usually hold by json_value
template<class T>
void my_allocator_clean_up(T* p)
{
    assert(p != nullptr);
    my_allocator<T> alloc;
    alloc.destroy(p);
    alloc.deallocate(p, 1);
}
}

TEST_CASE("controlled bad_alloc")
{
    // create JSON type using the throwing allocator
    using my_json = nlohmann::basic_json<std::map,
          std::vector,
          std::string,
          bool,
          std::int64_t,
          std::uint64_t,
          double,
          my_allocator>;

    SECTION("class json_value")
    {
        SECTION("json_value(value_t)")
        {
            SECTION("object")
            {
                next_construct_fails = false;
                auto t = my_json::value_t::object;
                CHECK_NOTHROW(my_allocator_clean_up(my_json::json_value(t).object));
                next_construct_fails = true;
                CHECK_THROWS_AS(my_json::json_value(t), std::bad_alloc&);
                next_construct_fails = false;
            }
            SECTION("array")
            {
                next_construct_fails = false;
                auto t = my_json::value_t::array;
                CHECK_NOTHROW(my_allocator_clean_up(my_json::json_value(t).array));
                next_construct_fails = true;
                CHECK_THROWS_AS(my_json::json_value(t), std::bad_alloc&);
                next_construct_fails = false;
            }
            SECTION("string")
            {
                next_construct_fails = false;
                auto t = my_json::value_t::string;
                CHECK_NOTHROW(my_allocator_clean_up(my_json::json_value(t).string));
                next_construct_fails = true;
                CHECK_THROWS_AS(my_json::json_value(t), std::bad_alloc&);
                next_construct_fails = false;
            }
        }

        SECTION("json_value(const string_t&)")
        {
            next_construct_fails = false;
            my_json::string_t v("foo");
            CHECK_NOTHROW(my_allocator_clean_up(my_json::json_value(v).string));
            next_construct_fails = true;
            CHECK_THROWS_AS(my_json::json_value(v), std::bad_alloc&);
            next_construct_fails = false;
        }
    }

    SECTION("class basic_json")
    {
        SECTION("basic_json(const CompatibleObjectType&)")
        {
            next_construct_fails = false;
            std::map<std::string, std::string> v {{"foo", "bar"}};
            CHECK_NOTHROW(my_json(v));
            next_construct_fails = true;
            CHECK_THROWS_AS(my_json(v), std::bad_alloc&);
            next_construct_fails = false;
        }

        SECTION("basic_json(const CompatibleArrayType&)")
        {
            next_construct_fails = false;
            std::vector<std::string> v {"foo", "bar", "baz"};
            CHECK_NOTHROW(my_json(v));
            next_construct_fails = true;
            CHECK_THROWS_AS(my_json(v), std::bad_alloc&);
            next_construct_fails = false;
        }

        SECTION("basic_json(const typename string_t::value_type*)")
        {
            next_construct_fails = false;
            CHECK_NOTHROW(my_json("foo"));
            next_construct_fails = true;
            CHECK_THROWS_AS(my_json("foo"), std::bad_alloc&);
            next_construct_fails = false;
        }

        SECTION("basic_json(const typename string_t::value_type*)")
        {
            next_construct_fails = false;
            std::string s("foo");
            CHECK_NOTHROW(my_json(s));
            next_construct_fails = true;
            CHECK_THROWS_AS(my_json(s), std::bad_alloc&);
            next_construct_fails = false;
        }
    }
}

namespace
{
template<class T>
struct allocator_no_forward : std::allocator<T>
{
    allocator_no_forward() {}
    template <class U>
    allocator_no_forward(allocator_no_forward<U>) {}

    template <class U>
    struct rebind
    {
        using other =  allocator_no_forward<U>;
    };

    template <class... Args>
    void construct(T* p, const Args& ... args)
    {
        // force copy even if move is available
        ::new (static_cast<void*>(p)) T(args...);
    }
};
}

TEST_CASE("bad my_allocator::construct")
{
    SECTION("my_allocator::construct doesn't forward")
    {
        using bad_alloc_json = nlohmann::basic_json<std::map,
              std::vector,
              std::string,
              bool,
              std::int64_t,
              std::uint64_t,
              double,
              allocator_no_forward>;

        bad_alloc_json json;
        json["test"] = bad_alloc_json::array_t();
        json["test"].push_back("should not leak");
    }
}
<|endoftext|>


<commit_before>/*

Copyright (c) 2015, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include "test.hpp"
#include "libtorrent/alert_manager.hpp"
#include "libtorrent/torrent_handle.hpp"
#include "libtorrent/alert_types.hpp"
#include "libtorrent/extensions.hpp"
#include "libtorrent/thread.hpp"
#include "setup_transfer.hpp"

#include <boost/bind.hpp>
#include <boost/make_shared.hpp>
#include <boost/shared_ptr.hpp>

using namespace libtorrent;

TORRENT_TEST(limit)
{
	alert_manager mgr(500, 0xffffffff);

	TEST_EQUAL(mgr.alert_queue_size_limit(), 500);
	TEST_EQUAL(mgr.pending(), false);

	// try add 600 torrent_add_alert to make sure we honor the limit of 500
	// alerts.
	for (int i = 0; i < 600; ++i)
		mgr.emplace_alert<torrent_added_alert>(torrent_handle());

	TEST_EQUAL(mgr.pending(), true);

	std::vector<alert*> alerts;
	int num_resume;
	mgr.get_all(alerts, num_resume);

	// even though we posted 600, the limit was 500
	TEST_EQUAL(alerts.size(), 500);

	TEST_EQUAL(mgr.pending(), false);

	// now, try lowering the limit and do the same thing again
	mgr.set_alert_queue_size_limit(200);

	for (int i = 0; i < 600; ++i)
		mgr.emplace_alert<torrent_added_alert>(torrent_handle());

	TEST_EQUAL(mgr.pending(), true);

	mgr.get_all(alerts, num_resume);

	// even though we posted 600, the limit was 200
	TEST_EQUAL(alerts.size(), 200);
}

TORRENT_TEST(priority_limit)
{
	alert_manager mgr(100, 0xffffffff);

	TEST_EQUAL(mgr.alert_queue_size_limit(), 100);

	// this should only add 100 because of the limit
	for (int i = 0; i < 200; ++i)
		mgr.emplace_alert<torrent_added_alert>(torrent_handle());

	// the limit is twice as high for priority alerts
	for (int i = 0; i < 200; ++i)
		mgr.emplace_alert<file_rename_failed_alert>(torrent_handle(), i, error_code());

	std::vector<alert*> alerts;
	int num_resume;
	mgr.get_all(alerts, num_resume);

	// even though we posted 400, the limit was 100 for half of them and
	// 200 for the other half, meaning we should have 200 alerts now
	TEST_EQUAL(alerts.size(), 200);
}

void test_dispatch_fun(int& cnt, std::auto_ptr<alert> a)
{
	++cnt;
}

TORRENT_TEST(dispatch_function)
{
#ifndef TORRENT_NO_DEPRECATE
	int cnt = 0;
	alert_manager mgr(100, 0xffffffff);

	TEST_EQUAL(mgr.alert_queue_size_limit(), 100);
	TEST_EQUAL(mgr.pending(), false);

	for (int i = 0; i < 20; ++i)
		mgr.emplace_alert<torrent_added_alert>(torrent_handle());

	TEST_EQUAL(mgr.pending(), true);

	mgr.set_dispatch_function(boost::bind(&test_dispatch_fun, boost::ref(cnt), _1));

	TEST_EQUAL(mgr.pending(), false);

	TEST_EQUAL(cnt, 20);

	for (int i = 0; i < 200; ++i)
		mgr.emplace_alert<torrent_added_alert>(torrent_handle());

	TEST_EQUAL(mgr.pending(), false);
	TEST_EQUAL(cnt, 220);
#endif
}

void test_notify_fun(int& cnt)
{
	++cnt;
}

TORRENT_TEST(notify_function)
{
	int cnt = 0;
	alert_manager mgr(100, 0xffffffff);

	TEST_EQUAL(mgr.alert_queue_size_limit(), 100);
	TEST_EQUAL(mgr.pending(), false);

	for (int i = 0; i < 20; ++i)
		mgr.emplace_alert<torrent_added_alert>(torrent_handle());

	TEST_EQUAL(mgr.pending(), true);

	// if there are queued alerts when we set the notify function,
	// that counts as an edge and it's called
	mgr.set_notify_function(boost::bind(&test_notify_fun, boost::ref(cnt)));

	TEST_EQUAL(mgr.pending(), true);
	TEST_EQUAL(cnt, 1);

	// subsequent posted alerts will not cause an edge (because there are
	// already alerts queued)
	for (int i = 0; i < 20; ++i)
		mgr.emplace_alert<torrent_added_alert>(torrent_handle());

	TEST_EQUAL(mgr.pending(), true);
	TEST_EQUAL(cnt, 1);

	// however, if we pop all the alerts and post new ones, there will be
	// and edge triggering the notify call
	std::vector<alert*> alerts;
	int num_resume;
	mgr.get_all(alerts, num_resume);

	TEST_EQUAL(mgr.pending(), false);

	for (int i = 0; i < 20; ++i)
		mgr.emplace_alert<torrent_added_alert>(torrent_handle());

	TEST_EQUAL(mgr.pending(), true);
	TEST_EQUAL(cnt, 2);
}

#ifndef TORRENT_DISABLE_EXTENSIONS
int plugin_alerts[3] = { 0, 0, 0 };

struct test_plugin : libtorrent::plugin
{
	test_plugin(int index) : m_index(index) {}
	virtual void on_alert(alert const* a)
	{
		++plugin_alerts[m_index];
	}
	int m_index;
};

#endif

TORRENT_TEST(extensions)
{
#ifndef TORRENT_DISABLE_EXTENSIONS
	alert_manager mgr(100, 0xffffffff);

	mgr.add_extension(boost::make_shared<test_plugin>(0));
	mgr.add_extension(boost::make_shared<test_plugin>(1));
	mgr.add_extension(boost::make_shared<test_plugin>(2));

	for (int i = 0; i < 53; ++i)
		mgr.emplace_alert<torrent_added_alert>(torrent_handle());

	TEST_EQUAL(plugin_alerts[0], 53);
	TEST_EQUAL(plugin_alerts[1], 53);
	TEST_EQUAL(plugin_alerts[2], 53);

	for (int i = 0; i < 17; ++i)
		mgr.emplace_alert<torrent_added_alert>(torrent_handle());

	TEST_EQUAL(plugin_alerts[0], 70);
	TEST_EQUAL(plugin_alerts[1], 70);
	TEST_EQUAL(plugin_alerts[2], 70);
#endif
}

void post_torrent_added(alert_manager* mgr)
{
	test_sleep(10);
	mgr->emplace_alert<torrent_added_alert>(torrent_handle());
}

TORRENT_TEST(wait_for_alert)
{
	alert_manager mgr(100, 0xffffffff);

	time_point start = clock_type::now();

	alert* a = mgr.wait_for_alert(seconds(1));

	time_point end = clock_type::now();
	TEST_EQUAL(a, NULL);
	TEST_CHECK(end - start > milliseconds(900));
	TEST_CHECK(end - start < milliseconds(1100));

	mgr.emplace_alert<torrent_added_alert>(torrent_handle());

	start = clock_type::now();
	a = mgr.wait_for_alert(seconds(1));
	end = clock_type::now();
	TEST_CHECK(end - start < milliseconds(1));
	TEST_CHECK(a->type() == torrent_added_alert::alert_type);

	std::vector<alert*> alerts;
	int num_resume = 0;
	mgr.get_all(alerts, num_resume);

	start = clock_type::now();
	thread posting_thread(boost::bind(&post_torrent_added, &mgr));

	a = mgr.wait_for_alert(seconds(10));
	end = clock_type::now();
	TEST_CHECK(end - start < milliseconds(500));
	TEST_CHECK(a->type() == torrent_added_alert::alert_type);

	posting_thread.join();
}

TORRENT_TEST(queued_resume)
{
	alert_manager mgr(100, 0xffffffff);

	TEST_EQUAL(mgr.num_queued_resume(), 0);

	for (int i = 0; i < 17; ++i)
		mgr.emplace_alert<torrent_added_alert>(torrent_handle());

	TEST_EQUAL(mgr.num_queued_resume(), 0);

	std::vector<alert*> alerts;
	int num_resume = 0;
	mgr.get_all(alerts, num_resume);
	TEST_EQUAL(num_resume, 0);
	TEST_EQUAL(alerts.size(), 17);

	TEST_EQUAL(mgr.num_queued_resume(), 0);

	error_code ec(boost::system::errc::no_such_file_or_directory
		, generic_category());

	for (int i = 0; i < 2; ++i)
		mgr.emplace_alert<save_resume_data_failed_alert>(torrent_handle(), ec);

	TEST_EQUAL(mgr.num_queued_resume(), 2);

	mgr.get_all(alerts, num_resume);
	TEST_EQUAL(num_resume, 2);
	TEST_EQUAL(alerts.size(), 2);

	TEST_EQUAL(mgr.num_queued_resume(), 0);
}

TORRENT_TEST(alert_mask)
{
	alert_manager mgr(100, 0xffffffff);

	TEST_CHECK(mgr.should_post<torrent_added_alert>());
	TEST_CHECK(mgr.should_post<torrent_paused_alert>());

	mgr.set_alert_mask(0);

	TEST_CHECK(!mgr.should_post<torrent_added_alert>());
	TEST_CHECK(!mgr.should_post<torrent_paused_alert>());
}

<commit_msg>fix unit test<commit_after>/*

Copyright (c) 2015, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include "test.hpp"
#include "libtorrent/alert_manager.hpp"
#include "libtorrent/torrent_handle.hpp"
#include "libtorrent/alert_types.hpp"
#include "libtorrent/extensions.hpp"
#include "libtorrent/thread.hpp"
#include "setup_transfer.hpp"

#include <boost/bind.hpp>
#include <boost/make_shared.hpp>
#include <boost/shared_ptr.hpp>

using namespace libtorrent;

TORRENT_TEST(limit)
{
	alert_manager mgr(500, 0xffffffff);

	TEST_EQUAL(mgr.alert_queue_size_limit(), 500);
	TEST_EQUAL(mgr.pending(), false);

	// try add 600 torrent_add_alert to make sure we honor the limit of 500
	// alerts.
	for (int i = 0; i < 600; ++i)
		mgr.emplace_alert<torrent_added_alert>(torrent_handle());

	TEST_EQUAL(mgr.pending(), true);

	std::vector<alert*> alerts;
	int num_resume;
	mgr.get_all(alerts, num_resume);

	// even though we posted 600, the limit was 500
	TEST_EQUAL(alerts.size(), 500);

	TEST_EQUAL(mgr.pending(), false);

	// now, try lowering the limit and do the same thing again
	mgr.set_alert_queue_size_limit(200);

	for (int i = 0; i < 600; ++i)
		mgr.emplace_alert<torrent_added_alert>(torrent_handle());

	TEST_EQUAL(mgr.pending(), true);

	mgr.get_all(alerts, num_resume);

	// even though we posted 600, the limit was 200
	TEST_EQUAL(alerts.size(), 200);
}

TORRENT_TEST(priority_limit)
{
	alert_manager mgr(100, 0xffffffff);

	TEST_EQUAL(mgr.alert_queue_size_limit(), 100);

	// this should only add 100 because of the limit
	for (int i = 0; i < 200; ++i)
		mgr.emplace_alert<torrent_added_alert>(torrent_handle());

	// the limit is twice as high for priority alerts
	for (int i = 0; i < 200; ++i)
		mgr.emplace_alert<file_rename_failed_alert>(torrent_handle(), i, error_code());

	std::vector<alert*> alerts;
	int num_resume;
	mgr.get_all(alerts, num_resume);

	// even though we posted 400, the limit was 100 for half of them and
	// 200 for the other half, meaning we should have 200 alerts now
	TEST_EQUAL(alerts.size(), 200);
}

void test_dispatch_fun(int& cnt, std::auto_ptr<alert> a)
{
	++cnt;
}

TORRENT_TEST(dispatch_function)
{
#ifndef TORRENT_NO_DEPRECATE
	int cnt = 0;
	alert_manager mgr(100, 0xffffffff);

	TEST_EQUAL(mgr.alert_queue_size_limit(), 100);
	TEST_EQUAL(mgr.pending(), false);

	for (int i = 0; i < 20; ++i)
		mgr.emplace_alert<torrent_added_alert>(torrent_handle());

	TEST_EQUAL(mgr.pending(), true);

	mgr.set_dispatch_function(boost::bind(&test_dispatch_fun, boost::ref(cnt), _1));

	TEST_EQUAL(mgr.pending(), false);

	TEST_EQUAL(cnt, 20);

	for (int i = 0; i < 200; ++i)
		mgr.emplace_alert<torrent_added_alert>(torrent_handle());

	TEST_EQUAL(mgr.pending(), false);
	TEST_EQUAL(cnt, 220);
#endif
}

void test_notify_fun(int& cnt)
{
	++cnt;
}

TORRENT_TEST(notify_function)
{
	int cnt = 0;
	alert_manager mgr(100, 0xffffffff);

	TEST_EQUAL(mgr.alert_queue_size_limit(), 100);
	TEST_EQUAL(mgr.pending(), false);

	for (int i = 0; i < 20; ++i)
		mgr.emplace_alert<torrent_added_alert>(torrent_handle());

	TEST_EQUAL(mgr.pending(), true);

	// if there are queued alerts when we set the notify function,
	// that counts as an edge and it's called
	mgr.set_notify_function(boost::bind(&test_notify_fun, boost::ref(cnt)));

	TEST_EQUAL(mgr.pending(), true);
	TEST_EQUAL(cnt, 1);

	// subsequent posted alerts will not cause an edge (because there are
	// already alerts queued)
	for (int i = 0; i < 20; ++i)
		mgr.emplace_alert<torrent_added_alert>(torrent_handle());

	TEST_EQUAL(mgr.pending(), true);
	TEST_EQUAL(cnt, 1);

	// however, if we pop all the alerts and post new ones, there will be
	// and edge triggering the notify call
	std::vector<alert*> alerts;
	int num_resume;
	mgr.get_all(alerts, num_resume);

	TEST_EQUAL(mgr.pending(), false);

	for (int i = 0; i < 20; ++i)
		mgr.emplace_alert<torrent_added_alert>(torrent_handle());

	TEST_EQUAL(mgr.pending(), true);
	TEST_EQUAL(cnt, 2);
}

#ifndef TORRENT_DISABLE_EXTENSIONS
int plugin_alerts[3] = { 0, 0, 0 };

struct test_plugin : libtorrent::plugin
{
	test_plugin(int index) : m_index(index) {}
	virtual void on_alert(alert const* a)
	{
		++plugin_alerts[m_index];
	}
	int m_index;
};

#endif

TORRENT_TEST(extensions)
{
#ifndef TORRENT_DISABLE_EXTENSIONS
	memset(plugin_alerts, 0, sizeof(plugin_alerts));
	alert_manager mgr(100, 0xffffffff);

	mgr.add_extension(boost::make_shared<test_plugin>(0));
	mgr.add_extension(boost::make_shared<test_plugin>(1));
	mgr.add_extension(boost::make_shared<test_plugin>(2));

	for (int i = 0; i < 53; ++i)
		mgr.emplace_alert<torrent_added_alert>(torrent_handle());

	TEST_EQUAL(plugin_alerts[0], 53);
	TEST_EQUAL(plugin_alerts[1], 53);
	TEST_EQUAL(plugin_alerts[2], 53);

	for (int i = 0; i < 17; ++i)
		mgr.emplace_alert<torrent_added_alert>(torrent_handle());

	TEST_EQUAL(plugin_alerts[0], 70);
	TEST_EQUAL(plugin_alerts[1], 70);
	TEST_EQUAL(plugin_alerts[2], 70);
#endif
}

void post_torrent_added(alert_manager* mgr)
{
	test_sleep(10);
	mgr->emplace_alert<torrent_added_alert>(torrent_handle());
}

TORRENT_TEST(wait_for_alert)
{
	alert_manager mgr(100, 0xffffffff);

	time_point start = clock_type::now();

	alert* a = mgr.wait_for_alert(seconds(1));

	time_point end = clock_type::now();
	TEST_EQUAL(a, NULL);
	TEST_CHECK(end - start > milliseconds(900));
	TEST_CHECK(end - start < milliseconds(1100));

	mgr.emplace_alert<torrent_added_alert>(torrent_handle());

	start = clock_type::now();
	a = mgr.wait_for_alert(seconds(1));
	end = clock_type::now();
	TEST_CHECK(end - start < milliseconds(1));
	TEST_CHECK(a->type() == torrent_added_alert::alert_type);

	std::vector<alert*> alerts;
	int num_resume = 0;
	mgr.get_all(alerts, num_resume);

	start = clock_type::now();
	thread posting_thread(boost::bind(&post_torrent_added, &mgr));

	a = mgr.wait_for_alert(seconds(10));
	end = clock_type::now();
	TEST_CHECK(end - start < milliseconds(500));
	TEST_CHECK(a->type() == torrent_added_alert::alert_type);

	posting_thread.join();
}

TORRENT_TEST(queued_resume)
{
	alert_manager mgr(100, 0xffffffff);

	TEST_EQUAL(mgr.num_queued_resume(), 0);

	for (int i = 0; i < 17; ++i)
		mgr.emplace_alert<torrent_added_alert>(torrent_handle());

	TEST_EQUAL(mgr.num_queued_resume(), 0);

	std::vector<alert*> alerts;
	int num_resume = 0;
	mgr.get_all(alerts, num_resume);
	TEST_EQUAL(num_resume, 0);
	TEST_EQUAL(alerts.size(), 17);

	TEST_EQUAL(mgr.num_queued_resume(), 0);

	error_code ec(boost::system::errc::no_such_file_or_directory
		, generic_category());

	for (int i = 0; i < 2; ++i)
		mgr.emplace_alert<save_resume_data_failed_alert>(torrent_handle(), ec);

	TEST_EQUAL(mgr.num_queued_resume(), 2);

	mgr.get_all(alerts, num_resume);
	TEST_EQUAL(num_resume, 2);
	TEST_EQUAL(alerts.size(), 2);

	TEST_EQUAL(mgr.num_queued_resume(), 0);
}

TORRENT_TEST(alert_mask)
{
	alert_manager mgr(100, 0xffffffff);

	TEST_CHECK(mgr.should_post<torrent_added_alert>());
	TEST_CHECK(mgr.should_post<torrent_paused_alert>());

	mgr.set_alert_mask(0);

	TEST_CHECK(!mgr.should_post<torrent_added_alert>());
	TEST_CHECK(!mgr.should_post<torrent_paused_alert>());
}

<|endoftext|>


<commit_before>/* -----------------------------------------------------------------------------
Copyright 2017 Google Inc.

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.
----------------------------------------------------------------------------- */

// Author: Kevin P. Barry [ta0kira@gmail.com] [kevinbarry@google.com]

#include <memory>
#include <thread>

#include <gmock/gmock.h>
#include <gtest/gtest.h>

#include "thread-capture.h"
#include "thread-crosser.h"

#include "callback-queue.h"
#include "log-text.h"
#include "log-values.h"

using testing::ElementsAre;

namespace capture_thread {

using testing::CallbackQueue;
using testing::LogText;
using testing::LogTextMultiThread;
using testing::LogTextSingleThread;
using testing::LogValues;
using testing::LogValuesMultiThread;

TEST(ThreadCrosserTest, WrapCallIsFineWithoutLogger) {
  bool called = false;
  ThreadCrosser::WrapCall([&called] {
    called = true;
    LogText::Log("not logged");
  })();
  EXPECT_TRUE(called);
}

TEST(ThreadCrosserTest, WrapCallIsNotLazy) {
  LogTextMultiThread logger1;
  bool called = false;
  const auto callback = ThreadCrosser::WrapCall([&called] {
    called = true;
    LogText::Log("logged 1");
  });
  LogTextMultiThread logger2;
  callback();
  EXPECT_TRUE(called);
  EXPECT_THAT(logger1.GetLines(), ElementsAre("logged 1"));
  EXPECT_THAT(logger2.GetLines(), ElementsAre());
}

TEST(ThreadCrosserTest, WrapCallOnlyCapturesCrossers) {
  LogTextMultiThread logger1;
  LogTextSingleThread logger2;
  bool called = false;
  const auto callback = ThreadCrosser::WrapCall([&called] {
    called = true;
    LogText::Log("logged 1");
  });
  callback();
  EXPECT_TRUE(called);
  LogText::Log("logged 2");
  EXPECT_THAT(logger1.GetLines(), ElementsAre("logged 1"));
  EXPECT_THAT(logger2.GetLines(), ElementsAre("logged 2"));
}

TEST(ThreadCrosserTest, WrapCallIsIdempotent) {
  LogTextMultiThread logger1;
  bool called = false;
  const auto callback =
      ThreadCrosser::WrapCall(ThreadCrosser::WrapCall([&called] {
        called = true;
        LogText::Log("logged 1");
      }));
  LogTextMultiThread logger2;
  callback();
  EXPECT_TRUE(called);
  EXPECT_THAT(logger1.GetLines(), ElementsAre("logged 1"));
  EXPECT_THAT(logger2.GetLines(), ElementsAre());
}

TEST(ThreadCrosserTest, WrapCallFallsThroughWithoutLogger) {
  bool called = false;
  const auto callback = ThreadCrosser::WrapCall([&called] {
    called = true;
    LogText::Log("logged 1");
  });
  LogTextMultiThread logger;
  callback();
  EXPECT_TRUE(called);
  EXPECT_THAT(logger.GetLines(), ElementsAre("logged 1"));
}

TEST(ThreadCrosserTest, WrapCallWithNullCallbackIsNull) {
  EXPECT_FALSE(ThreadCrosser::WrapCall(nullptr));
  LogTextMultiThread logger;
  EXPECT_FALSE(ThreadCrosser::WrapCall(nullptr));
}

TEST(ThreadCrosserTest, WrapFunctionTypeCheckValueReturn) {
  using Type = std::unique_ptr<int>;
  const std::function<const int(Type, Type&)> function(
  [](Type left, Type& right) {
    *right = *left;
    return 3;
  });
  const auto wrapped = ThreadCrosser::WrapFunction(function);
  Type left(new int(1)), right(new int(2));
  EXPECT_EQ(wrapped(std::move(left), right), 3);
  EXPECT_FALSE(left);
  EXPECT_EQ(*right, 1);
}

TEST(ThreadCrosserTest, WrapFunctionTypeCheckReferenceReturn) {
  using Type = std::unique_ptr<int>;
  const std::function<Type&(Type, Type&)> function(
  [](Type left, Type& right) -> Type& {
    *right = *left;
    return right;
  });
  const auto wrapped = ThreadCrosser::WrapFunction(function);
  Type left(new int(1)), right(new int(2));
  EXPECT_EQ(&wrapped(std::move(left), right), &right);
  EXPECT_FALSE(left);
  EXPECT_EQ(*right, 1);
}

TEST(ThreadCrosserTest, WrapFunctionTypeCheckVoidReturn) {
  using Type = std::unique_ptr<int>;
  const std::function<void(Type, Type&)> function([](Type left, Type& right) {
    *right = *left;
  });
  const auto wrapped = ThreadCrosser::WrapFunction(function);
  Type left(new int(1)), right(new int(2));
  wrapped(std::move(left), right);
  EXPECT_FALSE(left);
  EXPECT_EQ(*right, 1);
}

TEST(ThreadCrosserTest, WrapFunctionNotLazyWithValueReturn) {
  const std::function<int()> function([]() -> int {
    LogText::Log("logged 1");
    return 1;
  });
  LogTextMultiThread logger1;
  const auto wrapped = ThreadCrosser::WrapFunction(function);
  LogTextMultiThread logger2;
  EXPECT_EQ(wrapped(), 1);
  EXPECT_THAT(logger1.GetLines(), ElementsAre("logged 1"));
  EXPECT_THAT(logger2.GetLines(), ElementsAre());
}

TEST(ThreadCrosserTest, WrapFunctionNotLazyWithReferenceReturn) {
  int value = 0;
  const std::function<int&()> function([&value]() -> int& {
    LogText::Log("logged 1");
    return value;
  });
  LogTextMultiThread logger1;
  const auto wrapped = ThreadCrosser::WrapFunction(function);
  LogTextMultiThread logger2;
  EXPECT_EQ(&wrapped(), &value);
  EXPECT_THAT(logger1.GetLines(), ElementsAre("logged 1"));
  EXPECT_THAT(logger2.GetLines(), ElementsAre());
}

TEST(ThreadCrosserTest, WrapFunctionNotLazyWithVoidReturn) {
  const std::function<void()> function([]() {
    LogText::Log("logged 1");
  });
  LogTextMultiThread logger1;
  const auto wrapped = ThreadCrosser::WrapFunction(function);
  LogTextMultiThread logger2;
  wrapped();
  EXPECT_THAT(logger1.GetLines(), ElementsAre("logged 1"));
  EXPECT_THAT(logger2.GetLines(), ElementsAre());
}

TEST(ThreadCrosserTest, WrapFunctionWithNullCallbackIsNull) {
  std::function<int(int)> callback(nullptr);
  EXPECT_FALSE(ThreadCrosser::WrapFunction(callback));
  LogTextMultiThread logger;
  EXPECT_FALSE(ThreadCrosser::WrapFunction(callback));
}

TEST(ThreadCrosserTest, SingleThreadCrossing) {
  LogTextMultiThread logger;
  LogText::Log("logged 1");

  std::thread worker(ThreadCrosser::WrapCall([] { LogText::Log("logged 2"); }));
  worker.join();

  EXPECT_THAT(logger.GetLines(), ElementsAre("logged 1", "logged 2"));
}

TEST(ThreadCrosserTest, MultipleThreadCrossingWithMultipleLoggers) {
  LogTextMultiThread text_logger;
  LogText::Log("logged 1");
  LogValuesMultiThread count_logger;
  LogValues::Count(1);

  std::thread worker(ThreadCrosser::WrapCall([] {
    std::thread worker(ThreadCrosser::WrapCall([] {
      LogText::Log("logged 2");
      LogValues::Count(2);
    }));
    worker.join();
  }));
  worker.join();

  EXPECT_THAT(text_logger.GetLines(), ElementsAre("logged 1", "logged 2"));
  EXPECT_THAT(count_logger.GetCounts(), ElementsAre(1, 2));
}

TEST(ThreadCrosserTest, MultipleThreadCrossingWithDifferentLoggerScopes) {
  LogTextMultiThread text_logger;

  std::thread worker(ThreadCrosser::WrapCall([] {
    LogValuesMultiThread count_logger;
    std::thread worker(ThreadCrosser::WrapCall([] {
      LogText::Log("logged 1");
      LogValues::Count(1);
    }));
    worker.join();
    EXPECT_THAT(count_logger.GetCounts(), ElementsAre(1));
  }));
  worker.join();

  EXPECT_THAT(text_logger.GetLines(), ElementsAre("logged 1"));
}

TEST(ThreadCrosserTest, MultipleThreadCrossingWithOverride) {
  LogTextMultiThread logger1;

  std::thread worker(ThreadCrosser::WrapCall([] {
    LogTextMultiThread logger2;
    std::thread worker(
        ThreadCrosser::WrapCall([] { LogText::Log("logged 2"); }));
    worker.join();
    EXPECT_THAT(logger2.GetLines(), ElementsAre("logged 2"));
  }));
  worker.join();

  EXPECT_THAT(logger1.GetLines(), ElementsAre());
}

TEST(ThreadCrosserTest, DifferentLoggersInSameThread) {
  CallbackQueue queue;

  std::thread worker([&queue] {
    while (true) {
      LogTextMultiThread logger;
      if (!queue.PopAndCall()) {
        break;
      }
      LogText::Log("logged in thread");
      EXPECT_THAT(logger.GetLines(), ElementsAre("logged in thread"));
    }
  });

  LogTextMultiThread logger1;
  queue.Push(ThreadCrosser::WrapCall([] { LogText::Log("logged 1"); }));
  queue.WaitUntilEmpty();
  EXPECT_THAT(logger1.GetLines(), ElementsAre("logged 1"));

  {
    // It's important for the test case that logger2 overrides logger1, i.e.,
    // that they are both in scope at the same time.
    LogTextMultiThread logger2;
    queue.Push(ThreadCrosser::WrapCall([] { LogText::Log("logged 2"); }));
    queue.WaitUntilEmpty();
    EXPECT_THAT(logger2.GetLines(), ElementsAre("logged 2"));
  }

  queue.Push(ThreadCrosser::WrapCall([] { LogText::Log("logged 3"); }));
  queue.WaitUntilEmpty();
  EXPECT_THAT(logger1.GetLines(), ElementsAre("logged 1", "logged 3"));

  queue.Terminate();
  worker.join();
}

TEST(ThreadCrosserTest, ReverseOrderOfLoggersOnStack) {
  LogTextMultiThread logger1;
  const auto callback =
      ThreadCrosser::WrapCall([] { LogText::Log("logged 1"); });

  LogTextMultiThread logger2;
  const auto worker_call = ThreadCrosser::WrapCall([callback] {
    // In callback(), logger1 overrides logger2, whereas in the main thread
    // logger2 overrides logger1.
    callback();
    LogText::Log("logged 2");
  });

  LogTextMultiThread logger3;

  // Call using a thread.
  std::thread worker(worker_call);
  worker.join();

  EXPECT_THAT(logger1.GetLines(), ElementsAre("logged 1"));
  EXPECT_THAT(logger2.GetLines(), ElementsAre("logged 2"));
  EXPECT_THAT(logger3.GetLines(), ElementsAre());

  // Call in the main thread.
  worker_call();

  EXPECT_THAT(logger1.GetLines(), ElementsAre("logged 1", "logged 1"));
  EXPECT_THAT(logger2.GetLines(), ElementsAre("logged 2", "logged 2"));
  EXPECT_THAT(logger3.GetLines(), ElementsAre());
}

TEST(ThreadCrosserTest, ManualCrosserOverride) {
  LogTextMultiThread logger;
  const ThreadCrosser::SetOverride override_point;

  std::thread unwrapped_worker([&override_point] {
    override_point.Call([] { LogText::Log("logged 1"); });
  });
  unwrapped_worker.join();

  EXPECT_THAT(logger.GetLines(), ElementsAre("logged 1"));
}

TEST(ThreadCrosserTest, ManualOverrideIndependentOfNormalScope) {
  LogTextMultiThread text_logger1;
  const ThreadCrosser::SetOverride override_point;

  std::thread unwrapped_worker([&override_point] {
    LogTextMultiThread text_logger2;
    LogValuesMultiThread count_logger;
    // Overrides LogText but not LogValues.
    override_point.Call([] {
      LogText::Log("logged 1");
      LogValues::Count(1);
    });
    // Local scope supercedes override of LogText.
    override_point.Call(ThreadCrosser::WrapCall([] {
      LogText::Log("logged 2");
      LogValues::Count(2);
    }));
    EXPECT_THAT(text_logger2.GetLines(), ElementsAre("logged 2"));
    EXPECT_THAT(count_logger.GetCounts(), ElementsAre(1, 2));
  });
  unwrapped_worker.join();

  EXPECT_THAT(text_logger1.GetLines(), ElementsAre("logged 1"));
}

}  // namespace capture_thread

int main(int argc, char* argv[]) {
  testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}
<commit_msg>Adds a test to verify that WrapFunction uses copy elision when returning by non-const value.<commit_after>/* -----------------------------------------------------------------------------
Copyright 2017 Google Inc.

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.
----------------------------------------------------------------------------- */

// Author: Kevin P. Barry [ta0kira@gmail.com] [kevinbarry@google.com]

#include <memory>
#include <thread>

#include <gmock/gmock.h>
#include <gtest/gtest.h>

#include "thread-capture.h"
#include "thread-crosser.h"

#include "callback-queue.h"
#include "log-text.h"
#include "log-values.h"

using testing::ElementsAre;

namespace capture_thread {

using testing::CallbackQueue;
using testing::LogText;
using testing::LogTextMultiThread;
using testing::LogTextSingleThread;
using testing::LogValues;
using testing::LogValuesMultiThread;

TEST(ThreadCrosserTest, WrapCallIsFineWithoutLogger) {
  bool called = false;
  ThreadCrosser::WrapCall([&called] {
    called = true;
    LogText::Log("not logged");
  })();
  EXPECT_TRUE(called);
}

TEST(ThreadCrosserTest, WrapCallIsNotLazy) {
  LogTextMultiThread logger1;
  bool called = false;
  const auto callback = ThreadCrosser::WrapCall([&called] {
    called = true;
    LogText::Log("logged 1");
  });
  LogTextMultiThread logger2;
  callback();
  EXPECT_TRUE(called);
  EXPECT_THAT(logger1.GetLines(), ElementsAre("logged 1"));
  EXPECT_THAT(logger2.GetLines(), ElementsAre());
}

TEST(ThreadCrosserTest, WrapCallOnlyCapturesCrossers) {
  LogTextMultiThread logger1;
  LogTextSingleThread logger2;
  bool called = false;
  const auto callback = ThreadCrosser::WrapCall([&called] {
    called = true;
    LogText::Log("logged 1");
  });
  callback();
  EXPECT_TRUE(called);
  LogText::Log("logged 2");
  EXPECT_THAT(logger1.GetLines(), ElementsAre("logged 1"));
  EXPECT_THAT(logger2.GetLines(), ElementsAre("logged 2"));
}

TEST(ThreadCrosserTest, WrapCallIsIdempotent) {
  LogTextMultiThread logger1;
  bool called = false;
  const auto callback =
      ThreadCrosser::WrapCall(ThreadCrosser::WrapCall([&called] {
        called = true;
        LogText::Log("logged 1");
      }));
  LogTextMultiThread logger2;
  callback();
  EXPECT_TRUE(called);
  EXPECT_THAT(logger1.GetLines(), ElementsAre("logged 1"));
  EXPECT_THAT(logger2.GetLines(), ElementsAre());
}

TEST(ThreadCrosserTest, WrapCallFallsThroughWithoutLogger) {
  bool called = false;
  const auto callback = ThreadCrosser::WrapCall([&called] {
    called = true;
    LogText::Log("logged 1");
  });
  LogTextMultiThread logger;
  callback();
  EXPECT_TRUE(called);
  EXPECT_THAT(logger.GetLines(), ElementsAre("logged 1"));
}

TEST(ThreadCrosserTest, WrapCallWithNullCallbackIsNull) {
  EXPECT_FALSE(ThreadCrosser::WrapCall(nullptr));
  LogTextMultiThread logger;
  EXPECT_FALSE(ThreadCrosser::WrapCall(nullptr));
}

TEST(ThreadCrosserTest, WrapFunctionTypeCheckConstValueReturn) {
  using Type = std::unique_ptr<int>;
  const std::function<const int(Type, Type&)> function(
  [](Type left, Type& right) {
    *right = *left;
    return 3;
  });
  const auto wrapped = ThreadCrosser::WrapFunction(function);
  Type left(new int(1)), right(new int(2));
  EXPECT_EQ(wrapped(std::move(left), right), 3);
  EXPECT_FALSE(left);
  EXPECT_EQ(*right, 1);
}

TEST(ThreadCrosserTest, WrapFunctionTypeCheckValueReturn) {
  using Type = std::unique_ptr<int>;
  const std::function<Type(Type, Type&)> function(
  [](Type left, Type& right) -> Type {
    *right = *left;
    return left;
  });
  const auto wrapped = ThreadCrosser::WrapFunction(function);
  Type left(new int(1)), right(new int(2));
  int* left_ptr = left.get();
  Type result = wrapped(std::move(left), right);
  EXPECT_FALSE(left);
  EXPECT_EQ(result.get(), left_ptr);
  EXPECT_EQ(*right, 1);
}

TEST(ThreadCrosserTest, WrapFunctionTypeCheckReferenceReturn) {
  using Type = std::unique_ptr<int>;
  const std::function<Type&(Type, Type&)> function(
  [](Type left, Type& right) -> Type& {
    *right = *left;
    return right;
  });
  const auto wrapped = ThreadCrosser::WrapFunction(function);
  Type left(new int(1)), right(new int(2));
  EXPECT_EQ(&wrapped(std::move(left), right), &right);
  EXPECT_FALSE(left);
  EXPECT_EQ(*right, 1);
}

TEST(ThreadCrosserTest, WrapFunctionTypeCheckVoidReturn) {
  using Type = std::unique_ptr<int>;
  const std::function<void(Type, Type&)> function([](Type left, Type& right) {
    *right = *left;
  });
  const auto wrapped = ThreadCrosser::WrapFunction(function);
  Type left(new int(1)), right(new int(2));
  wrapped(std::move(left), right);
  EXPECT_FALSE(left);
  EXPECT_EQ(*right, 1);
}

TEST(ThreadCrosserTest, WrapFunctionNotLazyWithValueReturn) {
  const std::function<int()> function([]() -> int {
    LogText::Log("logged 1");
    return 1;
  });
  LogTextMultiThread logger1;
  const auto wrapped = ThreadCrosser::WrapFunction(function);
  LogTextMultiThread logger2;
  EXPECT_EQ(wrapped(), 1);
  EXPECT_THAT(logger1.GetLines(), ElementsAre("logged 1"));
  EXPECT_THAT(logger2.GetLines(), ElementsAre());
}

TEST(ThreadCrosserTest, WrapFunctionNotLazyWithReferenceReturn) {
  int value = 0;
  const std::function<int&()> function([&value]() -> int& {
    LogText::Log("logged 1");
    return value;
  });
  LogTextMultiThread logger1;
  const auto wrapped = ThreadCrosser::WrapFunction(function);
  LogTextMultiThread logger2;
  EXPECT_EQ(&wrapped(), &value);
  EXPECT_THAT(logger1.GetLines(), ElementsAre("logged 1"));
  EXPECT_THAT(logger2.GetLines(), ElementsAre());
}

TEST(ThreadCrosserTest, WrapFunctionNotLazyWithVoidReturn) {
  const std::function<void()> function([]() {
    LogText::Log("logged 1");
  });
  LogTextMultiThread logger1;
  const auto wrapped = ThreadCrosser::WrapFunction(function);
  LogTextMultiThread logger2;
  wrapped();
  EXPECT_THAT(logger1.GetLines(), ElementsAre("logged 1"));
  EXPECT_THAT(logger2.GetLines(), ElementsAre());
}

TEST(ThreadCrosserTest, WrapFunctionWithNullCallbackIsNull) {
  std::function<int(int)> callback(nullptr);
  EXPECT_FALSE(ThreadCrosser::WrapFunction(callback));
  LogTextMultiThread logger;
  EXPECT_FALSE(ThreadCrosser::WrapFunction(callback));
}

TEST(ThreadCrosserTest, SingleThreadCrossing) {
  LogTextMultiThread logger;
  LogText::Log("logged 1");

  std::thread worker(ThreadCrosser::WrapCall([] { LogText::Log("logged 2"); }));
  worker.join();

  EXPECT_THAT(logger.GetLines(), ElementsAre("logged 1", "logged 2"));
}

TEST(ThreadCrosserTest, MultipleThreadCrossingWithMultipleLoggers) {
  LogTextMultiThread text_logger;
  LogText::Log("logged 1");
  LogValuesMultiThread count_logger;
  LogValues::Count(1);

  std::thread worker(ThreadCrosser::WrapCall([] {
    std::thread worker(ThreadCrosser::WrapCall([] {
      LogText::Log("logged 2");
      LogValues::Count(2);
    }));
    worker.join();
  }));
  worker.join();

  EXPECT_THAT(text_logger.GetLines(), ElementsAre("logged 1", "logged 2"));
  EXPECT_THAT(count_logger.GetCounts(), ElementsAre(1, 2));
}

TEST(ThreadCrosserTest, MultipleThreadCrossingWithDifferentLoggerScopes) {
  LogTextMultiThread text_logger;

  std::thread worker(ThreadCrosser::WrapCall([] {
    LogValuesMultiThread count_logger;
    std::thread worker(ThreadCrosser::WrapCall([] {
      LogText::Log("logged 1");
      LogValues::Count(1);
    }));
    worker.join();
    EXPECT_THAT(count_logger.GetCounts(), ElementsAre(1));
  }));
  worker.join();

  EXPECT_THAT(text_logger.GetLines(), ElementsAre("logged 1"));
}

TEST(ThreadCrosserTest, MultipleThreadCrossingWithOverride) {
  LogTextMultiThread logger1;

  std::thread worker(ThreadCrosser::WrapCall([] {
    LogTextMultiThread logger2;
    std::thread worker(
        ThreadCrosser::WrapCall([] { LogText::Log("logged 2"); }));
    worker.join();
    EXPECT_THAT(logger2.GetLines(), ElementsAre("logged 2"));
  }));
  worker.join();

  EXPECT_THAT(logger1.GetLines(), ElementsAre());
}

TEST(ThreadCrosserTest, DifferentLoggersInSameThread) {
  CallbackQueue queue;

  std::thread worker([&queue] {
    while (true) {
      LogTextMultiThread logger;
      if (!queue.PopAndCall()) {
        break;
      }
      LogText::Log("logged in thread");
      EXPECT_THAT(logger.GetLines(), ElementsAre("logged in thread"));
    }
  });

  LogTextMultiThread logger1;
  queue.Push(ThreadCrosser::WrapCall([] { LogText::Log("logged 1"); }));
  queue.WaitUntilEmpty();
  EXPECT_THAT(logger1.GetLines(), ElementsAre("logged 1"));

  {
    // It's important for the test case that logger2 overrides logger1, i.e.,
    // that they are both in scope at the same time.
    LogTextMultiThread logger2;
    queue.Push(ThreadCrosser::WrapCall([] { LogText::Log("logged 2"); }));
    queue.WaitUntilEmpty();
    EXPECT_THAT(logger2.GetLines(), ElementsAre("logged 2"));
  }

  queue.Push(ThreadCrosser::WrapCall([] { LogText::Log("logged 3"); }));
  queue.WaitUntilEmpty();
  EXPECT_THAT(logger1.GetLines(), ElementsAre("logged 1", "logged 3"));

  queue.Terminate();
  worker.join();
}

TEST(ThreadCrosserTest, ReverseOrderOfLoggersOnStack) {
  LogTextMultiThread logger1;
  const auto callback =
      ThreadCrosser::WrapCall([] { LogText::Log("logged 1"); });

  LogTextMultiThread logger2;
  const auto worker_call = ThreadCrosser::WrapCall([callback] {
    // In callback(), logger1 overrides logger2, whereas in the main thread
    // logger2 overrides logger1.
    callback();
    LogText::Log("logged 2");
  });

  LogTextMultiThread logger3;

  // Call using a thread.
  std::thread worker(worker_call);
  worker.join();

  EXPECT_THAT(logger1.GetLines(), ElementsAre("logged 1"));
  EXPECT_THAT(logger2.GetLines(), ElementsAre("logged 2"));
  EXPECT_THAT(logger3.GetLines(), ElementsAre());

  // Call in the main thread.
  worker_call();

  EXPECT_THAT(logger1.GetLines(), ElementsAre("logged 1", "logged 1"));
  EXPECT_THAT(logger2.GetLines(), ElementsAre("logged 2", "logged 2"));
  EXPECT_THAT(logger3.GetLines(), ElementsAre());
}

TEST(ThreadCrosserTest, ManualCrosserOverride) {
  LogTextMultiThread logger;
  const ThreadCrosser::SetOverride override_point;

  std::thread unwrapped_worker([&override_point] {
    override_point.Call([] { LogText::Log("logged 1"); });
  });
  unwrapped_worker.join();

  EXPECT_THAT(logger.GetLines(), ElementsAre("logged 1"));
}

TEST(ThreadCrosserTest, ManualOverrideIndependentOfNormalScope) {
  LogTextMultiThread text_logger1;
  const ThreadCrosser::SetOverride override_point;

  std::thread unwrapped_worker([&override_point] {
    LogTextMultiThread text_logger2;
    LogValuesMultiThread count_logger;
    // Overrides LogText but not LogValues.
    override_point.Call([] {
      LogText::Log("logged 1");
      LogValues::Count(1);
    });
    // Local scope supercedes override of LogText.
    override_point.Call(ThreadCrosser::WrapCall([] {
      LogText::Log("logged 2");
      LogValues::Count(2);
    }));
    EXPECT_THAT(text_logger2.GetLines(), ElementsAre("logged 2"));
    EXPECT_THAT(count_logger.GetCounts(), ElementsAre(1, 2));
  });
  unwrapped_worker.join();

  EXPECT_THAT(text_logger1.GetLines(), ElementsAre("logged 1"));
}

}  // namespace capture_thread

int main(int argc, char* argv[]) {
  testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}
<|endoftext|>


<commit_before>/*****************************************************************************
 *
 * This file is part of Mapnik (c++ mapping toolkit)
 *
 * Copyright (C) 2015 Artem Pavlenko
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *****************************************************************************/

#ifndef MAPNIK_RENDERER_COMMON_PROCESS_BUILDING_SYMBOLIZER_HPP
#define MAPNIK_RENDERER_COMMON_PROCESS_BUILDING_SYMBOLIZER_HPP

#include <mapnik/segment.hpp>
#include <mapnik/feature.hpp>
#include <mapnik/vertex_adapters.hpp>
#include <mapnik/path.hpp>

#include <algorithm>
#include <deque>

namespace mapnik {

namespace detail {

template <typename F1, typename F2, typename F3>
void make_building(geometry::polygon<double> const& poly, double height, F1 const& face_func, F2 const& frame_func, F3 const& roof_func)
{
    const std::unique_ptr<path_type> frame(new path_type(path_type::types::LineString));
    const std::unique_ptr<path_type> roof(new path_type(path_type::types::Polygon));
    std::deque<segment_t> face_segments;
    double x0 = 0;
    double y0 = 0;
    double x,y;
    geometry::polygon_vertex_adapter<double> va(poly);
    va.rewind(0);
    for (unsigned cm = va.vertex(&x, &y); cm != SEG_END;
         cm = va.vertex(&x, &y))
    {
        if (cm == SEG_MOVETO)
        {
            frame->move_to(x,y);
        }
        else if (cm == SEG_LINETO)
        {
            frame->line_to(x,y);
            face_segments.push_back(segment_t(x0,y0,x,y));
        }
        else if (cm == SEG_CLOSE)
        {
            frame->close_path();
        }
        x0 = x;
        y0 = y;
    }

    std::sort(face_segments.begin(),face_segments.end(), y_order);
    for (auto const& seg : face_segments)
    {
        const std::unique_ptr<path_type> faces(new path_type(path_type::types::Polygon));
        faces->move_to(std::get<0>(seg),std::get<1>(seg));
        faces->line_to(std::get<2>(seg),std::get<3>(seg));
        faces->line_to(std::get<2>(seg),std::get<3>(seg) + height);
        faces->line_to(std::get<0>(seg),std::get<1>(seg) + height);

        face_func(*faces);
        //
        frame->move_to(std::get<0>(seg),std::get<1>(seg));
        frame->line_to(std::get<0>(seg),std::get<1>(seg)+height);
    }

    va.rewind(0);
    for (unsigned cm = va.vertex(&x, &y); cm != SEG_END;
         cm = va.vertex(&x, &y))
    {
        if (cm == SEG_MOVETO)
        {
            frame->move_to(x,y+height);
            roof->move_to(x,y+height);
        }
        else if (cm == SEG_LINETO)
        {
            frame->line_to(x,y+height);
            roof->line_to(x,y+height);
        }
        else if (cm == SEG_CLOSE)
        {
            frame->close_path();
            roof->close_path();
        }
    }

    frame_func(*frame);
    roof_func(*roof);
}

} // ns detail

template <typename F1, typename F2, typename F3>
void render_building_symbolizer(mapnik::feature_impl const& feature,
                                double height,
                                F1 face_func, F2 frame_func, F3 roof_func)
{

    auto const& geom = feature.get_geometry();
    if (geom.is<geometry::polygon<double> >())
    {
        auto const& poly = geom.get<geometry::polygon<double> >();
        detail::make_building(poly, height, face_func, frame_func, roof_func);
    }
    else if (geom.is<geometry::multi_polygon<double> >())
    {
        auto const& multi_poly = geom.get<geometry::multi_polygon<double> >();
        for (auto const& poly : multi_poly)
        {
            detail::make_building(poly, height, face_func, frame_func, roof_func);
        }
    }
}

} // namespace mapnik

#endif // MAPNIK_RENDERER_COMMON_PROCESS_BUILDING_SYMBOLIZER_HPP
<commit_msg>fix closing segment<commit_after>/*****************************************************************************
 *
 * This file is part of Mapnik (c++ mapping toolkit)
 *
 * Copyright (C) 2015 Artem Pavlenko
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *****************************************************************************/

#ifndef MAPNIK_RENDERER_COMMON_PROCESS_BUILDING_SYMBOLIZER_HPP
#define MAPNIK_RENDERER_COMMON_PROCESS_BUILDING_SYMBOLIZER_HPP

#include <mapnik/segment.hpp>
#include <mapnik/feature.hpp>
#include <mapnik/vertex_adapters.hpp>
#include <mapnik/path.hpp>

#include <algorithm>
#include <deque>

namespace mapnik {

namespace detail {

template <typename F1, typename F2, typename F3>
void make_building(geometry::polygon<double> const& poly, double height, F1 const& face_func, F2 const& frame_func, F3 const& roof_func)
{
    const std::unique_ptr<path_type> frame(new path_type(path_type::types::LineString));
    const std::unique_ptr<path_type> roof(new path_type(path_type::types::Polygon));
    std::deque<segment_t> face_segments;
    double ring_begin_x, ring_begin_y;
    double x0 = 0;
    double y0 = 0;
    double x,y;
    geometry::polygon_vertex_adapter<double> va(poly);
    va.rewind(0);
    for (unsigned cm = va.vertex(&x, &y); cm != SEG_END;
         cm = va.vertex(&x, &y))
    {
        if (cm == SEG_MOVETO)
        {
            frame->move_to(x,y);
            ring_begin_x = x;
            ring_begin_y = y;
        }
        else if (cm == SEG_LINETO)
        {
            frame->line_to(x,y);
            face_segments.emplace_back(x0,y0,x,y);
        }
        else if (cm == SEG_CLOSE)
        {
            frame->close_path();
            if (!face_segments.empty())
            {
                face_segments.emplace_back(x0, y0, ring_begin_x, ring_begin_y);
            }
        }
        x0 = x;
        y0 = y;
    }

    std::sort(face_segments.begin(),face_segments.end(), y_order);
    for (auto const& seg : face_segments)
    {
        const std::unique_ptr<path_type> faces(new path_type(path_type::types::Polygon));
        faces->move_to(std::get<0>(seg),std::get<1>(seg));
        faces->line_to(std::get<2>(seg),std::get<3>(seg));
        faces->line_to(std::get<2>(seg),std::get<3>(seg) + height);
        faces->line_to(std::get<0>(seg),std::get<1>(seg) + height);

        face_func(*faces);
        //
        frame->move_to(std::get<0>(seg),std::get<1>(seg));
        frame->line_to(std::get<0>(seg),std::get<1>(seg)+height);
    }

    va.rewind(0);
    for (unsigned cm = va.vertex(&x, &y); cm != SEG_END;
         cm = va.vertex(&x, &y))
    {
        if (cm == SEG_MOVETO)
        {
            frame->move_to(x,y+height);
            roof->move_to(x,y+height);
        }
        else if (cm == SEG_LINETO)
        {
            frame->line_to(x,y+height);
            roof->line_to(x,y+height);
        }
        else if (cm == SEG_CLOSE)
        {
            frame->close_path();
            roof->close_path();
        }
    }

    frame_func(*frame);
    roof_func(*roof);
}

} // ns detail

template <typename F1, typename F2, typename F3>
void render_building_symbolizer(mapnik::feature_impl const& feature,
                                double height,
                                F1 face_func, F2 frame_func, F3 roof_func)
{

    auto const& geom = feature.get_geometry();
    if (geom.is<geometry::polygon<double> >())
    {
        auto const& poly = geom.get<geometry::polygon<double> >();
        detail::make_building(poly, height, face_func, frame_func, roof_func);
    }
    else if (geom.is<geometry::multi_polygon<double> >())
    {
        auto const& multi_poly = geom.get<geometry::multi_polygon<double> >();
        for (auto const& poly : multi_poly)
        {
            detail::make_building(poly, height, face_func, frame_func, roof_func);
        }
    }
}

} // namespace mapnik

#endif // MAPNIK_RENDERER_COMMON_PROCESS_BUILDING_SYMBOLIZER_HPP
<|endoftext|>


<commit_before>/******************************************************************************
 * This file is part of the Gluon Development Platform
 * Copyright (C) 2010 Guillaume Martres <smarter@ubuntu.com>
 * Copyright (c) 2010 Dan Leinir Turthra Jensen <admin@leinir.dk>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "soundemittercomponent.h"

#include <QtCore/QDebug>

#include <core/debughelper.h>
#include <audio/engine.h>
#include <audio/sound.h>
#include <engine/assets/audio/sound/soundasset.h>
#include <engine/gameobject.h>
#include <QMimeData>

REGISTER_OBJECTTYPE(GluonEngine, SoundEmitterComponent)

using namespace GluonEngine;

class SoundEmitterComponent::SoundEmitterComponentPrivate
{
    public:
        SoundEmitterComponentPrivate()
            : radius(10000.0f)
            , asset(0)
            , sound(0)
            , buffer(0)
            , loop(false)
            , autoPlay(false)
            {}

        Asset *asset;

        GluonAudio::Sound *sound;
        GluonAudio::Buffer *buffer;
        float radius;
        bool loop;
        bool autoPlay;
};

SoundEmitterComponent::SoundEmitterComponent(QObject *parent)
        : Component(parent),
        d(new SoundEmitterComponentPrivate)
{

}

SoundEmitterComponent::SoundEmitterComponent(const GluonEngine::SoundEmitterComponent &other)
        : Component(other),
        d(other.d)
{
}

SoundEmitterComponent::~SoundEmitterComponent()
{
    stop();

    delete d;
}

void
SoundEmitterComponent::play()
{
    d->sound->play();
    debug(QString("Playing sound: %1").arg(name()));
}

Asset *
SoundEmitterComponent::sound()
{
    return d->asset;
}

void
SoundEmitterComponent::setSound(Asset* asset)
{
    d->asset = asset;

    if(d->sound && asset->data()->hasFormat("application/gluon-audio-buffer"))
    {
        d->sound->stop();
        d->buffer->setBuffer(asset->data()->data("application/gluon-audio-buffer").toUInt(), true);
        d->sound->load(d->buffer);
    }
}

void
SoundEmitterComponent::initialize()
{
    if(!d->sound)
        d->sound = new GluonAudio::Sound();

    if(!d->buffer)
        d->buffer = new GluonAudio::Buffer();
}

void
SoundEmitterComponent::start()
{
    if(d->asset)
    {
        if(!d->asset->isLoaded())
            d->asset->load();

        if(d->asset->data()->hasFormat("application/gluon-audio-buffer"))
        {
            d->buffer->setBuffer(d->asset->data()->data("application/gluon-audio-buffer").toUInt(), true);
        }
    }

    d->sound->load(d->buffer);
    d->sound->setPosition(gameObject()->position());
    d->sound->setRadius(d->radius);

    if(d->loop)
    {
        d->sound->setLoop(true);
        d->sound->play();
    }
    if(d->autoPlay)
    {
        d->sound->play();
    }
}

void
SoundEmitterComponent::draw(int timeLapse)
{
    Q_UNUSED(timeLapse);
    d->sound->setPosition(gameObject()->position());
}

void
SoundEmitterComponent::stop()
{
    if(d->sound)
    {
        d->sound->stop();
    }
}

void
SoundEmitterComponent::cleanup()
{
    delete d->sound;

    d->sound = 0;
    d->buffer = 0;
}

float
SoundEmitterComponent::radius() const
{
    return d->radius;
}

void
SoundEmitterComponent::setRadius(float radius)
{
    if(d->sound)
        d->sound->setRadius(radius);
    d->radius = radius;
}

bool
SoundEmitterComponent::isLooping() const
{
    return d->loop;
}

bool SoundEmitterComponent::isPlaying() const
{
    if(d->sound)
    {
        return d->sound->isPlaying();
    }
    return false;
}

void
SoundEmitterComponent::setLoop(bool loop)
{
    d->loop = loop;

    if(d->sound)
    {
        d->sound->setLoop(loop);
        if(loop)
        {
            d->sound->play();
        }
        else if(d->sound->isPlaying())
        {
            d->sound->stop();
        }
    }
}

bool
SoundEmitterComponent::autoPlay() const
{
    return d->autoPlay;
}

void
SoundEmitterComponent::setAutoPlay(bool autoPlay)
{
    d->autoPlay = autoPlay;
}

Q_EXPORT_PLUGIN2(gluon_component_soundemitter, GluonEngine::SoundEmitterComponent)

#include "soundemittercomponent.moc"
<commit_msg>No need to be saying which sound you're playing ;)<commit_after>/******************************************************************************
 * This file is part of the Gluon Development Platform
 * Copyright (C) 2010 Guillaume Martres <smarter@ubuntu.com>
 * Copyright (c) 2010 Dan Leinir Turthra Jensen <admin@leinir.dk>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "soundemittercomponent.h"

#include <QtCore/QDebug>

#include <core/debughelper.h>
#include <audio/engine.h>
#include <audio/sound.h>
#include <engine/assets/audio/sound/soundasset.h>
#include <engine/gameobject.h>
#include <QMimeData>

REGISTER_OBJECTTYPE(GluonEngine, SoundEmitterComponent)

using namespace GluonEngine;

class SoundEmitterComponent::SoundEmitterComponentPrivate
{
    public:
        SoundEmitterComponentPrivate()
            : radius(10000.0f)
            , asset(0)
            , sound(0)
            , buffer(0)
            , loop(false)
            , autoPlay(false)
            {}

        Asset *asset;

        GluonAudio::Sound *sound;
        GluonAudio::Buffer *buffer;
        float radius;
        bool loop;
        bool autoPlay;
};

SoundEmitterComponent::SoundEmitterComponent(QObject *parent)
        : Component(parent),
        d(new SoundEmitterComponentPrivate)
{

}

SoundEmitterComponent::SoundEmitterComponent(const GluonEngine::SoundEmitterComponent &other)
        : Component(other),
        d(other.d)
{
}

SoundEmitterComponent::~SoundEmitterComponent()
{
    stop();

    delete d;
}

void
SoundEmitterComponent::play()
{
    d->sound->play();
}

Asset *
SoundEmitterComponent::sound()
{
    return d->asset;
}

void
SoundEmitterComponent::setSound(Asset* asset)
{
    d->asset = asset;

    if(d->sound && asset->data()->hasFormat("application/gluon-audio-buffer"))
    {
        d->sound->stop();
        d->buffer->setBuffer(asset->data()->data("application/gluon-audio-buffer").toUInt(), true);
        d->sound->load(d->buffer);
    }
}

void
SoundEmitterComponent::initialize()
{
    if(!d->sound)
        d->sound = new GluonAudio::Sound();

    if(!d->buffer)
        d->buffer = new GluonAudio::Buffer();
}

void
SoundEmitterComponent::start()
{
    if(d->asset)
    {
        if(!d->asset->isLoaded())
            d->asset->load();

        if(d->asset->data()->hasFormat("application/gluon-audio-buffer"))
        {
            d->buffer->setBuffer(d->asset->data()->data("application/gluon-audio-buffer").toUInt(), true);
        }
    }

    d->sound->load(d->buffer);
    d->sound->setPosition(gameObject()->position());
    d->sound->setRadius(d->radius);

    if(d->loop)
    {
        d->sound->setLoop(true);
        d->sound->play();
    }
    if(d->autoPlay)
    {
        d->sound->play();
    }
}

void
SoundEmitterComponent::draw(int timeLapse)
{
    Q_UNUSED(timeLapse);
    d->sound->setPosition(gameObject()->position());
}

void
SoundEmitterComponent::stop()
{
    if(d->sound)
    {
        d->sound->stop();
    }
}

void
SoundEmitterComponent::cleanup()
{
    delete d->sound;

    d->sound = 0;
    d->buffer = 0;
}

float
SoundEmitterComponent::radius() const
{
    return d->radius;
}

void
SoundEmitterComponent::setRadius(float radius)
{
    if(d->sound)
        d->sound->setRadius(radius);
    d->radius = radius;
}

bool
SoundEmitterComponent::isLooping() const
{
    return d->loop;
}

bool SoundEmitterComponent::isPlaying() const
{
    if(d->sound)
    {
        return d->sound->isPlaying();
    }
    return false;
}

void
SoundEmitterComponent::setLoop(bool loop)
{
    d->loop = loop;

    if(d->sound)
    {
        d->sound->setLoop(loop);
        if(loop)
        {
            d->sound->play();
        }
        else if(d->sound->isPlaying())
        {
            d->sound->stop();
        }
    }
}

bool
SoundEmitterComponent::autoPlay() const
{
    return d->autoPlay;
}

void
SoundEmitterComponent::setAutoPlay(bool autoPlay)
{
    d->autoPlay = autoPlay;
}

Q_EXPORT_PLUGIN2(gluon_component_soundemitter, GluonEngine::SoundEmitterComponent)

#include "soundemittercomponent.moc"
<|endoftext|>