<commit_before>//
// Copyright (c) 2012 Daniel Lundin
//

#include "medida/timer.h"

#include <iostream>
#include <thread>

#include <gtest/gtest.h>

#include "medida/metrics_registry.h"

using namespace medida;

struct TimerTest : public ::testing::Test {
  Timer timer;
};

TEST_F(TimerTest, hasDurationUnit) {
  EXPECT_EQ(std::chrono::milliseconds(1), timer.duration_unit());
}

TEST_F(TimerTest, hasRateUnit) {
  EXPECT_EQ(std::chrono::seconds(1), timer.rate_unit());
}

TEST_F(TimerTest, createFromRegistry) {
  MetricsRegistry registry {};
  auto& timer2 = registry.NewTimer({"a", "b", "c"});
  EXPECT_EQ(0, timer2.count());
}

TEST_F(TimerTest, aBlankTimer) {
  EXPECT_EQ(0, timer.count());
  EXPECT_NEAR(0.0, timer.min(), 0.001);
  EXPECT_NEAR(0.0, timer.max(), 0.001);
  EXPECT_NEAR(0.0, timer.mean(), 0.001);
  EXPECT_NEAR(0.0, timer.std_dev(), 0.001);
  EXPECT_NEAR(0.0, timer.mean_rate(), 0.001);
  EXPECT_NEAR(0.0, timer.one_minute_rate(), 0.001);
  EXPECT_NEAR(0.0, timer.five_minute_rate(), 0.001);
  EXPECT_NEAR(0.0, timer.fifteen_minute_rate(), 0.001);

  auto snapshot = timer.GetSnapshot();
  EXPECT_NEAR(0.0, snapshot.getMedian(), 0.001);
  EXPECT_NEAR(0.0, snapshot.get75thPercentile(), 0.001);
  EXPECT_NEAR(0.0, snapshot.get99thPercentile(), 0.001);
  EXPECT_EQ(0, snapshot.size());
}

TEST_F(TimerTest, timingASeriesOfEvents) {
  timer.Update(std::chrono::milliseconds(10));
  timer.Update(std::chrono::milliseconds(20));
  timer.Update(std::chrono::milliseconds(20));
  timer.Update(std::chrono::milliseconds(30));
  timer.Update(std::chrono::milliseconds(40));

  EXPECT_EQ(5, timer.count());
  EXPECT_NEAR(10.0, timer.min(), 0.001);
  EXPECT_NEAR(40.0, timer.max(), 0.001);
  EXPECT_NEAR(24.0, timer.mean(), 0.001);
  EXPECT_NEAR(11.401, timer.std_dev(), 0.001);

  auto snapshot = timer.GetSnapshot();
  EXPECT_NEAR(20.0, snapshot.getMedian(), 0.001);
  EXPECT_NEAR(35.0, snapshot.get75thPercentile(), 0.001);
  EXPECT_NEAR(40.0, snapshot.get99thPercentile(), 0.001);
  EXPECT_EQ(5, snapshot.size());
}

TEST_F(TimerTest, timingVariantValues) {
  timer.Update(std::chrono::nanoseconds(9223372036854775807));  // INT64_MAX
  timer.Update(std::chrono::nanoseconds(0));
  EXPECT_NEAR(6.521908912666392E12, timer.std_dev(), 0.001);
}

TEST_F(TimerTest, timerTimeScope) {
  {
    auto t = timer.TimeScope();
    std::this_thread::sleep_for(std::chrono::milliseconds(10));
  }
  {
    auto t = timer.TimeScope();
    std::this_thread::sleep_for(std::chrono::milliseconds(20));
  }
  EXPECT_EQ(2, timer.count());
  EXPECT_NEAR(15.0, timer.mean(), 0.1);
}

void my_func() {
  std::this_thread::sleep_for(std::chrono::milliseconds(10));
}

TEST_F(TimerTest, timerTimeFunction) {
  timer.Time(my_func);
  EXPECT_EQ(1, timer.count());
  EXPECT_NEAR(10.0, timer.mean(), 0.1);
}

TEST_F(TimerTest, timerTimeLambda) {
  timer.Time([]() {
    std::this_thread::sleep_for(std::chrono::milliseconds(10));
  });
  EXPECT_EQ(1, timer.count());
  EXPECT_NEAR(10.0, timer.mean(), 0.1);
}
<commit_msg>Timer: Use larger timer values in tests.<commit_after>//
// Copyright (c) 2012 Daniel Lundin
//

#include "medida/timer.h"

#include <iostream>
#include <thread>

#include <gtest/gtest.h>

#include "medida/metrics_registry.h"

using namespace medida;

struct TimerTest : public ::testing::Test {
  Timer timer;
};

TEST_F(TimerTest, hasDurationUnit) {
  EXPECT_EQ(std::chrono::milliseconds(1), timer.duration_unit());
}

TEST_F(TimerTest, hasRateUnit) {
  EXPECT_EQ(std::chrono::seconds(1), timer.rate_unit());
}

TEST_F(TimerTest, createFromRegistry) {
  MetricsRegistry registry {};
  auto& timer2 = registry.NewTimer({"a", "b", "c"});
  EXPECT_EQ(0, timer2.count());
}

TEST_F(TimerTest, aBlankTimer) {
  EXPECT_EQ(0, timer.count());
  EXPECT_NEAR(0.0, timer.min(), 0.001);
  EXPECT_NEAR(0.0, timer.max(), 0.001);
  EXPECT_NEAR(0.0, timer.mean(), 0.001);
  EXPECT_NEAR(0.0, timer.std_dev(), 0.001);
  EXPECT_NEAR(0.0, timer.mean_rate(), 0.001);
  EXPECT_NEAR(0.0, timer.one_minute_rate(), 0.001);
  EXPECT_NEAR(0.0, timer.five_minute_rate(), 0.001);
  EXPECT_NEAR(0.0, timer.fifteen_minute_rate(), 0.001);

  auto snapshot = timer.GetSnapshot();
  EXPECT_NEAR(0.0, snapshot.getMedian(), 0.001);
  EXPECT_NEAR(0.0, snapshot.get75thPercentile(), 0.001);
  EXPECT_NEAR(0.0, snapshot.get99thPercentile(), 0.001);
  EXPECT_EQ(0, snapshot.size());
}

TEST_F(TimerTest, timingASeriesOfEvents) {
  timer.Update(std::chrono::milliseconds(10));
  timer.Update(std::chrono::milliseconds(20));
  timer.Update(std::chrono::milliseconds(20));
  timer.Update(std::chrono::milliseconds(30));
  timer.Update(std::chrono::milliseconds(40));

  EXPECT_EQ(5, timer.count());
  EXPECT_NEAR(10.0, timer.min(), 0.001);
  EXPECT_NEAR(40.0, timer.max(), 0.001);
  EXPECT_NEAR(24.0, timer.mean(), 0.001);
  EXPECT_NEAR(11.401, timer.std_dev(), 0.001);

  auto snapshot = timer.GetSnapshot();
  EXPECT_NEAR(20.0, snapshot.getMedian(), 0.001);
  EXPECT_NEAR(35.0, snapshot.get75thPercentile(), 0.001);
  EXPECT_NEAR(40.0, snapshot.get99thPercentile(), 0.001);
  EXPECT_EQ(5, snapshot.size());
}

TEST_F(TimerTest, timingVariantValues) {
  timer.Update(std::chrono::nanoseconds(9223372036854775807));  // INT64_MAX
  timer.Update(std::chrono::nanoseconds(0));
  EXPECT_NEAR(6.521908912666392E12, timer.std_dev(), 0.001);
}

TEST_F(TimerTest, timerTimeScope) {
  {
    auto t = timer.TimeScope();
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
  }
  {
    auto t = timer.TimeScope();
    std::this_thread::sleep_for(std::chrono::milliseconds(200));
  }
  EXPECT_EQ(2, timer.count());
  EXPECT_NEAR(150.0, timer.mean(), 0.5);
}

void my_func() {
  std::this_thread::sleep_for(std::chrono::milliseconds(100));
}

TEST_F(TimerTest, timerTimeFunction) {
  timer.Time(my_func);
  EXPECT_EQ(1, timer.count());
  EXPECT_NEAR(100.0, timer.mean(), 0.5);
}

TEST_F(TimerTest, timerTimeLambda) {
  timer.Time([]() {
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
  });
  EXPECT_EQ(1, timer.count());
  EXPECT_NEAR(100.0, timer.mean(), 0.5);
}
<|endoftext|>


<commit_before>#include "libtorrent/upnp.hpp"
#include "libtorrent/socket.hpp"
#include "libtorrent/connection_queue.hpp"
#include <boost/bind.hpp>
#include <boost/ref.hpp>

using namespace libtorrent;

void callback(int tcp, int udp, std::string const& err)
{
	std::cerr << "tcp: " << tcp << ", udp: " << udp << ", error: \"" << err << "\"\n";
}

int main(int argc, char* argv[])
{
	io_service ios;
	std::string user_agent = "test agent";

	if (argc != 3)
	{
		std::cerr << "usage: " << argv[0] << " tcp-port udp-port" << std::endl;
		return 1;
	}

	connection_queue cc(ios);
	upnp upnp_handler(ios, cc, address_v4(), user_agent, &callback);

	deadline_timer timer(ios);
	timer.expires_from_now(seconds(2));
	timer.async_wait(boost::bind(&io_service::stop, boost::ref(ios)));

	std::cerr << "broadcasting for UPnP device" << std::endl;

	ios.reset();
	ios.run();

	upnp_handler.set_mappings(atoi(argv[1]), atoi(argv[2]));
	timer.expires_from_now(seconds(5));
	timer.async_wait(boost::bind(&io_service::stop, boost::ref(ios)));
	std::cerr << "mapping ports TCP: " << argv[1]
		<< " UDP: " << argv[2] << std::endl;

	ios.reset();
	ios.run();
	std::cerr << "removing mappings" << std::endl;
	upnp_handler.close();

	ios.reset();
	ios.run();
	std::cerr << "closing" << std::endl;
}

<commit_msg>fixed bug in test_upnp program<commit_after>#include "libtorrent/upnp.hpp"
#include "libtorrent/socket.hpp"
#include "libtorrent/connection_queue.hpp"
#include <boost/bind.hpp>
#include <boost/ref.hpp>
#include <boost/intrusive_ptr.hpp>

using namespace libtorrent;

void callback(int tcp, int udp, std::string const& err)
{
	std::cerr << "tcp: " << tcp << ", udp: " << udp << ", error: \"" << err << "\"\n";
}

int main(int argc, char* argv[])
{
	io_service ios;
	std::string user_agent = "test agent";

	if (argc != 3)
	{
		std::cerr << "usage: " << argv[0] << " tcp-port udp-port" << std::endl;
		return 1;
	}

	connection_queue cc(ios);
	boost::intrusive_ptr<upnp> upnp_handler = new upnp(ios, cc, address_v4(), user_agent, &callback);

	deadline_timer timer(ios);
	timer.expires_from_now(seconds(2));
	timer.async_wait(boost::bind(&io_service::stop, boost::ref(ios)));

	std::cerr << "broadcasting for UPnP device" << std::endl;

	ios.reset();
	ios.run();

	upnp_handler->set_mappings(atoi(argv[1]), atoi(argv[2]));
	timer.expires_from_now(seconds(5));
	timer.async_wait(boost::bind(&io_service::stop, boost::ref(ios)));
	std::cerr << "mapping ports TCP: " << argv[1]
		<< " UDP: " << argv[2] << std::endl;

	ios.reset();
	ios.run();
	std::cerr << "removing mappings" << std::endl;
	upnp_handler->close();

	ios.reset();
	ios.run();
	std::cerr << "closing" << std::endl;
}

<|endoftext|>


