<commit_before>#pragma once

#include "Communicator.hpp"
#include "PoolAllocator.hpp"
#include "FullEmpty.hpp"
#include "LegacyDelegate.hpp"
#include "GlobalCompletionEvent.hpp"
#include "ParallelLoop.hpp"
#include <type_traits>

namespace Grappa {

  namespace delegate {

    /// Do asynchronous generic delegate with `void` return type. Uses message pool to allocate
    /// the message. Enrolls with GCE so you can guarantee all have completed after a global
    /// GlobalCompletionEvent::wait() call.
    template<GlobalCompletionEvent * GCE = &Grappa::impl::local_gce, typename F = decltype(nullptr)>
    inline void call_async(MessagePoolBase& pool, Core dest, F remote_work) {
      static_assert(std::is_same< decltype(remote_work()), void >::value, "return type of callable must be void when not associated with Promise.");
      delegate_stats.count_op();
      Core origin = Grappa::mycore();

      if (dest == origin) {
        // short-circuit if local
        remote_work();
      } else {
        delegate_stats.count_op_am();
        if (GCE) GCE->enroll();

        pool.send_message(dest, [origin, remote_work] {
          remote_work();
          if (GCE) complete(make_global(GCE,origin));
        });
      }
    }

    /// A 'Promise' is a wrapper around a FullEmpty for async delegates with return values.
    /// The idea is to allocate storage for the result, issue the delegate request, and then
    /// block on waiting for the value when it's needed.
    ///
    /// Usage example: @code {
    ///   delegate::Promise<int> x;
    ///   x.call_async(1, []()->int { return value; });
    ///   // other work
    ///   myvalue += x.get();
    /// }
    ///
    /// TODO: make this so you you can issue a call_async() directly and get a
    /// delegate::Promise back. This should be able to be done using the same mechanism for
    /// creating Messages (compiler-optimized-away move).
    template<typename R>
    class Promise {

      FullEmpty<R> _result;

      int64_t start_time;
      int64_t network_time;

    public:
      Promise(): _result(), network_time(0), start_time(0) {}

      inline void fill(const R& r) {
        _result.writeXF(r);
      }

      /// Block on result being returned.
      inline const R& get() {
        // ... and wait for the result
        const R& r = _result.readFF();
        delegate_stats.record_wakeup_latency(start_time, network_time);
        return r;
      }

      template <typename F>
      void call_async(MessagePoolBase& pool, Core dest, F func) {
        static_assert(std::is_same<R, decltype(func())>::value, "return type of callable must match the type of this Promise");
        _result.reset();
        delegate_stats.count_op();
        Core origin = Grappa::mycore();

        if (dest == origin) {
          // short-circuit if local
          fill(func());
        } else {
          delegate_stats.count_op_am();
          start_time = Grappa_get_timestamp();

          pool.send_message(dest, [origin, func, this] {
            R val = func();

            // TODO: replace with handler-safe send_message
            send_heap_message(origin, [val, this] {
              this->network_time = Grappa_get_timestamp();
              delegate_stats.record_network_latency(this->start_time);
              this->fill(val);
            });
          }); // send message
        }
      }
    };
  } // namespace delegate

} // namespace Grappa
<commit_msg>Add `delegate::increment_async()` because it comes up fairly frequently.<commit_after>#pragma once

#include "Communicator.hpp"
#include "PoolAllocator.hpp"
#include "FullEmpty.hpp"
#include "LegacyDelegate.hpp"
#include "GlobalCompletionEvent.hpp"
#include "ParallelLoop.hpp"
#include <type_traits>

namespace Grappa {

  namespace delegate {

    /// Do asynchronous generic delegate with `void` return type. Uses message pool to allocate
    /// the message. Enrolls with GCE so you can guarantee all have completed after a global
    /// GlobalCompletionEvent::wait() call.
    template<GlobalCompletionEvent * GCE = &Grappa::impl::local_gce, typename F = decltype(nullptr)>
    inline void call_async(MessagePoolBase& pool, Core dest, F remote_work) {
      static_assert(std::is_same< decltype(remote_work()), void >::value, "return type of callable must be void when not associated with Promise.");
      delegate_stats.count_op();
      Core origin = Grappa::mycore();

      if (dest == origin) {
        // short-circuit if local
        remote_work();
      } else {
        delegate_stats.count_op_am();
        if (GCE) GCE->enroll();

        pool.send_message(dest, [origin, remote_work] {
          remote_work();
          if (GCE) complete(make_global(GCE,origin));
        });
      }
    }

    template<typename T, typename U, GlobalCompletionEvent * GCE = &Grappa::impl::local_gce >
    inline void increment_async(MessagePoolBase& pool, GlobalAddress<T> target, U increment) {
      delegate::call_async(target.core(), [increment]{
        (target.pointer()) += increment;
      });
    }

    /// A 'Promise' is a wrapper around a FullEmpty for async delegates with return values.
    /// The idea is to allocate storage for the result, issue the delegate request, and then
    /// block on waiting for the value when it's needed.
    ///
    /// Usage example: @code {
    ///   delegate::Promise<int> x;
    ///   x.call_async(1, []()->int { return value; });
    ///   // other work
    ///   myvalue += x.get();
    /// }
    ///
    /// TODO: make this so you you can issue a call_async() directly and get a
    /// delegate::Promise back. This should be able to be done using the same mechanism for
    /// creating Messages (compiler-optimized-away move).
    template<typename R>
    class Promise {

      FullEmpty<R> _result;

      int64_t start_time;
      int64_t network_time;

    public:
      Promise(): _result(), network_time(0), start_time(0) {}

      inline void fill(const R& r) {
        _result.writeXF(r);
      }

      /// Block on result being returned.
      inline const R& get() {
        // ... and wait for the result
        const R& r = _result.readFF();
        delegate_stats.record_wakeup_latency(start_time, network_time);
        return r;
      }

      template <typename F>
      void call_async(MessagePoolBase& pool, Core dest, F func) {
        static_assert(std::is_same<R, decltype(func())>::value, "return type of callable must match the type of this Promise");
        _result.reset();
        delegate_stats.count_op();
        Core origin = Grappa::mycore();

        if (dest == origin) {
          // short-circuit if local
          fill(func());
        } else {
          delegate_stats.count_op_am();
          start_time = Grappa_get_timestamp();

          pool.send_message(dest, [origin, func, this] {
            R val = func();

            // TODO: replace with handler-safe send_message
            send_heap_message(origin, [val, this] {
              this->network_time = Grappa_get_timestamp();
              delegate_stats.record_network_latency(this->start_time);
              this->fill(val);
            });
          }); // send message
        }
      }
    };
  } // namespace delegate

} // namespace Grappa
<|endoftext|>


<commit_before>#include <atomic>
#include <string>
#include <gtest/gtest.h>
#include <teelogging/teelogging.h>
#include "../shell.h"
#include "../coroutine.h"
#include "../scheduler.h"
#include "../semaphore.h"
#include "../channel.h"

class CoroTest : testing::Test { };

using namespace cu;

TEST(CoroTest, Test_find)
{
	cu::scheduler sch;

	LOGI("---------- begin find test --------");
	cu::channel<std::string> c1(sch, 20);
	c1.pipeline(	  find()
			, grep("*.h")
			, cat()
			, replace("class", "object")
			, log() );
	c1("../..");
	LOGI("---------- end find test --------");
}

TEST(CoroTest, Test_run_ls_strip_quote_grep)
{
	cu::scheduler sch;

	cu::channel<std::string> c1(sch, 100);
	c1.pipeline(
			  run()
			, strip()
			, quote()
			, grep("shell_*")
			, assert_count(1)
			, assert_string("\"shell_exe\"")
			, log()
	);
	c1("ls .");

	/////////////////////////////////

	cu::channel<std::string> c2(sch, 100);
	c2.pipeline(
			  ls()
			, strip()
			, quote()
			, grep("shell_*")
			, assert_count(1)
			, assert_string("\"shell_exe\"")
			, log()
	);
	c2(".");
}

TEST(CoroTest, Test_run_ls_sort_grep_uniq_join)
{
	cu::scheduler sch;

	cu::channel<std::string> c1(sch, 100);
	std::string out_subproces;
	c1.pipeline(run(), strip(), sort(), grep("*fes*"), uniq(), join(), log(), out(out_subproces));
	c1("ls .");
	//
	cu::channel<std::string> c2(sch, 100);
	std::string out_ls;
	c2.pipeline(ls(), sort(), grep("*fes*"), uniq(), join(), log(), out(out_ls));
	c2(".");
	//
	ASSERT_STREQ(out_subproces.c_str(), out_ls.c_str());
}

TEST(CoroTest, TestCut)
{
	cu::scheduler sch;

	cu::channel<std::string> c1(sch, 100);
	c1.pipeline(
			  assert_count(1)
			, split()
			, assert_count(3)
			, join()
			, assert_count(1)
			, cut(0)
			, assert_count(1)
			, assert_string("hello")
	);
	c1("hello big world");

	cu::channel<std::string> c2(sch, 100);
	c2.pipeline(
			  assert_count(1)
			, split()
			, assert_count(3)
			, join()
			, assert_count(1)
			, cut(1)
			, assert_count(1)
			, assert_string("big")
	);
	c2("hello big world");

	cu::channel<std::string> c3(sch, 100);
	c3.pipeline(
			  assert_count(1)
			, split()
			, assert_count(3)
			, join()
			, assert_count(1)
			, cut(2)
			, assert_count(1)
			, assert_string("world")
	);
	c3("hello big world");
}

TEST(CoroTest, TestGrep)
{
	cu::scheduler sch;

	cu::channel<std::string> c1(sch, 100);
	c1.pipeline(
			  split("\n")
			, assert_count(3)
			, grep("line2")
			, assert_count(1)
			, assert_string("line2")
	);
	c1("line1\nline2\nline3");
}

TEST(CoroTest, TestGrep2)
{
	cu::scheduler sch;

	cu::channel<std::string> c1(sch, 100);
	c1.pipeline(
			  split("\n")
			, assert_count(4)
	);
	c1("line1\nline2\nline3\n");
}

// TEST(CoroTest, TestCount)
// {
// 	cu::scheduler sch;
//
// 	cu::channel<std::string> c1(sch, 100);
// 	int result;
// 	c1.pipeline(
// 			  split("\n")
// 			, count()
// 			, out(result)
// 	);
// 	c1("line1\nline2\nline3");
// 	ASSERT_EQ(result, 3) << "maybe count() is not working well";
// }

TEST(CoroTest, TestUpper)
{
	cu::scheduler sch;
	cu::channel<std::string> c1(sch, 10);
	c1.pipeline( replace("mundo", "gente"), toupper() );
	sch.spawn([&](auto& yield) {
		for(int x=1; x<=1; ++x)
		{
			LOGI("sending hola mundo");
			c1(yield, "hola mundo");
		}
		c1.close(yield);
	});
	sch.spawn([&](auto& yield) {
		LOGI("begin");
		for(auto& r : cu::range(yield, c1))
		{
			LOGI("recv %s", r.c_str());
			// ASSERT_STREQ("HOLA GENTE", r.c_str());
		}
		LOGI("end");
	});
	sch.run_until_complete();
}

