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

#include "analyzer.h"

#include "translator.h"
#include "yaml.h"

using namespace std;
using namespace std::placeholders;

Model initModel(string path)
{
    eraseSuffix(&path, ".yaml");
    auto dirPos = path.rfind('/');
    // The below code assumes that npos == -1 and that unsigned overflow works
    return Model(path.substr(0, dirPos + 1), path.substr(dirPos + 1));
}

Analyzer::Analyzer(string filePath, string basePath,
                   const Translator& translator)
        : fileName(filePath), baseDir(move(basePath))
        , model(initModel(move(filePath))), translator(translator)
{ }

TypeUsage Analyzer::analyzeType(const YamlMap& node, InOut inOut, string scope,
                                IsTopLevel isTopLevel)
{
    auto yamlTypeNode = node["type"];

    if (yamlTypeNode && yamlTypeNode.IsSequence())
        return translator.mapType("object"); // TODO: Multitype/variant support

    auto yamlType = yamlTypeNode.as<string>("object");
    if (yamlType == "array")
    {
        if (auto yamlElemType = node["items"].asMap())
            if (!yamlElemType.empty())
            {
                auto elemType =
                    analyzeType(yamlElemType, inOut, move(scope));
                const auto& protoType =
                    translator.mapType("array", elemType.baseName, "array");
                return protoType.instantiate(move(elemType));
            }

        return translator.mapType("array");
    }
    if (yamlType == "object")
    {
        auto schema = analyzeSchema(node, inOut, move(scope));
        if (isTopLevel && inOut&Out && schema.empty())
            return TypeUsage(""); // Non-existent object, void
        if (!schema.name.empty()) // Only ever filled for non-empty schemas
        {
            model.addSchema(schema);
            auto tu = translator.mapType("schema");
            tu.scope = schema.scope;
            tu.name = tu.baseName = schema.name;
            return tu;
        }
        if (schema.trivial()) // An alias for another type
            return schema.parentTypes.front();
        // An In empty object is schemaless but existing, mapType("object")
        // Also, a nameless non-empty schema is now treated as a generic
        // mapType("object"). TODO, low priority: ad-hoc typing (via tuples?)
    }
    auto tu = translator.mapType(yamlType, node["format"].as<string>(""));
    if (!tu.empty())
        return tu;

    throw YamlException(node, "Unknown type: " + yamlType);
}

ObjectSchema Analyzer::analyzeSchema(const YamlMap& yamlSchema, InOut inOut,
                                     string scope, string locus)
{
    ObjectSchema schema;
    schema.inOut = inOut;

    vector<string> refPaths;
    if (auto yamlRef = yamlSchema["$ref"])
        refPaths.emplace_back(yamlRef.as<string>());
    else if (auto yamlAllOf = yamlSchema["allOf"].asSequence())
    {
        refPaths.resize(yamlAllOf.size());
        transform(yamlAllOf.begin(), yamlAllOf.end(), refPaths.begin(),
                  [](YamlMap p) { return p.get("$ref").as<string>(); });
    }

    for (const auto& refPath: refPaths)
    {
        // First try to resolve refPath in types map; if there's no match, load
        // the schema from the reference path.
        auto tu = translator.mapType("object", refPath);
        if (!tu.empty())
        {
            cout << "Using type " << tu.name << " for " << refPath << endl;
            schema.parentTypes.emplace_back(move(tu));
            continue;
        }
        // The referenced file's path is relative to the current file's path;
        // we have to append a path to the current file's directory in order to
        // find the file.
        cout << "Sub-processing schema in "
             << model.fileDir << "./" << refPath << endl;
        const Model& m =
                translator.processFile(model.fileDir + refPath, baseDir);
        if (m.types.empty())
            throw YamlException(yamlSchema, "The target file has no schemas");

        schema.parentTypes.emplace_back(
            m.types.back().name,
            m.dstFiles.empty() ? string{} : "\"" + m.dstFiles.front() + "\"");
        // TODO: distinguish between interface files (that should be imported,
        // headers in C/C+) and implementation files (that should not).
        // filesList.front() assumes that there's only one interface file, and
        // it's at the front of the list, which is very naive.
    }

    if (schema.empty() && yamlSchema["type"].as<string>("object") != "object")
    {
        auto parentType = analyzeType(yamlSchema, inOut, scope);
        if (!parentType.empty())
            schema.parentTypes.emplace_back(move(parentType));
    }

    if (const auto properties = yamlSchema["properties"].asMap())
    {
        const auto requiredList = yamlSchema["required"].asSequence();
        for (const YamlNodePair property: properties)
        {
            const auto baseName = property.first.as<string>();
            auto required = any_of(requiredList.begin(), requiredList.end(),
                                [&baseName](const YamlNode& n)
                                    { return baseName == n.as<string>(); } );
            addVarDecl(schema.fields, analyzeType(property.second, inOut, scope),
                       baseName, required);
        }
    }
    if (!schema.empty())
    {
        auto name = camelCase(yamlSchema["title"].as<string>(""));
        // Checking for name difference commented out due to #28
        if (!schema.trivial()/* || name != qualifiedName(s.parentTypes.front()) */)
        {
            // If the schema is not just an alias for another type, name it.
            schema.name = move(name);
            schema.scope.swap(scope);
        }

        if (!schema.name.empty() || !schema.trivial())
        {
            cout << yamlSchema.location() << ": Found "
                 << (!locus.empty() ? locus + " schema"
                     : "schema " + qualifiedName(schema))
                 << " for "
                 << (schema.inOut == In ? "input" :
                     schema.inOut == Out ? "output" :
                     schema.inOut == (In|Out) ? "in/out" : "undefined use");
            if (schema.trivial())
                cout << " mapped to "
                     << qualifiedName(schema.parentTypes.front()) << endl;
            else
                cout << " (parent(s): " << schema.parentTypes.size()
                     << ", field(s): " << schema.fields.size() << ")" << endl;
        }
    }
    return schema;
}

void Analyzer::addParamsFromSchema(VarDecls& varList,
        const string& baseName, bool required, const ObjectSchema& paramSchema)
{
    if (paramSchema.parentTypes.empty())
    {
        for (const auto & param: paramSchema.fields)
            model.addVarDecl(varList, param);
    } else if (paramSchema.trivial())
    {
        // The schema consists of a single parent type, use that type instead.
        addVarDecl(varList, paramSchema.parentTypes.front(), baseName, required);
    } else
    {
        cerr << "Warning: found non-trivial schema for " << baseName
             << "; these are not supported, expect invalid parameter set"
             << endl;
        const auto typeName =
            paramSchema.name.empty() ? camelCase(baseName) : paramSchema.name;
        model.addSchema(paramSchema);
        addVarDecl(varList, TypeUsage(typeName), baseName, required);
    }
}

vector<string> loadContentTypes(const YamlMap& yaml, const char* keyName)
{
    if (auto yamlTypes = yaml[keyName].asSequence())
    {
        vector<string> result { yamlTypes.size() };
        transform(yamlTypes.begin(), yamlTypes.end(),
                  result.begin(), mem_fn(&YamlNode::as<string>));
        return result;
    }
    return {};
}