<commit_before>/*
 * Copyright (c) 2013, The University of Oxford
 * 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.
 * 3. Neither the name of the University of Oxford nor the names of its
 *    contributors may be used to endorse or promote products derived from this
 *    software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <oskar_global.h>

#include <apps/lib/oskar_OptionParser.h>

#include "utility/oskar_vector_types.h"
#include <utility/oskar_Log.h>
#include <utility/oskar_log_settings.h>
#include <utility/oskar_get_error_string.h>

#include <interferometry/oskar_visibilities_read.h>
#include <interferometry/oskar_visibilities_write.h>
#include <interferometry/oskar_visibilities_init.h>
#include <interferometry/oskar_visibilities_copy.h>

#include <string>
#include <vector>
#include <cfloat>
#include <cmath>
#include <iostream>
#include <cstdio>

using namespace std;

//------------------------------------------------------------------------------
void set_options(oskar_OptionParser& opt);
bool check_options(oskar_OptionParser& opt, int argc, char** argv);
void check_error(int status);
//------------------------------------------------------------------------------

int main(int argc, char** argv)
{
    typedef float2 Complex;
    typedef double2 DComplex;
    typedef float4c Jones;
    typedef double4c DJones;

    int status = OSKAR_SUCCESS;

    // Register options ========================================================
    oskar_OptionParser opt("oskar_visibility_stats");
    set_options(opt);
    if (!check_options(opt, argc, argv))
        return OSKAR_FAIL;

    // Retrieve options ========================================================
    vector<string> vis_filename = opt.getArgs();
    bool verbose = opt.isSet("-v") ? true : false;

    if (verbose)
    {
        cout << endl;
        cout << "-----------------------------------------------------------" << endl;
        cout << "Number of visibility files = " << vis_filename.size() << endl;
        for (int i = 0; i < (int)vis_filename.size(); ++i)
            cout << "  --> " << vis_filename[i] << endl;
        cout << "-----------------------------------------------------------" << endl;
        cout << endl;
    }

    for (int i = 0; i < (int)vis_filename.size(); ++i)
    {
        if (verbose)
            cout << "Loading visibility file: " << vis_filename[i] << endl;

        oskar_Visibilities vis;
        oskar_visibilities_read(&vis, vis_filename[i].c_str(), &status);
        check_error(status);
        double min = DBL_MAX, max = -DBL_MAX;
        double mean = 0.0, rms = 0.0, var = 0.0, std = 0.0;
        double sum = 0.0, sumsq = 0.0;
        if (verbose)
        {
            cout << "  No. of baselines: " << vis.num_baselines << endl;
            cout << "  No. of times: " << vis.num_times << endl;
            cout << "  No. of channels: " << vis.num_channels << endl;
        }

        switch (vis.amplitude.type)
        {
            case OSKAR_SINGLE_COMPLEX:
            {
                Complex* amps = (Complex*)vis.amplitude.data;
                break;
            }
            case OSKAR_SINGLE_COMPLEX_MATRIX:
            {
                Jones* amps = (Jones*)vis.amplitude.data;
                break;
            }
            case OSKAR_DOUBLE_COMPLEX:
            {
                DComplex* amps = (DComplex*)vis.amplitude.data;
                break;
            }
            case OSKAR_DOUBLE_COMPLEX_MATRIX:
            {
                DJones* amp = (DJones*)vis.amplitude.data;
                int num_zero = 0;
                for (int i = 0, c = 0; c < vis.num_channels; ++c)
                {
                    for (int t = 0; t < vis.num_times; ++t)
                    {
                        for (int b = 0; b < vis.num_baselines; ++b, ++i)
                        {
                            DComplex xx = amp[i].a;
                            DComplex yy = amp[i].d;
                            DComplex I;
                            I.x = 0.5 * (xx.x + yy.x);
                            I.y = 0.5 * (xx.y + yy.y);
                            double absI = std::sqrt(I.x * I.x + I.y * I.y);
                            if (absI < DBL_MIN)
                                num_zero++;
                            //printf("%i (%e %e) %e %e => %e\n", i, xx.x, xx.y, I.x, I.y, absI);
                            if (absI > max) max = absI;
                            if (absI < min) min = absI;
                            sum += absI;
                            sumsq += absI * absI;
                        }
                    }
                }
                int num_vis = vis.amplitude.num_elements;
                mean = sum / num_vis;
                rms = std::sqrt(sumsq / num_vis);
                var = sumsq/num_vis - mean*mean;
                std = std::sqrt(var);
                printf("min, max, mean, rms, std\n");
                printf("%e, %e, %e, %e, %e\n", min, max, mean, rms, std);
                printf("number of zero visibilities = %i\n", num_zero);
                printf("number of non-zero visibilities = %i\n", num_vis-num_zero);
                printf("percent zero visibilities = %f\n", (double)num_zero/num_vis * 100.0);
                break;
            }
            default:
                return OSKAR_ERR_BAD_DATA_TYPE;
                break;
        } // switch (vis.amplitude.type)

    } // end of loop over visibility files

    // XXX free memory visibilities structure...

    return status;
}

//------------------------------------------------------------------------------

void set_options(oskar_OptionParser& opt)
{
    opt.setDescription("Application to generate some stats from an OSKAR visibility file.");
    opt.addRequired("OSKAR visibility file(s)...");
    opt.addFlag("-v", "Verbose");
}

bool check_options(oskar_OptionParser& opt, int argc, char** argv)
{
    if (!opt.check_options(argc, argv))
        return false;
    return true;
}

void check_error(int status)
{
    if (status != OSKAR_SUCCESS) {
        cout << "ERROR: code[" << status << "] ";
        cout << oskar_get_error_string(status) << endl;
        exit(status);
    }
}

<commit_msg>debugging added to stats function<commit_after>/*
 * Copyright (c) 2013, The University of Oxford
 * 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.
 * 3. Neither the name of the University of Oxford nor the names of its
 *    contributors may be used to endorse or promote products derived from this
 *    software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <oskar_global.h>

#include <apps/lib/oskar_OptionParser.h>

#include "utility/oskar_vector_types.h"
#include <utility/oskar_Log.h>
#include <utility/oskar_log_settings.h>
#include <utility/oskar_get_error_string.h>

#include <interferometry/oskar_visibilities_read.h>
#include <interferometry/oskar_visibilities_write.h>
#include <interferometry/oskar_visibilities_init.h>
#include <interferometry/oskar_visibilities_copy.h>

#include <string>
#include <vector>
#include <cfloat>
#include <cmath>
#include <iostream>
#include <cstdio>

using namespace std;

//------------------------------------------------------------------------------
void set_options(oskar_OptionParser& opt);
bool check_options(oskar_OptionParser& opt, int argc, char** argv);
void check_error(int status);
char *doubleToRawString(double x) {
    // Assumes sizeof(long long) == 8.

    char *buffer = new char[32];
    sprintf(buffer, "%llx", *(unsigned long long *)&x);  // Evil!
    return buffer;
}
//------------------------------------------------------------------------------

int main(int argc, char** argv)
{
    typedef float2 Complex;
    typedef double2 DComplex;
    typedef float4c Jones;
    typedef double4c DJones;

    int status = OSKAR_SUCCESS;

    // Register options ========================================================
    oskar_OptionParser opt("oskar_visibility_stats");
    set_options(opt);
    if (!check_options(opt, argc, argv))
        return OSKAR_FAIL;

    // Retrieve options ========================================================
    vector<string> vis_filename = opt.getArgs();
    bool verbose = opt.isSet("-v") ? true : false;

    if (verbose)
    {
        cout << endl;
        cout << "-----------------------------------------------------------" << endl;
        cout << "Number of visibility files = " << vis_filename.size() << endl;
        for (int i = 0; i < (int)vis_filename.size(); ++i)
            cout << "  --> " << vis_filename[i] << endl;
        cout << "-----------------------------------------------------------" << endl;
        cout << endl;
    }

    for (int i = 0; i < (int)vis_filename.size(); ++i)
    {
        if (verbose)
            cout << "Loading visibility file: " << vis_filename[i] << endl;

        oskar_Visibilities vis;
        oskar_visibilities_read(&vis, vis_filename[i].c_str(), &status);
        check_error(status);
        double min = DBL_MAX, max = -DBL_MAX;
        double mean = 0.0, rms = 0.0, var = 0.0, std = 0.0;
        double sum = 0.0, sumsq = 0.0;
        if (verbose)
        {
            cout << "  No. of baselines: " << vis.num_baselines << endl;
            cout << "  No. of times: " << vis.num_times << endl;
            cout << "  No. of channels: " << vis.num_channels << endl;
        }

        switch (vis.amplitude.type)
        {
            case OSKAR_SINGLE_COMPLEX:
            {
                Complex* amps = (Complex*)vis.amplitude.data;
                break;
            }
            case OSKAR_SINGLE_COMPLEX_MATRIX:
            {
                Jones* amps = (Jones*)vis.amplitude.data;
                break;
            }
            case OSKAR_DOUBLE_COMPLEX:
            {
                DComplex* amps = (DComplex*)vis.amplitude.data;
                break;
            }
            case OSKAR_DOUBLE_COMPLEX_MATRIX:
            {
                DJones* amp = (DJones*)vis.amplitude.data;
                int num_zero = 0;
                for (int i = 0, c = 0; c < vis.num_channels; ++c)
                {
                    for (int t = 0; t < vis.num_times; ++t)
                    {
                        for (int b = 0; b < vis.num_baselines; ++b, ++i)
                        {
                            DComplex xx = amp[i].a;
                            DComplex yy = amp[i].d;
                            DComplex I;
                            I.x = 0.5 * (xx.x + yy.x);
                            I.y = 0.5 * (xx.y + yy.y);
                            double absI = std::sqrt(I.x * I.x + I.y * I.y);
                            if (absI < DBL_MIN)
                                num_zero++;
                            printf("%i (%e [%s] %e) %e %e => %e\n", i, xx.x, doubleToRawString(xx.x), xx.y, I.x, I.y, absI);
                            if (absI > max) max = absI;
                            if (absI < min) min = absI;
                            sum += absI;
                            sumsq += absI * absI;
                        }
                    }
                }
                int num_vis = vis.amplitude.num_elements;
                mean = sum / num_vis;
                rms = std::sqrt(sumsq / num_vis);
                var = sumsq/num_vis - mean*mean;
                std = std::sqrt(var);
                printf("min, max, mean, rms, std\n");
                printf("%e, %e, %e, %e, %e\n", min, max, mean, rms, std);
                printf("number of zero visibilities = %i\n", num_zero);
                printf("number of non-zero visibilities = %i\n", num_vis-num_zero);
                printf("percent zero visibilities = %f\n", (double)num_zero/num_vis * 100.0);
                break;
            }
            default:
                return OSKAR_ERR_BAD_DATA_TYPE;
                break;
        } // switch (vis.amplitude.type)

    } // end of loop over visibility files

    // XXX free memory visibilities structure...

    return status;
}

//------------------------------------------------------------------------------

void set_options(oskar_OptionParser& opt)
{
    opt.setDescription("Application to generate some stats from an OSKAR visibility file.");
    opt.addRequired("OSKAR visibility file(s)...");
    opt.addFlag("-v", "Verbose");
}

bool check_options(oskar_OptionParser& opt, int argc, char** argv)
{
    if (!opt.check_options(argc, argv))
        return false;
    return true;
}

void check_error(int status)
{
    if (status != OSKAR_SUCCESS) {
        cout << "ERROR: code[" << status << "] ";
        cout << oskar_get_error_string(status) << endl;
        exit(status);
    }
}

<|endoftext|>


<commit_before>////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
///
/// Copyright 2016 ArangoDB GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
///     http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
///
/// @author Jan Christoph Uhde
////////////////////////////////////////////////////////////////////////////////

#include "node_connection.h"
#include "node_request.h"
#include <iostream>
#include <memory>
#include <mutex>
#include <atomic>
#include <fuerte/FuerteLogger.h>

namespace arangodb { namespace fuerte { namespace js {

///////////////////////////////////////////////////////////////////////////////
// NConnectionBuilder /////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
NAN_METHOD(NConnectionBuilder::New) {
  if (info.IsConstructCall()) {
      NConnectionBuilder* obj = new NConnectionBuilder();
      obj->Wrap(info.This());
      info.GetReturnValue().Set(info.This());
  } else {
    v8::Local<v8::Function> builder = Nan::New(constructor());
    info.GetReturnValue().Set(Nan::NewInstance(builder).ToLocalChecked());
 }
}

NAN_METHOD(NConnectionBuilder::connect){
  try {
    v8::Local<v8::Function> connFunction = Nan::New(NConnection::constructor());
    const int argc = 1;
    v8::Local<v8::Value> argv[argc] = {info.This()};
    auto connInstance = Nan::NewInstance(connFunction, argc, argv).ToLocalChecked();
    info.GetReturnValue().Set(connInstance);
  } catch(std::exception const& e){
    std::cerr << "## DRIVER LEVEL EXCEPTION - START ##" << std::endl;
    std::cerr << e.what() << std::endl;
    std::cerr << "## DRIVER LEVEL EXCEPTION - END   ##" << std::endl;
    Nan::ThrowError("ConnectionBuilder.connect binding failed with exception"
                    " - Make sure the server is up and running");
  }
}

NAN_METHOD(NConnectionBuilder::host){
  try {
    if (info.Length() != 1 ) {
      Nan::ThrowTypeError("Wrong number of Arguments");
    }
    unwrapSelf<NConnectionBuilder>(info)->_cppClass.host(to<std::string>(info[0]));
    info.GetReturnValue().Set(info.This());
  } catch(std::exception const& e){
    std::cerr << "## DRIVER LEVEL EXCEPTION - START ##" << std::endl;
    std::cerr << e.what() << std::endl;
    std::cerr << "## DRIVER LEVEL EXCEPTION - END   ##" << std::endl;
    std::string errorMesasge = std::string("ConnectionBuilder.host binding failed with exception: ") + e.what();
    Nan::ThrowError(errorMesasge.c_str());
  }
}

NAN_METHOD(NConnectionBuilder::async){
  try {
    if (info.Length() != 1 ) {
      Nan::ThrowTypeError("Wrong number of Arguments");
    }
    unwrapSelf<NConnectionBuilder>(info)->_cppClass.async(to<bool>(info[0]));
    info.GetReturnValue().Set(info.This());
  } catch(std::exception const& e){
    Nan::ThrowError("ConnectionBuilder.async binding failed with exception");
  }
}

NAN_METHOD(NConnectionBuilder::user){
  try {
    if (info.Length() != 1 ) {
      Nan::ThrowTypeError("Wrong number of Arguments");
    }
    unwrapSelf<NConnectionBuilder>(info)->_cppClass.user(to<std::string>(info[0]));
    info.GetReturnValue().Set(info.This());
  } catch(std::exception const& e){
    Nan::ThrowError("ConnectionBuilder.user binding failed with exception");
  }
}

NAN_METHOD(NConnectionBuilder::password){
  try {
    if (info.Length() != 1 ) {
      Nan::ThrowTypeError("Wrong number of Arguments");
    }
    unwrapSelf<NConnectionBuilder>(info)->_cppClass.password(to<std::string>(info[0]));
    info.GetReturnValue().Set(info.This());
  } catch(std::exception const& e){
    Nan::ThrowError("ConnectionBuilder.user binding failed with exception");
  }
}

///////////////////////////////////////////////////////////////////////////////
// NConnection ////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
NAN_METHOD(NConnection::New) {
  if (info.IsConstructCall()) {
    NConnection* obj = new NConnection();
    if(info[0]->IsObject()){ // NConnectionBuilderObject -- exact type check?
      obj->_cppClass = unwrap<NConnectionBuilder>(info[0])->_cppClass.connect();
    }
    obj->Wrap(info.This());
    info.GetReturnValue().Set(info.This());
  } else {
    v8::Local<v8::Function> cons = Nan::New(constructor());
    info.GetReturnValue().Set(Nan::NewInstance(cons).ToLocalChecked());
 }
}

///////////////////////////////////////////////////////////////////////////////
// SendRequest ////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

// When we switch to c++14 we should use UniquePersistent and move it into
// the generalized lambda caputre avoiding the locking alltogehter
static std::map<fu::MessageID
               ,std::pair<v8::Persistent<v8::Function> // error
                         ,v8::Persistent<v8::Function> // success
                         >
              > callbackMap;

static std::mutex maplock;
static std::atomic<uint64_t> jsMessageID(0);

const std::string callbackErrorMessage(
  "##################################################\n"
  "#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#\n"
  "#@Failed to create request instance in callback.@#\n"
  "#@This could be an error in you JavaScript Code!@#\n"
  "#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#\n"
  "##################################################\n"
);

NAN_METHOD(NConnection::sendRequest) {
  try {
    if (info.Length() != 3 ) {
      Nan::ThrowTypeError("Not 3 Arguments");
    }

    if (!info[1]->IsFunction() || !info[2]->IsFunction()){
      Nan::ThrowTypeError("Callback is not a Function");
    }

    // get isolate - has node only one context??!?!? how do they work
    // context is probably a lighter version of isolate but does not allow threads
    v8::Isolate* iso = v8::Isolate::GetCurrent();

    uint64_t id = jsMessageID.fetch_add(1);
    {
      std::lock_guard<std::mutex> lock(maplock);
      auto& callbackPair = callbackMap[id]; //create map element
      auto jsOnErr = v8::Local<v8::Function>::Cast(info[1]);
      callbackPair.first.Reset(iso, jsOnErr);

      auto jsOnSucc = v8::Local<v8::Function>::Cast(info[2]);
      callbackPair.second.Reset(iso, jsOnSucc);
    }

    fu::OnErrorCallback err = [iso,id](unsigned err
                                ,std::unique_ptr<fu::Request> creq
                                ,std::unique_ptr<fu::Response> cres)
    {
      try {
        v8::HandleScope scope(iso);

        auto jsOnErr = v8::Local<v8::Function>();
        { //create local function and dispose persistent
          std::lock_guard<std::mutex> lock(maplock);
          auto& mapElement = callbackMap[id];
          jsOnErr = v8::Local<v8::Function>::New(iso,mapElement.first);
          mapElement.first.Reset();
          mapElement.second.Reset();
          callbackMap.erase(id);
        }

        // wrap request
        v8::Local<v8::Function> requestProto = v8::Local<v8::Function>::New(iso,NRequest::constructor());
        assert(!requestProto.IsEmpty());
        v8::Local<v8::Object> reqObj;
        bool ok = requestProto->NewInstance(iso->GetCurrentContext()).ToLocal(&reqObj);
        if(!ok) {
          std::cerr << callbackErrorMessage << std::endl;
          iso->ThrowException(v8::Exception::Error(v8::String::NewFromUtf8(iso,callbackErrorMessage.c_str())));
          return;
        }
        unwrap<NRequest>(reqObj)->setCppClass(std::move(creq));

        // wrap response
        v8::Local<v8::Function> responseProto = v8::Local<v8::Function>::New(iso,NResponse::constructor());
        assert(!responseProto.IsEmpty());
        v8::Local<v8::Object> resObj;
        ok = responseProto->NewInstance(iso->GetCurrentContext()).ToLocal(&resObj);
        if(!ok) {
          std::cerr << callbackErrorMessage << std::endl;
          iso->ThrowException(v8::Exception::Error(v8::String::NewFromUtf8(iso,callbackErrorMessage.c_str())));
          return;
        }
        unwrap<NResponse>(resObj)->setCppClass(std::move(cres));

        // build args
        const unsigned argc = 3;
        v8::Local<v8::Value> argv[argc] = { Nan::New<v8::Integer>(err), reqObj, resObj };

        // call
        jsOnErr->Call(iso->GetCurrentContext(), jsOnErr, argc, argv);
      } catch (std::exception const& e){
        std::string errorMesasge("Exception in success callback: ");
        errorMesasge += e.what();
        iso->ThrowException(v8::Exception::Error(v8::String::NewFromUtf8(iso,errorMesasge.c_str())));
      }
    };

    fu::OnSuccessCallback succ = [iso,id](std::unique_ptr<fu::Request> creq
                                   ,std::unique_ptr<fu::Response> cres)
    {
      try {
        FUERTE_LOG_NODE << "enter on fuerte-node success callback" << std::endl;
        v8::HandleScope scope(iso);

        auto jsOnSucc = v8::Local<v8::Function>();
        { // create locacl function and dispose persistent
          std::lock_guard<std::mutex> lock(maplock);
          auto& mapElement = callbackMap[id];
          //create local function
          jsOnSucc = v8::Local<v8::Function>::New(iso,callbackMap[id].second);

          //dispose map element
          mapElement.first.Reset(); // do not depend on kResetInDestructorFlag of Persistent
          mapElement.second.Reset();
          callbackMap.erase(id);
        }

            // other ways to do the same
            // v8::Local<v8::Function> requestProto = Nan::New(NConnection::constructor()); // with Nan
            // auto reqObj = Nan::NewInstance(requestProto).ToLocalChecked(); // with Nan
            // auto reqObj = requestProto->NewInstance(iso->GetCurrentContext()).ToLocalChecked(); // should not crash!

        // wrap request
        v8::Local<v8::Function> requestProto = v8::Local<v8::Function>::New(iso,NRequest::constructor());
        assert(!requestProto.IsEmpty());
        v8::Local<v8::Object> reqObj;
        bool ok = requestProto->NewInstance(iso->GetCurrentContext()).ToLocal(&reqObj);
        if(!ok) {
          std::cerr << callbackErrorMessage << std::endl;
          iso->ThrowException(v8::Exception::Error(v8::String::NewFromUtf8(iso,callbackErrorMessage.c_str())));
          return;
        }
        unwrap<NRequest>(reqObj)->setCppClass(std::move(creq));

        // wrap response
        v8::Local<v8::Function> responseProto = v8::Local<v8::Function>::New(iso,NResponse::constructor());
        assert(!responseProto.IsEmpty());
        v8::Local<v8::Object> resObj;
        ok = responseProto->NewInstance(iso->GetCurrentContext()).ToLocal(&resObj);
        if(!ok) {
          std::cerr << callbackErrorMessage << std::endl;
          iso->ThrowException(v8::Exception::Error(v8::String::NewFromUtf8(iso,callbackErrorMessage.c_str())));
          return;
        }
        unwrap<NResponse>(resObj)->setCppClass(std::move(cres));

        // build args
        const unsigned argc = 2;
        v8::Local<v8::Value> argv[argc] = { reqObj, resObj };

        // call
        //jsOnSucc->Call(v8::Null(iso), argc, argv);
        jsOnSucc->Call(iso->GetCurrentContext(), jsOnSucc, argc, argv);
        //jsOnSucc->Call(iso->GetCurrentContext(), iso->GetCurrentContext()->Global(), argc, argv);
      } catch (std::exception const& e){
        std::string errorMesasge("Exception in error callback: ");
        errorMesasge += e.what();
        iso->ThrowException(v8::Exception::Error(v8::String::NewFromUtf8(iso,errorMesasge.c_str())));
      }
    };

    //finally invoke the c++ callback
    auto req = std::unique_ptr<fu::Request>(new fu::Request(*(unwrap<NRequest>(info[0])->_cppClass))); //copy request so the old object stays valid
    unwrapSelf<NConnection>(info)->_cppClass->sendRequest(std::move(req), err, succ);
  } catch(std::exception const& e){
    Nan::ThrowError("Connection.sendRequest binding failed with exception");
    return;
  }
  FUERTE_LOG_NODE << "exit on fuerte-node success callback" << std::endl;
}

}}}
<commit_msg>add more exception handling<commit_after>////////////////////////////////////////////////////////////////////////////////
/// DISCLAIMER
///
/// Copyright 2016 ArangoDB GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
///     http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
///
/// @author Jan Christoph Uhde
////////////////////////////////////////////////////////////////////////////////

#include "node_connection.h"
#include "node_request.h"
#include <iostream>
#include <memory>
#include <mutex>
#include <atomic>
#include <fuerte/FuerteLogger.h>

namespace arangodb { namespace fuerte { namespace js {

///////////////////////////////////////////////////////////////////////////////
// NConnectionBuilder /////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
NAN_METHOD(NConnectionBuilder::New) {
  try {
    if (info.IsConstructCall()) {
      NConnectionBuilder* obj = new NConnectionBuilder();
      obj->Wrap(info.This());
      info.GetReturnValue().Set(info.This());
    } else {
      v8::Local<v8::Function> builder = Nan::New(constructor());
      info.GetReturnValue().Set(Nan::NewInstance(builder).ToLocalChecked());
    }
 } catch(std::exception const& e){
   Nan::ThrowError("ConnectionBuilder.New binding failed with exception");
 }
}

NAN_METHOD(NConnectionBuilder::connect){
  try {
    v8::Local<v8::Function> connFunction = Nan::New(NConnection::constructor());
    const int argc = 1;
    v8::Local<v8::Value> argv[argc] = {info.This()};
    v8::Local<v8::Object> connInstance;
    bool ok = Nan::NewInstance(connFunction, argc, argv).ToLocal(&connInstance);
    if(!ok){
      Nan::ThrowError("ConnectionBuilder.connect binding failed with exception"
                      " - please check connection parameters and server");
      return;
    }
    info.GetReturnValue().Set(connInstance);
  } catch(std::exception const& e){
    std::cerr << "## DRIVER LEVEL EXCEPTION - START ##" << std::endl;
    std::cerr << e.what() << std::endl;
    std::cerr << "## DRIVER LEVEL EXCEPTION - END   ##" << std::endl;
    Nan::ThrowError("ConnectionBuilder.connect binding failed with exception"
                    " - Make sure the server is up and running");
  }
}

NAN_METHOD(NConnectionBuilder::host){
  try {
    if (info.Length() != 1 ) {
      Nan::ThrowTypeError("Wrong number of Arguments");
    }
    unwrapSelf<NConnectionBuilder>(info)->_cppClass.host(to<std::string>(info[0]));
    info.GetReturnValue().Set(info.This());
  } catch(std::exception const& e){
    std::cerr << "## DRIVER LEVEL EXCEPTION - START ##" << std::endl;
    std::cerr << e.what() << std::endl;
    std::cerr << "## DRIVER LEVEL EXCEPTION - END   ##" << std::endl;
    std::string errorMesasge = std::string("ConnectionBuilder.host binding failed with exception: ") + e.what();
    Nan::ThrowError(errorMesasge.c_str());
  }
}

NAN_METHOD(NConnectionBuilder::async){
  try {
    if (info.Length() != 1 ) {
      Nan::ThrowTypeError("Wrong number of Arguments");
    }
    unwrapSelf<NConnectionBuilder>(info)->_cppClass.async(to<bool>(info[0]));
    info.GetReturnValue().Set(info.This());
  } catch(std::exception const& e){
    Nan::ThrowError("ConnectionBuilder.async binding failed with exception");
  }
}

NAN_METHOD(NConnectionBuilder::user){
  try {
    if (info.Length() != 1 ) {
      Nan::ThrowTypeError("Wrong number of Arguments");
    }
    unwrapSelf<NConnectionBuilder>(info)->_cppClass.user(to<std::string>(info[0]));
    info.GetReturnValue().Set(info.This());
  } catch(std::exception const& e){
    Nan::ThrowError("ConnectionBuilder.user binding failed with exception");
  }
}

NAN_METHOD(NConnectionBuilder::password){
  try {
    if (info.Length() != 1 ) {
      Nan::ThrowTypeError("Wrong number of Arguments");
    }
    unwrapSelf<NConnectionBuilder>(info)->_cppClass.password(to<std::string>(info[0]));
    info.GetReturnValue().Set(info.This());
  } catch(std::exception const& e){
    Nan::ThrowError("ConnectionBuilder.user binding failed with exception");
  }
}

///////////////////////////////////////////////////////////////////////////////
// NConnection ////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
NAN_METHOD(NConnection::New) {
  try {
    if (info.IsConstructCall()) {
      NConnection* obj = new NConnection();
      if(info[0]->IsObject()){ // NConnectionBuilderObject -- exact type check?
        obj->_cppClass = unwrap<NConnectionBuilder>(info[0])->_cppClass.connect();
        if(obj->_cppClass == nullptr){
          Nan::ThrowError("Connection.New binding failed with exception - check connetction string");
          return;
        }
      }
      obj->Wrap(info.This());
      info.GetReturnValue().Set(info.This());
    } else {
      v8::Local<v8::Function> cons = Nan::New(constructor());
      info.GetReturnValue().Set(Nan::NewInstance(cons).ToLocalChecked());
    }
  } catch(std::exception const& e){
    Nan::ThrowError("Connection.New binding failed with exception");
  }
}

///////////////////////////////////////////////////////////////////////////////
// SendRequest ////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

// When we switch to c++14 we should use UniquePersistent and move it into
// the generalized lambda caputre avoiding the locking alltogehter
static std::map<fu::MessageID
               ,std::pair<v8::Persistent<v8::Function> // error
                         ,v8::Persistent<v8::Function> // success
                         >
              > callbackMap;

static std::mutex maplock;
static std::atomic<uint64_t> jsMessageID(0);

const std::string callbackErrorMessage(
  "##################################################\n"
  "#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#\n"
  "#@Failed to create request instance in callback.@#\n"
  "#@This could be an error in you JavaScript Code!@#\n"
  "#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#\n"
  "##################################################\n"
);

NAN_METHOD(NConnection::sendRequest) {
  try {
    if (info.Length() != 3 ) {
      Nan::ThrowTypeError("Not 3 Arguments");
    }

    if (!info[1]->IsFunction() || !info[2]->IsFunction()){
      Nan::ThrowTypeError("Callback is not a Function");
    }

    // get isolate - has node only one context??!?!? how do they work
    // context is probably a lighter version of isolate but does not allow threads
    v8::Isolate* iso = v8::Isolate::GetCurrent();

    uint64_t id = jsMessageID.fetch_add(1);
    {
      std::lock_guard<std::mutex> lock(maplock);
      auto& callbackPair = callbackMap[id]; //create map element
      auto jsOnErr = v8::Local<v8::Function>::Cast(info[1]);
      callbackPair.first.Reset(iso, jsOnErr);

      auto jsOnSucc = v8::Local<v8::Function>::Cast(info[2]);
      callbackPair.second.Reset(iso, jsOnSucc);
    }

    fu::OnErrorCallback err = [iso,id](unsigned err
                                ,std::unique_ptr<fu::Request> creq
                                ,std::unique_ptr<fu::Response> cres)
    {
      try {
        v8::HandleScope scope(iso);

        auto jsOnErr = v8::Local<v8::Function>();
        { //create local function and dispose persistent
          std::lock_guard<std::mutex> lock(maplock);
          auto& mapElement = callbackMap[id];
          jsOnErr = v8::Local<v8::Function>::New(iso,mapElement.first);
          mapElement.first.Reset();
          mapElement.second.Reset();
          callbackMap.erase(id);
        }

        // wrap request
        v8::Local<v8::Function> requestProto = v8::Local<v8::Function>::New(iso,NRequest::constructor());
        assert(!requestProto.IsEmpty());
        v8::Local<v8::Object> reqObj;
        bool ok = requestProto->NewInstance(iso->GetCurrentContext()).ToLocal(&reqObj);
        if(!ok) {
          std::cerr << callbackErrorMessage << std::endl;
          iso->ThrowException(v8::Exception::Error(v8::String::NewFromUtf8(iso,callbackErrorMessage.c_str())));
          return;
        }
        unwrap<NRequest>(reqObj)->setCppClass(std::move(creq));

        // wrap response
        v8::Local<v8::Function> responseProto = v8::Local<v8::Function>::New(iso,NResponse::constructor());
        assert(!responseProto.IsEmpty());
        v8::Local<v8::Object> resObj;
        ok = responseProto->NewInstance(iso->GetCurrentContext()).ToLocal(&resObj);
        if(!ok) {
          std::cerr << callbackErrorMessage << std::endl;
          iso->ThrowException(v8::Exception::Error(v8::String::NewFromUtf8(iso,callbackErrorMessage.c_str())));
          return;
        }
        unwrap<NResponse>(resObj)->setCppClass(std::move(cres));

        // build args
        const unsigned argc = 3;
        v8::Local<v8::Value> argv[argc] = { Nan::New<v8::Integer>(err), reqObj, resObj };

        // call
        jsOnErr->Call(iso->GetCurrentContext(), jsOnErr, argc, argv);
      } catch (std::exception const& e){
        std::string errorMesasge("Exception in success callback: ");
        errorMesasge += e.what();
        iso->ThrowException(v8::Exception::Error(v8::String::NewFromUtf8(iso,errorMesasge.c_str())));
      }
    };

    fu::OnSuccessCallback succ = [iso,id](std::unique_ptr<fu::Request> creq
                                   ,std::unique_ptr<fu::Response> cres)
    {
      try {
        FUERTE_LOG_NODE << "enter on fuerte-node success callback" << std::endl;
        v8::HandleScope scope(iso);

        auto jsOnSucc = v8::Local<v8::Function>();
        { // create locacl function and dispose persistent
          std::lock_guard<std::mutex> lock(maplock);
          auto& mapElement = callbackMap[id];
          //create local function
          jsOnSucc = v8::Local<v8::Function>::New(iso,callbackMap[id].second);

          //dispose map element
          mapElement.first.Reset(); // do not depend on kResetInDestructorFlag of Persistent
          mapElement.second.Reset();
          callbackMap.erase(id);
        }

            // other ways to do the same
            // v8::Local<v8::Function> requestProto = Nan::New(NConnection::constructor()); // with Nan
            // auto reqObj = Nan::NewInstance(requestProto).ToLocalChecked(); // with Nan
            // auto reqObj = requestProto->NewInstance(iso->GetCurrentContext()).ToLocalChecked(); // should not crash!

        // wrap request
        v8::Local<v8::Function> requestProto = v8::Local<v8::Function>::New(iso,NRequest::constructor());
        assert(!requestProto.IsEmpty());
        v8::Local<v8::Object> reqObj;
        bool ok = requestProto->NewInstance(iso->GetCurrentContext()).ToLocal(&reqObj);
        if(!ok) {
          std::cerr << callbackErrorMessage << std::endl;
          iso->ThrowException(v8::Exception::Error(v8::String::NewFromUtf8(iso,callbackErrorMessage.c_str())));
          return;
        }
        unwrap<NRequest>(reqObj)->setCppClass(std::move(creq));

        // wrap response
        v8::Local<v8::Function> responseProto = v8::Local<v8::Function>::New(iso,NResponse::constructor());
        assert(!responseProto.IsEmpty());
        v8::Local<v8::Object> resObj;
        ok = responseProto->NewInstance(iso->GetCurrentContext()).ToLocal(&resObj);
        if(!ok) {
          std::cerr << callbackErrorMessage << std::endl;
          iso->ThrowException(v8::Exception::Error(v8::String::NewFromUtf8(iso,callbackErrorMessage.c_str())));
          return;
        }
        unwrap<NResponse>(resObj)->setCppClass(std::move(cres));

        // build args
        const unsigned argc = 2;
        v8::Local<v8::Value> argv[argc] = { reqObj, resObj };

        // call
        //jsOnSucc->Call(v8::Null(iso), argc, argv);
        jsOnSucc->Call(iso->GetCurrentContext(), jsOnSucc, argc, argv);
        //jsOnSucc->Call(iso->GetCurrentContext(), iso->GetCurrentContext()->Global(), argc, argv);
      } catch (std::exception const& e){
        std::string errorMesasge("Exception in error callback: ");
        errorMesasge += e.what();
        iso->ThrowException(v8::Exception::Error(v8::String::NewFromUtf8(iso,errorMesasge.c_str())));
      }
    };

    //finally invoke the c++ callback
    auto req = std::unique_ptr<fu::Request>(new fu::Request(*(unwrap<NRequest>(info[0])->_cppClass))); //copy request so the old object stays valid
    unwrapSelf<NConnection>(info)->_cppClass->sendRequest(std::move(req), err, succ);
  } catch(std::exception const& e){
    Nan::ThrowError("Connection.sendRequest binding failed with exception");
    return;
  }
  FUERTE_LOG_NODE << "exit on fuerte-node success callback" << std::endl;
}

}}}
<|endoftext|>


<commit_before>/*
** Copyright 2020-2021 Centreon
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
**     http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
**
** For more information : contact@centreon.com
*/
#include "com/centreon/broker/tcp/tcp_async.hh"