TEST(CoroTest, TestScheduler2)
{
	cu::scheduler sch;

	cu::channel<int> c1(sch, 10);
	cu::channel<int> c2(sch, 10);
	cu::channel<int> c3(sch, 10);

	c1.pipeline(	[]() -> cu::link<int>
		    	{
				return [](auto&& source, auto&& yield)
				{
					for (auto& s : source)
					{
						if(s)
							yield(*s - 256);
						else
							yield(s);
					}
				}
			}()
	);
	/*
	c2.pipeline(	cu::link<int>(
				[](auto&& source, auto&& yield)
				{
					for (auto& s : source)
					{
						if(s)
							yield(*s + 4096);
						else
							yield(s);
					}
				}
			)
	);

	c3.pipeline(	cu::link<int>(
				[](auto&& source, auto&& yield)
				{
					int total = 0;
					int count = 0;
					for (auto& s : source)
					{
						if(s)
						{
							total += *s;
							++count;
						}
						else
							yield(s);
					}
					yield( int(total / count) );
				}
			)
	);
	*/

	sch.spawn([&](auto& yield)
	{
		for(int x=1; x<=100; ++x)
		{
			LOGI("1. send %d", x);
			c1(yield, x);
		}
		c1.close(yield);
	});
	sch.spawn([&](auto& yield)
	{
		for(int y=1; y<=100; ++y)
		{
			LOGI("2. send %d", y);
			c2(yield, y);
		}
		c2.close(yield);
	});
	sch.spawn([&](auto& yield)
	{
		int a, b;
		for(auto& t : cu::range(yield, c1, c2))
		{
			std::tie(a, b) = t;
			LOGI("3. recv and resend %d", a+b);
			c3(yield, a + b);
		}
		c3.close(yield);
	});
	sch.spawn([&](auto& yield)
	{
		for(auto& r : cu::range(yield, c3))
		{
			LOGI("4. result = %d", r);
		}
	});
	sch.run_until_complete();
}
<commit_msg>Update test_shell.cpp<commit_after>#include <atomic>
#include <string>
#include <gtest/gtest.h>
#include <teelogging/teelogging.h>
#include "../shell.h"
#include "../coroutine.h"
#include "../scheduler.h"
#include "../semaphore.h"
#include "../channel.h"

class CoroTest : testing::Test { };

using namespace cu;

TEST(CoroTest, Test_find)
{
	cu::scheduler sch;

	LOGI("---------- begin find test --------");
	cu::channel<std::string> c1(sch, 20);
	c1.pipeline(	  find()
			, grep("*.h")
			, cat()
			, replace("class", "object")
			, log() );
	c1("../..");
	LOGI("---------- end find test --------");
}

TEST(CoroTest, Test_run_ls_strip_quote_grep)
{
	cu::scheduler sch;

	cu::channel<std::string> c1(sch, 100);
	c1.pipeline(
			  run()
			, strip()
			, quote()
			, grep("shell_*")
			, assert_count(1)
			, assert_string("\"shell_exe\"")
			, log()
	);
	c1("ls .");

	/////////////////////////////////

	cu::channel<std::string> c2(sch, 100);
	c2.pipeline(
			  ls()
			, strip()
			, quote()
			, grep("shell_*")
			, assert_count(1)
			, assert_string("\"shell_exe\"")
			, log()
	);
	c2(".");
}

TEST(CoroTest, Test_run_ls_sort_grep_uniq_join)
{
	cu::scheduler sch;

	cu::channel<std::string> c1(sch, 100);
	std::string out_subproces;
	c1.pipeline(run(), strip(), sort(), grep("*fes*"), uniq(), join(), log(), out(out_subproces));
	c1("ls .");
	//
	cu::channel<std::string> c2(sch, 100);
	std::string out_ls;
	c2.pipeline(ls(), sort(), grep("*fes*"), uniq(), join(), log(), out(out_ls));
	c2(".");
	//
	ASSERT_STREQ(out_subproces.c_str(), out_ls.c_str());
}

TEST(CoroTest, TestCut)
{
	cu::scheduler sch;

	cu::channel<std::string> c1(sch, 100);
	c1.pipeline(
			  assert_count(1)
			, split()
			, assert_count(3)
			, join()
			, assert_count(1)
			, cut(0)
			, assert_count(1)
			, assert_string("hello")
	);
	c1("hello big world");

	cu::channel<std::string> c2(sch, 100);
	c2.pipeline(
			  assert_count(1)
			, split()
			, assert_count(3)
			, join()
			, assert_count(1)
			, cut(1)
			, assert_count(1)
			, assert_string("big")
	);
	c2("hello big world");

	cu::channel<std::string> c3(sch, 100);
	c3.pipeline(
			  assert_count(1)
			, split()
			, assert_count(3)
			, join()
			, assert_count(1)
			, cut(2)
			, assert_count(1)
			, assert_string("world")
	);
	c3("hello big world");
}

TEST(CoroTest, TestGrep)
{
	cu::scheduler sch;

	cu::channel<std::string> c1(sch, 100);
	c1.pipeline(
			  split("\n")
			, assert_count(3)
			, grep("line2")
			, assert_count(1)
			, assert_string("line2")
	);
	c1("line1\nline2\nline3");
}

TEST(CoroTest, TestGrep2)
{
	cu::scheduler sch;

	cu::channel<std::string> c1(sch, 100);
	c1.pipeline(
			  split("\n")
			, assert_count(4)
	);
	c1("line1\nline2\nline3\n");
}

// TEST(CoroTest, TestCount)
// {
// 	cu::scheduler sch;
//
// 	cu::channel<std::string> c1(sch, 100);
// 	int result;
// 	c1.pipeline(
// 			  split("\n")
// 			, count()
// 			, out(result)
// 	);
// 	c1("line1\nline2\nline3");
// 	ASSERT_EQ(result, 3) << "maybe count() is not working well";
// }

TEST(CoroTest, TestUpper)
{
	cu::scheduler sch;
	cu::channel<std::string> c1(sch, 10);
	c1.pipeline( replace("mundo", "gente"), toupper() );
	sch.spawn([&](auto& yield) {
		for(int x=1; x<=1; ++x)
		{
			LOGI("sending hola mundo");
			c1(yield, "hola mundo");
		}
		c1.close(yield);
	});
	sch.spawn([&](auto& yield) {
		LOGI("begin");
		for(auto& r : cu::range(yield, c1))
		{
			LOGI("recv %s", r.c_str());
			// ASSERT_STREQ("HOLA GENTE", r.c_str());
		}
		LOGI("end");
	});
	sch.run_until_complete();
}

TEST(CoroTest, TestScheduler2)
{
	cu::scheduler sch;

	cu::channel<int> c1(sch, 10);
	cu::channel<int> c2(sch, 10);
	cu::channel<int> c3(sch, 10);

	c1.pipeline(	[]() -> cu::link<int>
		    	{
				return [](auto&& source, auto&& yield)
				{
					for (auto& s : source)
					{
						if(s)
							yield(*s - 256);
						else
							yield(s);
					}
				};
			}()
	);
	c2.pipeline(	[]() -> cu::link<int>
		    	{
				return [](auto&& source, auto&& yield)
				{
					for (auto& s : source)
					{
						if(s)
							yield(*s + 4096);
						else
							yield(s);
					}
				};
			}()
	);
	c3.pipeline(	[]() -> cu::link<int>
		    	{
				return [](auto&& source, auto&& yield)
				{
					int total = 0;
					int count = 0;
					for (auto& s : source)
					{
						if(s)
						{
							total += *s;
							++count;
						}
						else
							yield(s);
					}
					yield( int(total / count) );
				};
			}()
	);

	sch.spawn([&](auto& yield)
	{
		for(int x=1; x<=100; ++x)
		{
			LOGI("1. send %d", x);
			c1(yield, x);
		}
		c1.close(yield);
	});
	sch.spawn([&](auto& yield)
	{
		for(int y=1; y<=100; ++y)
		{
			LOGI("2. send %d", y);
			c2(yield, y);
		}
		c2.close(yield);
	});
	sch.spawn([&](auto& yield)
	{
		int a, b;
		for(auto& t : cu::range(yield, c1, c2))
		{
			std::tie(a, b) = t;
			LOGI("3. recv and resend %d", a+b);
			c3(yield, a + b);
		}
		c3.close(yield);
	});
	sch.spawn([&](auto& yield)
	{
		for(auto& r : cu::range(yield, c3))
		{
			LOGI("4. result = %d", r);
		}
	});
	sch.run_until_complete();
}
<|endoftext|>


<commit_before>#include "utest.h"
#include "math/random.h"
#include "math/numeric.h"
#include "math/epsilon.h"
#include "solver_stoch.h"
#include "functions/test.h"

using namespace nano;

static void check_function(const function_t& function)
{
        const auto epochs = size_t(100);
        const auto tune_epochs = size_t(4);
        const auto epoch_size = size_t(32);
        const auto trials = size_t(10);

        const auto dims = function.size();

        auto rgen = make_rng(scalar_t(-1), scalar_t(+1));

        // generate fixed random trials
        std::vector<vector_t> x0s(trials);
        for (auto& x0 : x0s)
        {
                x0.resize(dims);
                rgen(x0.data(), x0.data() + x0.size());
        }

        // solvers to try
        for (const auto& id : get_stoch_solvers().ids())
        {
                const auto solver = get_stoch_solvers().get(id);
                NANO_REQUIRE(solver);

                size_t out_of_domain = 0;
                for (size_t t = 0; t < trials; ++ t)
                {
                        const auto& x0 = x0s[t];
                        const auto f0 = function.eval(x0);
                        const auto g_thres = epsilon3<scalar_t>();

                        // optimize
                        const auto params = stoch_params_t(epochs, epoch_size, epsilon3<scalar_t>());
                        const auto tune_params = stoch_params_t(tune_epochs, epoch_size, epsilon3<scalar_t>());

                        const auto tstate = solver->tune(tune_params, function, x0);
                        const auto state = solver->minimize(params, function, tstate.x);

                        const auto x = state.x;
                        const auto f = state.f;
                        const auto g = state.convergence_criteria();

                        // ignore out-of-domain solutions
                        if (!function.is_valid(x))
                        {
                                out_of_domain ++;
                                continue;
                        }

                        std::cout << function.name() << ", " << id
                                  << " [" << (t + 1) << "/" << trials << "]"
                                  << ": x = [" << x0.transpose() << "]/[" << x.transpose() << "]"
                                  << ", f = " << f0 << "/" << f
                                  << ", g = " << g << ".\n";

                        // check function value decrease
                        NANO_CHECK_LESS_EQUAL(f, f0);

                        // check convergence
                        NANO_CHECK_LESS_EQUAL(g, g_thres);
                }

                std::cout << function.name() << ", " << id
                          << ": out of domain " << out_of_domain << "/" << trials << ".\n";
        }
}

NANO_BEGIN_MODULE(test_stoch_solvers)

NANO_CASE(evaluate)
{
        foreach_test_function(make_convex_functions(1, 1), check_function);
}

NANO_END_MODULE()

<commit_msg>more robust unit test for stochastic solvers<commit_after>#include "utest.h"
#include "math/random.h"
#include "math/numeric.h"
#include "math/epsilon.h"
#include "solver_stoch.h"
#include "functions/test.h"

using namespace nano;

