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

#include <conf.hpp>
#include <dbus/util.hpp>
#include <iostream>
#include <sdbusplus/bus.hpp>
#include <set>
#include <unordered_map>

static constexpr bool DEBUG = false; // enable to print found configuration

std::map<std::string, struct sensor> SensorConfig = {};
std::map<int64_t, PIDConf> ZoneConfig = {};
std::map<int64_t, struct zone> ZoneDetailsConfig = {};

constexpr const char *pidConfigurationInterface =
    "xyz.openbmc_project.Configuration.Pid";
constexpr const char *objectManagerInterface =
    "org.freedesktop.DBus.ObjectManager";
constexpr const char *pidZoneConfigurationInterface =
    "xyz.openbmc_project.Configuration.Pid.Zone";
constexpr const char *sensorInterface = "xyz.openbmc_project.Sensor.Value";
constexpr const char *pwmInterface = "xyz.openbmc_project.Control.FanPwm";

namespace dbus_configuration
{

bool findSensor(const std::unordered_map<std::string, std::string> &sensors,
                const std::string &search,
                std::pair<std::string, std::string> &sensor)
{
    for (const auto &s : sensors)
    {
        if (s.first.find(search) != std::string::npos)
        {
            sensor = s;
            return true;
        }
    }
    return false;
}

// this function prints the configuration into a form similar to the cpp
// generated code to help in verification, should be turned off during normal
// use
void debugPrint(void)
{
    // print sensor config
    std::cout << "sensor config:\n";
    std::cout << "{\n";
    for (auto &pair : SensorConfig)
    {

        std::cout << "\t{" << pair.first << ",\n\t\t{";
        std::cout << pair.second.type << ", ";
        std::cout << pair.second.readpath << ", ";
        std::cout << pair.second.writepath << ", ";
        std::cout << pair.second.min << ", ";
        std::cout << pair.second.max << ", ";
        std::cout << pair.second.timeout << "},\n\t},\n";
    }
    std::cout << "}\n\n";
    std::cout << "ZoneDetailsConfig\n";
    std::cout << "{\n";
    for (auto &zone : ZoneDetailsConfig)
    {
        std::cout << "\t{" << zone.first << ",\n";
        std::cout << "\t\t{" << zone.second.minthermalrpm << ", ";
        std::cout << zone.second.failsafepercent << "}\n\t},\n";
    }
    std::cout << "}\n\n";
    std::cout << "ZoneConfig\n";
    std::cout << "{\n";
    for (auto &zone : ZoneConfig)
    {
        std::cout << "\t{" << zone.first << "\n";
        for (auto &pidconf : zone.second)
        {
            std::cout << "\t\t{" << pidconf.first << ",\n";
            std::cout << "\t\t\t{" << pidconf.second.type << ",\n";
            std::cout << "\t\t\t{";
            for (auto &input : pidconf.second.inputs)
            {
                std::cout << "\n\t\t\t" << input << ",\n";
            }
            std::cout << "\t\t\t}\n";
            std::cout << "\t\t\t" << pidconf.second.setpoint << ",\n";
            std::cout << "\t\t\t{" << pidconf.second.info.ts << ",\n";
            std::cout << "\t\t\t" << pidconf.second.info.p_c << ",\n";
            std::cout << "\t\t\t" << pidconf.second.info.i_c << ",\n";
            std::cout << "\t\t\t" << pidconf.second.info.ff_off << ",\n";
            std::cout << "\t\t\t" << pidconf.second.info.ff_gain << ",\n";
            std::cout << "\t\t\t{" << pidconf.second.info.i_lim.min << ","
                      << pidconf.second.info.i_lim.max << "},\n";
            std::cout << "\t\t\t{" << pidconf.second.info.out_lim.min << ","
                      << pidconf.second.info.out_lim.max << "},\n";
            std::cout << "\t\t\t" << pidconf.second.info.slew_neg << ",\n";
            std::cout << "\t\t\t" << pidconf.second.info.slew_pos << ",\n";
            std::cout << "\t\t\t}\n\t\t}\n";
        }
        std::cout << "\t},\n";
    }
    std::cout << "}\n\n";
}

void init(sdbusplus::bus::bus &bus)
{
    using ManagedObjectType = std::unordered_map<
        sdbusplus::message::object_path,
        std::unordered_map<
            std::string,
            std::unordered_map<std::string,
                               sdbusplus::message::variant<
                                   uint64_t, int64_t, double, std::string,
                                   std::vector<std::string>>>>>;
    auto mapper =
        bus.new_method_call("xyz.openbmc_project.ObjectMapper",
                            "/xyz/openbmc_project/object_mapper",
                            "xyz.openbmc_project.ObjectMapper", "GetSubTree");
    mapper.append("", 0,
                  std::array<const char *, 5>{objectManagerInterface,
                                              pidConfigurationInterface,
                                              pidZoneConfigurationInterface,
                                              sensorInterface, pwmInterface});
    auto resp = bus.call(mapper);
    if (resp.is_method_error())
    {
        throw std::runtime_error("ObjectMapper Call Failure");
    }
    std::unordered_map<
        std::string, std::unordered_map<std::string, std::vector<std::string>>>
        respData;

    resp.read(respData);
    if (respData.empty())
    {
        throw std::runtime_error("No configuration data available from Mapper");
    }
    // create a map of pair of <has pid configuration, ObjectManager path>
    std::unordered_map<std::string, std::pair<bool, std::string>> owners;
    // and a map of <path, interface> for sensors
    std::unordered_map<std::string, std::string> sensors;
    for (const auto &objectPair : respData)
    {
        for (const auto &ownerPair : objectPair.second)
        {
            auto &owner = owners[ownerPair.first];
            for (const std::string &interface : ownerPair.second)
            {

                if (interface == objectManagerInterface)
                {
                    owner.second = objectPair.first;
                }
                if (interface == pidConfigurationInterface ||
                    interface == pidZoneConfigurationInterface)
                {
                    owner.first = true;
                }
                if (interface == sensorInterface || interface == pwmInterface)
                {
                    // we're not interested in pwm sensors, just pwm control
                    if (interface == sensorInterface &&
                        objectPair.first.find("pwm") != std::string::npos)
                    {
                        continue;
                    }
                    sensors[objectPair.first] = interface;
                }
            }
        }
    }
    ManagedObjectType configurations;
    for (const auto &owner : owners)
    {
        // skip if no pid configuration (means probably a sensor)
        if (!owner.second.first)
        {
            continue;
        }
        auto endpoint = bus.new_method_call(
            owner.first.c_str(), owner.second.second.c_str(),
            "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
        auto responce = bus.call(endpoint);
        if (responce.is_method_error())
        {
            throw std::runtime_error("Error getting managed objects from " +
                                     owner.first);
        }
        ManagedObjectType configuration;
        responce.read(configuration);
        for (auto &pathPair : configuration)
        {
            if (pathPair.second.find(pidConfigurationInterface) !=
                    pathPair.second.end() ||
                pathPair.second.find(pidZoneConfigurationInterface) !=
                    pathPair.second.end())
            {
                configurations.emplace(pathPair);
            }
        }
    }
    for (const auto &configuration : configurations)
    {
        auto findZone =
            configuration.second.find(pidZoneConfigurationInterface);
        if (findZone != configuration.second.end())
        {
            const auto &zone = findZone->second;
            auto &details =
                ZoneDetailsConfig[sdbusplus::message::variant_ns::get<uint64_t>(
                    zone.at("Index"))];
            details.minthermalrpm = mapbox::util::apply_visitor(
                VariantToFloatVisitor(), zone.at("MinThermalRpm"));
            details.failsafepercent = mapbox::util::apply_visitor(
                VariantToFloatVisitor(), zone.at("FailSafePercent"));
        }
        auto findBase = configuration.second.find(pidConfigurationInterface);
        if (findBase == configuration.second.end())
        {
            continue;
        }
        // if the base configuration is found, these are required
        const auto &base = configuration.second.at(pidConfigurationInterface);
        const auto &iLim = configuration.second.at(pidConfigurationInterface +
                                                   std::string(".ILimit"));
        const auto &outLim = configuration.second.at(pidConfigurationInterface +
                                                     std::string(".OutLimit"));
        PIDConf &conf =
            ZoneConfig[sdbusplus::message::variant_ns::get<uint64_t>(
                base.at("Index"))];
        struct controller_info &info =
            conf[sdbusplus::message::variant_ns::get<std::string>(
                base.at("Name"))];
        info.type =
            sdbusplus::message::variant_ns::get<std::string>(base.at("Class"));
        // todo: auto generation yaml -> c script seems to discard this value
        // for fans, verify this is okay
        if (info.type == "fan")
        {
            info.setpoint = 0;
        }
        else
        {
            info.setpoint = mapbox::util::apply_visitor(VariantToFloatVisitor(),
                                                        base.at("SetPoint"));
        }
        info.info.ts = 1.0; // currently unused
        info.info.p_c = mapbox::util::apply_visitor(VariantToFloatVisitor(),
                                                    base.at("PCoefficient"));
        info.info.i_c = mapbox::util::apply_visitor(VariantToFloatVisitor(),
                                                    base.at("ICoefficient"));
        info.info.ff_off = mapbox::util::apply_visitor(
            VariantToFloatVisitor(), base.at("FFOffCoefficient"));
        info.info.ff_gain = mapbox::util::apply_visitor(
            VariantToFloatVisitor(), base.at("FFGainCoefficient"));
        auto value = mapbox::util::apply_visitor(VariantToFloatVisitor(),
                                                 iLim.at("Max"));
        info.info.i_lim.max = value;
        info.info.i_lim.min = mapbox::util::apply_visitor(
            VariantToFloatVisitor(), iLim.at("Min"));
        info.info.out_lim.max = mapbox::util::apply_visitor(
            VariantToFloatVisitor(), outLim.at("Max"));
        info.info.out_lim.min = mapbox::util::apply_visitor(
            VariantToFloatVisitor(), outLim.at("Min"));
        info.info.slew_neg = mapbox::util::apply_visitor(
            VariantToFloatVisitor(), base.at("SlewNeg"));
        info.info.slew_pos = mapbox::util::apply_visitor(
            VariantToFloatVisitor(), base.at("SlewPos"));

        std::pair<std::string, std::string> sensorPathIfacePair;
        std::vector<std::string> sensorNames =
            sdbusplus::message::variant_ns::get<std::vector<std::string>>(
                base.at("Inputs"));

        for (const std::string &sensorName : sensorNames)
        {
            std::string name = sensorName;
            // replace spaces with underscores to be legal on dbus
            std::replace(name.begin(), name.end(), ' ', '_');

            if (!findSensor(sensors, name, sensorPathIfacePair))
            {
                throw std::runtime_error(
                    "Could not map configuration to sensor " + name);
            }
            if (sensorPathIfacePair.second == sensorInterface)
            {
                info.inputs.push_back(name);
                auto &config = SensorConfig[name];
                config.type = sdbusplus::message::variant_ns::get<std::string>(
                    base.at("Class"));
                config.readpath = sensorPathIfacePair.first;
                // todo: maybe un-hardcode this if we run into slower timeouts
                // with sensors
                if (config.type == "temp")
                {
                    config.timeout = 500;
                }
            }
            if (sensorPathIfacePair.second == pwmInterface)
            {
                // copy so we can modify it
                for (std::string otherSensor : sensorNames)
                {
                    if (otherSensor == sensorName)
                    {
                        continue;
                    }
                    std::replace(otherSensor.begin(), otherSensor.end(), ' ',
                                 '_');
                    auto &config = SensorConfig[otherSensor];
                    config.writepath = sensorPathIfacePair.first;
                    // todo: un-hardcode this if there are fans with different
                    // ranges
                    config.max = 255;
                    config.min = 0;
                }
            }
        }
    }
    if (DEBUG)
    {
        debugPrint();
    }
}
} // namespace dbus_configuration
<commit_msg>dbusConfiguration: restart on configuration change<commit_after>/*
// Copyright (c) 2018 Intel Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
*/

#include <chrono>
#include <conf.hpp>
#include <dbus/util.hpp>
#include <functional>
#include <iostream>
#include <sdbusplus/bus.hpp>
#include <sdbusplus/bus/match.hpp>
#include <set>
#include <thread>
#include <unordered_map>

static constexpr bool DEBUG = false; // enable to print found configuration

std::map<std::string, struct sensor> SensorConfig = {};
std::map<int64_t, PIDConf> ZoneConfig = {};
std::map<int64_t, struct zone> ZoneDetailsConfig = {};

constexpr const char *pidConfigurationInterface =
    "xyz.openbmc_project.Configuration.Pid";
constexpr const char *objectManagerInterface =
    "org.freedesktop.DBus.ObjectManager";
constexpr const char *pidZoneConfigurationInterface =
    "xyz.openbmc_project.Configuration.Pid.Zone";
constexpr const char *sensorInterface = "xyz.openbmc_project.Sensor.Value";
constexpr const char *pwmInterface = "xyz.openbmc_project.Control.FanPwm";

namespace dbus_configuration
{

bool findSensor(const std::unordered_map<std::string, std::string> &sensors,
                const std::string &search,
                std::pair<std::string, std::string> &sensor)
{
    for (const auto &s : sensors)
    {
        if (s.first.find(search) != std::string::npos)
        {
            sensor = s;
            return true;
        }
    }
    return false;
}

// this function prints the configuration into a form similar to the cpp
// generated code to help in verification, should be turned off during normal
// use
void debugPrint(void)
{
    // print sensor config
    std::cout << "sensor config:\n";
    std::cout << "{\n";
    for (auto &pair : SensorConfig)
    {

        std::cout << "\t{" << pair.first << ",\n\t\t{";
        std::cout << pair.second.type << ", ";
        std::cout << pair.second.readpath << ", ";
        std::cout << pair.second.writepath << ", ";
        std::cout << pair.second.min << ", ";
        std::cout << pair.second.max << ", ";
        std::cout << pair.second.timeout << "},\n\t},\n";
    }
    std::cout << "}\n\n";
    std::cout << "ZoneDetailsConfig\n";
    std::cout << "{\n";
    for (auto &zone : ZoneDetailsConfig)
    {
        std::cout << "\t{" << zone.first << ",\n";
        std::cout << "\t\t{" << zone.second.minthermalrpm << ", ";
        std::cout << zone.second.failsafepercent << "}\n\t},\n";
    }
    std::cout << "}\n\n";
    std::cout << "ZoneConfig\n";
    std::cout << "{\n";
    for (auto &zone : ZoneConfig)
    {
        std::cout << "\t{" << zone.first << "\n";
        for (auto &pidconf : zone.second)
        {
            std::cout << "\t\t{" << pidconf.first << ",\n";
            std::cout << "\t\t\t{" << pidconf.second.type << ",\n";
            std::cout << "\t\t\t{";
            for (auto &input : pidconf.second.inputs)
            {
                std::cout << "\n\t\t\t" << input << ",\n";
            }
            std::cout << "\t\t\t}\n";
            std::cout << "\t\t\t" << pidconf.second.setpoint << ",\n";
            std::cout << "\t\t\t{" << pidconf.second.info.ts << ",\n";
            std::cout << "\t\t\t" << pidconf.second.info.p_c << ",\n";
            std::cout << "\t\t\t" << pidconf.second.info.i_c << ",\n";
            std::cout << "\t\t\t" << pidconf.second.info.ff_off << ",\n";
            std::cout << "\t\t\t" << pidconf.second.info.ff_gain << ",\n";
            std::cout << "\t\t\t{" << pidconf.second.info.i_lim.min << ","
                      << pidconf.second.info.i_lim.max << "},\n";
            std::cout << "\t\t\t{" << pidconf.second.info.out_lim.min << ","
                      << pidconf.second.info.out_lim.max << "},\n";
            std::cout << "\t\t\t" << pidconf.second.info.slew_neg << ",\n";
            std::cout << "\t\t\t" << pidconf.second.info.slew_pos << ",\n";
            std::cout << "\t\t\t}\n\t\t}\n";
        }
        std::cout << "\t},\n";
    }
    std::cout << "}\n\n";
}

void init(sdbusplus::bus::bus &bus)
{
    using ManagedObjectType = std::unordered_map<
        sdbusplus::message::object_path,
        std::unordered_map<
            std::string,
            std::unordered_map<std::string,
                               sdbusplus::message::variant<
                                   uint64_t, int64_t, double, std::string,
                                   std::vector<std::string>>>>>;

    // install watch for properties changed
    std::function<void(sdbusplus::message::message & message)> eventHandler =
        [](const sdbusplus::message::message &) {
            // do a brief sleep as we tend to get a bunch of these events at
            // once
            std::this_thread::sleep_for(std::chrono::seconds(5));
            std::cout << "New configuration detected, restarting\n.";
            std::exit(EXIT_SUCCESS); // service file should make us restart
        };

    static sdbusplus::bus::match::match match(
        bus,
        "type='signal',member='PropertiesChanged',arg0namespace='" +
            std::string(pidConfigurationInterface) + "'",
        eventHandler);

    auto mapper =
        bus.new_method_call("xyz.openbmc_project.ObjectMapper",
                            "/xyz/openbmc_project/object_mapper",
                            "xyz.openbmc_project.ObjectMapper", "GetSubTree");
    mapper.append("", 0,
                  std::array<const char *, 5>{objectManagerInterface,
                                              pidConfigurationInterface,
                                              pidZoneConfigurationInterface,
                                              sensorInterface, pwmInterface});
    auto resp = bus.call(mapper);
    if (resp.is_method_error())
    {
        throw std::runtime_error("ObjectMapper Call Failure");
    }
    std::unordered_map<
        std::string, std::unordered_map<std::string, std::vector<std::string>>>
        respData;

    resp.read(respData);
    if (respData.empty())
    {
        throw std::runtime_error("No configuration data available from Mapper");
    }
    // create a map of pair of <has pid configuration, ObjectManager path>
    std::unordered_map<std::string, std::pair<bool, std::string>> owners;
    // and a map of <path, interface> for sensors
    std::unordered_map<std::string, std::string> sensors;
    for (const auto &objectPair : respData)
    {
        for (const auto &ownerPair : objectPair.second)
        {
            auto &owner = owners[ownerPair.first];
            for (const std::string &interface : ownerPair.second)
            {

                if (interface == objectManagerInterface)
                {
                    owner.second = objectPair.first;
                }
                if (interface == pidConfigurationInterface ||
                    interface == pidZoneConfigurationInterface)
                {
                    owner.first = true;
                }
                if (interface == sensorInterface || interface == pwmInterface)
                {
                    // we're not interested in pwm sensors, just pwm control
                    if (interface == sensorInterface &&
                        objectPair.first.find("pwm") != std::string::npos)
                    {
                        continue;
                    }
                    sensors[objectPair.first] = interface;
                }
            }
        }
    }
    ManagedObjectType configurations;
    for (const auto &owner : owners)
    {
        // skip if no pid configuration (means probably a sensor)
        if (!owner.second.first)
        {
            continue;
        }
        auto endpoint = bus.new_method_call(
            owner.first.c_str(), owner.second.second.c_str(),
            "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
        auto responce = bus.call(endpoint);
        if (responce.is_method_error())
        {
            throw std::runtime_error("Error getting managed objects from " +
                                     owner.first);
        }
        ManagedObjectType configuration;
        responce.read(configuration);
        for (auto &pathPair : configuration)
        {
            if (pathPair.second.find(pidConfigurationInterface) !=
                    pathPair.second.end() ||
                pathPair.second.find(pidZoneConfigurationInterface) !=
                    pathPair.second.end())
            {
                configurations.emplace(pathPair);
            }
        }
    }
    for (const auto &configuration : configurations)
    {
        auto findZone =
            configuration.second.find(pidZoneConfigurationInterface);
        if (findZone != configuration.second.end())
        {
            const auto &zone = findZone->second;
            auto &details =
                ZoneDetailsConfig[sdbusplus::message::variant_ns::get<uint64_t>(
                    zone.at("Index"))];
            details.minthermalrpm = mapbox::util::apply_visitor(
                VariantToFloatVisitor(), zone.at("MinThermalRpm"));
            details.failsafepercent = mapbox::util::apply_visitor(
                VariantToFloatVisitor(), zone.at("FailSafePercent"));
        }
        auto findBase = configuration.second.find(pidConfigurationInterface);
        if (findBase == configuration.second.end())
        {
            continue;
        }
        // if the base configuration is found, these are required
        const auto &base = configuration.second.at(pidConfigurationInterface);
        const auto &iLim = configuration.second.at(pidConfigurationInterface +
                                                   std::string(".ILimit"));
        const auto &outLim = configuration.second.at(pidConfigurationInterface +
                                                     std::string(".OutLimit"));
        PIDConf &conf =
            ZoneConfig[sdbusplus::message::variant_ns::get<uint64_t>(
                base.at("Index"))];
        struct controller_info &info =
            conf[sdbusplus::message::variant_ns::get<std::string>(
                base.at("Name"))];
        info.type =
            sdbusplus::message::variant_ns::get<std::string>(base.at("Class"));
        // todo: auto generation yaml -> c script seems to discard this value
        // for fans, verify this is okay
        if (info.type == "fan")
        {
            info.setpoint = 0;
        }
        else
        {
            info.setpoint = mapbox::util::apply_visitor(VariantToFloatVisitor(),
                                                        base.at("SetPoint"));
        }
        info.info.ts = 1.0; // currently unused
        info.info.p_c = mapbox::util::apply_visitor(VariantToFloatVisitor(),
                                                    base.at("PCoefficient"));
        info.info.i_c = mapbox::util::apply_visitor(VariantToFloatVisitor(),
                                                    base.at("ICoefficient"));
        info.info.ff_off = mapbox::util::apply_visitor(
            VariantToFloatVisitor(), base.at("FFOffCoefficient"));
        info.info.ff_gain = mapbox::util::apply_visitor(
            VariantToFloatVisitor(), base.at("FFGainCoefficient"));
        auto value = mapbox::util::apply_visitor(VariantToFloatVisitor(),
                                                 iLim.at("Max"));
        info.info.i_lim.max = value;
        info.info.i_lim.min = mapbox::util::apply_visitor(
            VariantToFloatVisitor(), iLim.at("Min"));
        info.info.out_lim.max = mapbox::util::apply_visitor(
            VariantToFloatVisitor(), outLim.at("Max"));
        info.info.out_lim.min = mapbox::util::apply_visitor(
            VariantToFloatVisitor(), outLim.at("Min"));
        info.info.slew_neg = mapbox::util::apply_visitor(
            VariantToFloatVisitor(), base.at("SlewNeg"));
        info.info.slew_pos = mapbox::util::apply_visitor(
            VariantToFloatVisitor(), base.at("SlewPos"));

        std::pair<std::string, std::string> sensorPathIfacePair;
        std::vector<std::string> sensorNames =
            sdbusplus::message::variant_ns::get<std::vector<std::string>>(
                base.at("Inputs"));

        for (const std::string &sensorName : sensorNames)
        {
            std::string name = sensorName;
            // replace spaces with underscores to be legal on dbus
            std::replace(name.begin(), name.end(), ' ', '_');

            if (!findSensor(sensors, name, sensorPathIfacePair))
            {
                throw std::runtime_error(
                    "Could not map configuration to sensor " + name);
            }
            if (sensorPathIfacePair.second == sensorInterface)
            {
                info.inputs.push_back(name);
                auto &config = SensorConfig[name];
                config.type = sdbusplus::message::variant_ns::get<std::string>(
                    base.at("Class"));
                config.readpath = sensorPathIfacePair.first;
                // todo: maybe un-hardcode this if we run into slower timeouts
                // with sensors
                if (config.type == "temp")
                {
                    config.timeout = 500;
                }
            }
            if (sensorPathIfacePair.second == pwmInterface)
            {
                // copy so we can modify it
                for (std::string otherSensor : sensorNames)
                {
                    if (otherSensor == sensorName)
                    {
                        continue;
                    }
                    std::replace(otherSensor.begin(), otherSensor.end(), ' ',
                                 '_');
                    auto &config = SensorConfig[otherSensor];
                    config.writepath = sensorPathIfacePair.first;
                    // todo: un-hardcode this if there are fans with different
                    // ranges
                    config.max = 255;
                    config.min = 0;
                }
            }
        }
    }
    if (DEBUG)
    {
        debugPrint();
    }
}
} // namespace dbus_configuration
<|endoftext|>


<commit_before>/**************************************************************************
 * Copyright(c) 1998-1999, ALICE Experiment at CERN, All rights reserved. *
 *                                                                        *
 * Author: The ALICE Off-line Project.                                    *
 * Contributors are mentioned in the code where appropriate.              *
 *                                                                        *
 * Permission to use, copy, modify and distribute this software and its   *
 * documentation strictly for non-commercial purposes is hereby granted   *
 * without fee, provided that the above copyright notice appears in all   *
 * copies and that both the copyright notice and this permission notice   *
 * appear in the supporting documentation. The authors make no claims     *
 * about the suitability of this software for any purpose. It is          *
 * provided "as is" without express or implied warranty.                  *
 **************************************************************************/
//...
//  Checks the quality assurance for ACORDE.
//  Default implementation
//  Authors:
//	Mario Rodriguez Cahuantzi <mrodrigu@mail.cern.ch> (FCFM-BUAP)
//	Luciano Diaz Gonzalez <luciano.diaz@nucleares.unam.mx> (ICN-UNAM)
//	Arturo Fernandez <afernan@mail.cern.ch> (FCFM-BUAP)
//...

// --- ROOT system ---
#include <TClass.h>
#include <TH1F.h>
#include <TH1I.h>
#include <TIterator.h>
#include <TKey.h>
#include <TFile.h>

// --- Standard library ---

// --- AliRoot header files ---
#include "AliLog.h"
#include "AliQAv1.h"
#include "AliQAChecker.h"
#include "AliACORDEQAChecker.h"
#include "AliCDBEntry.h"
#include "AliQAManager.h"

ClassImp(AliACORDEQAChecker)

//____________________________________________________________________________
Double_t * AliACORDEQAChecker::Check(AliQAv1::ALITASK_t /*index*/)
{
  Double_t * rv = new Double_t[AliRecoParam::kNSpecies] ;
  for (Int_t specie = 0 ; specie < AliRecoParam::kNSpecies ; specie++)
    rv[specie] = 0.0 ;
  return rv ;
}

//__________________________________________________________________
Double_t * AliACORDEQAChecker::Check(AliQAv1::ALITASK_t /*index*/, TObjArray ** list)
{

	// We added one check to the ACORDE's QA histograms:
	// 1.- We check if they are empty
	// we check for the reference histogram to start the QAChecker. If not QAref object
	// is found, we check that the number of hits per channel is not so far from
	// the maximum number of hits.
  Double_t * test = new Double_t[AliRecoParam::kNSpecies] ;
  Int_t * count   = new Int_t[AliRecoParam::kNSpecies] ;
  Double_t * acoTest = new Double_t[AliRecoParam::kNSpecies];
  Double_t acoHitsNorm = 0;
 Double_t * acoRefTest = new Double_t[AliRecoParam::kNSpecies];

	// Look at the QAref data for ACORDE

	char * acoOCDBDir = Form("ACORDE/%s/%s",AliQAv1::GetRefOCDBDirName(),AliQAv1::GetRefDataDirName());
	AliCDBEntry *acoQARefDir = AliQAManager::QAManager()->Get(acoOCDBDir);

  for (Int_t specie = 0 ; specie < AliRecoParam::kNSpecies ; specie++) {
    test[specie]    = 0.0 ;
    count[specie] = 0 ;
	acoTest[specie] = 0.0;
  }

  for (Int_t specie = 0 ; specie < AliRecoParam::kNSpecies ; specie++) {
    if (list[specie]->GetEntries() == 0){
      test[specie] = 1. ; // nothing to check
	acoTest[specie] = 1.;
    }
    else {
      TIter next(list[specie]) ;
      TH1 * hdata ;
      while ( (hdata = dynamic_cast<TH1 *>(next())) ) {
        if (hdata) {
          Double_t rv = 0.0 ;
          if(hdata->GetEntries()>0)rv=1;
          AliInfo(Form("%s -> %f", hdata->GetName(), rv)) ;
          count[specie]++ ;
          test[specie] += rv ;

	// here we implement the second version for ACORDEQAChecker
	// by the moment we only compare that the hits in every ACORDE's channel
	// are close and > 0
	for (Int_t i=0;i<60;i++)
	{
		acoHitsNorm =  hdata->GetBinContent(i)/hdata->GetMaximum();
		if  (acoQARefDir)
		{
	//		AliWarning("Using the QA Reference data for ACORDE !!!");
			test[specie] = CheckAcordeRefHits(list[specie],(TObjArray *)acoQARefDir->GetObject());
			if ((test[specie] = 0.86) || (acoHitsNorm>0.50))
			{
				acoRefTest[specie]=0.78;//printf("testMario: %f\n",acoRefTest[specie]);printf("histo:%f\n",hdata->GetMaximum());
			}
		}else{
	//	AliWarning("Using the inner ACORDE QA Checker !!!");
		if ( (acoHitsNorm>0.40) && (acoHitsNorm<=1) ) acoTest[specie] = 0.75;
		if ( (acoHitsNorm>0.0020) && (acoHitsNorm<=0.40) ) acoTest[specie] = 0.251;
		if ( (acoHitsNorm>0.0) && (acoHitsNorm<=0.0020) ) acoTest[specie] = 0.0010;
		if ( (acoHitsNorm>-1.0) && (acoHitsNorm<=0.0) ) acoTest[specie] = -0.5;
		}
	}
        }
        else{
          AliError("Data type cannot be processed") ;
        }
      }
      if (count[specie] != 0) {
        if (test[specie]==0) {
         // AliWarning("Histograms are there, but they are all empty: setting flag to kWARNING");
          test[specie] = 0.5;  //upper limit value to set kWARNING flag for a task
        }
        else {
	if (acoQARefDir) test[specie] = acoRefTest[specie];
	else{
	test[specie] = acoTest[specie];//printf("testDyMa: %f\n",test[specie]);
	}
        }
      }
    }
   // AliInfo(Form("Test Result = %f", test[specie])) ;
  }
  return test ;
}
Double_t AliACORDEQAChecker::CheckAcordeRefHits(TObjArray *AcordeList, TObjArray * /*AcordeRef */) const
{
	Double_t acoTest = 0;
	TIter next(AcordeList);
	TH1 *histo;
	for (Int_t i=0;i<60;i++)
	{
		while ( (histo = dynamic_cast<TH1 *>(next())) )
		{
		  if (histo->GetMaximum() && ((histo->GetBinContent(i)/histo->GetMaximum())<1.0) ) acoTest = 0.86;
//		if( histo->KolmogorovTest((TH1F *)AcordeRef->At(0))<0.8)  acoTest = 0.86;
			//printf("href:%f\n",histo->GetMaximum());
		}
	}
	return acoTest;
}
<commit_msg>Improvement of QAChecker<commit_after>/**************************************************************************
 * Copyright(c) 1998-1999, ALICE Experiment at CERN, All rights reserved. *
 *                                                                        *
 * Author: The ALICE Off-line Project.                                    *
 * Contributors are mentioned in the code where appropriate.              *
 *                                                                        *
 * Permission to use, copy, modify and distribute this software and its   *
 * documentation strictly for non-commercial purposes is hereby granted   *
 * without fee, provided that the above copyright notice appears in all   *
 * copies and that both the copyright notice and this permission notice   *
 * appear in the supporting documentation. The authors make no claims     *
 * about the suitability of this software for any purpose. It is          *
 * provided "as is" without express or implied warranty.                  *
 **************************************************************************/
//...
//  Checks the quality assurance for ACORDE.
//  Default implementation
//  Authors:
//	Mario Rodriguez Cahuantzi <mrodrigu@mail.cern.ch> (FCFM-BUAP)
//	Luciano Diaz Gonzalez <luciano.diaz@nucleares.unam.mx> (ICN-UNAM)
//	Arturo Fernandez <afernan@mail.cern.ch> (FCFM-BUAP)
//...

// --- ROOT system ---
#include <TClass.h>
#include <TH1F.h>
#include <TH1I.h>
#include <TIterator.h>
#include <TKey.h>
#include <TFile.h>

// --- Standard library ---

// --- AliRoot header files ---
#include "AliLog.h"
#include "AliQAv1.h"
#include "AliQAChecker.h"
#include "AliACORDEQAChecker.h"
#include "AliCDBEntry.h"
#include "AliQAManager.h"

ClassImp(AliACORDEQAChecker)

//____________________________________________________________________________
Double_t * AliACORDEQAChecker::Check(AliQAv1::ALITASK_t /*index*/)
{
  Double_t * rv = new Double_t[AliRecoParam::kNSpecies] ;
  for (Int_t specie = 0 ; specie < AliRecoParam::kNSpecies ; specie++)
    rv[specie] = 0.0 ;
  return rv ;
}

//__________________________________________________________________
Double_t * AliACORDEQAChecker::Check(AliQAv1::ALITASK_t /*index*/, TObjArray ** list)
{

	// We added one check to the ACORDE's QA histograms:
	// 1.- We check if they are empty
	// we check for the reference histogram to start the QAChecker. If not QAref object
	// is found, we check that the number of hits per channel is not so far from
	// the maximum number of hits.
  Double_t * test = new Double_t[AliRecoParam::kNSpecies] ;
  Int_t * count   = new Int_t[AliRecoParam::kNSpecies] ;
  Double_t * acoTest = new Double_t[AliRecoParam::kNSpecies];
 // Double_t acoHitsNorm = 0;
 Double_t * acoRefTest = new Double_t[AliRecoParam::kNSpecies];

	// Look at the QAref data for ACORDE

	char * acoOCDBDir = Form("ACORDE/%s/%s",AliQAv1::GetRefOCDBDirName(),AliQAv1::GetRefDataDirName());
	AliCDBEntry *acoQARefDir = AliQAManager::QAManager()->Get(acoOCDBDir);

  for (Int_t specie = 0 ; specie < AliRecoParam::kNSpecies ; specie++) {
    test[specie]    = 0.0 ;
    count[specie] = 0 ;
	acoTest[specie] = 0.0;
  }

  for (Int_t specie = 0 ; specie < AliRecoParam::kNSpecies ; specie++) {
    if (list[specie]->GetEntries() == 0){
      test[specie] = 1. ; // nothing to check
	acoTest[specie] = 1.;
    }
    else {
      TIter next(list[specie]) ;
      TH1 * hdata ;
      while ( (hdata = dynamic_cast<TH1 *>(next())) ) {
        if (hdata) {
          Double_t rv = 0.0 ;
          if(hdata->GetEntries()>0)rv=1;
          AliInfo(Form("%s -> %f", hdata->GetName(), rv)) ;
          count[specie]++ ;
          test[specie] += rv ;

	// here we implement the second version for ACORDEQAChecker
	// by the moment we only compare that the hits in every ACORDE's channel
	// are close and > 0
		Double_t acoHitsNorm = hdata->GetMaximum();
		if  (acoQARefDir)
		{
	//		AliWarning("Using the QA Reference data for ACORDE !!!");
			test[specie] = CheckAcordeRefHits(list[specie],(TObjArray *)acoQARefDir->GetObject());
			if ((test[specie] = 0.86) || (acoHitsNorm>0.50))
			{
				acoRefTest[specie]=0.78;//printf("testMario: %f\n",acoRefTest[specie]);printf("histo:%f\n",hdata->GetMaximum());
			}
		}else{
	//	AliWarning("Using the inner ACORDE QA Checker !!!");
		if ( (acoHitsNorm>0.40) && (acoHitsNorm<=1) ) acoTest[specie] = 0.75;
		if ( (acoHitsNorm>0.0020) && (acoHitsNorm<=0.40) ) acoTest[specie] = 0.251;
		if ( (acoHitsNorm>0.0) && (acoHitsNorm<=0.0020) ) acoTest[specie] = 0.0010;
		if ( (acoHitsNorm>-1.0) && (acoHitsNorm<=0.0) ) acoTest[specie] = -0.5;
		}
        }
        else{
          AliError("Data type cannot be processed") ;
        }
      }
      if (count[specie] != 0) {
        if (test[specie]==0) {
         // AliWarning("Histograms are there, but they are all empty: setting flag to kWARNING");
          test[specie] = 0.5;  //upper limit value to set kWARNING flag for a task
        }
        else {
	if (acoQARefDir) test[specie] = acoRefTest[specie];
	else{
	test[specie] = acoTest[specie];//printf("testDyMa: %f\n",test[specie]);
	}
        }
      }
    }
   // AliInfo(Form("Test Result = %f", test[specie])) ;
  }
  return test ;
}
Double_t AliACORDEQAChecker::CheckAcordeRefHits(TObjArray *AcordeList, TObjArray * /*AcordeRef */) const
{
	Double_t acoTest = 0;
	TIter next(AcordeList);
	TH1 *histo;
	for (Int_t i=0;i<60;i++)
	{
		while ( (histo = dynamic_cast<TH1 *>(next())) )
		{
		  if (histo->GetMaximum() && ((histo->GetBinContent(i)/histo->GetMaximum())<1.0) ) acoTest = 0.86;
//		if( histo->KolmogorovTest((TH1F *)AcordeRef->At(0))<0.8)  acoTest = 0.86;
			//printf("href:%f\n",histo->GetMaximum());
		}
	}
	return acoTest;
}
<|endoftext|>


<commit_before>#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <cstring>
#include <cstdlib>
#include <cerrno>
#include <signal.h>
#include <boost/thread.hpp>

using namespace boost;

/**
 * This file is used as a template to test the different ApplicationPool implementations.
 * It is #included in StandardApplicationPoolTest.cpp and ApplicationServer_ApplicationPoolTest.cpp
 */
#ifdef USE_TEMPLATE