#include <functional>

#include "com/centreon/broker/log_v2.hh"
#include "com/centreon/broker/pool.hh"
#include "com/centreon/exceptions/msg_fmt.hh"

using namespace com::centreon::exceptions;
using namespace com::centreon::broker;
using namespace com::centreon::broker::tcp;

tcp_async* tcp_async::_instance{nullptr};

/**
 * @brief Return the tcp_async singleton.
 *
 * @return A tcp_async singleton.
 */
tcp_async& tcp_async::instance() {
  assert(tcp_async::_instance);
  return *_instance;
}

/**
 * @brief Static function to initialize the tcp_sync object. It must be
 * executed before using the tcp_sync object and must be started after the
 * pool initialization.
 */
void tcp_async::load() {
  if (_instance == nullptr)
    _instance = new tcp_async();
  else
    log_v2::tcp()->error("tcp_async instance already started.");
}

/**
 * @brief This is the way to stop the tcp_sync instance. To call before the
 * pool unload since tcp_sync is heavily based on it.
 */
void tcp_async::unload() {
  if (_instance) {
    delete _instance;
    _instance = nullptr;
  }
}

/**
 * @brief Default constructor. Don't use it, it is private. Instead, call the
 * tcp_async::load() function to initialize it and then, use the instance()
 * method.
 */