static void check_function(const function_t& function)
{
        const auto epochs = size_t(32);
        const auto tune_epochs = size_t(1);
        const auto epoch_size = size_t(128);
        const auto trials = size_t(10);

        const auto dims = function.size();

        auto rgen = make_rng(scalar_t(-1), scalar_t(+1));

        // generate fixed random trials
        std::vector<vector_t> x0s(trials);
        for (auto& x0 : x0s)
        {
                x0.resize(dims);
                rgen(x0.data(), x0.data() + x0.size());
        }

        // solvers to try
        for (const auto& id : get_stoch_solvers().ids())
        {
                const auto solver = get_stoch_solvers().get(id);
                NANO_REQUIRE(solver);

                size_t out_of_domain = 0;
                for (size_t t = 0; t < trials; ++ t)
                {
                        const auto& x0 = x0s[t];
                        const auto f0 = function.eval(x0);
                        const auto g_thres = epsilon3<scalar_t>();

                        // optimize
                        const auto params = stoch_params_t(epochs, epoch_size, epsilon3<scalar_t>());
                        const auto tune_params = stoch_params_t(tune_epochs, epoch_size, epsilon3<scalar_t>());

                        const auto tstate = solver->tune(tune_params, function, x0);
                        const auto state = solver->minimize(params, function, tstate.x);

                        const auto x = state.x;
                        const auto f = state.f;
                        const auto g = state.convergence_criteria();

                        // ignore out-of-domain solutions
                        if (!function.is_valid(x))
                        {
                                out_of_domain ++;
                                continue;
                        }

                        std::cout << function.name() << ", " << id
                                  << " [" << (t + 1) << "/" << trials << "]"
                                  << ": x = [" << x0.transpose() << "]/[" << x.transpose() << "]"
                                  << ", f = " << f0 << "/" << f
                                  << ", g = " << g << ".\n";

                        // check function value decrease
                        NANO_CHECK_LESS_EQUAL(f, f0);

                        // check convergence
                        NANO_CHECK_LESS_EQUAL(g, g_thres);
                }

                std::cout << function.name() << ", " << id
                          << ": out of domain " << out_of_domain << "/" << trials << ".\n";
        }
}

NANO_BEGIN_MODULE(test_stoch_solvers)

NANO_CASE(evaluate)
{
        foreach_test_function(make_convex_functions(1, 1), check_function);
}

NANO_END_MODULE()

<|endoftext|>


<commit_before>/*********************************************************************/
/* Copyright (c) 2013, EPFL/Blue Brain Project                       */
/*                     Daniel Nachbaur <daniel.nachbaur@epfl.ch>     */
/* All rights reserved.                                              */
/*                                                                   */
/* Redistribution and use in source and binary forms, with or        */
/* without modification, are permitted provided that the following   */
/* conditions are met:                                               */
/*                                                                   */
/*   1. Redistributions of source code must retain the above         */
/*      copyright notice, this list of conditions and the following  */
/*      disclaimer.                                                  */
/*                                                                   */
/*   2. 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.                              */
/*                                                                   */
/*    THIS  SOFTWARE IS PROVIDED  BY THE  UNIVERSITY OF  TEXAS AT    */
/*    AUSTIN  ``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 UNIVERSITY  OF TEXAS AT    */
/*    AUSTIN 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.                                    */
/*                                                                   */
/* The views and conclusions contained in the software and           */
/* documentation are those of the authors and should not be          */
/* interpreted as representing official policies, either expressed   */
/* or implied, of The University of Texas at Austin.                 */
/*********************************************************************/

#define BOOST_TEST_MODULE WebBrowser
#include <boost/test/unit_test.hpp>
namespace ut = boost::unit_test;

#include "globals.h"
#include "Options.h"
#include "configuration/Configuration.h"
#include "WebkitPixelStreamer.h"

#include <QApplication>
#include <QWebElementCollection>
#include <QWebFrame>
#include <QWebPage>
#include <QWebView>

#include <X11/Xlib.h>

#define TEST_PAGE_URL         "webgl_interaction.html"
#define CONFIG_TEST_FILENAME  "configuration.xml"

namespace ut = boost::unit_test;

bool hasGLXDisplay()
{
    Display* display = XOpenDisplay( 0 );
    if( !display )
        return false;
    int major, event, error;
    const bool hasGLX = XQueryExtension( display, "GLX", &major, &event,
                                         &error );
    XCloseDisplay( display );
    return hasGLX;
}

// We need a global fixture because a bug in QApplication prevents
// deleting then recreating a QApplication in the same process.
// https://bugreports.qt-project.org/browse/QTBUG-7104
struct GlobalQtApp
{
    GlobalQtApp()
    {
        // need QApplication to instantiate WebkitPixelStreamer
        ut::master_test_suite_t& testSuite = ut::framework::master_test_suite();
        app = new QApplication( testSuite.argc, testSuite.argv );

        // To test wheel events the WebkitPixelStreamer needs access to the g_configuration element
        OptionsPtr options(new Options());
        g_configuration = new Configuration(CONFIG_TEST_FILENAME, options);
    }
    ~GlobalQtApp()
    {
        delete g_configuration;
        delete app;
    }

    QApplication* app;
};

BOOST_GLOBAL_FIXTURE( GlobalQtApp );

BOOST_AUTO_TEST_CASE( test_webgl_support )
{
    if( !hasGLXDisplay( ))
        return;

    // load the webgl website, exec() returns when loading is finished
    WebkitPixelStreamer* streamer = new WebkitPixelStreamer( "testBrowser" );
    QObject::connect( streamer->getView(), SIGNAL(loadFinished(bool)),
                      QApplication::instance(), SLOT(quit()));
    streamer->setUrl( TEST_PAGE_URL );
    QApplication::instance()->exec();

    QWebPage* page = streamer->getView()->page();
    BOOST_REQUIRE( page );
    QWebFrame* frame = page->mainFrame();
    BOOST_REQUIRE( frame );
    QWebElementCollection webglCanvases = frame->findAllElements( "canvas[id=webgl-canvas]" );
    BOOST_REQUIRE_EQUAL( webglCanvases.count(), 1 );

    // http://stackoverflow.com/questions/11871077/proper-way-to-detect-webgl-support
    QVariant hasContext = frame->evaluateJavaScript("var hasContext = false;"
                                                    "if( window.WebGLRenderingContext ) {"
                                                    "  hasContext = true;"
                                                    "}");

    QVariant hasGL = frame->evaluateJavaScript("var hasGL = false;"
                                               "gl = canvas.getContext(\"webgl\");"
                                               "if( gl ) {"
                                               "  hasGL = true;"
                                               "}");

    QVariant hasExperimentalGL = frame->evaluateJavaScript("var hasGL = false;"
                                                           "gl = canvas.getContext(\"experimental-webgl\");"
                                                           "if( gl ) {"
                                                           "  hasGL = true;"
                                                           "}");

    BOOST_CHECK( hasContext.toBool( ));
    BOOST_CHECK( hasGL.toBool() || hasExperimentalGL.toBool( ));

    delete streamer;
}

BOOST_AUTO_TEST_CASE( test_webgl_interaction )
{
    if( !hasGLXDisplay( ))
        return;

    // load the webgl website, exec() returns when loading is finished
    WebkitPixelStreamer* streamer = new WebkitPixelStreamer( "testBrowser" );
    QObject::connect( streamer->getView(), SIGNAL(loadFinished(bool)),
                      QApplication::instance(), SLOT(quit()));
    streamer->setUrl( TEST_PAGE_URL );
    QApplication::instance()->exec();

    QWebPage* page = streamer->getView()->page();
    BOOST_REQUIRE( page );
    QWebFrame* frame = page->mainFrame();
    BOOST_REQUIRE( frame );

    // Normalized mouse coordinates
    InteractionState pressState;
    pressState.mouseX = 0.1;
    pressState.mouseY = 0.1;
    pressState.mouseLeft = true;
    pressState.type = InteractionState::EVT_PRESS;

    InteractionState moveState;
    moveState.mouseX = 0.2;
    moveState.mouseY = 0.2;
    moveState.mouseLeft = true;
    moveState.type = InteractionState::EVT_MOVE;

    InteractionState releaseState;
    releaseState.mouseX = 0.2;
    releaseState.mouseY = 0.2;
    releaseState.mouseLeft = true;
    releaseState.type = InteractionState::EVT_RELEASE;

    streamer->updateInteractionState(pressState);
    streamer->updateInteractionState(moveState);
    streamer->updateInteractionState(releaseState);

    const int expectedDisplacementX = (releaseState.mouseX-pressState.mouseX) *
                                streamer->size().width() / streamer->getView()->zoomFactor();
    const int expectedDisplacementY = (releaseState.mouseY-pressState.mouseY) *
                                streamer->size().height() / streamer->getView()->zoomFactor();

    QString jsX = QString("deltaX == %1;").arg(expectedDisplacementX);
    QString jsY = QString("deltaY == %1;").arg(expectedDisplacementY);

    QVariant validDeltaX = frame->evaluateJavaScript(jsX);
    QVariant validDeltaY = frame->evaluateJavaScript(jsY);

    BOOST_CHECK( validDeltaX.toBool());
    BOOST_CHECK( validDeltaY.toBool());

    delete streamer;
}

BOOST_AUTO_TEST_CASE( test_webgl_click )
{
    if( !hasGLXDisplay( ))
        return;

    // load the webgl website, exec() returns when loading is finished
    WebkitPixelStreamer* streamer = new WebkitPixelStreamer( "testBrowser" );
    QObject::connect( streamer->getView(), SIGNAL(loadFinished(bool)),
                      QApplication::instance(), SLOT(quit()));
    streamer->setUrl( TEST_PAGE_URL );
    QApplication::instance()->exec();

    QWebPage* page = streamer->getView()->page();
    BOOST_REQUIRE( page );
    QWebFrame* frame = page->mainFrame();
    BOOST_REQUIRE( frame );

    // Normalized mouse coordinates
    InteractionState clickState;
    clickState.mouseX = 0.1;
    clickState.mouseY = 0.1;
    clickState.mouseLeft = true;
    clickState.type = InteractionState::EVT_CLICK;

    streamer->updateInteractionState(clickState);

    const int expectedPosX = clickState.mouseX * streamer->size().width() /
                             streamer->getView()->zoomFactor();
    const int expectedPosY = clickState.mouseY * streamer->size().height() /
                             streamer->getView()->zoomFactor();

    QString jsX = QString("lastMouseX == %1;").arg(expectedPosX);
    QString jsY = QString("lastMouseY == %1;").arg(expectedPosY);

    BOOST_CHECK( frame->evaluateJavaScript(jsX).toBool());
    BOOST_CHECK( frame->evaluateJavaScript(jsY).toBool());

    delete streamer;
}