	static string createRequestHeaders(const char *uri = "/foo/new") {
		string headers;
		#define ADD_HEADER(name, value) \
			headers.append(name); \
			headers.append(1, '\0'); \
			headers.append(value); \
			headers.append(1, '\0')
		ADD_HEADER("HTTP_HOST", "www.test.com");
		ADD_HEADER("QUERY_STRING", "");
		ADD_HEADER("REQUEST_URI", uri);
		ADD_HEADER("REQUEST_METHOD", "GET");
		ADD_HEADER("REMOTE_ADDR", "localhost");
		return headers;
	}

	static string readAll(int fd) {
		string result;
		char buf[1024 * 32];
		ssize_t ret;
		while (true) {
			do {
				ret = read(fd, buf, sizeof(buf));
			} while (ret == -1 && errno == EINTR);
			if (ret == 0) {
				break;
			} else if (ret == -1) {
				throw strerror(errno);
			} else {
				result.append(buf, ret);
			}
		}
		return result;
	}

	TEST_METHOD(1) {
		// Calling ApplicationPool.get() once should return a valid Session.
		Application::SessionPtr session(pool->get("stub/railsapp"));
		session->sendHeaders(createRequestHeaders());
		session->closeWriter();

		int reader = session->getReader();
		string result(readAll(reader));
		session->closeReader();
		ensure(result.find("hello world") != string::npos);
	}