tcp_async::tcp_async()
    : _clear_available_con_running(false),
      _strand{pool::instance().io_context()} {}

/**
 * @brief Stop the timer that clears available connections.
 */
void tcp_async::stop_timer() {
  log_v2::tcp()->trace("tcp_async::stop_timer");
  if (_clear_available_con_running) {
    std::promise<bool> p;
    std::future<bool> f(p.get_future());
    _clear_available_con_running = false;
    asio::post(_timer->get_executor(), [this, &p] {
      _timer->cancel();
      p.set_value(true);
    });
    f.get();
  }
  if (_timer)
    _timer.reset();
}

/**
 * @brief The destructor of tcp_async. You don't have to use it, instead, use
 * the unload() function.
 */
tcp_async::~tcp_async() noexcept {
  stop_timer();
  /* Before destroying the strand, we have to wait it is really empty. We post
   * a last action and wait it is over. */
  std::promise<bool> p;
  std::future<bool> f{p.get_future()};
  _strand.post([&p] { p.set_value(true); });
  f.get();
}

/**
 * @brief If the acceptor given in parameter has established a connection.
 * This method returns it. Otherwise, it returns an empty connection.
 *
 * @param acceptor The acceptor we want a connection to.
 *
 * @return A shared_ptr to a connection or nothing.
 */