BOOST_AUTO_TEST_CASE( test_webgl_wheel )
{
    if( !hasGLXDisplay( ))
        return;

    // load the webgl website, exec() returns when loading is finished
    WebkitPixelStreamer* streamer = new WebkitPixelStreamer( "testBrowser" );
    QObject::connect( streamer->getView(), SIGNAL(loadFinished(bool)),
                      QApplication::instance(), SLOT(quit()));
    streamer->setUrl( TEST_PAGE_URL );
    QApplication::instance()->exec();

    QWebPage* page = streamer->getView()->page();
    BOOST_REQUIRE( page );
    QWebFrame* frame = page->mainFrame();
    BOOST_REQUIRE( frame );

    // Normalized mouse coordinates
    InteractionState wheelState;
    wheelState.mouseX = 0.1;
    wheelState.mouseY = 0.1;
    wheelState.dy = 0.05;
    wheelState.type = InteractionState::EVT_WHEEL;

    streamer->updateInteractionState(wheelState);

    const int expectedPosX = wheelState.mouseX * streamer->size().width() /
                             streamer->getView()->zoomFactor();
    const int expectedPosY = wheelState.mouseY * streamer->size().height() /
                             streamer->getView()->zoomFactor();
    const int expectedWheelDelta = wheelState.dy * g_configuration->getTotalHeight();

    QString jsX = QString("lastMouseX == %1;").arg(expectedPosX);
    QString jsY = QString("lastMouseY == %1;").arg(expectedPosY);
    QString jsD = QString("wheelDelta == %1;").arg(expectedWheelDelta);

    BOOST_CHECK( frame->evaluateJavaScript(jsX).toBool());
    BOOST_CHECK( frame->evaluateJavaScript(jsY).toBool());
    BOOST_CHECK( frame->evaluateJavaScript(jsD).toBool());

    delete streamer;
}
<commit_msg>More fixes for headless unit tests<commit_after>/*********************************************************************/
/* Copyright (c) 2013, EPFL/Blue Brain Project                       */
/*                     Daniel Nachbaur <daniel.nachbaur@epfl.ch>     */
/* All rights reserved.                                              */
/*                                                                   */
/* Redistribution and use in source and binary forms, with or        */
/* without modification, are permitted provided that the following   */
/* conditions are met:                                               */
/*                                                                   */
/*   1. Redistributions of source code must retain the above         */
/*      copyright notice, this list of conditions and the following  */
/*      disclaimer.                                                  */
/*                                                                   */
/*   2. 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.                              */
/*                                                                   */
/*    THIS  SOFTWARE IS PROVIDED  BY THE  UNIVERSITY OF  TEXAS AT    */
/*    AUSTIN  ``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 UNIVERSITY  OF TEXAS AT    */
/*    AUSTIN 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.                                    */
/*                                                                   */
/* The views and conclusions contained in the software and           */
/* documentation are those of the authors and should not be          */
/* interpreted as representing official policies, either expressed   */
/* or implied, of The University of Texas at Austin.                 */
/*********************************************************************/

#define BOOST_TEST_MODULE WebBrowser
#include <boost/test/unit_test.hpp>
namespace ut = boost::unit_test;

#include "globals.h"
#include "Options.h"
#include "configuration/Configuration.h"
#include "WebkitPixelStreamer.h"

#include <QApplication>
#include <QWebElementCollection>
#include <QWebFrame>
#include <QWebPage>
#include <QWebView>

#include <X11/Xlib.h>

#define TEST_PAGE_URL         "webgl_interaction.html"
#define CONFIG_TEST_FILENAME  "configuration.xml"

namespace ut = boost::unit_test;

bool hasGLXDisplay()
{
    Display* display = XOpenDisplay( 0 );
    if( !display )
        return false;
    int major, event, error;
    const bool hasGLX = XQueryExtension( display, "GLX", &major, &event,
                                         &error );
    XCloseDisplay( display );
    return hasGLX;
}

// We need a global fixture because a bug in QApplication prevents
// deleting then recreating a QApplication in the same process.
// https://bugreports.qt-project.org/browse/QTBUG-7104
struct GlobalQtApp
{
    GlobalQtApp()
        : app( 0 )
    {
        if( !hasGLXDisplay( ))
          return;

        // need QApplication to instantiate WebkitPixelStreamer
        ut::master_test_suite_t& testSuite = ut::framework::master_test_suite();
        app = new QApplication( testSuite.argc, testSuite.argv );

        // To test wheel events the WebkitPixelStreamer needs access to the g_configuration element
        OptionsPtr options(new Options());
        g_configuration = new Configuration(CONFIG_TEST_FILENAME, options);
    }
    ~GlobalQtApp()
    {
        delete g_configuration;
        delete app;
    }

    QApplication* app;
};

BOOST_GLOBAL_FIXTURE( GlobalQtApp );

BOOST_AUTO_TEST_CASE( test_webgl_support )
{
    if( !hasGLXDisplay( ))
        return;

    // load the webgl website, exec() returns when loading is finished
    WebkitPixelStreamer* streamer = new WebkitPixelStreamer( "testBrowser" );
    QObject::connect( streamer->getView(), SIGNAL(loadFinished(bool)),
                      QApplication::instance(), SLOT(quit()));
    streamer->setUrl( TEST_PAGE_URL );
    QApplication::instance()->exec();

    QWebPage* page = streamer->getView()->page();
    BOOST_REQUIRE( page );
    QWebFrame* frame = page->mainFrame();
    BOOST_REQUIRE( frame );
    QWebElementCollection webglCanvases = frame->findAllElements( "canvas[id=webgl-canvas]" );
    BOOST_REQUIRE_EQUAL( webglCanvases.count(), 1 );

    // http://stackoverflow.com/questions/11871077/proper-way-to-detect-webgl-support
    QVariant hasContext = frame->evaluateJavaScript("var hasContext = false;"
                                                    "if( window.WebGLRenderingContext ) {"
                                                    "  hasContext = true;"
                                                    "}");

    QVariant hasGL = frame->evaluateJavaScript("var hasGL = false;"
                                               "gl = canvas.getContext(\"webgl\");"
                                               "if( gl ) {"
                                               "  hasGL = true;"
                                               "}");

    QVariant hasExperimentalGL = frame->evaluateJavaScript("var hasGL = false;"
                                                           "gl = canvas.getContext(\"experimental-webgl\");"
                                                           "if( gl ) {"
                                                           "  hasGL = true;"
                                                           "}");

    BOOST_CHECK( hasContext.toBool( ));
    BOOST_CHECK( hasGL.toBool() || hasExperimentalGL.toBool( ));

    delete streamer;
}

BOOST_AUTO_TEST_CASE( test_webgl_interaction )
{
    if( !hasGLXDisplay( ))
        return;

    // load the webgl website, exec() returns when loading is finished
    WebkitPixelStreamer* streamer = new WebkitPixelStreamer( "testBrowser" );
    QObject::connect( streamer->getView(), SIGNAL(loadFinished(bool)),
                      QApplication::instance(), SLOT(quit()));
    streamer->setUrl( TEST_PAGE_URL );
    QApplication::instance()->exec();

    QWebPage* page = streamer->getView()->page();
    BOOST_REQUIRE( page );
    QWebFrame* frame = page->mainFrame();
    BOOST_REQUIRE( frame );

    // Normalized mouse coordinates
    InteractionState pressState;
    pressState.mouseX = 0.1;
    pressState.mouseY = 0.1;
    pressState.mouseLeft = true;
    pressState.type = InteractionState::EVT_PRESS;

    InteractionState moveState;
    moveState.mouseX = 0.2;
    moveState.mouseY = 0.2;
    moveState.mouseLeft = true;
    moveState.type = InteractionState::EVT_MOVE;

    InteractionState releaseState;
    releaseState.mouseX = 0.2;
    releaseState.mouseY = 0.2;
    releaseState.mouseLeft = true;
    releaseState.type = InteractionState::EVT_RELEASE;

    streamer->updateInteractionState(pressState);
    streamer->updateInteractionState(moveState);
    streamer->updateInteractionState(releaseState);

    const int expectedDisplacementX = (releaseState.mouseX-pressState.mouseX) *
                                streamer->size().width() / streamer->getView()->zoomFactor();
    const int expectedDisplacementY = (releaseState.mouseY-pressState.mouseY) *
                                streamer->size().height() / streamer->getView()->zoomFactor();

    QString jsX = QString("deltaX == %1;").arg(expectedDisplacementX);
    QString jsY = QString("deltaY == %1;").arg(expectedDisplacementY);

    QVariant validDeltaX = frame->evaluateJavaScript(jsX);
    QVariant validDeltaY = frame->evaluateJavaScript(jsY);

    BOOST_CHECK( validDeltaX.toBool());
    BOOST_CHECK( validDeltaY.toBool());

    delete streamer;
}

BOOST_AUTO_TEST_CASE( test_webgl_click )
{
    if( !hasGLXDisplay( ))
        return;

    // load the webgl website, exec() returns when loading is finished
    WebkitPixelStreamer* streamer = new WebkitPixelStreamer( "testBrowser" );
    QObject::connect( streamer->getView(), SIGNAL(loadFinished(bool)),
                      QApplication::instance(), SLOT(quit()));
    streamer->setUrl( TEST_PAGE_URL );
    QApplication::instance()->exec();

    QWebPage* page = streamer->getView()->page();
    BOOST_REQUIRE( page );
    QWebFrame* frame = page->mainFrame();
    BOOST_REQUIRE( frame );

    // Normalized mouse coordinates
    InteractionState clickState;
    clickState.mouseX = 0.1;
    clickState.mouseY = 0.1;
    clickState.mouseLeft = true;
    clickState.type = InteractionState::EVT_CLICK;

    streamer->updateInteractionState(clickState);

    const int expectedPosX = clickState.mouseX * streamer->size().width() /
                             streamer->getView()->zoomFactor();
    const int expectedPosY = clickState.mouseY * streamer->size().height() /
                             streamer->getView()->zoomFactor();

    QString jsX = QString("lastMouseX == %1;").arg(expectedPosX);
    QString jsY = QString("lastMouseY == %1;").arg(expectedPosY);

    BOOST_CHECK( frame->evaluateJavaScript(jsX).toBool());
    BOOST_CHECK( frame->evaluateJavaScript(jsY).toBool());

    delete streamer;
}

BOOST_AUTO_TEST_CASE( test_webgl_wheel )
{
    if( !hasGLXDisplay( ))
        return;

    // load the webgl website, exec() returns when loading is finished
    WebkitPixelStreamer* streamer = new WebkitPixelStreamer( "testBrowser" );
    QObject::connect( streamer->getView(), SIGNAL(loadFinished(bool)),
                      QApplication::instance(), SLOT(quit()));
    streamer->setUrl( TEST_PAGE_URL );
    QApplication::instance()->exec();

    QWebPage* page = streamer->getView()->page();
    BOOST_REQUIRE( page );
    QWebFrame* frame = page->mainFrame();
    BOOST_REQUIRE( frame );

    // Normalized mouse coordinates
    InteractionState wheelState;
    wheelState.mouseX = 0.1;
    wheelState.mouseY = 0.1;
    wheelState.dy = 0.05;
    wheelState.type = InteractionState::EVT_WHEEL;

    streamer->updateInteractionState(wheelState);

    const int expectedPosX = wheelState.mouseX * streamer->size().width() /
                             streamer->getView()->zoomFactor();
    const int expectedPosY = wheelState.mouseY * streamer->size().height() /
                             streamer->getView()->zoomFactor();
    const int expectedWheelDelta = wheelState.dy * g_configuration->getTotalHeight();

    QString jsX = QString("lastMouseX == %1;").arg(expectedPosX);
    QString jsY = QString("lastMouseY == %1;").arg(expectedPosY);
    QString jsD = QString("wheelDelta == %1;").arg(expectedWheelDelta);

    BOOST_CHECK( frame->evaluateJavaScript(jsX).toBool());
    BOOST_CHECK( frame->evaluateJavaScript(jsY).toBool());
    BOOST_CHECK( frame->evaluateJavaScript(jsD).toBool());

    delete streamer;
}
<|endoftext|>