Model Analyzer::loadModel(const pair_vector_t<string>& substitutions,
                          InOut inOut)
{
    cout << "Loading from " << baseDir + fileName << endl;
    auto yaml = YamlMap::loadFromFile(baseDir + fileName, substitutions);

    // Detect which file we have: API description or just data definition
    // TODO: This should be refactored to two separate methods, since we shouldn't
    // allow loading an API description file referenced from another API description.
    if (const auto paths = yaml.get("paths", true).asMap())
    {
        if (yaml.get("swagger").as<string>() != "2.0")
            throw Exception(
                    "This software only supports swagger version 2.0 for now");

        auto defaultConsumed = loadContentTypes(yaml, "consumes");
        auto defaultProduced = loadContentTypes(yaml, "produces");
        model.hostAddress = yaml["host"].as<string>("");
        model.basePath = yaml["basePath"].as<string>("");

        for (const YamlNodePair& yaml_path: paths)
        try {
            const Path path { yaml_path.first.as<string>() };

            for (const YamlNodePair& yaml_call_pair: yaml_path.second.asMap())
            {
                auto verb = yaml_call_pair.first.as<string>();
                const YamlMap yamlCall { yaml_call_pair.second };
                auto operationId = yamlCall.get("operationId").as<string>();
                bool needsSecurity = false;
                if (const auto security = yamlCall["security"].asSequence())
                    needsSecurity = security[0]["accessToken"].IsDefined();

                cout << yamlCall.location() << ": Found operation "
                     << operationId
                     << " (" << path << ", " << verb << ')' << endl;

                Call& call =
                    model.addCall(path, move(verb), move(operationId),
                                  needsSecurity);

                call.consumedContentTypes =
                        loadContentTypes(yamlCall, "consumes");
                if (call.consumedContentTypes.empty())
                    call.consumedContentTypes = defaultConsumed;
                call.producedContentTypes =
                        loadContentTypes(yamlCall, "produces");
                if (call.producedContentTypes.empty())
                    call.producedContentTypes = defaultProduced;

                const auto yamlParams = yamlCall["parameters"].asSequence();
                for (const YamlMap yamlParam: yamlParams)
                {
                    const auto& name = yamlParam.get("name").as<string>();
                    auto&& in = yamlParam.get("in").as<string>();
                    auto required = yamlParam["required"].as<bool>(false);
                    if (!required && in == "path")
                    {
                        clog << yamlParam.location() << ": warning: '" << name
                             << "' is in path but has no 'required' attribute"
                             << " - treating as required anyway" << endl;
                        required = true;
                    }
//                    clog << "Parameter: " << name << endl;
//                    for (const YamlNodePair p: yamlParam)
//                    {
//                        clog << "  At " << p.first.location() << ": " << p.first.as<string>() << endl;
//                    }
                    if (in != "body")
                    {
                        addVarDecl(call.getParamsBlock(in),
                            analyzeType(yamlParam, In, call.name, TopLevel),
                            name, required, yamlParam["default"].as<string>(""));
                        continue;
                    }

                    auto&& bodySchema =
                        analyzeSchema(yamlParam.get("schema"), In, call.name,
                                      "request body");
                    if (bodySchema.empty())
                    {
                        // Special case: an empty schema for a body parameter
                        // means a freeform object.
                        call.inlineBody = true;
                        addVarDecl(call.bodyParams(),
                            translator.mapType("object"), name, false);
                    } else {
                        // The schema consists of a single parent type, inline that type.
                        if (bodySchema.trivial())
                            call.inlineBody = true;
                        addParamsFromSchema(call.bodyParams(),
                                            name, required, bodySchema);
                    }
                }
                const auto yamlResponses = yamlCall.get("responses").asMap();
                if (const auto yamlResponse = yamlResponses["200"].asMap())
                {
                    Response response { "200" };
                    if (auto yamlHeaders = yamlResponse["headers"])
                        for (const auto& yamlHeader: yamlHeaders.asMap())
                        {
                            addVarDecl(response.headers,
                                analyzeType(yamlHeader.second, Out, call.name,
                                            TopLevel),
                                yamlHeader.first.as<string>(), false);
                        }
                    if (auto yamlSchema = yamlResponse["schema"])
                    {
                        auto&& responseSchema =
                            analyzeSchema(yamlSchema, Out, call.name, "response");
                        if (!responseSchema.empty())
                            addParamsFromSchema(response.properties,
                                "content", true, responseSchema);
                    }
                    call.responses.emplace_back(move(response));
                }
            }
        }
        catch (ModelException& me)
        {
            throw YamlException(yaml_path.first, me.message);
        }
    } else {
        auto schema = analyzeSchema(yaml, inOut);
        if (schema.name.empty())
            schema.name = camelCase(model.srcFilename);
        model.addSchema(schema);
    }
    return std::move(model);
}
<commit_msg>Support additionalProperties<commit_after>/******************************************************************************
 * Copyright (C) 2018 Kitsune Ral <kitsune-ral@users.sf.net>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "analyzer.h"

#include "translator.h"
#include "yaml.h"

using namespace std;
using namespace std::placeholders;

Model initModel(string path)
{
    eraseSuffix(&path, ".yaml");
    auto dirPos = path.rfind('/');
    // The below code assumes that npos == -1 and that unsigned overflow works
    return Model(path.substr(0, dirPos + 1), path.substr(dirPos + 1));
}

Analyzer::Analyzer(string filePath, string basePath,
                   const Translator& translator)
        : fileName(filePath), baseDir(move(basePath))
        , model(initModel(move(filePath))), translator(translator)
{ }

TypeUsage Analyzer::analyzeType(const YamlMap& node, InOut inOut, string scope,
                                IsTopLevel isTopLevel)
{
    auto yamlTypeNode = node["type"];

    if (yamlTypeNode && yamlTypeNode.IsSequence())
        return translator.mapType("object"); // TODO: Multitype/variant support

    auto yamlType = yamlTypeNode.as<string>("object");
    if (yamlType == "array")
    {
        if (auto yamlElemType = node["items"].asMap())
            if (!yamlElemType.empty())
            {
                auto elemType =
                    analyzeType(yamlElemType, inOut, move(scope));
                const auto& protoType =
                    translator.mapType("array", elemType.baseName, "array");
                return protoType.instantiate(move(elemType));
            }

        return translator.mapType("array");
    }
    if (yamlType == "object")
    {
        auto schema = analyzeSchema(node, inOut, scope);
        if (schema.empty())
        {
            if (const auto propertyMap = node["additionalProperties"])
                switch (propertyMap.Type())
                {
                    case YAML::NodeType::Map:
                    {
                        auto elemType =
                                analyzeType(propertyMap.asMap(), inOut, scope);
                        const auto& protoType =
                                translator.mapType("map", elemType.baseName,
                                                   "additionalProperties");
                        return protoType.instantiate(move(elemType));
                    }
                    case YAML::NodeType::Scalar:
                        if (propertyMap.as<bool>()) // Generic map
                            return translator.mapType("map");
                        else // Additional properties forbidden - no special treatment
                            break;
                    default:
                        throw YamlException(propertyMap,
                            "additionalProperties should be either a boolean or a map");
                }

            if (isTopLevel && inOut&Out)
                return TypeUsage(""); // The type returned by this API is void
        }
        if (!schema.name.empty()) // Only ever filled for non-empty schemas
        {
            model.addSchema(schema);
            auto tu = translator.mapType("schema");
            tu.scope = schema.scope;
            tu.name = tu.baseName = schema.name;
            return tu;
        }
        if (schema.trivial()) // An alias for another type
            return schema.parentTypes.front();
        // An In empty object is schemaless but existing, mapType("object")
        // Also, a nameless non-empty schema is now treated as a generic
        // mapType("object"). TODO, low priority: ad-hoc typing (via tuples?)
    }
    auto tu = translator.mapType(yamlType, node["format"].as<string>(""));
    if (!tu.empty())
        return tu;

    throw YamlException(node, "Unknown type: " + yamlType);
}

ObjectSchema Analyzer::analyzeSchema(const YamlMap& yamlSchema, InOut inOut,
                                     string scope, string locus)
{
    ObjectSchema schema;
    schema.inOut = inOut;

    vector<string> refPaths;
    if (auto yamlRef = yamlSchema["$ref"])
        refPaths.emplace_back(yamlRef.as<string>());
    else if (auto yamlAllOf = yamlSchema["allOf"].asSequence())
    {
        refPaths.resize(yamlAllOf.size());
        transform(yamlAllOf.begin(), yamlAllOf.end(), refPaths.begin(),
                  [](YamlMap p) { return p.get("$ref").as<string>(); });
    }

    for (const auto& refPath: refPaths)
    {
        // First try to resolve refPath in types map; if there's no match, load
        // the schema from the reference path.
        auto tu = translator.mapType("object", refPath);
        if (!tu.empty())
        {
            cout << "Using type " << tu.name << " for " << refPath << endl;
            schema.parentTypes.emplace_back(move(tu));
            continue;
        }
        // The referenced file's path is relative to the current file's path;
        // we have to append a path to the current file's directory in order to
        // find the file.
        cout << "Sub-processing schema in "
             << model.fileDir << "./" << refPath << endl;
        const Model& m =
                translator.processFile(model.fileDir + refPath, baseDir);
        if (m.types.empty())
            throw YamlException(yamlSchema, "The target file has no schemas");

        schema.parentTypes.emplace_back(
            m.types.back().name,
            m.dstFiles.empty() ? string{} : "\"" + m.dstFiles.front() + "\"");
        // TODO: distinguish between interface files (that should be imported,
        // headers in C/C+) and implementation files (that should not).
        // filesList.front() assumes that there's only one interface file, and
        // it's at the front of the list, which is very naive.
    }

    if (schema.empty() && yamlSchema["type"].as<string>("object") != "object")
    {
        auto parentType = analyzeType(yamlSchema, inOut, scope);
        if (!parentType.empty())
            schema.parentTypes.emplace_back(move(parentType));
    }

    if (const auto properties = yamlSchema["properties"].asMap())
    {
        const auto requiredList = yamlSchema["required"].asSequence();
        for (const YamlNodePair property: properties)
        {
            const auto baseName = property.first.as<string>();
            auto required = any_of(requiredList.begin(), requiredList.end(),
                                [&baseName](const YamlNode& n)
                                    { return baseName == n.as<string>(); } );
            addVarDecl(schema.fields, analyzeType(property.second, inOut, scope),
                       baseName, required);
        }
    }
    if (!schema.empty())
    {
        auto name = camelCase(yamlSchema["title"].as<string>(""));
        // Checking for name difference commented out due to #28
        if (!schema.trivial()/* || name != qualifiedName(s.parentTypes.front()) */)
        {
            // If the schema is not just an alias for another type, name it.
            schema.name = move(name);
            schema.scope.swap(scope);
        }

        if (!schema.name.empty() || !schema.trivial())
        {
            cout << yamlSchema.location() << ": Found "
                 << (!locus.empty() ? locus + " schema"
                     : "schema " + qualifiedName(schema))
                 << " for "
                 << (schema.inOut == In ? "input" :
                     schema.inOut == Out ? "output" :
                     schema.inOut == (In|Out) ? "in/out" : "undefined use");
            if (schema.trivial())
                cout << " mapped to "
                     << qualifiedName(schema.parentTypes.front()) << endl;
            else
                cout << " (parent(s): " << schema.parentTypes.size()
                     << ", field(s): " << schema.fields.size() << ")" << endl;
        }
    }
    return schema;
}

void Analyzer::addParamsFromSchema(VarDecls& varList,
        const string& baseName, bool required, const ObjectSchema& paramSchema)
{
    if (paramSchema.parentTypes.empty())
    {
        for (const auto & param: paramSchema.fields)
            model.addVarDecl(varList, param);
    } else if (paramSchema.trivial())
    {
        // The schema consists of a single parent type, use that type instead.
        addVarDecl(varList, paramSchema.parentTypes.front(), baseName, required);
    } else
    {
        cerr << "Warning: found non-trivial schema for " << baseName
             << "; these are not supported, expect invalid parameter set"
             << endl;
        const auto typeName =
            paramSchema.name.empty() ? camelCase(baseName) : paramSchema.name;
        model.addSchema(paramSchema);
        addVarDecl(varList, TypeUsage(typeName), baseName, required);
    }
}