	TEST_METHOD(2) {
		// Verify that the pool spawns a new app, and that
		// after the session is closed, the app is kept around.
		Application::SessionPtr session(pool->get("stub/railsapp"));
		ensure_equals("Before the session was closed, the app was busy", pool->getActive(), 1u);
		ensure_equals("Before the session was closed, the app was in the pool", pool->getCount(), 1u);
		session.reset();
		ensure_equals("After the session is closed, the app is no longer busy", pool->getActive(), 0u);
		ensure_equals("After the session is closed, the app is kept around", pool->getCount(), 1u);
	}

	TEST_METHOD(4) {
		// If we call get() with an application root, then we close the session,
		// and then we call get() again with the same application root,
		// then the pool should not have spawned more than 1 app in total.
		Application::SessionPtr session(pool->get("stub/railsapp"));
		session.reset();
		session = pool->get("stub/railsapp");
		ensure_equals(pool->getCount(), 1u);
	}

	TEST_METHOD(5) {
		// If we call get() with an application root, then we call get() again before closing
		// the session, then the pool should have spawned 2 apps in total.
		Application::SessionPtr session(pool->get("stub/railsapp"));
		Application::SessionPtr session2(pool->get("stub/railsapp"));
		ensure_equals(pool->getCount(), 2u);
	}