<commit_before>/*********************************************************************/
/* Copyright (c) 2013, EPFL/Blue Brain Project                       */
/*                     Daniel Nachbaur <daniel.nachbaur@epfl.ch>     */
/* All rights reserved.                                              */
/*                                                                   */
/* Redistribution and use in source and binary forms, with or        */
/* without modification, are permitted provided that the following   */
/* conditions are met:                                               */
/*                                                                   */
/*   1. Redistributions of source code must retain the above         */
/*      copyright notice, this list of conditions and the following  */
/*      disclaimer.                                                  */
/*                                                                   */
/*   2. 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.                              */
/*                                                                   */
/*    THIS  SOFTWARE IS PROVIDED  BY THE  UNIVERSITY OF  TEXAS AT    */
/*    AUSTIN  ``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 UNIVERSITY  OF TEXAS AT    */
/*    AUSTIN 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.                                    */
/*                                                                   */
/* The views and conclusions contained in the software and           */
/* documentation are those of the authors and should not be          */
/* interpreted as representing official policies, either expressed   */
/* or implied, of The University of Texas at Austin.                 */
/*********************************************************************/

#define BOOST_TEST_MODULE WebBrowser
#include <boost/test/unit_test.hpp>
namespace ut = boost::unit_test;

#include "globals.h"
#include "Options.h"
#include "configuration/Configuration.h"
#include "WebkitPixelStreamer.h"

#include <QApplication>
#include <QWebElementCollection>
#include <QWebFrame>
#include <QWebPage>
#include <QWebView>

#include <X11/Xlib.h>

#define TEST_PAGE_URL         "webgl_interaction.html"
#define CONFIG_TEST_FILENAME  "configuration.xml"

namespace ut = boost::unit_test;

bool hasGLXDisplay()
{
    Display* display = XOpenDisplay( 0 );
    if( !display )
        return false;
    int major, event, error;
    const bool hasGLX = XQueryExtension( display, "GLX", &major, &event,
                                         &error );
    XCloseDisplay( display );
    return hasGLX;
}

// We need a global fixture because a bug in QApplication prevents
// deleting then recreating a QApplication in the same process.
// https://bugreports.qt-project.org/browse/QTBUG-7104
struct GlobalQtApp
{
    GlobalQtApp()
    {
        // need QApplication to instantiate WebkitPixelStreamer
        ut::master_test_suite_t& testSuite = ut::framework::master_test_suite();
        app = new QApplication( testSuite.argc, testSuite.argv );

        // To test wheel events the WebkitPixelStreamer needs access to the g_configuration element
        OptionsPtr options(new Options());
        g_configuration = new Configuration(CONFIG_TEST_FILENAME, options);
    }
    ~GlobalQtApp()
    {
        delete g_configuration;
        delete app;
    }

    QApplication* app;
};

BOOST_GLOBAL_FIXTURE( GlobalQtApp );

BOOST_AUTO_TEST_CASE( test_webgl_support )
{
    if( !hasGLXDisplay( ))
        return;

    // load the webgl website, exec() returns when loading is finished
    WebkitPixelStreamer* streamer = new WebkitPixelStreamer( "testBrowser" );
    QObject::connect( streamer->getView(), SIGNAL(loadFinished(bool)),
                      QApplication::instance(), SLOT(quit()));
    streamer->setUrl( TEST_PAGE_URL );
    QApplication::instance()->exec();

    QWebPage* page = streamer->getView()->page();
    BOOST_REQUIRE( page );
    QWebFrame* frame = page->mainFrame();
    BOOST_REQUIRE( frame );
    QWebElementCollection webglCanvases = frame->findAllElements( "canvas[id=webgl-canvas]" );
    BOOST_REQUIRE_EQUAL( webglCanvases.count(), 1 );

    // http://stackoverflow.com/questions/11871077/proper-way-to-detect-webgl-support
    QVariant hasContext = frame->evaluateJavaScript("var hasContext = false;"
                                                    "if( window.WebGLRenderingContext ) {"
                                                    "  hasContext = true;"
                                                    "}");

    QVariant hasGL = frame->evaluateJavaScript("var hasGL = false;"
                                               "gl = canvas.getContext(\"webgl\");"
                                               "if( gl ) {"
                                               "  hasGL = true;"
                                               "}");

    QVariant hasExperimentalGL = frame->evaluateJavaScript("var hasGL = false;"
                                                           "gl = canvas.getContext(\"experimental-webgl\");"
                                                           "if( gl ) {"
                                                           "  hasGL = true;"
                                                           "}");

    BOOST_CHECK( hasContext.toBool( ));
    BOOST_CHECK( hasGL.toBool() || hasExperimentalGL.toBool( ));

    delete streamer;
}

BOOST_AUTO_TEST_CASE( test_webgl_interaction )
{
    if( !hasGLXDisplay( ))
        return;

    // load the webgl website, exec() returns when loading is finished
    WebkitPixelStreamer* streamer = new WebkitPixelStreamer( "testBrowser" );
    QObject::connect( streamer->getView(), SIGNAL(loadFinished(bool)),
                      QApplication::instance(), SLOT(quit()));
    streamer->setUrl( TEST_PAGE_URL );
    QApplication::instance()->exec();

    QWebPage* page = streamer->getView()->page();
    BOOST_REQUIRE( page );
    QWebFrame* frame = page->mainFrame();
    BOOST_REQUIRE( frame );

    // Normalized mouse coordinates
    InteractionState pressState;
    pressState.mouseX = 0.1;
    pressState.mouseY = 0.1;
    pressState.mouseLeft = true;
    pressState.type = InteractionState::EVT_PRESS;

    InteractionState moveState;
    moveState.mouseX = 0.2;
    moveState.mouseY = 0.2;
    moveState.mouseLeft = true;
    moveState.type = InteractionState::EVT_MOVE;

    InteractionState releaseState;
    releaseState.mouseX = 0.2;
    releaseState.mouseY = 0.2;
    releaseState.mouseLeft = true;
    releaseState.type = InteractionState::EVT_RELEASE;

    streamer->updateInteractionState(pressState);
    streamer->updateInteractionState(moveState);
    streamer->updateInteractionState(releaseState);

    const int expectedDisplacementX = (releaseState.mouseX-pressState.mouseX) *
                                streamer->size().width() / streamer->getView()->zoomFactor();
    const int expectedDisplacementY = (releaseState.mouseY-pressState.mouseY) *
                                streamer->size().height() / streamer->getView()->zoomFactor();

    QString jsX = QString("deltaX == %1;").arg(expectedDisplacementX);
    QString jsY = QString("deltaY == %1;").arg(expectedDisplacementY);

    QVariant validDeltaX = frame->evaluateJavaScript(jsX);
    QVariant validDeltaY = frame->evaluateJavaScript(jsY);

    BOOST_CHECK( validDeltaX.toBool());
    BOOST_CHECK( validDeltaY.toBool());

    delete streamer;
}

BOOST_AUTO_TEST_CASE( test_webgl_click )
{
    // load the webgl website, exec() returns when loading is finished
    WebkitPixelStreamer* streamer = new WebkitPixelStreamer( "testBrowser" );
    QObject::connect( streamer->getView(), SIGNAL(loadFinished(bool)),
                      QApplication::instance(), SLOT(quit()));
    streamer->setUrl( TEST_PAGE_URL );
    QApplication::instance()->exec();

    QWebPage* page = streamer->getView()->page();
    BOOST_REQUIRE( page );
    QWebFrame* frame = page->mainFrame();
    BOOST_REQUIRE( frame );

    // Normalized mouse coordinates
    InteractionState clickState;
    clickState.mouseX = 0.1;
    clickState.mouseY = 0.1;
    clickState.mouseLeft = true;
    clickState.type = InteractionState::EVT_CLICK;

    streamer->updateInteractionState(clickState);

    const int expectedPosX = clickState.mouseX * streamer->size().width() /
                             streamer->getView()->zoomFactor();
    const int expectedPosY = clickState.mouseY * streamer->size().height() /
                             streamer->getView()->zoomFactor();

    QString jsX = QString("lastMouseX == %1;").arg(expectedPosX);
    QString jsY = QString("lastMouseY == %1;").arg(expectedPosY);

    BOOST_CHECK( frame->evaluateJavaScript(jsX).toBool());
    BOOST_CHECK( frame->evaluateJavaScript(jsY).toBool());

    delete streamer;
}

BOOST_AUTO_TEST_CASE( test_webgl_wheel )
{
    // load the webgl website, exec() returns when loading is finished
    WebkitPixelStreamer* streamer = new WebkitPixelStreamer( "testBrowser" );
    QObject::connect( streamer->getView(), SIGNAL(loadFinished(bool)),
                      QApplication::instance(), SLOT(quit()));
    streamer->setUrl( TEST_PAGE_URL );
    QApplication::instance()->exec();

    QWebPage* page = streamer->getView()->page();
    BOOST_REQUIRE( page );
    QWebFrame* frame = page->mainFrame();
    BOOST_REQUIRE( frame );

    // Normalized mouse coordinates
    InteractionState wheelState;
    wheelState.mouseX = 0.1;
    wheelState.mouseY = 0.1;
    wheelState.dy = 0.05;
    wheelState.type = InteractionState::EVT_WHEEL;

    streamer->updateInteractionState(wheelState);

    const int expectedPosX = wheelState.mouseX * streamer->size().width() /
                             streamer->getView()->zoomFactor();
    const int expectedPosY = wheelState.mouseY * streamer->size().height() /
                             streamer->getView()->zoomFactor();
    const int expectedWheelDelta = wheelState.dy * g_configuration->getTotalHeight();

    QString jsX = QString("lastMouseX == %1;").arg(expectedPosX);
    QString jsY = QString("lastMouseY == %1;").arg(expectedPosY);
    QString jsD = QString("wheelDelta == %1;").arg(expectedWheelDelta);

    BOOST_CHECK( frame->evaluateJavaScript(jsX).toBool());
    BOOST_CHECK( frame->evaluateJavaScript(jsY).toBool());
    BOOST_CHECK( frame->evaluateJavaScript(jsD).toBool());

    delete streamer;
}
<commit_msg>Fix tests on travis<commit_after>/*********************************************************************/
/* Copyright (c) 2013, EPFL/Blue Brain Project                       */
/*                     Daniel Nachbaur <daniel.nachbaur@epfl.ch>     */
/* All rights reserved.                                              */
/*                                                                   */
/* Redistribution and use in source and binary forms, with or        */
/* without modification, are permitted provided that the following   */
/* conditions are met:                                               */
/*                                                                   */
/*   1. Redistributions of source code must retain the above         */
/*      copyright notice, this list of conditions and the following  */
/*      disclaimer.                                                  */
/*                                                                   */
/*   2. 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.                              */
/*                                                                   */
/*    THIS  SOFTWARE IS PROVIDED  BY THE  UNIVERSITY OF  TEXAS AT    */
/*    AUSTIN  ``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 UNIVERSITY  OF TEXAS AT    */
/*    AUSTIN 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.                                    */
/*                                                                   */
/* The views and conclusions contained in the software and           */
/* documentation are those of the authors and should not be          */
/* interpreted as representing official policies, either expressed   */
/* or implied, of The University of Texas at Austin.                 */
/*********************************************************************/

#define BOOST_TEST_MODULE WebBrowser
#include <boost/test/unit_test.hpp>
namespace ut = boost::unit_test;