vector<string> loadContentTypes(const YamlMap& yaml, const char* keyName)
{
    if (auto yamlTypes = yaml[keyName].asSequence())
    {
        vector<string> result { yamlTypes.size() };
        transform(yamlTypes.begin(), yamlTypes.end(),
                  result.begin(), mem_fn(&YamlNode::as<string>));
        return result;
    }
    return {};
}

Model Analyzer::loadModel(const pair_vector_t<string>& substitutions,
                          InOut inOut)
{
    cout << "Loading from " << baseDir + fileName << endl;
    auto yaml = YamlMap::loadFromFile(baseDir + fileName, substitutions);

    // Detect which file we have: API description or just data definition
    // TODO: This should be refactored to two separate methods, since we shouldn't
    // allow loading an API description file referenced from another API description.
    if (const auto paths = yaml.get("paths", true).asMap())
    {
        if (yaml.get("swagger").as<string>() != "2.0")
            throw Exception(
                    "This software only supports swagger version 2.0 for now");

        auto defaultConsumed = loadContentTypes(yaml, "consumes");
        auto defaultProduced = loadContentTypes(yaml, "produces");
        model.hostAddress = yaml["host"].as<string>("");
        model.basePath = yaml["basePath"].as<string>("");

        for (const YamlNodePair& yaml_path: paths)
        try {
            const Path path { yaml_path.first.as<string>() };

            for (const YamlNodePair& yaml_call_pair: yaml_path.second.asMap())
            {
                auto verb = yaml_call_pair.first.as<string>();
                const YamlMap yamlCall { yaml_call_pair.second };
                auto operationId = yamlCall.get("operationId").as<string>();
                bool needsSecurity = false;
                if (const auto security = yamlCall["security"].asSequence())
                    needsSecurity = security[0]["accessToken"].IsDefined();

                cout << yamlCall.location() << ": Found operation "
                     << operationId
                     << " (" << path << ", " << verb << ')' << endl;

                Call& call =
                    model.addCall(path, move(verb), move(operationId),
                                  needsSecurity);

                call.consumedContentTypes =
                        loadContentTypes(yamlCall, "consumes");
                if (call.consumedContentTypes.empty())
                    call.consumedContentTypes = defaultConsumed;
                call.producedContentTypes =
                        loadContentTypes(yamlCall, "produces");
                if (call.producedContentTypes.empty())
                    call.producedContentTypes = defaultProduced;

                const auto yamlParams = yamlCall["parameters"].asSequence();
                for (const YamlMap yamlParam: yamlParams)
                {
                    const auto& name = yamlParam.get("name").as<string>();
                    auto&& in = yamlParam.get("in").as<string>();
                    auto required = yamlParam["required"].as<bool>(false);
                    if (!required && in == "path")
                    {
                        clog << yamlParam.location() << ": warning: '" << name
                             << "' is in path but has no 'required' attribute"
                             << " - treating as required anyway" << endl;
                        required = true;
                    }
//                    clog << "Parameter: " << name << endl;
//                    for (const YamlNodePair p: yamlParam)
//                    {
//                        clog << "  At " << p.first.location() << ": " << p.first.as<string>() << endl;
//                    }
                    if (in != "body")
                    {
                        addVarDecl(call.getParamsBlock(in),
                            analyzeType(yamlParam, In, call.name, TopLevel),
                            name, required, yamlParam["default"].as<string>(""));
                        continue;
                    }

                    auto&& bodySchema =
                        analyzeSchema(yamlParam.get("schema"), In, call.name,
                                      "request body");
                    if (bodySchema.empty())
                    {
                        // Special case: an empty schema for a body parameter
                        // means a freeform object.
                        call.inlineBody = true;
                        addVarDecl(call.bodyParams(),
                            translator.mapType("object"), name, false);
                    } else {
                        // The schema consists of a single parent type, inline that type.
                        if (bodySchema.trivial())
                            call.inlineBody = true;
                        addParamsFromSchema(call.bodyParams(),
                                            name, required, bodySchema);
                    }
                }
                const auto yamlResponses = yamlCall.get("responses").asMap();
                if (const auto yamlResponse = yamlResponses["200"].asMap())
                {
                    Response response { "200" };
                    if (auto yamlHeaders = yamlResponse["headers"])
                        for (const auto& yamlHeader: yamlHeaders.asMap())
                        {
                            addVarDecl(response.headers,
                                analyzeType(yamlHeader.second, Out, call.name,
                                            TopLevel),
                                yamlHeader.first.as<string>(), false);
                        }
                    if (auto yamlSchema = yamlResponse["schema"])
                    {
                        auto&& responseSchema =
                            analyzeSchema(yamlSchema, Out, call.name, "response");
                        if (!responseSchema.empty())
                            addParamsFromSchema(response.properties,
                                "content", true, responseSchema);
                    }
                    call.responses.emplace_back(move(response));
                }
            }
        }
        catch (ModelException& me)
        {
            throw YamlException(yaml_path.first, me.message);
        }
    } else {
        auto schema = analyzeSchema(yaml, inOut);
        if (schema.name.empty())
            schema.name = camelCase(model.srcFilename);
        model.addSchema(schema);
    }
    return std::move(model);
}
<|endoftext|>


<commit_before>//!
//! @file   ParameterDialog.cpp
//! @author Björn Eriksson <bjorn.eriksson@liu.se>
//! @date   2010-03-01
//!
//! @brief Contains a class for interact with paramters
//!
//$Id$

#include <QtGui>
#include <cassert>

#include "ParameterDialog.h"
#include "HopsanCore.h"

//! Constructor.
//! @param coreComponent is a ponter to the core component.
//! @param parent defines a parent to the new instanced object.
ParameterDialog::ParameterDialog(Component *coreComponent, QWidget *parent)
    : QDialog(parent)
{
    mpCoreComponent = coreComponent;

    std::vector<CompParameter>::iterator it;

    vector<CompParameter> paramVector = mpCoreComponent->getParameterVector();

    for ( it=paramVector.begin() ; it !=paramVector.end(); it++ )
    {
        mVarVector.push_back(new QLabel(QString::fromStdString(it->getName())));
        mVarVector.back()->setAlignment(Qt::AlignVCenter | Qt::AlignRight);
        mDescriptionVector.push_back(new QLabel(QString::fromStdString(it->getDesc())));
        mUnitVector.push_back(new QLabel(QString::fromStdString(it->getUnit())));

        mValueVector.push_back(new QLineEdit());
        mValueVector.back()->setValidator(new QDoubleValidator(-999.0, 999.0, 6, mValueVector.back()));

        QString valueTxt;
        valueTxt.setNum(it->getValue(), 'g', 6 );
        mValueVector.back()->setText(valueTxt);

        mVarVector.back()->setBuddy(mValueVector.back());
    }

    okButton = new QPushButton(tr("&Ok"));
    okButton->setDefault(true);
    cancelButton = new QPushButton(tr("&Cancel"));
    cancelButton->setDefault(false);

    buttonBox = new QDialogButtonBox(Qt::Vertical);
    buttonBox->addButton(okButton, QDialogButtonBox::ActionRole);
    buttonBox->addButton(cancelButton, QDialogButtonBox::ActionRole);

    connect(okButton, SIGNAL(pressed()), SLOT(setParameters()));
    connect(cancelButton, SIGNAL(pressed()), SLOT(close()));

    QVBoxLayout *topLeftLayout1 = new QVBoxLayout;
    QVBoxLayout *topLeftLayout2 = new QVBoxLayout;
    QVBoxLayout *topLeftLayout3 = new QVBoxLayout;
    QVBoxLayout *topLeftLayout4 = new QVBoxLayout;
    for (size_t i=0 ; i <mVarVector.size(); ++i )
    {
        topLeftLayout1->addWidget(mDescriptionVector[i]);
        topLeftLayout2->addWidget(mVarVector[i]);
        topLeftLayout3->addWidget(mValueVector[i]);
        topLeftLayout4->addWidget(mUnitVector[i]);
    }

    QHBoxLayout *leftLayout = new QHBoxLayout;
    leftLayout->addLayout(topLeftLayout1);
    leftLayout->addLayout(topLeftLayout2);
    leftLayout->addLayout(topLeftLayout3);
    leftLayout->addLayout(topLeftLayout4);
    leftLayout->addStretch(1);

    QGridLayout *mainLayout = new QGridLayout;
    mainLayout->setSizeConstraint(QLayout::SetFixedSize);
    mainLayout->addLayout(leftLayout, 0, 0);
    mainLayout->addWidget(buttonBox, 0, 1);
    setLayout(mainLayout);

    setWindowTitle(tr("Parameters"));
}

//! Sets the parameters in the core component. Read the values from the dialog and write them into the core component.
void ParameterDialog::setParameters()
{
    for (size_t i=0 ; i < mValueVector.size(); ++i )
    {
        bool ok;
        mpCoreComponent->setParameter(mVarVector[i]->text().toStdString(), mValueVector[i]->text().toDouble(&ok));
        //std::cout << "i: " << i << qPrintable(labelList[i]->text()) << "  " << mpCoreComponent->getParameterVector().at(i).getName() << std::endl;
        if (!ok)
        {
            assert(false);
        }
    }
    std::cout << "Parameters updated." << std::endl;
    this->close();
}
<commit_msg>handle empty input in parameterdialogs<commit_after>//!
//! @file   ParameterDialog.cpp
//! @author Björn Eriksson <bjorn.eriksson@liu.se>
//! @date   2010-03-01
//!
//! @brief Contains a class for interact with paramters
//!
//$Id$