	TEST_METHOD(6) {
		// If we call get() twice with different application roots,
		// then the pool should spawn two different apps.
		Application::SessionPtr session(pool->get("stub/railsapp"));
		Application::SessionPtr session2(pool->get("stub/railsapp2"));
		ensure_equals("Before the sessions were closed, both apps were busy", pool->getActive(), 2u);
		ensure_equals("Before the sessions were closed, both apps were in the pool", pool->getCount(), 2u);

		session->sendHeaders(createRequestHeaders());
		string result(readAll(session->getReader()));
		ensure("Session 1 belongs to the correct app", result.find("hello world"));
		session.reset();

		session2->sendHeaders(createRequestHeaders());
		result = readAll(session2->getReader());
		ensure("Session 2 belongs to the correct app", result.find("this is railsapp2"));
		session2.reset();
	}

	TEST_METHOD(7) {
		// If we call get() twice with different application roots,
		// and we close both sessions, then both 2 apps should still
		// be in the pool.
		Application::SessionPtr session(pool->get("stub/railsapp"));
		Application::SessionPtr session2(pool->get("stub/railsapp2"));
		session.reset();
		session2.reset();
		ensure_equals(pool->getActive(), 0u);
		ensure_equals(pool->getCount(), 2u);
	}

	TEST_METHOD(8) {
		// If we call get() even though the pool is already full
		// (active == max), and the application root is already
		// in the pool, then the pool should have tried to open
		// a session in an already active app.
		pool->setMax(1);
		Application::SessionPtr session1(pool->get("stub/railsapp"));
		Application::SessionPtr session2(pool->get("stub/railsapp"));
		ensure_equals("An attempt to open a session on an already busy app was made", pool->getActive(), 2u);
		ensure_equals("No new app has been spawned", pool->getCount(), 1u);
	}