#include "globals.h"
#include "Options.h"
#include "configuration/Configuration.h"
#include "WebkitPixelStreamer.h"

#include <QApplication>
#include <QWebElementCollection>
#include <QWebFrame>
#include <QWebPage>
#include <QWebView>

#include <X11/Xlib.h>

#define TEST_PAGE_URL         "webgl_interaction.html"
#define CONFIG_TEST_FILENAME  "configuration.xml"

namespace ut = boost::unit_test;

bool hasGLXDisplay()
{
    Display* display = XOpenDisplay( 0 );
    if( !display )
        return false;
    int major, event, error;
    const bool hasGLX = XQueryExtension( display, "GLX", &major, &event,
                                         &error );
    XCloseDisplay( display );
    return hasGLX;
}

// We need a global fixture because a bug in QApplication prevents
// deleting then recreating a QApplication in the same process.
// https://bugreports.qt-project.org/browse/QTBUG-7104
struct GlobalQtApp
{
    GlobalQtApp()
    {
        // need QApplication to instantiate WebkitPixelStreamer
        ut::master_test_suite_t& testSuite = ut::framework::master_test_suite();
        app = new QApplication( testSuite.argc, testSuite.argv );

        // To test wheel events the WebkitPixelStreamer needs access to the g_configuration element
        OptionsPtr options(new Options());
        g_configuration = new Configuration(CONFIG_TEST_FILENAME, options);
    }
    ~GlobalQtApp()
    {
        delete g_configuration;
        delete app;
    }

    QApplication* app;
};

BOOST_GLOBAL_FIXTURE( GlobalQtApp );

BOOST_AUTO_TEST_CASE( test_webgl_support )
{
    if( !hasGLXDisplay( ))
        return;

    // load the webgl website, exec() returns when loading is finished
    WebkitPixelStreamer* streamer = new WebkitPixelStreamer( "testBrowser" );
    QObject::connect( streamer->getView(), SIGNAL(loadFinished(bool)),
                      QApplication::instance(), SLOT(quit()));
    streamer->setUrl( TEST_PAGE_URL );
    QApplication::instance()->exec();

    QWebPage* page = streamer->getView()->page();
    BOOST_REQUIRE( page );
    QWebFrame* frame = page->mainFrame();
    BOOST_REQUIRE( frame );
    QWebElementCollection webglCanvases = frame->findAllElements( "canvas[id=webgl-canvas]" );
    BOOST_REQUIRE_EQUAL( webglCanvases.count(), 1 );

    // http://stackoverflow.com/questions/11871077/proper-way-to-detect-webgl-support
    QVariant hasContext = frame->evaluateJavaScript("var hasContext = false;"
                                                    "if( window.WebGLRenderingContext ) {"
                                                    "  hasContext = true;"
                                                    "}");

    QVariant hasGL = frame->evaluateJavaScript("var hasGL = false;"
                                               "gl = canvas.getContext(\"webgl\");"
                                               "if( gl ) {"
                                               "  hasGL = true;"
                                               "}");

    QVariant hasExperimentalGL = frame->evaluateJavaScript("var hasGL = false;"
                                                           "gl = canvas.getContext(\"experimental-webgl\");"
                                                           "if( gl ) {"
                                                           "  hasGL = true;"
                                                           "}");

    BOOST_CHECK( hasContext.toBool( ));
    BOOST_CHECK( hasGL.toBool() || hasExperimentalGL.toBool( ));

    delete streamer;
}

BOOST_AUTO_TEST_CASE( test_webgl_interaction )
{
    if( !hasGLXDisplay( ))
        return;

    // load the webgl website, exec() returns when loading is finished
    WebkitPixelStreamer* streamer = new WebkitPixelStreamer( "testBrowser" );
    QObject::connect( streamer->getView(), SIGNAL(loadFinished(bool)),
                      QApplication::instance(), SLOT(quit()));
    streamer->setUrl( TEST_PAGE_URL );
    QApplication::instance()->exec();

    QWebPage* page = streamer->getView()->page();
    BOOST_REQUIRE( page );
    QWebFrame* frame = page->mainFrame();
    BOOST_REQUIRE( frame );

    // Normalized mouse coordinates
    InteractionState pressState;
    pressState.mouseX = 0.1;
    pressState.mouseY = 0.1;
    pressState.mouseLeft = true;
    pressState.type = InteractionState::EVT_PRESS;

    InteractionState moveState;
    moveState.mouseX = 0.2;
    moveState.mouseY = 0.2;
    moveState.mouseLeft = true;
    moveState.type = InteractionState::EVT_MOVE;

    InteractionState releaseState;
    releaseState.mouseX = 0.2;
    releaseState.mouseY = 0.2;
    releaseState.mouseLeft = true;
    releaseState.type = InteractionState::EVT_RELEASE;

    streamer->updateInteractionState(pressState);
    streamer->updateInteractionState(moveState);
    streamer->updateInteractionState(releaseState);

    const int expectedDisplacementX = (releaseState.mouseX-pressState.mouseX) *
                                streamer->size().width() / streamer->getView()->zoomFactor();
    const int expectedDisplacementY = (releaseState.mouseY-pressState.mouseY) *
                                streamer->size().height() / streamer->getView()->zoomFactor();

    QString jsX = QString("deltaX == %1;").arg(expectedDisplacementX);
    QString jsY = QString("deltaY == %1;").arg(expectedDisplacementY);

    QVariant validDeltaX = frame->evaluateJavaScript(jsX);
    QVariant validDeltaY = frame->evaluateJavaScript(jsY);

    BOOST_CHECK( validDeltaX.toBool());
    BOOST_CHECK( validDeltaY.toBool());

    delete streamer;
}

BOOST_AUTO_TEST_CASE( test_webgl_click )
{
    if( !hasGLXDisplay( ))
        return;

    // load the webgl website, exec() returns when loading is finished
    WebkitPixelStreamer* streamer = new WebkitPixelStreamer( "testBrowser" );
    QObject::connect( streamer->getView(), SIGNAL(loadFinished(bool)),
                      QApplication::instance(), SLOT(quit()));
    streamer->setUrl( TEST_PAGE_URL );
    QApplication::instance()->exec();

    QWebPage* page = streamer->getView()->page();
    BOOST_REQUIRE( page );
    QWebFrame* frame = page->mainFrame();
    BOOST_REQUIRE( frame );

    // Normalized mouse coordinates
    InteractionState clickState;
    clickState.mouseX = 0.1;
    clickState.mouseY = 0.1;
    clickState.mouseLeft = true;
    clickState.type = InteractionState::EVT_CLICK;

    streamer->updateInteractionState(clickState);

    const int expectedPosX = clickState.mouseX * streamer->size().width() /
                             streamer->getView()->zoomFactor();
    const int expectedPosY = clickState.mouseY * streamer->size().height() /
                             streamer->getView()->zoomFactor();

    QString jsX = QString("lastMouseX == %1;").arg(expectedPosX);
    QString jsY = QString("lastMouseY == %1;").arg(expectedPosY);

    BOOST_CHECK( frame->evaluateJavaScript(jsX).toBool());
    BOOST_CHECK( frame->evaluateJavaScript(jsY).toBool());

    delete streamer;
}

BOOST_AUTO_TEST_CASE( test_webgl_wheel )
{
    if( !hasGLXDisplay( ))
        return;

    // load the webgl website, exec() returns when loading is finished
    WebkitPixelStreamer* streamer = new WebkitPixelStreamer( "testBrowser" );
    QObject::connect( streamer->getView(), SIGNAL(loadFinished(bool)),
                      QApplication::instance(), SLOT(quit()));
    streamer->setUrl( TEST_PAGE_URL );
    QApplication::instance()->exec();

    QWebPage* page = streamer->getView()->page();
    BOOST_REQUIRE( page );
    QWebFrame* frame = page->mainFrame();
    BOOST_REQUIRE( frame );

    // Normalized mouse coordinates
    InteractionState wheelState;
    wheelState.mouseX = 0.1;
    wheelState.mouseY = 0.1;
    wheelState.dy = 0.05;
    wheelState.type = InteractionState::EVT_WHEEL;

    streamer->updateInteractionState(wheelState);

    const int expectedPosX = wheelState.mouseX * streamer->size().width() /
                             streamer->getView()->zoomFactor();
    const int expectedPosY = wheelState.mouseY * streamer->size().height() /
                             streamer->getView()->zoomFactor();
    const int expectedWheelDelta = wheelState.dy * g_configuration->getTotalHeight();

    QString jsX = QString("lastMouseX == %1;").arg(expectedPosX);
    QString jsY = QString("lastMouseY == %1;").arg(expectedPosY);
    QString jsD = QString("wheelDelta == %1;").arg(expectedWheelDelta);

    BOOST_CHECK( frame->evaluateJavaScript(jsX).toBool());
    BOOST_CHECK( frame->evaluateJavaScript(jsY).toBool());
    BOOST_CHECK( frame->evaluateJavaScript(jsD).toBool());

    delete streamer;
}
<|endoftext|>


<commit_before>// Copyright (c) 2010, Lawrence Livermore National Security, LLC. Produced at
// the Lawrence Livermore National Laboratory. LLNL-CODE-443211. All Rights
// reserved. See file COPYRIGHT for details.
//
// This file is part of the MFEM library. For more information and source code
// availability see http://mfem.org.
//
// MFEM 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) version 2.1 dated February 1999.
//
//   ------------------------------------------------------------------------
//   Extruder Miniapp: Extrude a low dimensional mesh into a higher dimension
//   ------------------------------------------------------------------------
//
// This miniapp performs multiple levels of adaptive mesh refinement to resolve
// the interfaces between different "materials" in the mesh, as specified by the
// given material() function. It can be used as a simple initial mesh generator,
// for example in the case when the interface is too complex to describe without
// local refinement. Both conforming and non-conforming refinements are supported.
//
// Compile with: make extruder
//
// Sample runs:
//    extruder
//    extruder -m ../../data/inline-segment.mesh -ny 8 -wy 2
//    extruder -m ../../data/inline-segment.mesh -ny 8 -wy 2 -nz 12 -hz 3
//    extruder -m ../../data/star.mesh -nz 3
//    extruder -m ../../data/star-mixed.mesh -nz 3
//    extruder -m ../../data/square-disc.mesh -nz 3
//    extruder -m ../../data/square-disc.mesh -nz 3
//    extruder -m ../../data/inline-segment.mesh -ny 8 -wy 2 -trans
//    extruder -m ../../data/inline-segment.mesh -ny 8 -wy 2 -nz 12 -hz 3 -trans
//    extruder -m ../../data/star.mesh -nz 3 -trans

#include "mfem.hpp"
#include <fstream>
#include <iostream>

using namespace mfem;
using namespace std;

void trans2D(const Vector&, Vector&);
void trans3D(const Vector&, Vector&);