#include <QtGui>
#include <cassert>

#include "ParameterDialog.h"
#include "HopsanCore.h"

//! Constructor.
//! @param coreComponent is a ponter to the core component.
//! @param parent defines a parent to the new instanced object.
ParameterDialog::ParameterDialog(Component *coreComponent, QWidget *parent)
    : QDialog(parent)
{
    mpCoreComponent = coreComponent;

    std::vector<CompParameter>::iterator it;

    vector<CompParameter> paramVector = mpCoreComponent->getParameterVector();

    for ( it=paramVector.begin() ; it !=paramVector.end(); it++ )
    {
        mVarVector.push_back(new QLabel(QString::fromStdString(it->getName())));
        mVarVector.back()->setAlignment(Qt::AlignVCenter | Qt::AlignRight);
        mDescriptionVector.push_back(new QLabel(QString::fromStdString(it->getDesc())));
        mUnitVector.push_back(new QLabel(QString::fromStdString(it->getUnit())));

        mValueVector.push_back(new QLineEdit());
        mValueVector.back()->setValidator(new QDoubleValidator(-999.0, 999.0, 6, mValueVector.back()));

        QString valueTxt;
        valueTxt.setNum(it->getValue(), 'g', 6 );
        mValueVector.back()->setText(valueTxt);

        mVarVector.back()->setBuddy(mValueVector.back());
    }

    okButton = new QPushButton(tr("&Ok"));
    okButton->setDefault(true);
    cancelButton = new QPushButton(tr("&Cancel"));
    cancelButton->setDefault(false);

    buttonBox = new QDialogButtonBox(Qt::Vertical);
    buttonBox->addButton(okButton, QDialogButtonBox::ActionRole);
    buttonBox->addButton(cancelButton, QDialogButtonBox::ActionRole);

    connect(okButton, SIGNAL(pressed()), SLOT(setParameters()));
    connect(cancelButton, SIGNAL(pressed()), SLOT(close()));

    QVBoxLayout *topLeftLayout1 = new QVBoxLayout;
    QVBoxLayout *topLeftLayout2 = new QVBoxLayout;
    QVBoxLayout *topLeftLayout3 = new QVBoxLayout;
    QVBoxLayout *topLeftLayout4 = new QVBoxLayout;
    for (size_t i=0 ; i <mVarVector.size(); ++i )
    {
        topLeftLayout1->addWidget(mDescriptionVector[i]);
        topLeftLayout2->addWidget(mVarVector[i]);
        topLeftLayout3->addWidget(mValueVector[i]);
        topLeftLayout4->addWidget(mUnitVector[i]);
    }

    QHBoxLayout *leftLayout = new QHBoxLayout;
    leftLayout->addLayout(topLeftLayout1);
    leftLayout->addLayout(topLeftLayout2);
    leftLayout->addLayout(topLeftLayout3);
    leftLayout->addLayout(topLeftLayout4);
    leftLayout->addStretch(1);

    QGridLayout *mainLayout = new QGridLayout;
    mainLayout->setSizeConstraint(QLayout::SetFixedSize);
    mainLayout->addLayout(leftLayout, 0, 0);
    mainLayout->addWidget(buttonBox, 0, 1);
    setLayout(mainLayout);

    setWindowTitle(tr("Parameters"));
}

//! Sets the parameters in the core component. Read the values from the dialog and write them into the core component.
void ParameterDialog::setParameters()
{
    for (size_t i=0 ; i < mValueVector.size(); ++i )
    {
        bool ok;
        double newValue = mValueVector[i]->text().toDouble(&ok);
        //std::cout << "i: " << i << qPrintable(labelList[i]->text()) << "  " << mpCoreComponent->getParameterVector().at(i).getName() << std::endl;
        if (!ok)
        {
            std::cout << "ParameterDialog::setParameters(): You must give a correct value for '" << mVarVector[i]->text().toStdString() << "'', putz. Try again!" << std::endl;
            //this->parent()->parent()->parent()
            return;
        }
        mpCoreComponent->setParameter(mVarVector[i]->text().toStdString(), newValue);
    }
    std::cout << "Parameters updated." << std::endl;
    this->close();
}
<|endoftext|>