	TEST_METHOD(9) {
		// If ApplicationPool spawns a new instance,
		// and we kill it, then the next get() with the
		// same application root should throw an exception.
		// But the get() thereafter should not:
		// ApplicationPool should have spawned a new instance
		// after detecting that the original one died.
		Application::SessionPtr session(pool->get("stub/railsapp"));
		kill(session->getPid(), SIGTERM);
		session.reset();
		try {
			session = pool->get("stub/railsapp");
			fail("ApplicationPool::get() is supposed to "
				"throw an exception because we killed "
				"the app instance.");
		} catch (const exception &e) {
			session = pool->get("stub/railsapp");
			// Should not throw.
		}
	}

	struct TestThread1 {
		ApplicationPoolPtr pool;
		Application::SessionPtr &m_session;
		bool &m_done;

		TestThread1(const ApplicationPoolPtr &pool,
			Application::SessionPtr &session,
			bool &done)
		: m_session(session), m_done(done) {
			this->pool = pool;
			done = false;
		}

		void operator()() {
			m_session = pool->get("stub/minimal-railsapp");
			m_done = true;
		}
	};

	TEST_METHOD(10) {
		// If we call get() even though the pool is already full
		// (active == max), and the application root is *not* already
		// in the pool, then the pool will wait until enough sessions
		// have been closed.
		pool->setMax(2);
		Application::SessionPtr session1(pool->get("stub/railsapp"));
		Application::SessionPtr session2(pool->get("stub/railsapp"));
		Application::SessionPtr session3;
		bool done;

		thread *thr = new thread(TestThread1(pool2, session3, done));
		usleep(500000);
		ensure("ApplicationPool is waiting", !done);
		ensure_equals(pool->getActive(), 2u);
		ensure_equals(pool->getCount(), 2u);

		session1.reset();

		// Wait at most 10 seconds.
		time_t begin = time(NULL);
		while (!done && time(NULL) - begin < 10) {
			usleep(100000);
		}

		ensure("Session 3 is openend", done);
		ensure_equals(pool->getActive(), 2u);
		ensure_equals(pool->getCount(), 2u);

		thr->join();
		delete thr;
	}

	TEST_METHOD(12) {
		// If we call get(), and:
		// * the pool is already full, but there are inactive apps
		//   (active < count && count == max)
		// and
		// * the application root is *not* already in the pool
		// then the an inactive app should be killed in order to
		// satisfy this get() command.
		pool->setMax(2);
		Application::SessionPtr session1(pool->get("stub/railsapp"));
		Application::SessionPtr session2(pool->get("stub/railsapp"));
		session1.reset();
		session2.reset();

		ensure_equals(pool->getActive(), 0u);
		ensure_equals(pool->getCount(), 2u);
		session1 = pool2->get("stub/railsapp2");
		ensure_equals(pool->getActive(), 1u);
		ensure_equals(pool->getCount(), 2u);
	}

	TEST_METHOD(13) {
		// Test whether Session is still usable after the Application has been destroyed.
		Application::SessionPtr session(pool->get("stub/railsapp"));
		pool->clear();
		pool.reset();
		pool2.reset();

		session->sendHeaders(createRequestHeaders());
		session->closeWriter();

		int reader = session->getReader();
		string result(readAll(reader));
		session->closeReader();
		ensure(result.find("hello world") != string::npos);
	}

	TEST_METHOD(14) {
		// If tmp/restart.txt is present, then the applications under app_root
		// should be restarted.
		struct stat buf;
		Application::SessionPtr session1 = pool->get("stub/railsapp");
		Application::SessionPtr session2 = pool2->get("stub/railsapp");
		session1.reset();
		session2.reset();

		system("touch stub/railsapp/tmp/restart.txt");
		pool->get("stub/railsapp");

		ensure_equals("No apps are active", pool->getActive(), 0u);
		ensure_equals("Both apps are killed, and a new one was spawned",
			pool->getCount(), 1u);
		ensure("Restart file has been deleted",
			stat("stub/railsapp/tmp/restart.txt", &buf) == -1
			&& errno == ENOENT);
	}

	TEST_METHOD(15) {
		// If tmp/restart.txt is present, but cannot be deleted, then
		// the applications under app_root should still be restarted.
		// However, a subsequent get() should not result in a restart.
		pid_t old_pid, pid;
		struct stat buf;
		Application::SessionPtr session1 = pool->get("stub/railsapp");
		Application::SessionPtr session2 = pool2->get("stub/railsapp");
		session1.reset();
		session2.reset();

		setenv("nextRestartTxtDeletionShouldFail", "1", 1);
		system("touch stub/railsapp/tmp/restart.txt");

		old_pid = pool->get("stub/railsapp")->getPid();
		ensure("Restart file has not been deleted",
			stat("stub/railsapp/tmp/restart.txt", &buf) == 0);

		setenv("nextRestartTxtDeletionShouldFail", "1", 1);
		pid = pool->get("stub/railsapp")->getPid();
		ensure_equals("The app was not restarted", pid, old_pid);

		unlink("stub/railsapp/tmp/restart.txt");
	}

	TEST_METHOD(16) {
		// If tmp/restart.txt is present, but cannot be deleted, then
		// the applications under app_root should still be restarted.
		// A subsequent get() should only restart if we've changed
		// restart.txt's mtime.
		pid_t old_pid;
		Application::SessionPtr session1 = pool->get("stub/railsapp");
		Application::SessionPtr session2 = pool2->get("stub/railsapp");
		session1.reset();
		session2.reset();

		setenv("nextRestartTxtDeletionShouldFail", "1", 1);
		system("touch stub/railsapp/tmp/restart.txt");
		old_pid = pool->get("stub/railsapp")->getPid();
		ensure_equals(pool->getActive(), 0u);
		ensure_equals(pool->getCount(), 1u);

		sleep(1); // Allow the next mtime to be different.
		system("touch stub/railsapp/tmp/restart.txt");
		ensure("The app is restarted, and the last app instance was not reused",
			pool2->get("stub/railsapp")->getPid() != old_pid);

		unlink("stub/railsapp/tmp/restart.txt");
	}

	TEST_METHOD(17) {
		// Test whether restarting really results in code reload.
		system("cp -f stub/railsapp/app/controllers/bar_controller_1.rb "
			"stub/railsapp/app/controllers/bar_controller.rb");
		Application::SessionPtr session = pool->get("stub/railsapp");
		session->sendHeaders(createRequestHeaders("/bar"));
		string result = readAll(session->getReader());
		ensure(result.find("bar 1!"));
		session.reset();

		system("cp -f stub/railsapp/app/controllers/bar_controller_2.rb "
			"stub/railsapp/app/controllers/bar_controller.rb");
		system("touch stub/railsapp/tmp/restart.txt");
		session = pool->get("stub/railsapp");
		session->sendHeaders(createRequestHeaders("/bar"));
		result = readAll(session->getReader());
		ensure("App code has been reloaded", result.find("bar 2!"));
		unlink("stub/railsapp/app/controllers/bar_controller.rb");
	}

	TEST_METHOD(18) {
		// The cleaner thread should clean idle applications without crashing.
		pool->setMaxIdleTime(1);
		pool->get("stub/minimal-railsapp");

		time_t begin = time(NULL);
		while (pool->getCount() == 1u && time(NULL) - begin < 10) {
			usleep(100000);
		}
		ensure_equals("App should have been cleaned up", pool->getCount(), 0u);
	}

#endif /* USE_TEMPLATE */
<commit_msg>Fix a few bugs in ApplicationPoolTest<commit_after>#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <cstring>
#include <cstdlib>
#include <cerrno>
#include <signal.h>
#include <boost/thread.hpp>

using namespace boost;

/**
 * This file is used as a template to test the different ApplicationPool implementations.
 * It is #included in StandardApplicationPoolTest.cpp and ApplicationServer_ApplicationPoolTest.cpp
 */
#ifdef USE_TEMPLATE

	static string createRequestHeaders(const char *uri = "/foo/new") {
		string headers;
		#define ADD_HEADER(name, value) \
			headers.append(name); \
			headers.append(1, '\0'); \
			headers.append(value); \
			headers.append(1, '\0')
		ADD_HEADER("HTTP_HOST", "www.test.com");
		ADD_HEADER("QUERY_STRING", "");
		ADD_HEADER("REQUEST_URI", uri);
		ADD_HEADER("REQUEST_METHOD", "GET");
		ADD_HEADER("REMOTE_ADDR", "localhost");
		return headers;
	}