int main(int argc, char *argv[])
{
   const char *mesh_file = "../../data/inline-quad.mesh";
   int ny = -1, nz = -1;
   double wy = 1.0, hz = 1.0;
   bool trans = false;
   bool visualization = 1;

   // Parse command line
   OptionsParser args(argc, argv);
   args.AddOption(&mesh_file, "-m", "--mesh",
                  "Input mesh file to shape materials in.");
   args.AddOption(&ny, "-ny", "--num-elem-in-y",
                  "Extrude a 1D mesh into ny elements in the y-direction.");
   args.AddOption(&wy, "-wy", "--width-in-y",
                  "Extrude a 1D mesh to a width wy in the y-direction.");
   args.AddOption(&nz, "-nz", "--num-elem-in-z",
                  "Extrude a 2D mesh into nz elements in the z-direction.");
   args.AddOption(&hz, "-hz", "--height-in-z",
                  "Extrude a 2D mesh to a height hz in the z-direction.");
   args.AddOption(&trans, "-trans", "--transform", "-no-trans",
                  "--no-transform",
                  "Enable or disable mesh transformation.");
   args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
                  "--no-visualization",
                  "Enable or disable GLVis visualization.");
   args.Parse();
   if (!args.Good()) { args.PrintUsage(cout); return 1; }
   args.PrintOptions(cout);

   // Read initial mesh, get dimensions and bounding box
   Mesh *mesh = new Mesh(mesh_file, 1, 1);
   int dim = mesh->Dimension();

   bool newMesh = false;

   if ( dim == 3 )
   {
      cout << "Extruding 3D meshes is not (yet) supported." << endl;
      delete mesh;
      exit(0);
   }
   if ( dim == 1 && ny > 0 )
   {
      cout << "Extruding 1D mesh to a width of " << wy
           << " using " << ny << " elements." << endl;

      Mesh *mesh2d = Extrude1D(mesh, ny, wy);
      delete mesh;
      mesh = mesh2d;
      dim = 2;
      if (trans)
      {
         mesh->SetCurvature(3, false, 2, Ordering::byVDIM);
         mesh->Transform(trans2D);
      }
      newMesh = true;
   }
   if ( dim == 2 && nz > 0 )
   {
      cout << "Extruding 2D mesh to a height of " << hz
           << " using " << nz << " elements." << endl;

      Mesh *mesh3d = Extrude2D(mesh, nz, hz);
      delete mesh;
      mesh = mesh3d;
      dim = 3;
      if (trans)
      {
         mesh->SetCurvature(3, false, 3, Ordering::byVDIM);
         mesh->Transform(trans3D);
      }
      newMesh = true;
   }

   if ( newMesh )
   {
      if (visualization)
      {
         // GLVis server to visualize to
         char vishost[] = "localhost";
         int  visport   = 19916;
         socketstream sol_sock(vishost, visport);
         sol_sock.precision(8);
         sol_sock << "mesh\n" << *mesh << flush;
      }

      // Save the final mesh
      ofstream mesh_ofs("extruder.mesh");
      mesh_ofs.precision(8);
      mesh->Print(mesh_ofs);
   }
   else
   {
      cout << "No mesh extrusion performed." << endl;
   }

   delete mesh;
}

void trans2D(const Vector&x, Vector&p)
{
   p[0] = x[0] + 0.25 * sin(M_PI * x[1]);
   p[1] = x[1];
}

void trans3D(const Vector&x, Vector&p)
{
   double r = sqrt(x[0] * x[0] + x[1] * x[1]);
   double theta = atan2(x[1], x[0]);
   p[0] = r * cos(theta + 0.25 * M_PI * x[2]);
   p[1] = r * sin(theta + 0.25 * M_PI * x[2]);
   p[2] = x[2];
}

<commit_msg>Controlling the order used for transformed meshes<commit_after>// Copyright (c) 2010, Lawrence Livermore National Security, LLC. Produced at
// the Lawrence Livermore National Laboratory. LLNL-CODE-443211. All Rights
// reserved. See file COPYRIGHT for details.
//
// This file is part of the MFEM library. For more information and source code
// availability see http://mfem.org.
//
// MFEM 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) version 2.1 dated February 1999.
//
//   ------------------------------------------------------------------------
//   Extruder Miniapp: Extrude a low dimensional mesh into a higher dimension
//   ------------------------------------------------------------------------
//
// This miniapp performs multiple levels of adaptive mesh refinement to resolve
// the interfaces between different "materials" in the mesh, as specified by the
// given material() function. It can be used as a simple initial mesh generator,
// for example in the case when the interface is too complex to describe without
// local refinement. Both conforming and non-conforming refinements are supported.
//
// Compile with: make extruder
//
// Sample runs:
//    extruder
//    extruder -m ../../data/inline-segment.mesh -ny 8 -wy 2
//    extruder -m ../../data/inline-segment.mesh -ny 8 -wy 2 -nz 12 -hz 3
//    extruder -m ../../data/star.mesh -nz 3
//    extruder -m ../../data/star-mixed.mesh -nz 3
//    extruder -m ../../data/square-disc.mesh -nz 3
//    extruder -m ../../data/square-disc.mesh -nz 3
//    extruder -m ../../data/inline-segment.mesh -ny 8 -wy 2 -trans
//    extruder -m ../../data/inline-segment.mesh -ny 8 -wy 2 -nz 12 -hz 3 -trans
//    extruder -m ../../data/star.mesh -nz 3 -trans

#include "mfem.hpp"
#include <fstream>
#include <iostream>

using namespace mfem;
using namespace std;

void trans2D(const Vector&, Vector&);
void trans3D(const Vector&, Vector&);

int main(int argc, char *argv[])
{
   const char *mesh_file = "../../data/inline-quad.mesh";
   int order = -1;
   int ny = -1, nz = -1;
   double wy = 1.0, hz = 1.0;
   bool trans = false;
   bool visualization = 1;

   // Parse command line
   OptionsParser args(argc, argv);
   args.AddOption(&mesh_file, "-m", "--mesh",
                  "Input mesh file to shape materials in.");
   args.AddOption(&order, "-o", "--mesh-order",
                  "Order (polynomial degree) of the mesh elements.");
   args.AddOption(&ny, "-ny", "--num-elem-in-y",
                  "Extrude a 1D mesh into ny elements in the y-direction.");
   args.AddOption(&wy, "-wy", "--width-in-y",
                  "Extrude a 1D mesh to a width wy in the y-direction.");
   args.AddOption(&nz, "-nz", "--num-elem-in-z",
                  "Extrude a 2D mesh into nz elements in the z-direction.");
   args.AddOption(&hz, "-hz", "--height-in-z",
                  "Extrude a 2D mesh to a height hz in the z-direction.");
   args.AddOption(&trans, "-trans", "--transform", "-no-trans",
                  "--no-transform",
                  "Enable or disable mesh transformation.");
   args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
                  "--no-visualization",
                  "Enable or disable GLVis visualization.");
   args.Parse();
   if (!args.Good()) { args.PrintUsage(cout); return 1; }
   args.PrintOptions(cout);

   // Read initial mesh, get dimensions and bounding box
   Mesh *mesh = new Mesh(mesh_file, 1, 1);
   int dim = mesh->Dimension();

   // Determine the order to use for a transformed mesh
   if ( order < 0 && trans )
   {
      int meshOrder = 1;
      if ( mesh->GetNodalFESpace() != NULL )
      {
         meshOrder = mesh->GetNodalFESpace()->GetORder(0);
      }
      order = meshOrder;
   }

   bool newMesh = false;

   if ( dim == 3 )
   {
      cout << "Extruding 3D meshes is not (yet) supported." << endl;
      delete mesh;
      exit(0);
   }
   if ( dim == 1 && ny > 0 )
   {
      cout << "Extruding 1D mesh to a width of " << wy
           << " using " << ny << " elements." << endl;

      Mesh *mesh2d = Extrude1D(mesh, ny, wy);
      delete mesh;
      mesh = mesh2d;
      dim = 2;
      if (trans)
      {
         mesh->SetCurvature(order, false, 2, Ordering::byVDIM);
         mesh->Transform(trans2D);
      }
      newMesh = true;
   }
   if ( dim == 2 && nz > 0 )
   {
      cout << "Extruding 2D mesh to a height of " << hz
           << " using " << nz << " elements." << endl;

      Mesh *mesh3d = Extrude2D(mesh, nz, hz);
      delete mesh;
      mesh = mesh3d;
      dim = 3;
      if (trans)
      {
         mesh->SetCurvature(order, false, 3, Ordering::byVDIM);
         mesh->Transform(trans3D);
      }
      newMesh = true;
   }

   if ( newMesh )
   {
      if (visualization)
      {
         // GLVis server to visualize to
         char vishost[] = "localhost";
         int  visport   = 19916;
         socketstream sol_sock(vishost, visport);
         sol_sock.precision(8);
         sol_sock << "mesh\n" << *mesh << flush;
      }

      // Save the final mesh
      ofstream mesh_ofs("extruder.mesh");
      mesh_ofs.precision(8);
      mesh->Print(mesh_ofs);
   }
   else
   {
      cout << "No mesh extrusion performed." << endl;
   }

   delete mesh;
}

void trans2D(const Vector&x, Vector&p)
{
   p[0] = x[0] + 0.25 * sin(M_PI * x[1]);
   p[1] = x[1];
}

void trans3D(const Vector&x, Vector&p)
{
   double r = sqrt(x[0] * x[0] + x[1] * x[1]);
   double theta = atan2(x[1], x[0]);
   p[0] = r * cos(theta + 0.25 * M_PI * x[2]);
   p[1] = r * sin(theta + 0.25 * M_PI * x[2]);
   p[2] = x[2];
}

<|endoftext|>


<commit_before>#include "sampler.hpp"
#include "context.hpp"
#include "texture.hpp"

#include "internal/wrapper.hpp"

#include "internal/modules.hpp"
#include "internal/tools.hpp"
#include "internal/glsl.hpp"

/* MGLContext.sampler(texture)
 */