<commit_before>/** \file krimdok_flag_pda_records.cc
 *  \brief A tool for adding a PDA field to KrimDok records.
 *  \author Dr. Johannes Ruscheinski (johannes.ruscheinski@uni-tuebingen.de)
 *  \author Johannes Riedl (johannes.riedl@uni-tuebingen.de)
 *
 *  \copyright 2016,2017,2018 Universitätsbibliothek Tübingen.  All rights reserved.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <iostream>
#include <cstdlib>
#include "MARC.h"
#include "StringUtil.h"
#include "TimeUtil.h"
#include "util.h"

void Usage() {
    std::cerr << "Usage: " << ::progname << " no_of_years marc_input_file marc_output_file\n";
    std::exit(EXIT_FAILURE);
}

bool IsMatchingRecord(const MARC::Record &record, const std::vector<std::pair<MARC::Record::const_iterator, MARC::Record::const_iterator>> &local_block_boundaries,
                      std::vector<std::string> &matching_subfield_a_values)
{
    for (const auto local_block_boundary : local_block_boundaries) {
        std::vector<MARC::Record::const_iterator> fields;
        if (record.findFieldsInLocalBlock("852", "??", local_block_boundary, &fields) == 0)
            return false;

        for (const auto &field : fields) {
            std::vector<std::string> subfield_a_values(field->getSubfields().extractSubfields('a'));
                for (const auto &subfield_a_value : subfield_a_values)
                    for (const auto &matching_subfield_a_value : matching_subfield_a_values)
                        if (subfield_a_value == matching_subfield_a_value)
                            return true;
        }
    }
    return false;
}

bool IsMPIRecord(const MARC::Record &record, const std::vector<std::pair<MARC::Record::const_iterator, MARC::Record::const_iterator>> &local_block_boundaries) {
    static std::vector<std::string> subfield_a_values{ "DE-Frei85" };
    return IsMatchingRecord(record, local_block_boundaries, subfield_a_values);
}

bool IsUBOrIFKRecord(const MARC::Record &record, const std::vector<std::pair<MARC::Record::const_iterator, MARC::Record::const_iterator>> &local_block_boundaries) {
    static std::vector<std::string> subfield_a_values{ "DE-21", "DE-21-110" };
    return IsMatchingRecord(record, local_block_boundaries, subfield_a_values);
}

bool IsARecognisableYear(const std::string &year_candidate) {
    if (year_candidate.length() != 4)
        return false;

    for (char ch : year_candidate) {
        if (not StringUtil::IsDigit(ch))
            return false;
    }

    return true;
}

// If we can find a recognisable year in 260$c we return it, o/w we return the empty string.
std::string GetPublicationYear(const MARC::Record &record) {
    for (const auto &_260_field : record.getTagRange("260")) {
        for (const auto &year_candidate : _260_field.getSubfields().extractSubfields('c'))
            if (IsARecognisableYear(year_candidate))
                return year_candidate;
    }
    return "";
}

void FindNonMPIInstitutions(const MARC::Record &record,
                            const std::vector<std::pair<MARC::Record::const_iterator, MARC::Record::const_iterator>> &local_block_boundaries,
                            std::vector<std::string> * const non_mpi_institutions)
{
    non_mpi_institutions->clear();

    for (const auto local_block_boundary : local_block_boundaries) {
        std::vector<MARC::Record::const_iterator> fields;
        if (record.findFieldsInLocalBlock("852", "??", local_block_boundary, &fields) == 0)
            return;

        for (const auto &field : fields) {
            std::vector<std::string> subfield_a_values(field->getSubfields().extractSubfields('a'));
            for (const auto &subfield_a_value : subfield_a_values)
                if (subfield_a_value != "DE-Frei85")
                    non_mpi_institutions->emplace_back(subfield_a_value);
        }
    }
}

void AddPDAFieldToRecords(const std::string &cutoff_year, MARC::Reader * const marc_reader,
                          MARC::Writer * const marc_writer)
{
    unsigned pda_field_added_count(0);
    while (MARC::Record record = marc_reader->read()) {
        if (not record.isMonograph()) {
            marc_writer->write(record);
            continue;
        }

        std::vector<std::pair<MARC::Record::const_iterator, MARC::Record::const_iterator>> local_block_boundaries;
        record.findAllLocalDataBlocks(&local_block_boundaries);
        if (IsMPIRecord(record, local_block_boundaries) and not IsUBOrIFKRecord(record, local_block_boundaries)) {
            const std::string publication_year(GetPublicationYear(record));
            if (publication_year >= cutoff_year) {
                std::vector<std::string> non_mpi_institutions;
                FindNonMPIInstitutions(record, local_block_boundaries, &non_mpi_institutions);
                if (non_mpi_institutions.empty()) {
                    ++pda_field_added_count;
                    record.insertField("PDA", MARC::Subfields({ MARC::Subfield('a', "yes") }));
                    marc_writer->write(record);
                    continue;
                }
            }
        }

        marc_writer->write(record);
    }

    std::cout << "Added a PDA field to " << pda_field_added_count << " record(s).\n";
}

std::string GetCutoffYear(const unsigned no_of_years) {
    const unsigned current_year(StringUtil::ToUnsigned(TimeUtil::GetCurrentYear(TimeUtil::LOCAL)));
    return std::to_string(current_year - no_of_years);
}

const unsigned MAX_NO_OF_YEARS_TO_CONSIDER(10);

int main(int argc, char *argv[]) {
    ::progname = argv[0];

    try {
        if (argc != 4)
            Usage();

        const unsigned no_of_years(StringUtil::ToUnsigned(argv[1]));
        if (no_of_years > MAX_NO_OF_YEARS_TO_CONSIDER)
            logger->error("the number of years we want to consider is probably incorrect!");

        const std::unique_ptr<MARC::Reader> marc_reader(MARC::Reader::Factory(argv[2], MARC::Reader::AUTO));
        const std::unique_ptr<MARC::Writer> marc_writer(MARC::Writer::Factory(argv[3], MARC::Writer::AUTO));
        AddPDAFieldToRecords(GetCutoffYear(no_of_years), marc_reader.get(), marc_writer.get());
    } catch (const std::exception &x) {
        logger->error("caught exception: " + std::string(x.what()));
    }
}
<commit_msg>Minor improvements<commit_after>/** \file krimdok_flag_pda_records.cc
 *  \brief A tool for adding a PDA field to KrimDok records.
 *  \author Dr. Johannes Ruscheinski (johannes.ruscheinski@uni-tuebingen.de)
 *  \author Johannes Riedl (johannes.riedl@uni-tuebingen.de)
 *
 *  \copyright 2016,2017,2018 Universitätsbibliothek Tübingen.  All rights reserved.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <iostream>
#include <cstdlib>
#include "MARC.h"
#include "StringUtil.h"
#include "TimeUtil.h"
#include "util.h"

void Usage() {
    std::cerr << "Usage: " << ::progname << " no_of_years marc_input_file marc_output_file\n";
    std::exit(EXIT_FAILURE);
}

bool IsMatchingRecord(const MARC::Record &record, const std::vector<std::pair<MARC::Record::const_iterator, MARC::Record::const_iterator>> &local_block_boundaries,
                      std::vector<std::string> &matching_subfield_a_values)
{
    for (const auto local_block_boundary : local_block_boundaries) {
        std::vector<MARC::Record::const_iterator> fields;
        if (record.findFieldsInLocalBlock("852", "??", local_block_boundary, &fields) == 0)
            return false;

        for (const auto &field : fields) {
            std::vector<std::string> subfield_a_values(field->getSubfields().extractSubfields('a'));
                for (const auto &subfield_a_value : subfield_a_values)
                    for (const auto &matching_subfield_a_value : matching_subfield_a_values)
                        if (subfield_a_value == matching_subfield_a_value)
                            return true;
        }
    }
    return false;
}

bool IsMPIRecord(const MARC::Record &record, const std::vector<std::pair<MARC::Record::const_iterator, MARC::Record::const_iterator>> &local_block_boundaries) {
    static std::vector<std::string> subfield_a_values{ "DE-Frei85" };
    return IsMatchingRecord(record, local_block_boundaries, subfield_a_values);
}

bool IsUBOrIFKRecord(const MARC::Record &record, const std::vector<std::pair<MARC::Record::const_iterator, MARC::Record::const_iterator>> &local_block_boundaries) {
    static std::vector<std::string> subfield_a_values{ "DE-21", "DE-21-110" };
    return IsMatchingRecord(record, local_block_boundaries, subfield_a_values);
}

bool IsARecognisableYear(const std::string &year_candidate) {
    if (year_candidate.length() != 4)
        return false;

    for (char ch : year_candidate) {
        if (not StringUtil::IsDigit(ch))
            return false;
    }

    return true;
}

// If we can find a recognisable year in 260$c we return it, o/w we return the empty string.
std::string GetPublicationYear(const MARC::Record &record) {
    for (const auto &_260_field : record.getTagRange("260")) {
        for (const auto &year_candidate : _260_field.getSubfields().extractSubfields('c'))
            if (IsARecognisableYear(year_candidate))
                return year_candidate;
    }
    return "";
}

void FindNonMPIInstitutions(const MARC::Record &record,
                            const std::vector<std::pair<MARC::Record::const_iterator, MARC::Record::const_iterator>> &local_block_boundaries,
                            std::vector<std::string> * const non_mpi_institutions)
{
    non_mpi_institutions->clear();

    for (const auto &local_block_boundary : local_block_boundaries) {
        std::vector<MARC::Record::const_iterator> fields;
        if (record.findFieldsInLocalBlock("852", "??", local_block_boundary, &fields) == 0)
            return;

        for (const auto &field : fields) {
            std::vector<std::string> subfield_a_values(field->getSubfields().extractSubfields('a'));
            for (const auto &subfield_a_value : subfield_a_values)
                if (subfield_a_value != "DE-Frei85")
                    non_mpi_institutions->emplace_back(subfield_a_value);
        }
    }
}

void AddPDAFieldToRecords(const std::string &cutoff_year, MARC::Reader * const marc_reader,
                          MARC::Writer * const marc_writer)
{
    unsigned pda_field_added_count(0);
    while (MARC::Record record = marc_reader->read()) {
        if (not record.isMonograph()) {
            marc_writer->write(record);
            continue;
        }

        std::vector<std::pair<MARC::Record::const_iterator, MARC::Record::const_iterator>> local_block_boundaries;
        record.findAllLocalDataBlocks(&local_block_boundaries);
        if (IsMPIRecord(record, local_block_boundaries) and not IsUBOrIFKRecord(record, local_block_boundaries)) {
            const std::string publication_year(GetPublicationYear(record));
            if (publication_year >= cutoff_year) {
                std::vector<std::string> non_mpi_institutions;
                FindNonMPIInstitutions(record, local_block_boundaries, &non_mpi_institutions);
                if (non_mpi_institutions.empty()) {
                    ++pda_field_added_count;
                    record.insertField("PDA", { { 'a', "yes" } });
                    marc_writer->write(record);
                    continue;
                }
            }
        }

        marc_writer->write(record);
    }

    std::cout << "Added a PDA field to " << pda_field_added_count << " record(s).\n";
}

std::string GetCutoffYear(const unsigned no_of_years) {
    const unsigned current_year(StringUtil::ToUnsigned(TimeUtil::GetCurrentYear(TimeUtil::LOCAL)));
    return std::to_string(current_year - no_of_years);
}

int main(int argc, char *argv[]) {
    ::progname = argv[0];

    try {
        if (argc != 4)
            Usage();

        const unsigned no_of_years(StringUtil::ToUnsigned(argv[1]));
        const std::unique_ptr<MARC::Reader> marc_reader(MARC::Reader::Factory(argv[2], MARC::Reader::AUTO));
        const std::unique_ptr<MARC::Writer> marc_writer(MARC::Writer::Factory(argv[3], MARC::Writer::AUTO));
        AddPDAFieldToRecords(GetCutoffYear(no_of_years), marc_reader.get(), marc_writer.get());
    } catch (const std::exception &x) {
        logger->error("caught exception: " + std::string(x.what()));
    }
}
<|endoftext|>


<commit_before>#include "queue.h"
#include "gtest/gtest.h"

class queueTest : public testing::Test {
 protected:
  // SetUp() & TearDown() are virtual functions from testting::Test,
  // so we just signify that we override them here
  virtual void SetUp() {
    q1_.push(1);
    q1_.push(2);
    q2_.push(1);
    q2_.push(3);
  }

  virtual void TearDown() {
  }

  // setup fixtures
  queue<int> q0_;
  queue<int> q1_;
  queue<int> q2_;
};

// Use TEST_F to test with fixtures.
TEST_F(queueTest, DefaultConstructor) {
  EXPECT_EQ(0u, q0_.size());
}

TEST_F(queueTest, frontback) {
  EXPECT_EQ(1, q1_.front());
  EXPECT_EQ(1, q2_.front());
  EXPECT_EQ(2, q1_.back());
  EXPECT_EQ(3, q2_.back());
}

TEST_F(queueTest, push){
  EXPECT_EQ(2u, q1_.size());
  EXPECT_EQ(2u, q2_.size());
  q2_.push(100);
  EXPECT_EQ(3u, q2_.size());
  EXPECT_EQ(100, q2_.back());
}

TEST_F(queueTest, pop){
  EXPECT_EQ(2u, q2_.size());
  EXPECT_EQ(1, q2_.front());
  q2_.pop();
  EXPECT_EQ(3, q2_.front());
  q2_.pop();
  EXPECT_TRUE(q2_.empty());
}

TEST_F(queueTest, clear) {
  q2_.clear();
  EXPECT_TRUE(q2_.empty());
}
<commit_msg>queue: write tests for the move version of push().<commit_after>#include "queue.h"
#include "gtest/gtest.h"
#include <string>
class queueTest : public testing::Test {
 protected:
  // SetUp() & TearDown() are virtual functions from testting::Test,
  // so we just signify that we override them here
  virtual void SetUp() {
    q1_.push(1);
    q1_.push(2);
    q2_.push(1);
    q2_.push(3);
  }

  virtual void TearDown() {
  }

  // setup fixtures
  queue<int> q0_;
  queue<int> q1_;
  queue<int> q2_;
};

// Use TEST_F to test with fixtures.
TEST_F(queueTest, DefaultConstructor) {
  EXPECT_EQ(0u, q0_.size());
}

TEST_F(queueTest, frontback) {
  EXPECT_EQ(1, q1_.front());
  EXPECT_EQ(1, q2_.front());
  EXPECT_EQ(2, q1_.back());
  EXPECT_EQ(3, q2_.back());
}

TEST_F(queueTest, push){
  EXPECT_EQ(2u, q1_.size());
  EXPECT_EQ(2u, q2_.size());
  q2_.push(100); // this is move push
  EXPECT_EQ(3u, q2_.size());
  EXPECT_EQ(100, q2_.back());
  // test normal push
  int item = 1000;
  q2_.push(item);
  EXPECT_EQ(1000, q2_.back());
  EXPECT_EQ(1000, item);
}

TEST_F(queueTest, pop){
  EXPECT_EQ(2u, q2_.size());
  EXPECT_EQ(1, q2_.front());
  q2_.pop();
  EXPECT_EQ(3, q2_.front());
  q2_.pop();
  EXPECT_TRUE(q2_.empty());
}

TEST_F(queueTest, clear) {
  q2_.clear();
  EXPECT_TRUE(q2_.empty());
}

// test the move version of push
// use std::string here for non-trivial object
// that we can actually increase efficiency by
// the new move operation
TEST_F(queueTest, movepush) {
  queue<std::string> qstr;
  qstr.push("First string");
  EXPECT_EQ(qstr.front(), "First string");
  // test copy push: after the push, str_item is the same
  std::string str_item("Second string");
  qstr.push(str_item);
  EXPECT_EQ(qstr.back(), "Second string");
  EXPECT_EQ(str_item, "Second string");
  // test move push: after the push, str_item is empty
  str_item = "Third string";
  qstr.push(std::move(str_item));
  EXPECT_EQ(qstr.back(), "Third string");
  EXPECT_TRUE(str_item.empty());
}
<|endoftext|>


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

#include "testing/gtest/include/gtest/gtest.h"
#include "webkit/glue/devtools/devtools_mock_rpc.h"
#include "webkit/glue/devtools/devtools_rpc.h"

namespace {

#define TEST_RPC_STRUCT(METHOD0, METHOD1, METHOD2, METHOD3) \
  METHOD0(Method0) \
  METHOD1(Method1, int) \
  METHOD2(Method2, int, String) \
  METHOD3(Method3, int, String, Value)
DEFINE_RPC_CLASS(TestRpcClass, TEST_RPC_STRUCT)

#define ANOTHER_TEST_RPC_STRUCT(METHOD0, METHOD1, METHOD2, METHOD3) \
  METHOD0(Method0)
DEFINE_RPC_CLASS(AnotherTestRpcClass, ANOTHER_TEST_RPC_STRUCT)

class MockTestRpcClass : public TestRpcClassStub,
                         public DevToolsMockRpc {
 public:
  MockTestRpcClass() : TestRpcClassStub(NULL) {
    set_delegate(this);
  }
  ~MockTestRpcClass() {}
};

class MockAnotherTestRpcClass : public AnotherTestRpcClassStub,
                                public DevToolsMockRpc {
 public:
  MockAnotherTestRpcClass() : AnotherTestRpcClassStub(NULL) {
    set_delegate(this);
  }
  ~MockAnotherTestRpcClass() {}
};

class DevToolsRpcTests : public testing::Test {
 public:
  DevToolsRpcTests() {}

 protected:
  virtual void SetUp() {}
  virtual void TearDown() {}
};

// Tests method call serialization.
TEST_F(DevToolsRpcTests, TestSerialize) {
  MockTestRpcClass mock;
  mock.Method0();
  int id = RpcTypeToNumber<TestRpcClass>::number;
  EXPECT_EQ(StringPrintf("[%d,0]", id), mock.get_log());
  mock.Reset();

  mock.Method1(10);
  EXPECT_EQ(StringPrintf("[%d,1,10]", id), mock.get_log());
  mock.Reset();

  mock.Method2(20, "foo");
  EXPECT_EQ(StringPrintf("[%d,2,20,\"foo\"]", id), mock.get_log());
  mock.Reset();

  StringValue value("bar");
  mock.Method3(30, "foo", value);
  EXPECT_EQ(StringPrintf("[%d,3,30,\"foo\",\"bar\"]", id), mock.get_log());
  mock.Reset();
}

// Tests method call dispatch.
TEST_F(DevToolsRpcTests, TestDispatch) {
  MockTestRpcClass local;
  MockTestRpcClass remote;

  // Call 1.
  local.Reset();
  local.Method0();
  remote.Reset();
  remote.Method0();
  remote.Replay();

  TestRpcClassDispatch::Dispatch(&remote, local.get_log());
  remote.Verify();

  // Call 2.
  local.Reset();
  local.Method1(10);
  remote.Reset();
  remote.Method1(10);
  remote.Replay();
  TestRpcClassDispatch::Dispatch(&remote, local.get_log());
  remote.Verify();

  // Call 3.
  local.Reset();
  remote.Reset();
  local.Method2(20, "foo");
  remote.Method2(20, "foo");

  remote.Replay();
  TestRpcClassDispatch::Dispatch(&remote, local.get_log());
  remote.Verify();

  // Call 4.
  local.Reset();
  remote.Reset();
  StringValue value("bar");
  local.Method3(30, "foo", value);
  remote.Method3(30, "foo", value);

  remote.Replay();
  TestRpcClassDispatch::Dispatch(&remote, local.get_log());
  remote.Verify();
}

// Tests class unique id.
TEST_F(DevToolsRpcTests, TestClassId) {
  MockAnotherTestRpcClass mock;
  ASSERT_TRUE(RpcTypeToNumber<TestRpcClass>::number !=
      RpcTypeToNumber<AnotherTestRpcClass>::number);

  int id = RpcTypeToNumber<AnotherTestRpcClass>::number;
  mock.Method0();
  ASSERT_EQ(StringPrintf("[%d,0]", id), mock.get_log());
}

}  // namespace
<commit_msg>Fix devtools tests.<commit_after>// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "PlatformString.h"
#undef LOG

#include "testing/gtest/include/gtest/gtest.h"
#include "webkit/glue/devtools/devtools_mock_rpc.h"
#include "webkit/glue/devtools/devtools_rpc.h"

namespace {

#define TEST_RPC_STRUCT(METHOD0, METHOD1, METHOD2, METHOD3, METHOD4) \
  METHOD0(Method0) \
  METHOD1(Method1, int) \
  METHOD2(Method2, int, String) \
  METHOD3(Method3, int, String, Value)
DEFINE_RPC_CLASS(TestRpcClass, TEST_RPC_STRUCT)

#define ANOTHER_TEST_RPC_STRUCT(METHOD0, METHOD1, METHOD2, METHOD3, METHOD4) \
  METHOD0(Method0)
DEFINE_RPC_CLASS(AnotherTestRpcClass, ANOTHER_TEST_RPC_STRUCT)

class MockTestRpcClass : public TestRpcClassStub,
                         public DevToolsMockRpc {
 public:
  MockTestRpcClass() : TestRpcClassStub(NULL) {
    set_delegate(this);
  }
  ~MockTestRpcClass() {}
};

class MockAnotherTestRpcClass : public AnotherTestRpcClassStub,
                                public DevToolsMockRpc {
 public:
  MockAnotherTestRpcClass() : AnotherTestRpcClassStub(NULL) {
    set_delegate(this);
  }
  ~MockAnotherTestRpcClass() {}
};

class DevToolsRpcTests : public testing::Test {
 public:
  DevToolsRpcTests() {}

 protected:
  virtual void SetUp() {}
  virtual void TearDown() {}
};

// Tests method call serialization.
TEST_F(DevToolsRpcTests, TestSerialize) {
  MockTestRpcClass mock;
  mock.Method0();
  int id = RpcTypeToNumber<TestRpcClass>::number;
  EXPECT_EQ(StringPrintf("[%d,0]", id), mock.get_log());
  mock.Reset();

  mock.Method1(10);
  EXPECT_EQ(StringPrintf("[%d,1,10]", id), mock.get_log());
  mock.Reset();

  mock.Method2(20, "foo");
  EXPECT_EQ(StringPrintf("[%d,2,20,\"foo\"]", id), mock.get_log());
  mock.Reset();

  StringValue value("bar");
  mock.Method3(30, "foo", value);
  EXPECT_EQ(StringPrintf("[%d,3,30,\"foo\",\"bar\"]", id), mock.get_log());
  mock.Reset();
}

// Tests method call dispatch.
TEST_F(DevToolsRpcTests, TestDispatch) {
  MockTestRpcClass local;
  MockTestRpcClass remote;

  // Call 1.
  local.Reset();
  local.Method0();
  remote.Reset();
  remote.Method0();
  remote.Replay();

  TestRpcClassDispatch::Dispatch(&remote, local.get_log());
  remote.Verify();

  // Call 2.
  local.Reset();
  local.Method1(10);
  remote.Reset();
  remote.Method1(10);
  remote.Replay();
  TestRpcClassDispatch::Dispatch(&remote, local.get_log());
  remote.Verify();

  // Call 3.
  local.Reset();
  remote.Reset();
  local.Method2(20, "foo");
  remote.Method2(20, "foo");

  remote.Replay();
  TestRpcClassDispatch::Dispatch(&remote, local.get_log());
  remote.Verify();

  // Call 4.
  local.Reset();
  remote.Reset();
  StringValue value("bar");
  local.Method3(30, "foo", value);
  remote.Method3(30, "foo", value);

  remote.Replay();
  TestRpcClassDispatch::Dispatch(&remote, local.get_log());
  remote.Verify();
}

// Tests class unique id.
TEST_F(DevToolsRpcTests, TestClassId) {
  MockAnotherTestRpcClass mock;
  ASSERT_TRUE(RpcTypeToNumber<TestRpcClass>::number !=
      RpcTypeToNumber<AnotherTestRpcClass>::number);

  int id = RpcTypeToNumber<AnotherTestRpcClass>::number;
  mock.Method0();
  ASSERT_EQ(StringPrintf("[%d,0]", id), mock.get_log());
}

}  // namespace
<|endoftext|>


<commit_before>
//
// This source file is part of appleseed.
// Visit http://appleseedhq.net/ for additional information and resources.
//
// This software is released under the MIT license.
//
// Copyright (c) 2010-2013 Francois Beaune, Jupiter Jazz Limited
// Copyright (c) 2014-2016 Francois Beaune, The appleseedhq Organization
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

#ifdef _WIN32

// appleseed.foundation headers.
#include "foundation/platform/windows.h"

// Standard headers.
#include <ios>
#include <stdio.h>

// Platform headers.
#include <fcntl.h>
#include <io.h>

using namespace std;

namespace
{
    void redirect(FILE* fp, const char* mode, const DWORD std_device)
    {
#pragma warning (push)
#pragma warning (disable : 4311)    // 'variable' : pointer truncation from 'type' to 'type'
        const long handle = (long)GetStdHandle(std_device);
        const int fd = _open_osfhandle(handle, _O_TEXT);
        *fp = *_fdopen(fd, mode);
        setvbuf(fp, NULL, _IONBF, 0);
#pragma warning (pop)
    }

    void open_console()
    {
        // Allocates a console for this process.
        AllocConsole();

        // Redirect stdout, stdin and stderr to the console.
        redirect(stdout, "w", STD_OUTPUT_HANDLE);
        redirect(stdin, "r", STD_INPUT_HANDLE);
        redirect(stderr, "w", STD_ERROR_HANDLE);

        // Make cout, wcout, cin, wcin, cerr, wcerr, clog and wclog point to the console as well.
        ios::sync_with_stdio();
    }
}

//
// DLL entry point.
//

BOOL APIENTRY DllMain(
    HINSTANCE   module,
    DWORD       reason,
    LPVOID      /*reserved*/)
{
    if (reason == DLL_PROCESS_ATTACH)
    {
#ifndef SHIP
        // Open a console.
        // open_console();
#endif
    }

    return TRUE;
}