tcp_connection::pointer tcp_async::get_connection(
    std::shared_ptr<asio::ip::tcp::acceptor> acceptor,
    uint32_t timeout_s) {
  auto end = std::chrono::system_clock::now() + std::chrono::seconds{timeout_s};
  do {
    std::promise<tcp_connection::pointer> p;
    std::future<tcp_connection::pointer> f{p.get_future()};
    _strand.post([&p, a = acceptor.get(), this] {
      auto found = _acceptor_available_con.find(a);
      if (found != _acceptor_available_con.end()) {
        tcp_connection::pointer retval = std::move(found->second.first);
        _acceptor_available_con.erase(found);
        p.set_value(retval);
      } else
        p.set_value(nullptr);
    });
    auto retval = f.get();
    if (retval)
      return retval;
    std::this_thread::sleep_for(std::chrono::milliseconds(10));
  } while (std::chrono::system_clock::now() < end);

  return nullptr;
}

bool tcp_async::contains_available_acceptor_connections(
    asio::ip::tcp::acceptor* acceptor) const {
  std::promise<bool> p;
  std::future<bool> f{p.get_future()};
  _strand.post([&p, &acceptor, this] {
    p.set_value(_acceptor_available_con.find(acceptor) !=
                _acceptor_available_con.end());
  });

  return f.get();
}

/**
 * @brief Create an ASIO acceptor listening on the given port. Once it is
 * operational, it begins to accept connections.
 *
 * @param port The port to listen on.
 *
 * @return The created acceptor as a shared_ptr.
 */
std::shared_ptr<asio::ip::tcp::acceptor> tcp_async::create_acceptor(
    uint16_t port) {
  auto retval(std::make_shared<asio::ip::tcp::acceptor>(
      pool::io_context(), asio::ip::tcp::endpoint(asio::ip::tcp::v4(), port)));

  asio::ip::tcp::acceptor::reuse_address option(true);
  retval->set_option(option);
  return retval;
}

/**
 * @brief this function works
 *
 * @param ec
 */
void tcp_async::_clear_available_con(asio::error_code ec) {
  if (ec)
    log_v2::core()->info("Available connections cleaning: {}", ec.message());
  else {
    log_v2::core()->debug("Available connections cleaning");
    std::time_t now = std::time(nullptr);
    _strand.post([now, this] {
      for (auto it = _acceptor_available_con.begin();
           it != _acceptor_available_con.end();) {
        if (now >= it->second.second + 10) {
          log_v2::tcp()->debug("Destroying connection to '{}'",
                               it->second.first->peer());
          it = _acceptor_available_con.erase(it);
        } else
          ++it;
      }
      if (!_acceptor_available_con.empty()) {
        _timer->expires_after(std::chrono::seconds(10));
        _timer->async_wait(std::bind(&tcp_async::_clear_available_con, this,
                                     std::placeholders::_1));
      } else
        _clear_available_con_running = false;
    });
  }
}

/**
 * @brief Starts the acceptor given in parameter. To accept the acceptor needs
 * the IO Context to be running. A timer is started/restarted so that in 10s
 * not used connections will be erased.
 *
 * @param acceptor The acceptor that you want it to accept.
 */
void tcp_async::start_acceptor(
    std::shared_ptr<asio::ip::tcp::acceptor> acceptor) {
  log_v2::tcp()->trace("Start acceptor");
  if (!_timer)
    _timer =
        std::make_unique<asio::steady_timer>(pool::instance().io_context());

  if (!_clear_available_con_running)
    _clear_available_con_running = true;

  log_v2::tcp()->debug("Reschedule available connections cleaning in 10s");
  _timer->expires_after(std::chrono::seconds(10));
  _timer->async_wait(
      std::bind(&tcp_async::_clear_available_con, this, std::placeholders::_1));

  tcp_connection::pointer new_connection =
      std::make_shared<tcp_connection>(pool::io_context());

  log_v2::tcp()->debug("Waiting for a connection");
  acceptor->async_accept(new_connection->socket(),
                         std::bind(&tcp_async::handle_accept, this, acceptor,
                                   new_connection, std::placeholders::_1));
}

/**
 * @brief Stop the acceptor.
 *
 * @param acceptor The acceptor to stop.
 */
void tcp_async::stop_acceptor(
    std::shared_ptr<asio::ip::tcp::acceptor> acceptor) {
  std::error_code ec;
  acceptor->cancel(ec);
  if (ec)
    log_v2::tcp()->warn("Error while cancelling acceptor: {}", ec.message());
  acceptor->close(ec);
  if (ec)
    log_v2::tcp()->warn("Error while closing acceptor: {}", ec.message());
}

/**
 * @brief The handler called after an async_accept.
 *
 * @param acceptor The acceptor accepting a connection.
 * @param new_connection The established connection.
 * @param ec An error code if any.
 */
void tcp_async::handle_accept(std::shared_ptr<asio::ip::tcp::acceptor> acceptor,
                              tcp_connection::pointer new_connection,
                              const asio::error_code& ec) {
  /* If we got a connection, we store it */
  if (!ec) {
    asio::error_code ecc;
    new_connection->update_peer(ecc);
    if (ecc)
      log_v2::tcp()->error(
          "tcp acceptor handling connection: unable to get peer endpoint: {}",
          ecc.message());
    else {
      std::time_t now = std::time(nullptr);
      _strand.post([new_connection, now, acceptor, this] {
        _acceptor_available_con.insert(std::make_pair(
            acceptor.get(), std::make_pair(new_connection, now)));
      });
      start_acceptor(acceptor);
    }
  } else
    log_v2::tcp()->info("TCP acceptor interrupted: {}", ec.message());
}

/**
 * @brief Creates a connection to the given host on the given port.
 *
 * @param host The host to connect to.
 * @param port The port to use for the connection.
 *
 * @return A shared_ptr to the connection or an empty shared_ptr.
 */