PyObject * MGLContext_meth_sampler(MGLContext * self, PyObject * const * args, Py_ssize_t nargs) {
    if (nargs != 1) {
        // TODO: error
        return 0;
    }

    PyObject * texture = args[0];

    MGLSampler * sampler = MGLContext_new_object(self, Sampler);

    const GLMethods & gl = self->gl;
    gl.GenSamplers(1, (GLuint *)&sampler->sampler_obj);

    if (!sampler->sampler_obj) {
        PyErr_Format(moderngl_error, "cannot create sampler");
        Py_DECREF(sampler);
        return 0;
    }

    gl.SamplerParameteri(sampler->sampler_obj, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    gl.SamplerParameteri(sampler->sampler_obj, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

    SLOT(sampler->wrapper, PyObject, Sampler_class_texture) = texture;
    return NEW_REF(sampler->wrapper);
}

/* MGLSampler.use(location)
 */
PyObject * MGLSampler_meth_use(MGLSampler * self, PyObject * arg) {
    PyObject * wrapper = SLOT(self->wrapper, PyObject, Sampler_class_texture);
    MGLTexture * texture = SLOT(wrapper, MGLTexture, Texture_class_mglo);

    int location = PyLong_AsLong(arg);
    self->context->bind_sampler(location, texture->texture_target, texture->texture_obj, self->sampler_obj);
    Py_RETURN_NONE;
}

int MGLSampler_set_filter(MGLSampler * self, PyObject * value) {
    return 0;
}

enum MGLSamplerWrapModes {
    MGL_CLAMP_TO_EDGE = 0x01,
    MGL_REPEAT = 0x02,
    MGL_MIRRORED_REPEAT = 0x04,
    MGL_MIRROR_CLAMP_TO_EDGE = 0x08,
    MGL_CLAMP_TO_BORDER = 0x10,
};

int MGLSampler_set_wrap(MGLSampler * self, PyObject * value) {
    int wrap = PyLong_AsLong(value);
    SLOT(self->wrapper, PyObject, Sampler_class_wrap) = PyLong_FromLong(wrap);
    switch (((unsigned char *)&wrap)[0]) {
        case 0:
        case MGL_CLAMP_TO_EDGE:
    		self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
            break;
        case MGL_REPEAT:
    		self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_S, GL_REPEAT);
            break;
        case MGL_MIRRORED_REPEAT:
    		self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
            break;
        case MGL_MIRROR_CLAMP_TO_EDGE:
    		self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_S, GL_MIRROR_CLAMP_TO_EDGE);
            break;
        case MGL_CLAMP_TO_BORDER:
    		self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
            break;
        default:
            // TODO: error
            return -1;
    }
    switch (((unsigned char *)&wrap)[1]) {
        case 0:
        case MGL_CLAMP_TO_EDGE:
    		self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
            break;
        case MGL_REPEAT:
    		self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_T, GL_REPEAT);
            break;
        case MGL_MIRRORED_REPEAT:
    		self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
            break;
        case MGL_MIRROR_CLAMP_TO_EDGE:
    		self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_T, GL_MIRROR_CLAMP_TO_EDGE);
            break;
        case MGL_CLAMP_TO_BORDER:
    		self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
            break;
        default:
            // TODO: error
            return -1;
    }
    PyObject * wrapper = SLOT(self->wrapper, PyObject, Sampler_class_texture);
    MGLTexture * texture = SLOT(wrapper, MGLTexture, Texture_class_mglo);
    if (texture->texture_target == GL_TEXTURE_3D) {
        switch (((unsigned char *)&wrap)[2]) {
            case 0:
            case MGL_CLAMP_TO_EDGE:
                self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
                break;
            case MGL_REPEAT:
                self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_R, GL_REPEAT);
                break;
            case MGL_MIRRORED_REPEAT:
                self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_R, GL_MIRRORED_REPEAT);
                break;
            case MGL_MIRROR_CLAMP_TO_EDGE:
                self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_R, GL_MIRROR_CLAMP_TO_EDGE);
                break;
            case MGL_CLAMP_TO_BORDER:
                self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_BORDER);
                break;
            default:
                // TODO: error
                return -1;
        }
    } else if (((unsigned char *)&wrap)[2]) {
        return -1;
    }
    return 0;
}

int MGLSampler_set_anisotropy(MGLSampler * self, PyObject * value) {
    return 0;
}

int MGLSampler_set_min_lod(MGLSampler * self, PyObject * value) {
    int min_lod = PyLong_AsLong(value);
    SLOT(self->wrapper, PyObject, Sampler_class_min_lod) = NEW_REF(value);
	self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_MIN_LOD, min_lod);
    return 0;
}

int MGLSampler_set_max_lod(MGLSampler * self, PyObject * value) {
    int max_lod = PyLong_AsLong(value);
    SLOT(self->wrapper, PyObject, Sampler_class_max_lod) = NEW_REF(value);
	self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_MAX_LOD, max_lod);
    return 0;
}

#if PY_VERSION_HEX >= 0x03070000

PyMethodDef MGLSampler_methods[] = {
    {"use", (PyCFunction)MGLSampler_meth_use, METH_O, 0},
    {0},
};

#else

PyMethodDef MGLSampler_methods[] = {
    {"use", (PyCFunction)MGLSampler_meth_use, METH_O, 0},
    {0},
};

#endif

PyGetSetDef MGLSampler_getset[] = {
    {"filter", 0, (setter)MGLSampler_set_filter, 0, 0},
    {"wrap", 0, (setter)MGLSampler_set_wrap, 0, 0},
    {"anisotropy", 0, (setter)MGLSampler_set_anisotropy, 0, 0},
    {"min_lod", 0, (setter)MGLSampler_set_min_lod, 0, 0},
    {"max_lod", 0, (setter)MGLSampler_set_max_lod, 0, 0},
    {0},
};

PyType_Slot MGLSampler_slots[] = {
    {Py_tp_methods, MGLSampler_methods},
    {Py_tp_getset, MGLSampler_getset},
    {0},
};

PyType_Spec MGLSampler_spec = {
    mgl_ext ".Sampler",
    sizeof(MGLSampler),
    0,
    Py_TPFLAGS_DEFAULT,
    MGLSampler_slots,
};
<commit_msg>anisotropy setter<commit_after>#include "sampler.hpp"
#include "context.hpp"
#include "texture.hpp"

#include "internal/wrapper.hpp"

#include "internal/modules.hpp"
#include "internal/tools.hpp"
#include "internal/glsl.hpp"

/* MGLContext.sampler(texture)
 */
PyObject * MGLContext_meth_sampler(MGLContext * self, PyObject * const * args, Py_ssize_t nargs) {
    if (nargs != 1) {
        // TODO: error
        return 0;
    }

    PyObject * texture = args[0];

    MGLSampler * sampler = MGLContext_new_object(self, Sampler);

    const GLMethods & gl = self->gl;
    gl.GenSamplers(1, (GLuint *)&sampler->sampler_obj);

    if (!sampler->sampler_obj) {
        PyErr_Format(moderngl_error, "cannot create sampler");
        Py_DECREF(sampler);
        return 0;
    }

    gl.SamplerParameteri(sampler->sampler_obj, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    gl.SamplerParameteri(sampler->sampler_obj, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

    SLOT(sampler->wrapper, PyObject, Sampler_class_texture) = texture;
    return NEW_REF(sampler->wrapper);
}

/* MGLSampler.use(location)
 */
PyObject * MGLSampler_meth_use(MGLSampler * self, PyObject * arg) {
    PyObject * wrapper = SLOT(self->wrapper, PyObject, Sampler_class_texture);
    MGLTexture * texture = SLOT(wrapper, MGLTexture, Texture_class_mglo);

    int location = PyLong_AsLong(arg);
    self->context->bind_sampler(location, texture->texture_target, texture->texture_obj, self->sampler_obj);
    Py_RETURN_NONE;
}

int MGLSampler_set_filter(MGLSampler * self, PyObject * value) {
    return 0;
}

enum MGLSamplerWrapModes {
    MGL_CLAMP_TO_EDGE = 0x01,
    MGL_REPEAT = 0x02,
    MGL_MIRRORED_REPEAT = 0x04,
    MGL_MIRROR_CLAMP_TO_EDGE = 0x08,
    MGL_CLAMP_TO_BORDER = 0x10,
};

int MGLSampler_set_wrap(MGLSampler * self, PyObject * value) {
    int wrap = PyLong_AsLong(value);
    SLOT(self->wrapper, PyObject, Sampler_class_wrap) = PyLong_FromLong(wrap);
    switch (((unsigned char *)&wrap)[0]) {
        case 0:
        case MGL_CLAMP_TO_EDGE:
    		self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
            break;
        case MGL_REPEAT:
    		self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_S, GL_REPEAT);
            break;
        case MGL_MIRRORED_REPEAT:
    		self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
            break;
        case MGL_MIRROR_CLAMP_TO_EDGE:
    		self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_S, GL_MIRROR_CLAMP_TO_EDGE);
            break;
        case MGL_CLAMP_TO_BORDER:
    		self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
            break;
        default:
            // TODO: error
            return -1;
    }
    switch (((unsigned char *)&wrap)[1]) {
        case 0:
        case MGL_CLAMP_TO_EDGE:
    		self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
            break;
        case MGL_REPEAT:
    		self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_T, GL_REPEAT);
            break;
        case MGL_MIRRORED_REPEAT:
    		self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
            break;
        case MGL_MIRROR_CLAMP_TO_EDGE:
    		self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_T, GL_MIRROR_CLAMP_TO_EDGE);
            break;
        case MGL_CLAMP_TO_BORDER:
    		self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
            break;
        default:
            // TODO: error
            return -1;
    }
    PyObject * wrapper = SLOT(self->wrapper, PyObject, Sampler_class_texture);
    MGLTexture * texture = SLOT(wrapper, MGLTexture, Texture_class_mglo);
    if (texture->texture_target == GL_TEXTURE_3D) {
        switch (((unsigned char *)&wrap)[2]) {
            case 0:
            case MGL_CLAMP_TO_EDGE:
                self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
                break;
            case MGL_REPEAT:
                self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_R, GL_REPEAT);
                break;
            case MGL_MIRRORED_REPEAT:
                self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_R, GL_MIRRORED_REPEAT);
                break;
            case MGL_MIRROR_CLAMP_TO_EDGE:
                self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_R, GL_MIRROR_CLAMP_TO_EDGE);
                break;
            case MGL_CLAMP_TO_BORDER:
                self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_BORDER);
                break;
            default:
                // TODO: error
                return -1;
        }
    } else if (((unsigned char *)&wrap)[2]) {
        return -1;
    }
    return 0;
}

int MGLSampler_set_anisotropy(MGLSampler * self, PyObject * value) {
	float anisotropy = PyFloat_AsDouble(value);
    if (anisotropy < 1.0) {
        anisotropy = 1.0;
    }
    // if (anisotropy > max_anisotropy) {
    //     anisotropy = max_anisotropy;
    // }
    SLOT(self->wrapper, PyObject, Sampler_class_max_lod) = PyFloat_FromDouble(anisotropy);
	self->context->gl.SamplerParameterf(self->sampler_obj, GL_TEXTURE_MAX_ANISOTROPY, anisotropy);
    return 0;
}

int MGLSampler_set_min_lod(MGLSampler * self, PyObject * value) {
    int min_lod = PyLong_AsLong(value);
    SLOT(self->wrapper, PyObject, Sampler_class_min_lod) = NEW_REF(value);
	self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_MIN_LOD, min_lod);
    return 0;
}

int MGLSampler_set_max_lod(MGLSampler * self, PyObject * value) {
    int max_lod = PyLong_AsLong(value);
    SLOT(self->wrapper, PyObject, Sampler_class_max_lod) = NEW_REF(value);
	self->context->gl.SamplerParameteri(self->sampler_obj, GL_TEXTURE_MAX_LOD, max_lod);
    return 0;
}

#if PY_VERSION_HEX >= 0x03070000

PyMethodDef MGLSampler_methods[] = {
    {"use", (PyCFunction)MGLSampler_meth_use, METH_O, 0},
    {0},
};

#else

PyMethodDef MGLSampler_methods[] = {
    {"use", (PyCFunction)MGLSampler_meth_use, METH_O, 0},
    {0},
};

#endif

PyGetSetDef MGLSampler_getset[] = {
    {"filter", 0, (setter)MGLSampler_set_filter, 0, 0},
    {"wrap", 0, (setter)MGLSampler_set_wrap, 0, 0},
    {"anisotropy", 0, (setter)MGLSampler_set_anisotropy, 0, 0},
    {"min_lod", 0, (setter)MGLSampler_set_min_lod, 0, 0},
    {"max_lod", 0, (setter)MGLSampler_set_max_lod, 0, 0},
    {0},
};

PyType_Slot MGLSampler_slots[] = {
    {Py_tp_methods, MGLSampler_methods},
    {Py_tp_getset, MGLSampler_getset},
    {0},
};

PyType_Spec MGLSampler_spec = {
    mgl_ext ".Sampler",
    sizeof(MGLSampler),
    0,
    Py_TPFLAGS_DEFAULT,
    MGLSampler_slots,
};
<|endoftext|>