#endif  // _WIN32
<commit_msg>Code cleanup<commit_after>
//
// This source file is part of appleseed.
// Visit http://appleseedhq.net/ for additional information and resources.
//
// This software is released under the MIT license.
//
// Copyright (c) 2010-2013 Francois Beaune, Jupiter Jazz Limited
// Copyright (c) 2014-2016 Francois Beaune, The appleseedhq Organization
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

#ifdef _WIN32

// appleseed.foundation headers.
#include "foundation/platform/windows.h"

// Standard headers.
#include <ios>
#include <stdio.h>

// Platform headers.
#include <fcntl.h>
#include <io.h>

using namespace std;

namespace
{
    void redirect(FILE* fp, const char* mode, const DWORD std_device)
    {
        const intptr_t handle = (intptr_t)GetStdHandle(std_device);
        const int fd = _open_osfhandle(handle, _O_TEXT);
        *fp = *_fdopen(fd, mode);
        setvbuf(fp, NULL, _IONBF, 0);
    }

    void open_console()
    {
        // Allocates a console for this process.
        AllocConsole();

        // Redirect stdout, stdin and stderr to the console.
        redirect(stdout, "w", STD_OUTPUT_HANDLE);
        redirect(stdin, "r", STD_INPUT_HANDLE);
        redirect(stderr, "w", STD_ERROR_HANDLE);

        // Make cout, wcout, cin, wcin, cerr, wcerr, clog and wclog point to the console as well.
        ios::sync_with_stdio();
    }
}