	static string readAll(int fd) {
		string result;
		char buf[1024 * 32];
		ssize_t ret;
		while (true) {
			do {
				ret = read(fd, buf, sizeof(buf));
			} while (ret == -1 && errno == EINTR);
			if (ret == 0) {
				break;
			} else if (ret == -1) {
				throw strerror(errno);
			} else {
				result.append(buf, ret);
			}
		}
		return result;
	}

	TEST_METHOD(1) {
		// Calling ApplicationPool.get() once should return a valid Session.
		Application::SessionPtr session(pool->get("stub/railsapp"));
		session->sendHeaders(createRequestHeaders());
		session->closeWriter();

		int reader = session->getReader();
		string result(readAll(reader));
		session->closeReader();
		ensure(result.find("hello world") != string::npos);
	}

	TEST_METHOD(2) {
		// Verify that the pool spawns a new app, and that
		// after the session is closed, the app is kept around.
		Application::SessionPtr session(pool->get("stub/railsapp"));
		ensure_equals("Before the session was closed, the app was busy", pool->getActive(), 1u);
		ensure_equals("Before the session was closed, the app was in the pool", pool->getCount(), 1u);
		session.reset();
		ensure_equals("After the session is closed, the app is no longer busy", pool->getActive(), 0u);
		ensure_equals("After the session is closed, the app is kept around", pool->getCount(), 1u);
	}

	TEST_METHOD(3) {
		// If we call get() with an application root, then we close the session,
		// and then we call get() again with the same application root,
		// then the pool should not have spawned more than 1 app in total.
		Application::SessionPtr session(pool->get("stub/railsapp"));
		session.reset();
		session = pool->get("stub/railsapp");
		ensure_equals(pool->getCount(), 1u);
	}

	TEST_METHOD(4) {
		// If we call get() with an application root, then we call get() again before closing
		// the session, then the pool should have spawned 2 apps in total.
		Application::SessionPtr session(pool->get("stub/railsapp"));
		Application::SessionPtr session2(pool2->get("stub/railsapp"));
		ensure_equals(pool->getCount(), 2u);
	}

	TEST_METHOD(5) {
		// If we call get() twice with different application roots,
		// then the pool should spawn two different apps.
		Application::SessionPtr session(pool->get("stub/railsapp"));
		Application::SessionPtr session2(pool2->get("stub/railsapp2"));
		ensure_equals("Before the sessions were closed, both apps were busy", pool->getActive(), 2u);
		ensure_equals("Before the sessions were closed, both apps were in the pool", pool->getCount(), 2u);

		session->sendHeaders(createRequestHeaders());
		string result(readAll(session->getReader()));
		ensure("Session 1 belongs to the correct app", result.find("hello world"));
		session.reset();

		session2->sendHeaders(createRequestHeaders());
		result = readAll(session2->getReader());
		ensure("Session 2 belongs to the correct app", result.find("this is railsapp2"));
		session2.reset();
	}

	TEST_METHOD(6) {
		// If we call get() twice with different application roots,
		// and we close both sessions, then both 2 apps should still
		// be in the pool.
		Application::SessionPtr session(pool->get("stub/railsapp"));
		Application::SessionPtr session2(pool->get("stub/railsapp2"));
		session.reset();
		session2.reset();
		ensure_equals(pool->getActive(), 0u);
		ensure_equals(pool->getCount(), 2u);
	}

	TEST_METHOD(7) {
		// If we call get() even though the pool is already full
		// (active == max), and the application root is already
		// in the pool, then the pool should have tried to open
		// a session in an already active app.
		pool->setMax(1);
		Application::SessionPtr session1(pool->get("stub/railsapp"));
		Application::SessionPtr session2(pool->get("stub/railsapp"));
		ensure_equals("An attempt to open a session on an already busy app was made", pool->getActive(), 2u);
		ensure_equals("No new app has been spawned", pool->getCount(), 1u);
	}

	TEST_METHOD(8) {
		// If ApplicationPool spawns a new instance,
		// and we kill it, then the next get() with the
		// same application root should throw an exception.
		// But the get() thereafter should not:
		// ApplicationPool should have spawned a new instance
		// after detecting that the original one died.
		Application::SessionPtr session(pool->get("stub/railsapp"));
		kill(session->getPid(), SIGTERM);
		session.reset();
		try {
			session = pool->get("stub/railsapp");
			fail("ApplicationPool::get() is supposed to "
				"throw an exception because we killed "
				"the app instance.");
		} catch (const exception &e) {
			session = pool->get("stub/railsapp");
			// Should not throw.
		}
	}

	struct TestThread1 {
		ApplicationPoolPtr pool;
		Application::SessionPtr &m_session;
		bool &m_done;

		TestThread1(const ApplicationPoolPtr &pool,
			Application::SessionPtr &session,
			bool &done)
		: m_session(session), m_done(done) {
			this->pool = pool;
			done = false;
		}

		void operator()() {
			m_session = pool->get("stub/minimal-railsapp");
			m_done = true;
		}
	};

	TEST_METHOD(9) {
		// If we call get() even though the pool is already full
		// (active == max), and the application root is *not* already
		// in the pool, then the pool will wait until enough sessions
		// have been closed.
		pool->setMax(2);
		Application::SessionPtr session1(pool->get("stub/railsapp"));
		Application::SessionPtr session2(pool2->get("stub/railsapp"));
		Application::SessionPtr session3;
		bool done;

		thread *thr = new thread(TestThread1(pool2, session3, done));
		usleep(500000);
		ensure("ApplicationPool is waiting", !done);
		ensure_equals(pool->getActive(), 2u);
		ensure_equals(pool->getCount(), 2u);

		session1.reset();

		// Wait at most 10 seconds.
		time_t begin = time(NULL);
		while (!done && time(NULL) - begin < 10) {
			usleep(100000);
		}

		ensure("Session 3 is openend", done);
		ensure_equals(pool->getActive(), 2u);
		ensure_equals(pool->getCount(), 2u);

		thr->join();
		delete thr;
	}

	TEST_METHOD(10) {
		// If we call get(), and:
		// * the pool is already full, but there are inactive apps
		//   (active < count && count == max)
		// and
		// * the application root is *not* already in the pool
		// then the an inactive app should be killed in order to
		// satisfy this get() command.
		pool->setMax(2);
		Application::SessionPtr session1(pool->get("stub/railsapp"));
		Application::SessionPtr session2(pool->get("stub/railsapp"));
		session1.reset();
		session2.reset();

		ensure_equals(pool->getActive(), 0u);
		ensure_equals(pool->getCount(), 2u);
		session1 = pool2->get("stub/railsapp2");
		ensure_equals(pool->getActive(), 1u);
		ensure_equals(pool->getCount(), 2u);
	}

	TEST_METHOD(11) {
		// Test whether Session is still usable after the Application has been destroyed.
		Application::SessionPtr session(pool->get("stub/railsapp"));
		pool->clear();
		pool.reset();
		pool2.reset();

		session->sendHeaders(createRequestHeaders());
		session->closeWriter();

		int reader = session->getReader();
		string result(readAll(reader));
		session->closeReader();
		ensure(result.find("hello world") != string::npos);
	}

	TEST_METHOD(12) {
		// If tmp/restart.txt is present, then the applications under app_root
		// should be restarted.
		struct stat buf;
		Application::SessionPtr session1 = pool->get("stub/railsapp");
		Application::SessionPtr session2 = pool2->get("stub/railsapp");
		session1.reset();
		session2.reset();

		system("touch stub/railsapp/tmp/restart.txt");
		pool->get("stub/railsapp");

		ensure_equals("No apps are active", pool->getActive(), 0u);
		ensure_equals("Both apps are killed, and a new one was spawned",
			pool->getCount(), 1u);
		ensure("Restart file has been deleted",
			stat("stub/railsapp/tmp/restart.txt", &buf) == -1
			&& errno == ENOENT);
	}

	TEST_METHOD(13) {
		// If tmp/restart.txt is present, but cannot be deleted, then
		// the applications under app_root should still be restarted.
		// However, a subsequent get() should not result in a restart.
		pid_t old_pid, pid;
		struct stat buf;
		Application::SessionPtr session1 = pool->get("stub/railsapp");
		Application::SessionPtr session2 = pool2->get("stub/railsapp");
		session1.reset();
		session2.reset();

		setenv("nextRestartTxtDeletionShouldFail", "1", 1);
		system("touch stub/railsapp/tmp/restart.txt");

		old_pid = pool->get("stub/railsapp")->getPid();
		ensure("Restart file has not been deleted",
			stat("stub/railsapp/tmp/restart.txt", &buf) == 0);

		setenv("nextRestartTxtDeletionShouldFail", "1", 1);
		pid = pool->get("stub/railsapp")->getPid();
		ensure_equals("The app was not restarted", pid, old_pid);

		unlink("stub/railsapp/tmp/restart.txt");
	}

	TEST_METHOD(14) {
		// If tmp/restart.txt is present, but cannot be deleted, then
		// the applications under app_root should still be restarted.
		// A subsequent get() should only restart if we've changed
		// restart.txt's mtime.
		pid_t old_pid;
		Application::SessionPtr session1 = pool->get("stub/railsapp");
		Application::SessionPtr session2 = pool2->get("stub/railsapp");
		session1.reset();
		session2.reset();

		setenv("nextRestartTxtDeletionShouldFail", "1", 1);
		system("touch stub/railsapp/tmp/restart.txt");
		old_pid = pool->get("stub/railsapp")->getPid();
		ensure_equals(pool->getActive(), 0u);
		ensure_equals(pool->getCount(), 1u);

		sleep(1); // Allow the next mtime to be different.
		system("touch stub/railsapp/tmp/restart.txt");
		ensure("The app is restarted, and the last app instance was not reused",
			pool2->get("stub/railsapp")->getPid() != old_pid);

		unlink("stub/railsapp/tmp/restart.txt");
	}