tcp_connection::pointer tcp_async::create_connection(std::string const& host,
                                                     uint16_t port) {
  log_v2::tcp()->trace("create connection to host {}:{}", host, port);
  tcp_connection::pointer conn =
      std::make_shared<tcp_connection>(pool::io_context(), host, port);
  asio::ip::tcp::socket& sock = conn->socket();

  asio::ip::tcp::resolver resolver(pool::io_context());
  asio::ip::tcp::resolver::query query(host, std::to_string(port));
  asio::ip::tcp::resolver::iterator it = resolver.resolve(query), end;

  std::error_code err{std::make_error_code(std::errc::host_unreachable)};

  // it can resolve multiple addresses like ipv4 or ipv6
  // We need to try all to find the first available socket
  for (; err && it != end; ++it) {
    sock.connect(*it, err);

    if (err)
      sock.close();
  }

  /* Connection refused */
  if (err.value() == 111) {
    log_v2::tcp()->error("TCP: Connection refused to {}:{}", host, port);
    throw std::system_error(err);
  } else if (err) {
    log_v2::tcp()->error("TCP: could not connect to {}:{}", host, port);
    throw msg_fmt(err.message());
  } else {
    asio::socket_base::keep_alive option{true};
    sock.set_option(option);
    return conn;
  }
}
<commit_msg>fix(tcp): attempt to add a keep alive on the acceptor side (#669)<commit_after>/*
** Copyright 2020-2021 Centreon
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
**     http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
**
** For more information : contact@centreon.com
*/
#include "com/centreon/broker/tcp/tcp_async.hh"

#include <functional>

#include "com/centreon/broker/log_v2.hh"
#include "com/centreon/broker/pool.hh"
#include "com/centreon/exceptions/msg_fmt.hh"

using namespace com::centreon::exceptions;
using namespace com::centreon::broker;
using namespace com::centreon::broker::tcp;

tcp_async* tcp_async::_instance{nullptr};

/**
 * @brief Return the tcp_async singleton.
 *
 * @return A tcp_async singleton.
 */
tcp_async& tcp_async::instance() {
  assert(tcp_async::_instance);
  return *_instance;
}

/**
 * @brief Static function to initialize the tcp_sync object. It must be
 * executed before using the tcp_sync object and must be started after the
 * pool initialization.
 */
void tcp_async::load() {
  if (_instance == nullptr)
    _instance = new tcp_async();
  else
    log_v2::tcp()->error("tcp_async instance already started.");
}

/**
 * @brief This is the way to stop the tcp_sync instance. To call before the
 * pool unload since tcp_sync is heavily based on it.
 */
void tcp_async::unload() {
  if (_instance) {
    delete _instance;
    _instance = nullptr;
  }
}

/**
 * @brief Default constructor. Don't use it, it is private. Instead, call the
 * tcp_async::load() function to initialize it and then, use the instance()
 * method.
 */
tcp_async::tcp_async()
    : _clear_available_con_running(false),
      _strand{pool::instance().io_context()} {}

/**
 * @brief Stop the timer that clears available connections.
 */
void tcp_async::stop_timer() {
  log_v2::tcp()->trace("tcp_async::stop_timer");
  if (_clear_available_con_running) {
    std::promise<bool> p;
    std::future<bool> f(p.get_future());
    _clear_available_con_running = false;
    asio::post(_timer->get_executor(), [this, &p] {
      _timer->cancel();
      p.set_value(true);
    });
    f.get();
  }
  if (_timer)
    _timer.reset();
}

/**
 * @brief The destructor of tcp_async. You don't have to use it, instead, use
 * the unload() function.
 */
tcp_async::~tcp_async() noexcept {
  stop_timer();
  /* Before destroying the strand, we have to wait it is really empty. We post
   * a last action and wait it is over. */
  std::promise<bool> p;
  std::future<bool> f{p.get_future()};
  _strand.post([&p] { p.set_value(true); });
  f.get();
}

/**
 * @brief If the acceptor given in parameter has established a connection.
 * This method returns it. Otherwise, it returns an empty connection.
 *
 * @param acceptor The acceptor we want a connection to.
 *
 * @return A shared_ptr to a connection or nothing.
 */
tcp_connection::pointer tcp_async::get_connection(
    std::shared_ptr<asio::ip::tcp::acceptor> acceptor,
    uint32_t timeout_s) {
  auto end = std::chrono::system_clock::now() + std::chrono::seconds{timeout_s};
  do {
    std::promise<tcp_connection::pointer> p;
    std::future<tcp_connection::pointer> f{p.get_future()};
    _strand.post([&p, a = acceptor.get(), this] {
      auto found = _acceptor_available_con.find(a);
      if (found != _acceptor_available_con.end()) {
        tcp_connection::pointer retval = std::move(found->second.first);
        _acceptor_available_con.erase(found);
        p.set_value(retval);
      } else
        p.set_value(nullptr);
    });
    auto retval = f.get();
    if (retval)
      return retval;
    std::this_thread::sleep_for(std::chrono::milliseconds(10));
  } while (std::chrono::system_clock::now() < end);

  return nullptr;
}

bool tcp_async::contains_available_acceptor_connections(
    asio::ip::tcp::acceptor* acceptor) const {
  std::promise<bool> p;
  std::future<bool> f{p.get_future()};
  _strand.post([&p, &acceptor, this] {
    p.set_value(_acceptor_available_con.find(acceptor) !=
                _acceptor_available_con.end());
  });

  return f.get();
}

/**
 * @brief Create an ASIO acceptor listening on the given port. Once it is
 * operational, it begins to accept connections.
 *
 * @param port The port to listen on.
 *
 * @return The created acceptor as a shared_ptr.
 */
std::shared_ptr<asio::ip::tcp::acceptor> tcp_async::create_acceptor(
    uint16_t port) {
  auto retval(std::make_shared<asio::ip::tcp::acceptor>(
      pool::io_context(), asio::ip::tcp::endpoint(asio::ip::tcp::v4(), port)));

  asio::ip::tcp::acceptor::reuse_address option(true);
  retval->set_option(option);
  return retval;
}

/**
 * @brief this function works
 *
 * @param ec
 */
void tcp_async::_clear_available_con(asio::error_code ec) {
  if (ec)
    log_v2::core()->info("Available connections cleaning: {}", ec.message());
  else {
    log_v2::core()->debug("Available connections cleaning");
    std::time_t now = std::time(nullptr);
    _strand.post([now, this] {
      for (auto it = _acceptor_available_con.begin();
           it != _acceptor_available_con.end();) {
        if (now >= it->second.second + 10) {
          log_v2::tcp()->debug("Destroying connection to '{}'",
                               it->second.first->peer());
          it = _acceptor_available_con.erase(it);
        } else
          ++it;
      }
      if (!_acceptor_available_con.empty()) {
        _timer->expires_after(std::chrono::seconds(10));
        _timer->async_wait(std::bind(&tcp_async::_clear_available_con, this,
                                     std::placeholders::_1));
      } else
        _clear_available_con_running = false;
    });
  }
}

/**
 * @brief Starts the acceptor given in parameter. To accept the acceptor needs
 * the IO Context to be running. A timer is started/restarted so that in 10s
 * not used connections will be erased.
 *
 * @param acceptor The acceptor that you want it to accept.
 */
void tcp_async::start_acceptor(
    std::shared_ptr<asio::ip::tcp::acceptor> acceptor) {
  log_v2::tcp()->trace("Start acceptor");
  if (!_timer)
    _timer =
        std::make_unique<asio::steady_timer>(pool::instance().io_context());

  if (!_clear_available_con_running)
    _clear_available_con_running = true;

  log_v2::tcp()->debug("Reschedule available connections cleaning in 10s");
  _timer->expires_after(std::chrono::seconds(10));
  _timer->async_wait(
      std::bind(&tcp_async::_clear_available_con, this, std::placeholders::_1));

  tcp_connection::pointer new_connection =
      std::make_shared<tcp_connection>(pool::io_context());

  log_v2::tcp()->debug("Waiting for a connection");
  acceptor->async_accept(new_connection->socket(),
                         std::bind(&tcp_async::handle_accept, this, acceptor,
                                   new_connection, std::placeholders::_1));
}

/**
 * @brief Stop the acceptor.
 *
 * @param acceptor The acceptor to stop.
 */
void tcp_async::stop_acceptor(
    std::shared_ptr<asio::ip::tcp::acceptor> acceptor) {
  std::error_code ec;
  acceptor->cancel(ec);
  if (ec)
    log_v2::tcp()->warn("Error while cancelling acceptor: {}", ec.message());
  acceptor->close(ec);
  if (ec)
    log_v2::tcp()->warn("Error while closing acceptor: {}", ec.message());
}

/**
 * @brief The handler called after an async_accept.
 *
 * @param acceptor The acceptor accepting a connection.
 * @param new_connection The established connection.
 * @param ec An error code if any.
 */
void tcp_async::handle_accept(std::shared_ptr<asio::ip::tcp::acceptor> acceptor,
                              tcp_connection::pointer new_connection,
                              const asio::error_code& ec) {
  /* If we got a connection, we store it */
  if (!ec) {
    asio::error_code ecc;
    new_connection->update_peer(ecc);
    if (ecc)
      log_v2::tcp()->error(
          "tcp acceptor handling connection: unable to get peer endpoint: {}",
          ecc.message());
    else {
      std::time_t now = std::time(nullptr);
      asio::ip::tcp::socket& sock = new_connection->socket();
      asio::socket_base::keep_alive option{true};
      sock.set_option(option);
      _strand.post([new_connection, now, acceptor, this] {
        _acceptor_available_con.insert(std::make_pair(
            acceptor.get(), std::make_pair(new_connection, now)));
      });
      start_acceptor(acceptor);
    }
  } else
    log_v2::tcp()->info("TCP acceptor interrupted: {}", ec.message());
}

/**
 * @brief Creates a connection to the given host on the given port.
 *
 * @param host The host to connect to.
 * @param port The port to use for the connection.
 *
 * @return A shared_ptr to the connection or an empty shared_ptr.
 */
tcp_connection::pointer tcp_async::create_connection(std::string const& host,
                                                     uint16_t port) {
  log_v2::tcp()->trace("create connection to host {}:{}", host, port);
  tcp_connection::pointer conn =
      std::make_shared<tcp_connection>(pool::io_context(), host, port);
  asio::ip::tcp::socket& sock = conn->socket();

  asio::ip::tcp::resolver resolver(pool::io_context());
  asio::ip::tcp::resolver::query query(host, std::to_string(port));
  asio::ip::tcp::resolver::iterator it = resolver.resolve(query), end;

  std::error_code err{std::make_error_code(std::errc::host_unreachable)};

  // it can resolve multiple addresses like ipv4 or ipv6
  // We need to try all to find the first available socket
  for (; err && it != end; ++it) {
    sock.connect(*it, err);

    if (err)
      sock.close();
  }

  /* Connection refused */
  if (err.value() == 111) {
    log_v2::tcp()->error("TCP: Connection refused to {}:{}", host, port);
    throw std::system_error(err);
  } else if (err) {
    log_v2::tcp()->error("TCP: could not connect to {}:{}", host, port);
    throw msg_fmt(err.message());
  } else {
    asio::socket_base::keep_alive option{true};
    sock.set_option(option);
    return conn;
  }
}
<|endoftext|>


<commit_before><commit_msg>add comment<commit_after><|endoftext|>


<commit_before>/*
 * Copyright 2021 Google LLC
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "gm/gm.h"
#include "include/core/SkBitmap.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkData.h"
#include "include/core/SkFont.h"
#include "include/core/SkPaint.h"
#include "include/core/SkSize.h"
#include "include/core/SkString.h"
#include "include/core/SkSurface.h"
#include "include/effects/SkGradientShader.h"
#include "include/effects/SkImageFilters.h"
#include "include/effects/SkRuntimeEffect.h"
#include "include/private/SkSLDefines.h"  // for kDefaultInlineThreshold
#include "include/utils/SkRandom.h"
#include "tests/Test.h"
#include "tools/Resources.h"
#include "tools/ToolUtils.h"

static const SkRect kRect = SkRect::MakeWH(1, 1);

template <typename T>
static void set_uniform(SkRuntimeShaderBuilder* builder, const char* name, const T& value) {
    SkRuntimeShaderBuilder::BuilderUniform uniform = builder->uniform(name);
    if (uniform.fVar) {
        uniform = value;
    }
}

static void test_one_permutation(skiatest::Reporter* r,
                                 SkSurface* surface,
                                 const char* testFile,
                                 const char* permutationSuffix,
                                 const SkRuntimeEffect::Options& options) {
    SkString resourcePath = SkStringPrintf("sksl/%s", testFile);
    sk_sp<SkData> shaderData = GetResourceAsData(resourcePath.c_str());
    if (!shaderData) {
        ERRORF(r, "%s%s: Unable to load file", testFile, permutationSuffix);
        return;
    }

    SkString shaderString{reinterpret_cast<const char*>(shaderData->bytes()), shaderData->size()};
    SkRuntimeEffect::Result result = SkRuntimeEffect::Make(shaderString, options);
    if (!result.effect) {
        ERRORF(r, "%s%s: %s", testFile, permutationSuffix, result.errorText.c_str());
        return;
    }

    SkRuntimeShaderBuilder builder(result.effect);
    set_uniform(&builder, "colorBlack",       SkV4{0, 0, 0, 1});
    set_uniform(&builder, "colorRed",         SkV4{1, 0, 0, 1});
    set_uniform(&builder, "colorGreen",       SkV4{0, 1, 0, 1});
    set_uniform(&builder, "colorBlue",        SkV4{0, 0, 1, 1});
    set_uniform(&builder, "colorWhite",       SkV4{1, 1, 1, 1});
    set_uniform(&builder, "testInputs",       SkV4{-1.25, 0, 0.75, 2.25});
    set_uniform(&builder, "testMatrix2x2",    std::array<float,4>{1, 2,
                                                                  3, 4});
    set_uniform(&builder, "testMatrix3x3",    std::array<float,9>{1, 2, 3,
                                                                  4, 5, 6,
                                                                  7, 8, 9});
    set_uniform(&builder, "unknownInput",     1.0f);
    set_uniform(&builder, "testMatrix2x2",    std::array<float,4>{1, 2,
                                                                  3, 4});
    set_uniform(&builder, "testMatrix3x3",    std::array<float,9>{1, 2, 3,
                                                                  4, 5, 6,
                                                                  7, 8, 9});

    sk_sp<SkShader> shader = builder.makeShader(/*localMatrix=*/nullptr, /*isOpaque=*/true);
    if (!shader) {
        ERRORF(r, "%s%s: Unable to build shader", testFile, permutationSuffix);
        return;
    }

    SkPaint paintShader;
    paintShader.setShader(shader);
    surface->getCanvas()->drawRect(kRect, paintShader);

    SkBitmap bitmap;
    REPORTER_ASSERT(r, bitmap.tryAllocPixels(surface->imageInfo()));
    REPORTER_ASSERT(r, surface->readPixels(bitmap.info(), bitmap.getPixels(), bitmap.rowBytes(),
                                           /*srcX=*/0, /*srcY=*/0));

    SkColor color = bitmap.getColor(0, 0);
    REPORTER_ASSERT(r, color == SkColorSetARGB(0xFF, 0x00, 0xFF, 0x00),
                    "Expected: solid green. Actual: A=%02X R=%02X G=%02X B=%02X.",
                    SkColorGetA(color), SkColorGetR(color), SkColorGetG(color), SkColorGetB(color));
}

static void test_permutations(skiatest::Reporter* r, SkSurface* surface, const char* testFile) {
    SkRuntimeEffect::Options options;
    options.inlineThreshold = 0;
    test_one_permutation(r, surface, testFile, " (NoInline)", options);

    options.inlineThreshold = SkSL::kDefaultInlineThreshold;
    test_one_permutation(r, surface, testFile, "", options);
}

static void test_cpu(skiatest::Reporter* r, const char* testFile) {
    const SkImageInfo info = SkImageInfo::MakeN32Premul(kRect.width(), kRect.height());
    sk_sp<SkSurface> surface(SkSurface::MakeRaster(info));

    test_permutations(r, surface.get(), testFile);
}

static void test_gpu(skiatest::Reporter* r, GrDirectContext* ctx, const char* testFile) {
    const SkImageInfo info = SkImageInfo::MakeN32Premul(kRect.width(), kRect.height());
    sk_sp<SkSurface> surface(SkSurface::MakeRenderTarget(ctx, SkBudgeted::kNo, info));

    test_permutations(r, surface.get(), testFile);
}

#define SKSL_TEST_CPU(name, path)                                   \
    DEF_TEST(name ## _CPU, r) {                                     \
        test_cpu(r, path);                                          \
    }
#define SKSL_TEST_GPU(name, path)                                   \
    DEF_GPUTEST_FOR_RENDERING_CONTEXTS(name ## _GPU, r, ctxInfo) {  \
        test_gpu(r, ctxInfo.directContext(), path);                 \
    }