//
// DLL entry point.
//

BOOL APIENTRY DllMain(
    HINSTANCE   module,
    DWORD       reason,
    LPVOID      /*reserved*/)
{
    if (reason == DLL_PROCESS_ATTACH)
    {
#ifndef SHIP
        // Open a console.
        // open_console();
#endif
    }

    return TRUE;
}

#endif  // _WIN32
<|endoftext|>


<commit_before>#include "videoplayer.h"

#include <QGuiApplication>
#include <QTimer>

#include <QOpenGLShaderProgram>
#include <QOpenGLTexture>
#include <QMouseEvent>

#define PROGRAM_VERTEX_ATTRIBUTE 0
#define PROGRAM_TEXCOORD_ATTRIBUTE 1

VideoPlayer::VideoPlayer(QWidget *parent)
    : QOpenGLWidget(parent),
      clearColor(Qt::black)
{
}

VideoPlayer::~VideoPlayer() {
    makeCurrent();
    vbo.destroy();

    delete program;
    doneCurrent();
}

void VideoPlayer::open(const QString &filename) {
    _pipeline->open(filename);
}

void VideoPlayer::stop() {
    _pipeline->stop();
}

QSize VideoPlayer::minimumSizeHint() const
{
    return QSize(50, 50);
}

QSize VideoPlayer::sizeHint() const
{
    return QSize(200, 200);
}

void VideoPlayer::setClearColor(const QColor &color)
{
    clearColor = color;
    update();
}

void VideoPlayer::initializeGL()
{
    initializeOpenGLFunctions();
    makeObject();

    QOpenGLShader *vshader = new QOpenGLShader(QOpenGLShader::Vertex, this);
    const char *vsrc =
            "attribute highp vec4 vertex;\n"
            "attribute mediump vec4 texCoord;\n"
            "varying mediump vec4 texc;\n"
            "uniform mediump mat4 matrix;\n"
            "void main(void)\n"
            "{\n"
            "    gl_Position = matrix * vertex;\n"
            "    texc = texCoord;\n"
            "}\n";
    vshader->compileSourceCode(vsrc);

    QOpenGLShader *fshader = new QOpenGLShader(QOpenGLShader::Fragment, this);
    const char *fsrc =
            "uniform sampler2D texture;\n"
            "varying mediump vec4 texc;\n"
            "void main(void)\n"
            "{\n"
            "    gl_FragColor = texture2D(texture, texc.st);\n"
            "}\n";
    fshader->compileSourceCode(fsrc);

    program = new QOpenGLShaderProgram;
    program->addShader(vshader);
    program->addShader(fshader);
    program->bindAttributeLocation("vertex", PROGRAM_VERTEX_ATTRIBUTE);
    program->bindAttributeLocation("texCoord", PROGRAM_TEXCOORD_ATTRIBUTE);
    program->link();

    program->bind();
    program->setUniformValue("texture", 0);
}

void VideoPlayer::paintGL() {
    glClearColor(clearColor.redF(), clearColor.greenF(), clearColor.blueF(), clearColor.alphaF());
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    program->setUniformValue("matrix", matrix);
    program->enableAttributeArray(PROGRAM_VERTEX_ATTRIBUTE);
    program->enableAttributeArray(PROGRAM_TEXCOORD_ATTRIBUTE);
    program->setAttributeBuffer(PROGRAM_VERTEX_ATTRIBUTE, GL_FLOAT, 0, 3, 5 * sizeof(GLfloat));
    program->setAttributeBuffer(PROGRAM_TEXCOORD_ATTRIBUTE, GL_FLOAT, 3 * sizeof(GLfloat), 2, 5 * sizeof(GLfloat));

    glBindTexture (GL_TEXTURE_2D, textureId);
    glDrawArrays(GL_TRIANGLES, 0, 6);

    if (_pipeline) {
        _pipeline->frameDrawn();
    }
}

void VideoPlayer::resizeGL(int width, int height) {
    glViewport(0, 0, width, height);

    makeObject();
}

void VideoPlayer::makeObject() {
    static const GLfloat coords[6][3] =
        { { -1.0f, -1.0f,  0.0f },
          {  1.0f, -1.0f,  0.0f },
          { -1.0f,  1.0f,  0.0f },
          { -1.0f,  1.0f,  0.0f },
          {  1.0f,  1.0f,  0.0f },
          {  1.0f, -1.0f,  0.0f } };

    QVector<GLfloat> vertData;

    double scaledWidth = (double) width() / (double) _videoWidth;
    double scaledHeight = (double) height() / (double) _videoHeight;

    if (scaledWidth < scaledHeight) {
        double modHeight = ((double) _videoHeight * (double) width()) / (double) _videoWidth;
        double totalHeight = (double) modHeight / (double) height();

        for (int i = 0; i < 6; i++) {
            // vertex position
            vertData.append(coords[i][0]);
            vertData.append(coords[i][1] * totalHeight);
            vertData.append(coords[i][2]);
            // texture coordinate
            vertData.append(coords[i][0] > 0 ? 1 : 0);
            vertData.append(coords[i][1] > 0 ? 0 : 1);
        }
    } else if (scaledWidth > scaledHeight) {
        double modWidth = ((double) _videoWidth * (double) height()) / (double) _videoHeight;
        double totalWidth = (double) modWidth / (double) width();

        for (int i = 0; i < 6; i++) {
            // vertex position
            vertData.append(coords[i][0] * totalWidth);
            vertData.append(coords[i][1]);
            vertData.append(coords[i][2]);
            // texture coordinate
            vertData.append(coords[i][0] > 0 ? 1 : 0);
            vertData.append(coords[i][1] > 0 ? 0 : 1);
        }
    } else {
        for (int i = 0; i < 6; i++) {
            // vertex position
            vertData.append(coords[i][0]);
            vertData.append(coords[i][1]);
            vertData.append(coords[i][2]);
            // texture coordinate
            vertData.append(coords[i][0] > 0 ? 1 : 0);
            vertData.append(coords[i][1] > 0 ? 0 : 1);
        }
    }

    // new
    if (! vbo.isCreated()) {
        vbo.create();
    }

    vbo.bind();
    vbo.allocate(vertData.constData(), vertData.count() * sizeof(GLfloat));
}