	TEST_METHOD(15) {
		// Test whether restarting really results in code reload.
		system("cp -f stub/railsapp/app/controllers/bar_controller_1.rb "
			"stub/railsapp/app/controllers/bar_controller.rb");
		Application::SessionPtr session = pool->get("stub/railsapp");
		session->sendHeaders(createRequestHeaders("/bar"));
		string result = readAll(session->getReader());
		ensure(result.find("bar 1!"));
		session.reset();

		system("cp -f stub/railsapp/app/controllers/bar_controller_2.rb "
			"stub/railsapp/app/controllers/bar_controller.rb");
		system("touch stub/railsapp/tmp/restart.txt");
		session = pool->get("stub/railsapp");
		session->sendHeaders(createRequestHeaders("/bar"));
		result = readAll(session->getReader());
		ensure("App code has been reloaded", result.find("bar 2!"));
		unlink("stub/railsapp/app/controllers/bar_controller.rb");
	}

	TEST_METHOD(16) {
		// The cleaner thread should clean idle applications without crashing.
		pool->setMaxIdleTime(1);
		pool->get("stub/minimal-railsapp");

		time_t begin = time(NULL);
		while (pool->getCount() == 1u && time(NULL) - begin < 10) {
			usleep(100000);
		}
		ensure_equals("App should have been cleaned up", pool->getCount(), 0u);
	}

#endif /* USE_TEMPLATE */
<|endoftext|>


<commit_before>///////////////////////////////////////////////////////////////////////////////
// Unit Test for Loki
//
// Copyright Terje Sletteb and Pavel Vozenilek 2002.
//
// Permission to use, copy, modify, and distribute this software for any
// purpose is hereby granted without fee, provided that this copyright and
// permissions notice appear in all copies and derivatives.
//
// This software is provided "as is" without express or implied warranty.
//
///////////////////////////////////////////////////////////////////////////////

// $Header$

#ifdef __INTEL_COMPILER
# pragma warning(disable: 111 193 304 383 444 488 981 1418)
#elif defined(_MSC_VER) && !defined(__MWERKS__)
# pragma warning(disable: 4018 4097 4100 4213 4290 4512 4514 4700 4702 4710 4786 4800)
#endif

//#define LOKI_CLASS_LEVEL_THREADING
#define LOKI_OBJECT_LEVEL_THREADING

// Some platforms might have difficulty with this
// Need to ifdef around those cases.
// TODO SGB

#include "UnitTest.h"

// static variable defintion, do not remove

Test::tests_type Test::tests;

// Merely comment out any of the following headers to
// prevent thier execution during the test.
//
// A pluggable-factory-like method is used to
// auto-register the test, so all that is needed
// is the header inclusion to execute the correspond
// unit test.

#include "SmallObjectTest.h"
#include "SingletonTest.h"

#include "ThreadsTest.h"
#include "TypelistTest.h"
#include "SequenceTest.h"
#include "TypeManipTest.h"
#include "TypeTraitsTest.h"
#include "TypeTraitsTest2.h"
#include "SmartPtrTest.h"
#include "FactoryTest.h"
#include "FactoryParmTest.h"
#include "AbstractFactoryTest.h"
#include "FunctorTest.h"
#include "DataGeneratorsTest.h"
//#include "AssocVectorTest.h"

int main()
{
    int result = Test::run("Loki Unit Test");

    #if defined(__BORLANDC__) || defined(__GNUC__) || defined(_MSC_VER)
    // Stop console window from closing if run from IDE.
    system("pause");
    #endif

    return result;
}

/*
 * Table is out of date (24.10.2005)
 *
 * AP - All Pass
 * FC - Fails to Compile
 * ?  - Unknown/Not Tested/Not Recorded
 *
 *               TypelistTest    TypeManipTest  TypeTraitsTest  SmallObjectTest  SingletonTest
 * gcc   2.95.3  ?               ?              ?               ?                ?
 * gcc   3.2     AP              AP             AP              AP               P (Only SingleThreaded)
 * MSVC  6.0     P               AP             FC              FC               AP
 * MSVC  7.0     AP              Conversion     FC              AP               P (Only SingleThreaded) ?
 * Intel 5.0     AP              AP             AP              FC               FC
 * Intel 6.0     AP              AP             AP              FC               P (Only SingleThreaded)
 * Intel 7.0     AP              AP             AP              FC               P (Only SingleThreaded)
 * BCC   5.5     ?               ?              ?               ?                ?
 * BCC   5.6     ?               ?              ?               ?                ?
 * CW    6.0     ?               ?              ?               ?                ?
 *
 *               SmartPtrTest  FactoryTest  AbstractFactoryTest  AssocVectorTest  FunctorTest
 * gcc   2.95.3  ?             ?            ?                    ?                ?
 * gcc   3.2     AP            AP           AP                   AP               AP
 * MSVC  6.0     FC            AP           FC                   FC               FC
 * MSVC  7.0     FC            AP           AP                   FC               AP
 * Intel 5.0     FC            FC           FC                   FC               FC
 * Intel 6.0     FC            AP           AP                   FC               FC
 * Intel 7.0     FC            AP           AP                   FC               FC
 * BCC   5.5     ?             ?            ?                    ?                ?
 * CW    6.0     ?             ?            ?                    ?                ?
 *
 *               DataGeneratorsTest
 * gcc   2.95.3  ?
 * gcc   3.2     AP
 * MSVC  6.0     FC
 * MSVC  7.0     AP
 * Intel 5.0     FC
 * Intel 6.0     AP
 * Intel 7.0     AP
 * BCC   5.5     ?
 * BCC   5.6     ?
 * CW    6.0     ?

 */

// $Log$
// Revision 1.11  2005/10/30 13:49:44  syntheticpp
// make disabling the TYPELIST macros possible
//
// Revision 1.10  2005/10/24 20:51:38  syntheticpp
// Table is out of date
//
// Revision 1.9  2005/10/24 20:35:12  syntheticpp
// small changes for Threads; add compile test for Threads.h
//
// Revision 1.8  2005/10/06 17:50:14  syntheticpp
// adding template based list/sequence implementation, should replace LOKI_TYPELIST_, update some files
//
// Revision 1.7  2005/09/29 08:09:17  syntheticpp
// update msvc build process
//
// Revision 1.6  2005/09/26 07:33:05  syntheticpp
// move macros into LOKI_ namespace
//
// Revision 1.5  2005/09/24 15:49:40  syntheticpp
// is it really binary?
//
// Revision 1.4  2005/09/24 15:25:20  syntheticpp
// ove RegressionTest
//
// Revision 1.17  2005/09/15 17:51:48  syntheticpp
// add new TypeTraits test from Kalle Rutanen
//
// Revision 1.16  2005/08/10 18:13:13  syntheticpp
// change default to single threading
//
// Revision 1.15  2005/07/31 14:00:48  syntheticpp
// make object level threading possible
//
// Revision 1.14  2005/07/28 14:26:10  syntheticpp
// add cvs Header/Log
//
<commit_msg>disable threading because the sdk (windows.h) is not detected automatically by the batch scripts<commit_after>///////////////////////////////////////////////////////////////////////////////
// Unit Test for Loki
//
// Copyright Terje Sletteb and Pavel Vozenilek 2002.
//
// Permission to use, copy, modify, and distribute this software for any
// purpose is hereby granted without fee, provided that this copyright and
// permissions notice appear in all copies and derivatives.
//
// This software is provided "as is" without express or implied warranty.
//
///////////////////////////////////////////////////////////////////////////////

// $Header$

#ifdef __INTEL_COMPILER
# pragma warning(disable: 111 193 304 383 444 488 981 1418)
#elif defined(_MSC_VER) && !defined(__MWERKS__)
# pragma warning(disable: 4018 4097 4100 4213 4290 4512 4514 4700 4702 4710 4786 4800)
#endif

//#define LOKI_CLASS_LEVEL_THREADING
//#define LOKI_OBJECT_LEVEL_THREADING

// Some platforms might have difficulty with this
// Need to ifdef around those cases.
// TODO SGB

#include "UnitTest.h"

// static variable defintion, do not remove

Test::tests_type Test::tests;

// Merely comment out any of the following headers to
// prevent thier execution during the test.
//
// A pluggable-factory-like method is used to
// auto-register the test, so all that is needed
// is the header inclusion to execute the correspond
// unit test.

#include "SmallObjectTest.h"
#include "SingletonTest.h"

#include "ThreadsTest.h"
#include "TypelistTest.h"
#include "SequenceTest.h"
#include "TypeManipTest.h"
#include "TypeTraitsTest.h"
#include "TypeTraitsTest2.h"
#include "SmartPtrTest.h"
#include "FactoryTest.h"
#include "FactoryParmTest.h"
#include "AbstractFactoryTest.h"
#include "FunctorTest.h"
#include "DataGeneratorsTest.h"
#include "AssocVectorTest.h"

int main()
{
    int result = Test::run("Loki Unit Test");

    #if defined(__BORLANDC__) || defined(__GNUC__) || defined(_MSC_VER)
    // Stop console window from closing if run from IDE.
    system("pause");
    #endif

    return result;
}

/*
 * Table is out of date (24.10.2005)
 *
 * AP - All Pass
 * FC - Fails to Compile
 * ?  - Unknown/Not Tested/Not Recorded
 *
 *               TypelistTest    TypeManipTest  TypeTraitsTest  SmallObjectTest  SingletonTest
 * gcc   2.95.3  ?               ?              ?               ?                ?
 * gcc   3.2     AP              AP             AP              AP               P (Only SingleThreaded)
 * MSVC  6.0     P               AP             FC              FC               AP
 * MSVC  7.0     AP              Conversion     FC              AP               P (Only SingleThreaded) ?
 * Intel 5.0     AP              AP             AP              FC               FC
 * Intel 6.0     AP              AP             AP              FC               P (Only SingleThreaded)
 * Intel 7.0     AP              AP             AP              FC               P (Only SingleThreaded)
 * BCC   5.5     ?               ?              ?               ?                ?
 * BCC   5.6     ?               ?              ?               ?                ?
 * CW    6.0     ?               ?              ?               ?                ?
 *
 *               SmartPtrTest  FactoryTest  AbstractFactoryTest  AssocVectorTest  FunctorTest
 * gcc   2.95.3  ?             ?            ?                    ?                ?
 * gcc   3.2     AP            AP           AP                   AP               AP
 * MSVC  6.0     FC            AP           FC                   FC               FC
 * MSVC  7.0     FC            AP           AP                   FC               AP
 * Intel 5.0     FC            FC           FC                   FC               FC
 * Intel 6.0     FC            AP           AP                   FC               FC
 * Intel 7.0     FC            AP           AP                   FC               FC
 * BCC   5.5     ?             ?            ?                    ?                ?
 * CW    6.0     ?             ?            ?                    ?                ?
 *
 *               DataGeneratorsTest
 * gcc   2.95.3  ?
 * gcc   3.2     AP
 * MSVC  6.0     FC
 * MSVC  7.0     AP
 * Intel 5.0     FC
 * Intel 6.0     AP
 * Intel 7.0     AP
 * BCC   5.5     ?
 * BCC   5.6     ?
 * CW    6.0     ?

 */

// $Log$
// Revision 1.12  2005/10/30 14:22:31  syntheticpp
// disable threading because the sdk (windows.h) is not detected automatically by the batch scripts
//
// Revision 1.11  2005/10/30 13:49:44  syntheticpp
// make disabling the TYPELIST macros possible
//
// Revision 1.10  2005/10/24 20:51:38  syntheticpp
// Table is out of date
//
// Revision 1.9  2005/10/24 20:35:12  syntheticpp
// small changes for Threads; add compile test for Threads.h
//
// Revision 1.8  2005/10/06 17:50:14  syntheticpp
// adding template based list/sequence implementation, should replace LOKI_TYPELIST_, update some files
//
// Revision 1.7  2005/09/29 08:09:17  syntheticpp
// update msvc build process
//
// Revision 1.6  2005/09/26 07:33:05  syntheticpp
// move macros into LOKI_ namespace
//
// Revision 1.5  2005/09/24 15:49:40  syntheticpp
// is it really binary?
//
// Revision 1.4  2005/09/24 15:25:20  syntheticpp
// ove RegressionTest
//
// Revision 1.17  2005/09/15 17:51:48  syntheticpp
// add new TypeTraits test from Kalle Rutanen
//
// Revision 1.16  2005/08/10 18:13:13  syntheticpp
// change default to single threading
//
// Revision 1.15  2005/07/31 14:00:48  syntheticpp
// make object level threading possible
//
// Revision 1.14  2005/07/28 14:26:10  syntheticpp
// add cvs Header/Log
//
<|endoftext|>


<commit_before>#include <gtest/gtest.h>
#include <entt/core/type_traits.hpp>
#include <entt/signal/emitter.hpp>

struct test_emitter: entt::emitter<test_emitter> {};

struct foo_event { int i; char c; };
struct bar_event {};
struct quux_event {};

TEST(Emitter, Clear) {
    test_emitter emitter;

    ASSERT_TRUE(emitter.empty());
    ASSERT_TRUE(emitter.empty<quux_event>());

    emitter.on<foo_event>([](const auto &, const auto &){});

    ASSERT_FALSE(emitter.empty());
    ASSERT_FALSE(emitter.empty<foo_event>());
    ASSERT_TRUE(emitter.empty<bar_event>());

    emitter.clear<bar_event>();

    ASSERT_FALSE(emitter.empty());
    ASSERT_FALSE(emitter.empty<foo_event>());
    ASSERT_TRUE(emitter.empty<bar_event>());

    emitter.clear<foo_event>();

    ASSERT_TRUE(emitter.empty());
    ASSERT_TRUE(emitter.empty<foo_event>());
    ASSERT_TRUE(emitter.empty<bar_event>());

    emitter.on<foo_event>([](const auto &, const auto &){});
    emitter.on<bar_event>([](const auto &, const auto &){});

    ASSERT_FALSE(emitter.empty());
    ASSERT_FALSE(emitter.empty<foo_event>());
    ASSERT_FALSE(emitter.empty<bar_event>());

    emitter.clear();

    ASSERT_TRUE(emitter.empty());
    ASSERT_TRUE(emitter.empty<foo_event>());
    ASSERT_TRUE(emitter.empty<bar_event>());
}

TEST(Emitter, ClearPublishing) {
    test_emitter emitter;
    bool invoked = false;

    ASSERT_TRUE(emitter.empty());

    emitter.on<bar_event>([&invoked](const auto &, auto &em){
        invoked = true;
        em.clear();
    });

    emitter.publish<bar_event>();

    ASSERT_TRUE(emitter.empty());
    ASSERT_TRUE(invoked);
}

TEST(Emitter, On) {
    test_emitter emitter;

    emitter.on<foo_event>([](const auto &, const auto &){});

    ASSERT_FALSE(emitter.empty());
    ASSERT_FALSE(emitter.empty<foo_event>());

    emitter.publish<foo_event>(0, 'c');

    ASSERT_FALSE(emitter.empty());
    ASSERT_FALSE(emitter.empty<foo_event>());
}

TEST(Emitter, Once) {
    test_emitter emitter;

    emitter.once<bar_event>([](const auto &, const auto &){});

    ASSERT_FALSE(emitter.empty());
    ASSERT_FALSE(emitter.empty<bar_event>());

    emitter.publish<bar_event>();

    ASSERT_TRUE(emitter.empty());
    ASSERT_TRUE(emitter.empty<bar_event>());
}

TEST(Emitter, OnceAndErase) {
    test_emitter emitter;

    auto conn = emitter.once<foo_event>([](const auto &, const auto &){});

    ASSERT_FALSE(emitter.empty());
    ASSERT_FALSE(emitter.empty<foo_event>());

    emitter.erase(conn);

    ASSERT_TRUE(emitter.empty());
    ASSERT_TRUE(emitter.empty<foo_event>());
}

TEST(Emitter, OnAndErase) {
    test_emitter emitter;

    auto conn = emitter.on<bar_event>([](const auto &, const auto &){});

    ASSERT_FALSE(emitter.empty());
    ASSERT_FALSE(emitter.empty<bar_event>());

    emitter.erase(conn);

    ASSERT_TRUE(emitter.empty());
    ASSERT_TRUE(emitter.empty<bar_event>());
}
<commit_msg>tests: code coverage<commit_after>#include <gtest/gtest.h>
#include <entt/core/type_traits.hpp>
#include <entt/signal/emitter.hpp>

struct test_emitter: entt::emitter<test_emitter> {};

struct foo_event { int i; char c; };
struct bar_event {};
struct quux_event {};

TEST(Emitter, Clear) {
    test_emitter emitter;

    ASSERT_TRUE(emitter.empty());

    emitter.on<foo_event>([](const auto &, const auto &){});
    emitter.once<quux_event>([](const auto &, const auto &){});

    ASSERT_FALSE(emitter.empty());
    ASSERT_FALSE(emitter.empty<foo_event>());
    ASSERT_FALSE(emitter.empty<quux_event>());
    ASSERT_TRUE(emitter.empty<bar_event>());

    emitter.clear<bar_event>();

    ASSERT_FALSE(emitter.empty());
    ASSERT_FALSE(emitter.empty<foo_event>());
    ASSERT_FALSE(emitter.empty<quux_event>());
    ASSERT_TRUE(emitter.empty<bar_event>());

    emitter.clear<foo_event>();

    ASSERT_FALSE(emitter.empty());
    ASSERT_TRUE(emitter.empty<foo_event>());
    ASSERT_FALSE(emitter.empty<quux_event>());
    ASSERT_TRUE(emitter.empty<bar_event>());

    emitter.on<foo_event>([](const auto &, const auto &){});
    emitter.on<bar_event>([](const auto &, const auto &){});

    ASSERT_FALSE(emitter.empty());
    ASSERT_FALSE(emitter.empty<foo_event>());
    ASSERT_FALSE(emitter.empty<quux_event>());
    ASSERT_FALSE(emitter.empty<bar_event>());

    emitter.clear();

    ASSERT_TRUE(emitter.empty());
    ASSERT_TRUE(emitter.empty<foo_event>());
    ASSERT_TRUE(emitter.empty<bar_event>());
}

TEST(Emitter, ClearPublishing) {
    test_emitter emitter;

    ASSERT_TRUE(emitter.empty());

    emitter.once<foo_event>([](const auto &, auto &em){
        em.template once<foo_event>([](const auto &, auto &){});
        em.template clear<foo_event>();
    });

    emitter.on<bar_event>([](const auto &, auto &em){
        em.template once<bar_event>([](const auto &, auto &){});
        em.template clear<bar_event>();
    });

    ASSERT_FALSE(emitter.empty());

    emitter.publish<foo_event>();
    emitter.publish<bar_event>();

    ASSERT_TRUE(emitter.empty());
}

TEST(Emitter, On) {
    test_emitter emitter;

    emitter.on<foo_event>([](const auto &, const auto &){});

    ASSERT_FALSE(emitter.empty());
    ASSERT_FALSE(emitter.empty<foo_event>());

    emitter.publish<foo_event>(0, 'c');

    ASSERT_FALSE(emitter.empty());
    ASSERT_FALSE(emitter.empty<foo_event>());
}

TEST(Emitter, Once) {
    test_emitter emitter;

    emitter.once<bar_event>([](const auto &, const auto &){});

    ASSERT_FALSE(emitter.empty());
    ASSERT_FALSE(emitter.empty<bar_event>());

    emitter.publish<bar_event>();

    ASSERT_TRUE(emitter.empty());
    ASSERT_TRUE(emitter.empty<bar_event>());
}

TEST(Emitter, OnceAndErase) {
    test_emitter emitter;

    auto conn = emitter.once<foo_event>([](const auto &, const auto &){});

    ASSERT_FALSE(emitter.empty());
    ASSERT_FALSE(emitter.empty<foo_event>());

    emitter.erase(conn);

    ASSERT_TRUE(emitter.empty());
    ASSERT_TRUE(emitter.empty<foo_event>());
}

TEST(Emitter, OnAndErase) {
    test_emitter emitter;

    auto conn = emitter.on<bar_event>([](const auto &, const auto &){});

    ASSERT_FALSE(emitter.empty());
    ASSERT_FALSE(emitter.empty<bar_event>());

    emitter.erase(conn);

    ASSERT_TRUE(emitter.empty());
    ASSERT_TRUE(emitter.empty<bar_event>());
}
<|endoftext|>