#define SKSL_TEST(name, path) SKSL_TEST_CPU(name, path) SKSL_TEST_GPU(name, path)

SKSL_TEST(SkSLAssignmentOps,                   "folding/AssignmentOps.sksl")
SKSL_TEST(SkSLBoolFolding,                     "folding/BoolFolding.sksl")
SKSL_TEST(SkSLIntFoldingES2,                   "folding/IntFoldingES2.sksl")
SKSL_TEST(SkSLFloatFolding,                    "folding/FloatFolding.sksl")
SKSL_TEST(SkSLMatrixFoldingES2,                "folding/MatrixFoldingES2.sksl")
SKSL_TEST(SkSLSelfAssignment,                  "folding/SelfAssignment.sksl")
SKSL_TEST(SkSLShortCircuitBoolFolding,         "folding/ShortCircuitBoolFolding.sksl")
SKSL_TEST(SkSLVectorScalarFolding,             "folding/VectorScalarFolding.sksl")
SKSL_TEST(SkSLVectorVectorFolding,             "folding/VectorVectorFolding.sksl")

// TODO(skia:11052): SPIR-V does not yet honor `out` param semantics correctly
SKSL_TEST_CPU(SkSLInlinerHonorsGLSLOutParamSemantics,
              "inliner/InlinerHonorsGLSLOutParamSemantics.sksl")

SKSL_TEST(SkSLIntrinsicAbsFloat,               "intrinsics/AbsFloat.sksl")
SKSL_TEST(SkSLIntrinsicCeil,                   "intrinsics/Ceil.sksl")
SKSL_TEST(SkSLIntrinsicClampFloat,             "intrinsics/ClampFloat.sksl")
SKSL_TEST(SkSLIntrinsicMaxFloat,               "intrinsics/MaxFloat.sksl")
SKSL_TEST(SkSLIntrinsicMinFloat,               "intrinsics/MinFloat.sksl")
SKSL_TEST(SkSLIntrinsicMixFloat,               "intrinsics/MixFloat.sksl")
SKSL_TEST(SkSLIntrinsicSignFloat,              "intrinsics/SignFloat.sksl")

SKSL_TEST(SkSLArrayTypes,                      "shared/ArrayTypes.sksl")
SKSL_TEST(SkSLAssignment,                      "shared/Assignment.sksl")
SKSL_TEST(SkSLCastsRoundTowardZero,            "shared/CastsRoundTowardZero.sksl")
SKSL_TEST(SkSLCommaMixedTypes,                 "shared/CommaMixedTypes.sksl")
SKSL_TEST(SkSLCommaSideEffects,                "shared/CommaSideEffects.sksl")
SKSL_TEST(SkSLConstantIf,                      "shared/ConstantIf.sksl")
SKSL_TEST(SkSLConstVariableComparison,         "shared/ConstVariableComparison.sksl")
SKSL_TEST(SkSLDeadIfStatement,                 "shared/DeadIfStatement.sksl")
SKSL_TEST(SkSLDeadStripFunctions,              "shared/DeadStripFunctions.sksl")
SKSL_TEST(SkSLDependentInitializers,           "shared/DependentInitializers.sksl")
SKSL_TEST(SkSLEmptyBlocksES2,                  "shared/EmptyBlocksES2.sksl")
SKSL_TEST(SkSLForLoopControlFlow,              "shared/ForLoopControlFlow.sksl")
SKSL_TEST(SkSLFunctionArgTypeMatch,            "shared/FunctionArgTypeMatch.sksl")
SKSL_TEST(SkSLFunctionReturnTypeMatch,         "shared/FunctionReturnTypeMatch.sksl")
SKSL_TEST(SkSLFunctions,                       "shared/Functions.sksl")
SKSL_TEST(SkSLGeometricIntrinsics,             "shared/GeometricIntrinsics.sksl")
SKSL_TEST(SkSLHelloWorld,                      "shared/HelloWorld.sksl")
SKSL_TEST(SkSLHex,                             "shared/Hex.sksl")
SKSL_TEST(SkSLMatrices,                        "shared/Matrices.sksl")
SKSL_TEST(SkSLMatrixEquality,                  "shared/MatrixEquality.sksl")
SKSL_TEST(SkSLMultipleAssignments,             "shared/MultipleAssignments.sksl")
SKSL_TEST(SkSLNegatedVectorLiteral,            "shared/NegatedVectorLiteral.sksl")
SKSL_TEST(SkSLNumberCasts,                     "shared/NumberCasts.sksl")
SKSL_TEST(SkSLOperatorsES2,                    "shared/OperatorsES2.sksl")
SKSL_TEST(SkSLOutParams,                       "shared/OutParams.sksl")
SKSL_TEST(SkSLOutParamsNoInline,               "shared/OutParamsNoInline.sksl")
SKSL_TEST(SkSLOutParamsTricky,                 "shared/OutParamsTricky.sksl")
SKSL_TEST(SkSLResizeMatrix,                    "shared/ResizeMatrix.sksl")
SKSL_TEST(SkSLReturnsValueOnEveryPathES2,      "shared/ReturnsValueOnEveryPathES2.sksl")
SKSL_TEST(SkSLScalarConversionConstructorsES2, "shared/ScalarConversionConstructorsES2.sksl")
SKSL_TEST(SkSLStackingVectorCasts,             "shared/StackingVectorCasts.sksl")
SKSL_TEST(SkSLStaticIf,                        "shared/StaticIf.sksl")
SKSL_TEST(SkSLStructsInFunctions,              "shared/StructsInFunctions.sksl")
SKSL_TEST(SkSLSwizzleBoolConstants,            "shared/SwizzleBoolConstants.sksl")
SKSL_TEST(SkSLSwizzleByConstantIndex,          "shared/SwizzleByConstantIndex.sksl")
SKSL_TEST(SkSLSwizzleConstants,                "shared/SwizzleConstants.sksl")
SKSL_TEST(SkSLSwizzleLTRB,                     "shared/SwizzleLTRB.sksl")
SKSL_TEST(SkSLSwizzleOpt,                      "shared/SwizzleOpt.sksl")
SKSL_TEST(SkSLSwizzleScalar,                   "shared/SwizzleScalar.sksl")
SKSL_TEST(SkSLTernaryAsLValueEntirelyFoldable, "shared/TernaryAsLValueEntirelyFoldable.sksl")
SKSL_TEST(SkSLTernaryAsLValueFoldableTest,     "shared/TernaryAsLValueFoldableTest.sksl")
SKSL_TEST(SkSLTernaryExpression,               "shared/TernaryExpression.sksl")
SKSL_TEST(SkSLUnaryPositiveNegative,           "shared/UnaryPositiveNegative.sksl")
SKSL_TEST(SkSLUnusedVariables,                 "shared/UnusedVariables.sksl")
SKSL_TEST(SkSLVectorConstructors,              "shared/VectorConstructors.sksl")

/*
// Incompatible with Runtime Effects because calling a function before its definition is disallowed.
// (This was done to prevent recursion, as required by ES2.)
SKSL_TEST(SkSLFunctionPrototype,               "shared/FunctionPrototype.sksl")
*/

/*
TODO(skia:11209): enable these tests when Runtime Effects have support for ES3

SKSL_TEST(SkSLIntFoldingES3,                   "folding/IntFoldingES3.sksl")
SKSL_TEST(SkSLMatrixFoldingES3,                "folding/MatrixFoldingES3.sksl")

SKSL_TEST(SkSLIntrinsicAbsInt,                 "intrinsics/AbsInt.sksl")
SKSL_TEST(SkSLIntrinsicClampInt,               "intrinsics/ClampInt.sksl")
SKSL_TEST(SkSLIntrinsicMaxInt,                 "intrinsics/MaxInt.sksl")
SKSL_TEST(SkSLIntrinsicMinInt,                 "intrinsics/MinInt.sksl")
SKSL_TEST(SkSLIntrinsicMixBool,                "intrinsics/MixBool.sksl")
SKSL_TEST(SkSLIntrinsicSignInt,                "intrinsics/SignInt.sksl")

SKSL_TEST(SkSLArrayConstructors,               "shared/ArrayConstructors.sksl")
SKSL_TEST(SkSLDeadLoopVariable,                "shared/DeadLoopVariable.sksl")
SKSL_TEST(SkSLDoWhileControlFlow,              "shared/DoWhileControlFlow.sksl")
SKSL_TEST(SkSLEmptyBlocksES3,                  "shared/EmptyBlocksES3.sksl")
SKSL_TEST(SkSLHexUnsigned,                     "shared/HexUnsigned.sksl")
SKSL_TEST(SkSLMatricesNonsquare,               "shared/MatricesNonsquare.sksl")
SKSL_TEST(SkSLOperatorsES3,                    "shared/OperatorsES3.sksl")
SKSL_TEST(SkSLResizeMatrixNonsquare,           "shared/ResizeMatrixNonsquare.sksl")
SKSL_TEST(SkSLReturnsValueOnEveryPathES3,      "shared/ReturnsValueOnEveryPathES3.sksl")
SKSL_TEST(SkSLScalarConversionConstructorsES3, "shared/ScalarConversionConstructorsES3.sksl")
SKSL_TEST(SkSLSwizzleByIndex,                  "shared/SwizzleByIndex.sksl")
SKSL_TEST(SkSLWhileLoopControlFlow,            "shared/WhileLoopControlFlow.sksl")
*/
<commit_msg>Disable CommaSideEffects test on GPU.<commit_after>/*
 * Copyright 2021 Google LLC
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "gm/gm.h"
#include "include/core/SkBitmap.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkData.h"
#include "include/core/SkFont.h"
#include "include/core/SkPaint.h"
#include "include/core/SkSize.h"
#include "include/core/SkString.h"
#include "include/core/SkSurface.h"
#include "include/effects/SkGradientShader.h"
#include "include/effects/SkImageFilters.h"
#include "include/effects/SkRuntimeEffect.h"
#include "include/private/SkSLDefines.h"  // for kDefaultInlineThreshold
#include "include/utils/SkRandom.h"
#include "tests/Test.h"
#include "tools/Resources.h"
#include "tools/ToolUtils.h"

static const SkRect kRect = SkRect::MakeWH(1, 1);

template <typename T>
static void set_uniform(SkRuntimeShaderBuilder* builder, const char* name, const T& value) {
    SkRuntimeShaderBuilder::BuilderUniform uniform = builder->uniform(name);
    if (uniform.fVar) {
        uniform = value;
    }
}

static void test_one_permutation(skiatest::Reporter* r,
                                 SkSurface* surface,
                                 const char* testFile,
                                 const char* permutationSuffix,
                                 const SkRuntimeEffect::Options& options) {
    SkString resourcePath = SkStringPrintf("sksl/%s", testFile);
    sk_sp<SkData> shaderData = GetResourceAsData(resourcePath.c_str());
    if (!shaderData) {
        ERRORF(r, "%s%s: Unable to load file", testFile, permutationSuffix);
        return;
    }

    SkString shaderString{reinterpret_cast<const char*>(shaderData->bytes()), shaderData->size()};
    SkRuntimeEffect::Result result = SkRuntimeEffect::Make(shaderString, options);
    if (!result.effect) {
        ERRORF(r, "%s%s: %s", testFile, permutationSuffix, result.errorText.c_str());
        return;
    }

    SkRuntimeShaderBuilder builder(result.effect);
    set_uniform(&builder, "colorBlack",       SkV4{0, 0, 0, 1});
    set_uniform(&builder, "colorRed",         SkV4{1, 0, 0, 1});
    set_uniform(&builder, "colorGreen",       SkV4{0, 1, 0, 1});
    set_uniform(&builder, "colorBlue",        SkV4{0, 0, 1, 1});
    set_uniform(&builder, "colorWhite",       SkV4{1, 1, 1, 1});
    set_uniform(&builder, "testInputs",       SkV4{-1.25, 0, 0.75, 2.25});
    set_uniform(&builder, "testMatrix2x2",    std::array<float,4>{1, 2,
                                                                  3, 4});
    set_uniform(&builder, "testMatrix3x3",    std::array<float,9>{1, 2, 3,
                                                                  4, 5, 6,
                                                                  7, 8, 9});
    set_uniform(&builder, "unknownInput",     1.0f);
    set_uniform(&builder, "testMatrix2x2",    std::array<float,4>{1, 2,
                                                                  3, 4});
    set_uniform(&builder, "testMatrix3x3",    std::array<float,9>{1, 2, 3,
                                                                  4, 5, 6,
                                                                  7, 8, 9});

    sk_sp<SkShader> shader = builder.makeShader(/*localMatrix=*/nullptr, /*isOpaque=*/true);
    if (!shader) {
        ERRORF(r, "%s%s: Unable to build shader", testFile, permutationSuffix);
        return;
    }

    SkPaint paintShader;
    paintShader.setShader(shader);
    surface->getCanvas()->drawRect(kRect, paintShader);

    SkBitmap bitmap;
    REPORTER_ASSERT(r, bitmap.tryAllocPixels(surface->imageInfo()));
    REPORTER_ASSERT(r, surface->readPixels(bitmap.info(), bitmap.getPixels(), bitmap.rowBytes(),
                                           /*srcX=*/0, /*srcY=*/0));

    SkColor color = bitmap.getColor(0, 0);
    REPORTER_ASSERT(r, color == SkColorSetARGB(0xFF, 0x00, 0xFF, 0x00),
                    "Expected: solid green. Actual: A=%02X R=%02X G=%02X B=%02X.",
                    SkColorGetA(color), SkColorGetR(color), SkColorGetG(color), SkColorGetB(color));
}