void VideoPlayer::videoSize(int width, int height) {
    _videoWidth = width;
    _videoHeight = height;

    makeObject();
}

void VideoPlayer::newFrame (GLuint texture) {
    textureId = texture;

    update();
}

void VideoPlayer::initPipeline() {
    if (! _pipeline) {
        _pipeline = std::unique_ptr<GstreamerPipeline>(new GstreamerPipeline());
        _pipeline->initialize(context());
        connect(_pipeline.get(), &GstreamerPipeline::newFrameReady, this, &VideoPlayer::newFrame, Qt::DirectConnection);
        connect(_pipeline.get(), &GstreamerPipeline::videoSize, this, &VideoPlayer::videoSize);
        connect(_pipeline.get(), &GstreamerPipeline::finished, this, &VideoPlayer::finished);
    }
}
<commit_msg>make sure the context is current when updating the object<commit_after>#include "videoplayer.h"

#include <QGuiApplication>
#include <QTimer>

#include <QOpenGLShaderProgram>
#include <QOpenGLTexture>
#include <QMouseEvent>

#define PROGRAM_VERTEX_ATTRIBUTE 0
#define PROGRAM_TEXCOORD_ATTRIBUTE 1

VideoPlayer::VideoPlayer(QWidget *parent)
    : QOpenGLWidget(parent),
      clearColor(Qt::black)
{
}

VideoPlayer::~VideoPlayer() {
    makeCurrent();
    vbo.destroy();

    delete program;
    doneCurrent();
}

void VideoPlayer::open(const QString &filename) {
    _pipeline->open(filename);
}

void VideoPlayer::stop() {
    _pipeline->stop();
}

QSize VideoPlayer::minimumSizeHint() const
{
    return QSize(50, 50);
}

QSize VideoPlayer::sizeHint() const
{
    return QSize(200, 200);
}

void VideoPlayer::setClearColor(const QColor &color)
{
    clearColor = color;
    update();
}

void VideoPlayer::initializeGL()
{
    initializeOpenGLFunctions();
    makeObject();

    QOpenGLShader *vshader = new QOpenGLShader(QOpenGLShader::Vertex, this);
    const char *vsrc =
            "attribute highp vec4 vertex;\n"
            "attribute mediump vec4 texCoord;\n"
            "varying mediump vec4 texc;\n"
            "uniform mediump mat4 matrix;\n"
            "void main(void)\n"
            "{\n"
            "    gl_Position = matrix * vertex;\n"
            "    texc = texCoord;\n"
            "}\n";
    vshader->compileSourceCode(vsrc);

    QOpenGLShader *fshader = new QOpenGLShader(QOpenGLShader::Fragment, this);
    const char *fsrc =
            "uniform sampler2D texture;\n"
            "varying mediump vec4 texc;\n"
            "void main(void)\n"
            "{\n"
            "    gl_FragColor = texture2D(texture, texc.st);\n"
            "}\n";
    fshader->compileSourceCode(fsrc);

    program = new QOpenGLShaderProgram;
    program->addShader(vshader);
    program->addShader(fshader);
    program->bindAttributeLocation("vertex", PROGRAM_VERTEX_ATTRIBUTE);
    program->bindAttributeLocation("texCoord", PROGRAM_TEXCOORD_ATTRIBUTE);
    program->link();

    program->bind();
    program->setUniformValue("texture", 0);
}

void VideoPlayer::paintGL() {
    glClearColor(clearColor.redF(), clearColor.greenF(), clearColor.blueF(), clearColor.alphaF());
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    program->setUniformValue("matrix", matrix);
    program->enableAttributeArray(PROGRAM_VERTEX_ATTRIBUTE);
    program->enableAttributeArray(PROGRAM_TEXCOORD_ATTRIBUTE);
    program->setAttributeBuffer(PROGRAM_VERTEX_ATTRIBUTE, GL_FLOAT, 0, 3, 5 * sizeof(GLfloat));
    program->setAttributeBuffer(PROGRAM_TEXCOORD_ATTRIBUTE, GL_FLOAT, 3 * sizeof(GLfloat), 2, 5 * sizeof(GLfloat));

    glBindTexture (GL_TEXTURE_2D, textureId);
    glDrawArrays(GL_TRIANGLES, 0, 6);

    if (_pipeline) {
        _pipeline->frameDrawn();
    }
}

void VideoPlayer::resizeGL(int width, int height) {
    glViewport(0, 0, width, height);

    makeObject();
}

void VideoPlayer::makeObject() {
    static const GLfloat coords[6][3] =
        { { -1.0f, -1.0f,  0.0f },
          {  1.0f, -1.0f,  0.0f },
          { -1.0f,  1.0f,  0.0f },
          { -1.0f,  1.0f,  0.0f },
          {  1.0f,  1.0f,  0.0f },
          {  1.0f, -1.0f,  0.0f } };

    QVector<GLfloat> vertData;

    double scaledWidth = (double) width() / (double) _videoWidth;
    double scaledHeight = (double) height() / (double) _videoHeight;

    if (scaledWidth < scaledHeight) {
        double modHeight = ((double) _videoHeight * (double) width()) / (double) _videoWidth;
        double totalHeight = (double) modHeight / (double) height();

        for (int i = 0; i < 6; i++) {
            // vertex position
            vertData.append(coords[i][0]);
            vertData.append(coords[i][1] * totalHeight);
            vertData.append(coords[i][2]);
            // texture coordinate
            vertData.append(coords[i][0] > 0 ? 1 : 0);
            vertData.append(coords[i][1] > 0 ? 0 : 1);
        }
    } else if (scaledWidth > scaledHeight) {
        double modWidth = ((double) _videoWidth * (double) height()) / (double) _videoHeight;
        double totalWidth = (double) modWidth / (double) width();

        for (int i = 0; i < 6; i++) {
            // vertex position
            vertData.append(coords[i][0] * totalWidth);
            vertData.append(coords[i][1]);
            vertData.append(coords[i][2]);
            // texture coordinate
            vertData.append(coords[i][0] > 0 ? 1 : 0);
            vertData.append(coords[i][1] > 0 ? 0 : 1);
        }
    } else {
        for (int i = 0; i < 6; i++) {
            // vertex position
            vertData.append(coords[i][0]);
            vertData.append(coords[i][1]);
            vertData.append(coords[i][2]);
            // texture coordinate
            vertData.append(coords[i][0] > 0 ? 1 : 0);
            vertData.append(coords[i][1] > 0 ? 0 : 1);
        }
    }

    // new
    if (! vbo.isCreated()) {
        vbo.create();
    }

    vbo.bind();
    vbo.allocate(vertData.constData(), vertData.count() * sizeof(GLfloat));
}

void VideoPlayer::videoSize(int width, int height) {
    _videoWidth = width;
    _videoHeight = height;

    makeCurrent();
    makeObject();
    doneCurrent();
}

void VideoPlayer::newFrame (GLuint texture) {
    textureId = texture;

    update();
}

void VideoPlayer::initPipeline() {
    if (! _pipeline) {
        _pipeline = std::unique_ptr<GstreamerPipeline>(new GstreamerPipeline());
        _pipeline->initialize(context());
        connect(_pipeline.get(), &GstreamerPipeline::newFrameReady, this, &VideoPlayer::newFrame, Qt::DirectConnection);
        connect(_pipeline.get(), &GstreamerPipeline::videoSize, this, &VideoPlayer::videoSize);
        connect(_pipeline.get(), &GstreamerPipeline::finished, this, &VideoPlayer::finished);
    }
}
<|endoftext|>


<commit_before>
#include <UnitTest++.h>
#include <TestReporterStdout.h>

#include <OSGConfig.h>
#include <OSGBaseInitFunctions.h>

int main(int argc, char *argv[])
{
    OSG::osgInit(argc, argv);

    return UnitTest::RunAllTests();

//     UnitTest::TestReporterStdout rep;
//     return UnitTest::RunAllTests(rep, UnitTest::Test::GetTestList(), argv[1]);
}
<commit_msg>removed: svn:externals for unittest-cpp on Tools/ it moved to Tools/unittest-cpp<commit_after>
#include <UnitTest++.h>
#include <TestReporterStdout.h>

#include <OSGConfig.h>
#include <OSGBaseInitFunctions.h>

struct TestNameEquals
{
	TestNameEquals(const std::string &testName)
		: _testName(testName)
	{
	}

	bool operator()(const UnitTest::Test * const test) const
	{
		return (_testName == test->m_details.testName);
	}

	std::string _testName;
};

int main(int argc, char *argv[])
{
    OSG::osgInit(argc, argv);

	int retVal = -1;

	if(argc > 1)
	{
		TestNameEquals               pred(argv[1]);
		UnitTest::TestReporterStdout rep;
		UnitTest::TestRunner         runner(rep);

		retVal = runner.RunTestsIf(
			UnitTest::Test::GetTestList(), NULL, pred, 0);
	}
	else
	{
		retVal = UnitTest::RunAllTests();
	}

	OSG::osgExit();

    return retVal;
}
<|endoftext|>