static void test_permutations(skiatest::Reporter* r, SkSurface* surface, const char* testFile) {
    SkRuntimeEffect::Options options;
    options.inlineThreshold = 0;
    test_one_permutation(r, surface, testFile, " (NoInline)", options);

    options.inlineThreshold = SkSL::kDefaultInlineThreshold;
    test_one_permutation(r, surface, testFile, "", options);
}

static void test_cpu(skiatest::Reporter* r, const char* testFile) {
    const SkImageInfo info = SkImageInfo::MakeN32Premul(kRect.width(), kRect.height());
    sk_sp<SkSurface> surface(SkSurface::MakeRaster(info));

    test_permutations(r, surface.get(), testFile);
}

static void test_gpu(skiatest::Reporter* r, GrDirectContext* ctx, const char* testFile) {
    const SkImageInfo info = SkImageInfo::MakeN32Premul(kRect.width(), kRect.height());
    sk_sp<SkSurface> surface(SkSurface::MakeRenderTarget(ctx, SkBudgeted::kNo, info));

    test_permutations(r, surface.get(), testFile);
}

#define SKSL_TEST_CPU(name, path)                                   \
    DEF_TEST(name ## _CPU, r) {                                     \
        test_cpu(r, path);                                          \
    }
#define SKSL_TEST_GPU(name, path)                                   \
    DEF_GPUTEST_FOR_RENDERING_CONTEXTS(name ## _GPU, r, ctxInfo) {  \
        test_gpu(r, ctxInfo.directContext(), path);                 \
    }
#define SKSL_TEST(name, path) SKSL_TEST_CPU(name, path) SKSL_TEST_GPU(name, path)

SKSL_TEST(SkSLAssignmentOps,                   "folding/AssignmentOps.sksl")
SKSL_TEST(SkSLBoolFolding,                     "folding/BoolFolding.sksl")
SKSL_TEST(SkSLIntFoldingES2,                   "folding/IntFoldingES2.sksl")
SKSL_TEST(SkSLFloatFolding,                    "folding/FloatFolding.sksl")
SKSL_TEST(SkSLMatrixFoldingES2,                "folding/MatrixFoldingES2.sksl")
SKSL_TEST(SkSLSelfAssignment,                  "folding/SelfAssignment.sksl")
SKSL_TEST(SkSLShortCircuitBoolFolding,         "folding/ShortCircuitBoolFolding.sksl")
SKSL_TEST(SkSLVectorScalarFolding,             "folding/VectorScalarFolding.sksl")
SKSL_TEST(SkSLVectorVectorFolding,             "folding/VectorVectorFolding.sksl")

// TODO(skia:11052): SPIR-V does not yet honor `out` param semantics correctly
SKSL_TEST_CPU(SkSLInlinerHonorsGLSLOutParamSemantics,
              "inliner/InlinerHonorsGLSLOutParamSemantics.sksl")

SKSL_TEST(SkSLIntrinsicAbsFloat,               "intrinsics/AbsFloat.sksl")
SKSL_TEST(SkSLIntrinsicCeil,                   "intrinsics/Ceil.sksl")
SKSL_TEST(SkSLIntrinsicClampFloat,             "intrinsics/ClampFloat.sksl")
SKSL_TEST(SkSLIntrinsicMaxFloat,               "intrinsics/MaxFloat.sksl")
SKSL_TEST(SkSLIntrinsicMinFloat,               "intrinsics/MinFloat.sksl")
SKSL_TEST(SkSLIntrinsicMixFloat,               "intrinsics/MixFloat.sksl")
SKSL_TEST(SkSLIntrinsicSignFloat,              "intrinsics/SignFloat.sksl")

SKSL_TEST(SkSLArrayTypes,                      "shared/ArrayTypes.sksl")
SKSL_TEST(SkSLAssignment,                      "shared/Assignment.sksl")
SKSL_TEST(SkSLCastsRoundTowardZero,            "shared/CastsRoundTowardZero.sksl")
SKSL_TEST(SkSLCommaMixedTypes,                 "shared/CommaMixedTypes.sksl")
// This test causes the Adreno 330 driver to crash, and does not pass on Quadro P400 in wasm.
// The CPU test confirms that we can get it right, even if not all drivers do.
SKSL_TEST_CPU(SkSLCommaSideEffects,            "shared/CommaSideEffects.sksl")
SKSL_TEST(SkSLConstantIf,                      "shared/ConstantIf.sksl")
SKSL_TEST(SkSLConstVariableComparison,         "shared/ConstVariableComparison.sksl")
SKSL_TEST(SkSLDeadIfStatement,                 "shared/DeadIfStatement.sksl")
SKSL_TEST(SkSLDeadStripFunctions,              "shared/DeadStripFunctions.sksl")
SKSL_TEST(SkSLDependentInitializers,           "shared/DependentInitializers.sksl")
SKSL_TEST(SkSLEmptyBlocksES2,                  "shared/EmptyBlocksES2.sksl")
SKSL_TEST(SkSLForLoopControlFlow,              "shared/ForLoopControlFlow.sksl")
SKSL_TEST(SkSLFunctionArgTypeMatch,            "shared/FunctionArgTypeMatch.sksl")
SKSL_TEST(SkSLFunctionReturnTypeMatch,         "shared/FunctionReturnTypeMatch.sksl")
SKSL_TEST(SkSLFunctions,                       "shared/Functions.sksl")
SKSL_TEST(SkSLGeometricIntrinsics,             "shared/GeometricIntrinsics.sksl")
SKSL_TEST(SkSLHelloWorld,                      "shared/HelloWorld.sksl")
SKSL_TEST(SkSLHex,                             "shared/Hex.sksl")
SKSL_TEST(SkSLMatrices,                        "shared/Matrices.sksl")
SKSL_TEST(SkSLMatrixEquality,                  "shared/MatrixEquality.sksl")
SKSL_TEST(SkSLMultipleAssignments,             "shared/MultipleAssignments.sksl")
SKSL_TEST(SkSLNegatedVectorLiteral,            "shared/NegatedVectorLiteral.sksl")
SKSL_TEST(SkSLNumberCasts,                     "shared/NumberCasts.sksl")
SKSL_TEST(SkSLOperatorsES2,                    "shared/OperatorsES2.sksl")
SKSL_TEST(SkSLOutParams,                       "shared/OutParams.sksl")
SKSL_TEST(SkSLOutParamsNoInline,               "shared/OutParamsNoInline.sksl")
SKSL_TEST(SkSLOutParamsTricky,                 "shared/OutParamsTricky.sksl")
SKSL_TEST(SkSLResizeMatrix,                    "shared/ResizeMatrix.sksl")
SKSL_TEST(SkSLReturnsValueOnEveryPathES2,      "shared/ReturnsValueOnEveryPathES2.sksl")
SKSL_TEST(SkSLScalarConversionConstructorsES2, "shared/ScalarConversionConstructorsES2.sksl")
SKSL_TEST(SkSLStackingVectorCasts,             "shared/StackingVectorCasts.sksl")
SKSL_TEST(SkSLStaticIf,                        "shared/StaticIf.sksl")
SKSL_TEST(SkSLStructsInFunctions,              "shared/StructsInFunctions.sksl")
SKSL_TEST(SkSLSwizzleBoolConstants,            "shared/SwizzleBoolConstants.sksl")
SKSL_TEST(SkSLSwizzleByConstantIndex,          "shared/SwizzleByConstantIndex.sksl")
SKSL_TEST(SkSLSwizzleConstants,                "shared/SwizzleConstants.sksl")
SKSL_TEST(SkSLSwizzleLTRB,                     "shared/SwizzleLTRB.sksl")
SKSL_TEST(SkSLSwizzleOpt,                      "shared/SwizzleOpt.sksl")
SKSL_TEST(SkSLSwizzleScalar,                   "shared/SwizzleScalar.sksl")
SKSL_TEST(SkSLTernaryAsLValueEntirelyFoldable, "shared/TernaryAsLValueEntirelyFoldable.sksl")
SKSL_TEST(SkSLTernaryAsLValueFoldableTest,     "shared/TernaryAsLValueFoldableTest.sksl")
SKSL_TEST(SkSLTernaryExpression,               "shared/TernaryExpression.sksl")
SKSL_TEST(SkSLUnaryPositiveNegative,           "shared/UnaryPositiveNegative.sksl")
SKSL_TEST(SkSLUnusedVariables,                 "shared/UnusedVariables.sksl")
SKSL_TEST(SkSLVectorConstructors,              "shared/VectorConstructors.sksl")

/*
// Incompatible with Runtime Effects because calling a function before its definition is disallowed.
// (This was done to prevent recursion, as required by ES2.)
SKSL_TEST(SkSLFunctionPrototype,               "shared/FunctionPrototype.sksl")
*/

/*
TODO(skia:11209): enable these tests when Runtime Effects have support for ES3

SKSL_TEST(SkSLIntFoldingES3,                   "folding/IntFoldingES3.sksl")
SKSL_TEST(SkSLMatrixFoldingES3,                "folding/MatrixFoldingES3.sksl")

SKSL_TEST(SkSLIntrinsicAbsInt,                 "intrinsics/AbsInt.sksl")
SKSL_TEST(SkSLIntrinsicClampInt,               "intrinsics/ClampInt.sksl")
SKSL_TEST(SkSLIntrinsicMaxInt,                 "intrinsics/MaxInt.sksl")
SKSL_TEST(SkSLIntrinsicMinInt,                 "intrinsics/MinInt.sksl")
SKSL_TEST(SkSLIntrinsicMixBool,                "intrinsics/MixBool.sksl")
SKSL_TEST(SkSLIntrinsicSignInt,                "intrinsics/SignInt.sksl")

SKSL_TEST(SkSLArrayConstructors,               "shared/ArrayConstructors.sksl")
SKSL_TEST(SkSLDeadLoopVariable,                "shared/DeadLoopVariable.sksl")
SKSL_TEST(SkSLDoWhileControlFlow,              "shared/DoWhileControlFlow.sksl")
SKSL_TEST(SkSLEmptyBlocksES3,                  "shared/EmptyBlocksES3.sksl")
SKSL_TEST(SkSLHexUnsigned,                     "shared/HexUnsigned.sksl")
SKSL_TEST(SkSLMatricesNonsquare,               "shared/MatricesNonsquare.sksl")
SKSL_TEST(SkSLOperatorsES3,                    "shared/OperatorsES3.sksl")
SKSL_TEST(SkSLResizeMatrixNonsquare,           "shared/ResizeMatrixNonsquare.sksl")
SKSL_TEST(SkSLReturnsValueOnEveryPathES3,      "shared/ReturnsValueOnEveryPathES3.sksl")
SKSL_TEST(SkSLScalarConversionConstructorsES3, "shared/ScalarConversionConstructorsES3.sksl")
SKSL_TEST(SkSLSwizzleByIndex,                  "shared/SwizzleByIndex.sksl")
SKSL_TEST(SkSLWhileLoopControlFlow,            "shared/WhileLoopControlFlow.sksl")
*/
<|endoftext|>


<commit_before><commit_msg>test exception when dividing by zero<commit_after><|endoftext|>