<commit_before>// The MIT License (MIT)

// Copyright (c) 2016, Microsoft

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

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

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

#include <algorithm>
#include <ostream>

#include "BitFunnel/Plan/Factories.h"
#include "MatchVerifier.h"

namespace BitFunnel
{
    std::unique_ptr<IMatchVerifier> Factories::CreateMatchVerifier()
    {
        return std::unique_ptr<IMatchVerifier>(new MatchVerifier);
    }

    void MatchVerifier::AddExpected(DocId id)
    {
        m_expected.push_back(id);
    }

    void MatchVerifier::AddObserved(DocId id)
    {
        m_observed.push_back(id);
    }

    void MatchVerifier::Verify()
    {
        // Sort from smallest to largest.
        std::sort(m_expected.begin(), m_expected.end());
        std::sort(m_observed.begin(), m_observed.end());

        // Indices into expected and observed.
        size_t e = 0;
        size_t o = 0;
        while (e < m_expected.size() && o < m_observed.size())
        {
            DocId expected = m_expected[e];
            DocId observed = m_observed[o];
            if (expected < observed)
            {
                m_falseNegatives.push_back(expected);
                ++e;
                // Force a test failure.
                // TODO: consider doing something that will only fail tests.
                throw;
            }
            else if (expected > observed)
            {
                m_falsePositives.push_back(observed);
                ++o;
            }
            else // observed == expected
            {
                m_truePositives.push_back(expected);
                ++o;
                ++e;
            }
        }

        while (o < m_observed.size())
        {
            m_falsePositives.push_back(m_observed[o]);
            ++o;
        }

        while (e < m_expected.size())
        {
            m_falseNegatives.push_back(m_expected[e]);
            ++e;
        }
    }

    void PrintDocIdVector(std::ostream & out,
                          std::vector<DocId> const & documents)
    {
        for (size_t i = 0; i < documents.size(); ++i)
        {
            if (i > 0)
            {
                out << ", ";
            }
            out << documents[i];
        }
        out << std::endl;
    }

    void MatchVerifier::Print(std::ostream & out) const
    {
        out << "False Positives: ";
        PrintDocIdVector(out, m_falsePositives);
        out << std::endl;

        out << "False Negatives: ";
        PrintDocIdVector(out, m_falseNegatives);
        out << std::endl;

        out << "True Positives: ";
        PrintDocIdVector(out, m_truePositives);
        out << std::endl;

        out << "False Positives: "
            << m_falsePositives.size();

        size_t totalPositives =
            m_falsePositives.size() + m_truePositives.size();
        if (totalPositives > 0)
        {
            out
                << " (rate = "
                << static_cast<double>(
                    m_falsePositives.size()) /
                totalPositives
                << ") ";
        }

        out << std::endl
            << "False Negatives: "
            << m_falseNegatives.size()
            << std::endl
            << "True Positives: "
            << m_truePositives.size()
            << std::endl;
    }

    void MatchVerifier::Reset()
    {
        m_expected.clear();
        m_observed.clear();
        m_truePositives.clear();
        m_falsePositives.clear();
        m_falseNegatives.clear();
    }
}
<commit_msg>Throw RecoverableError instead of empty. See #252.<commit_after>// The MIT License (MIT)

// Copyright (c) 2016, Microsoft

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

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

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

#include <algorithm>
#include <ostream>

#include "BitFunnel/Exceptions.h"
#include "BitFunnel/Plan/Factories.h"
#include "MatchVerifier.h"

namespace BitFunnel
{
    std::unique_ptr<IMatchVerifier> Factories::CreateMatchVerifier()
    {
        return std::unique_ptr<IMatchVerifier>(new MatchVerifier);
    }

    void MatchVerifier::AddExpected(DocId id)
    {
        m_expected.push_back(id);
    }

    void MatchVerifier::AddObserved(DocId id)
    {
        m_observed.push_back(id);
    }

    void MatchVerifier::Verify()
    {
        // Sort from smallest to largest.
        std::sort(m_expected.begin(), m_expected.end());
        std::sort(m_observed.begin(), m_observed.end());

        // Indices into expected and observed.
        size_t e = 0;
        size_t o = 0;
        while (e < m_expected.size() && o < m_observed.size())
        {
            DocId expected = m_expected[e];
            DocId observed = m_observed[o];
            if (expected < observed)
            {
                m_falseNegatives.push_back(expected);
                ++e;
                // Force a test failure.
                // TODO: consider doing something that will only fail tests.
                throw RecoverableError("MatchVerifier: false negative detected.");
            }
            else if (expected > observed)
            {
                m_falsePositives.push_back(observed);
                ++o;
            }
            else // observed == expected
            {
                m_truePositives.push_back(expected);
                ++o;
                ++e;
            }
        }

        while (o < m_observed.size())
        {
            m_falsePositives.push_back(m_observed[o]);
            ++o;
        }

        while (e < m_expected.size())
        {
            m_falseNegatives.push_back(m_expected[e]);
            ++e;
        }
    }

    void PrintDocIdVector(std::ostream & out,
                          std::vector<DocId> const & documents)
    {
        for (size_t i = 0; i < documents.size(); ++i)
        {
            if (i > 0)
            {
                out << ", ";
            }
            out << documents[i];
        }
        out << std::endl;
    }

    void MatchVerifier::Print(std::ostream & out) const
    {
        out << "False Positives: ";
        PrintDocIdVector(out, m_falsePositives);
        out << std::endl;

        out << "False Negatives: ";
        PrintDocIdVector(out, m_falseNegatives);
        out << std::endl;

        out << "True Positives: ";
        PrintDocIdVector(out, m_truePositives);
        out << std::endl;

        out << "False Positives: "
            << m_falsePositives.size();

        size_t totalPositives =
            m_falsePositives.size() + m_truePositives.size();
        if (totalPositives > 0)
        {
            out
                << " (rate = "
                << static_cast<double>(
                    m_falsePositives.size()) /
                totalPositives
                << ") ";
        }

        out << std::endl
            << "False Negatives: "
            << m_falseNegatives.size()
            << std::endl
            << "True Positives: "
            << m_truePositives.size()
            << std::endl;
    }

    void MatchVerifier::Reset()
    {
        m_expected.clear();
        m_observed.clear();
        m_truePositives.clear();
        m_falsePositives.clear();
        m_falseNegatives.clear();
    }
}
<|endoftext|>


<commit_before>/*
 * main.cpp (Tutorial10_Instancing)
 *
 * This file is part of the "LLGL" project (Copyright (c) 2015 by Lukas Hermanns)
 * See "LICENSE.txt" for license information.
 */

#include "../tutorial.h"

class Tutorial10 : public Tutorial
{

    // Static configuration for this demo
    static const unsigned int   numPlantInstances   = 20000;
    static const unsigned int   numPlantImages      = 10;
    const float                 positionRange       = 40.0f;

    LLGL::ShaderProgram*        shaderProgram       = nullptr;

    LLGL::GraphicsPipeline*     pipeline            = nullptr;

    // Two vertex buffer, one for per-vertex data, one for per-instance data
    LLGL::Buffer*               vertexBuffers[2]    = { nullptr };
    LLGL::BufferArray*          vertexBufferArray   = nullptr;

    LLGL::Buffer*               constantBuffer      = nullptr;

    // 2D-array texture for all plant images
    LLGL::Texture*              arrayTexture        = nullptr;

    LLGL::Sampler*              samplers[2]         = { nullptr };

    float                       viewRotation        = 0.0f;

    struct Settings
    {
        Gs::Matrix4f    vpMatrix;          // View-projection matrix
        Gs::Vector2f    animationVector;   // Animation vector to make the plants wave in the wind
        float           _pad0[2];
    }
    settings;

public:

    Tutorial10() :
        Tutorial( L"LLGL Tutorial 10: Instancing" )
    {
        // Create all graphics objects
        auto vertexFormat = CreateBuffers();
        shaderProgram = LoadStandardShaderProgram(vertexFormat);
        CreateTextures();
        CreateSamplers();
        CreatePipelines();

        // Show info
        std::cout << "press LEFT/RIGHT MOUSE BUTTON to rotate the camera around the scene" << std::endl;
    }

private:

    float Random(float a, float b) const
    {
        auto rnd = static_cast<float>(rand()) / RAND_MAX;
        return a + (b - a) * rnd;
    }

    LLGL::VertexFormat CreateBuffers()
    {
        // Specify vertex formats
        LLGL::VertexFormat vertexFormatPerVertex;
        vertexFormatPerVertex.AppendAttribute({ "position", LLGL::VectorType::Float3 });
        vertexFormatPerVertex.AppendAttribute({ "texCoord", LLGL::VectorType::Float2 });

        LLGL::VertexFormat vertexFormatPerInstance;
        vertexFormatPerInstance.AppendAttribute({ "color",      LLGL::VectorType::Float3, 1 });
        vertexFormatPerInstance.AppendAttribute({ "wMatrix", 0, LLGL::VectorType::Float4, 1 });
        vertexFormatPerInstance.AppendAttribute({ "wMatrix", 1, LLGL::VectorType::Float4, 1 });
        vertexFormatPerInstance.AppendAttribute({ "wMatrix", 2, LLGL::VectorType::Float4, 1 });
        vertexFormatPerInstance.AppendAttribute({ "wMatrix", 3, LLGL::VectorType::Float4, 1 });
        vertexFormatPerInstance.AppendAttribute({ "arrayLayer", LLGL::VectorType::Float,  1 });

        LLGL::VertexFormat vertexFormat;
        vertexFormat.AppendAttributes(vertexFormatPerVertex);
        vertexFormat.AppendAttributes(vertexFormatPerInstance);

        // Initialize per-vertex data (4 vertices for the plane of each plant)
        static const float grassSize    = 100.0f;
        static const float grassTexSize = 40.0f;

        struct Vertex
        {
            Gs::Vector3f position;
            Gs::Vector2f texCoord;
        }
        vertexData[] =
        {
            // Vertices for plants plane
            { { -1, 0, 0 }, { 0, 1 } },
            { { -1, 2, 0 }, { 0, 0 } },
            { {  1, 0, 0 }, { 1, 1 } },
            { {  1, 2, 0 }, { 1, 0 } },

            // Vertices for grass plane
            { { -grassSize, 0, -grassSize }, {            0, grassTexSize } },
            { { -grassSize, 0,  grassSize }, {            0,            0 } },
            { {  grassSize, 0, -grassSize }, { grassTexSize, grassTexSize } },
            { {  grassSize, 0,  grassSize }, { grassTexSize,            0 } },
        };

        // Initialize per-instance data (use dynamic container to avoid a stack overflow)
        struct Instance
        {
            LLGL::ColorRGBf color;      // Instance color
            Gs::Matrix4f    wMatrix;    // World matrix
            float           arrayLayer; // Array texture layer
        };

        std::vector<Instance> instanceData(numPlantInstances + 1);

        for (std::size_t i = 0; i < numPlantInstances; ++i)
        {
            auto& instance = instanceData[i];

            // Set random color variations
            instance.color.r = Random(0.6f, 1.0f);
            instance.color.g = Random(0.8f, 1.0f);
            instance.color.b = Random(0.6f, 1.0f);
            instance.color *= Random(0.8f, 1.0f);

            // Distribute instances randomly over the specified position range
            Gs::Translate(
                instance.wMatrix,
                {
                    Random(-positionRange, positionRange),
                    0.0f,
                    Random(-positionRange, positionRange)
                }
            );

            // Rotate plane randomly
            Gs::RotateFree(instance.wMatrix, { 0, 1, 0 }, Random(0.0f, Gs::pi*2.0f));

            // Scale size randomly
            Gs::Scale(instance.wMatrix, Gs::Vector3f(Random(0.7f, 1.5f)));

            // Set array texture layer randomly, too
            instance.arrayLayer = std::floor( Random(0.0f, static_cast<float>(numPlantImages) - Gs::Epsilon<float>()) );
        }

        // Initialize last instance (for grass plane)
        auto& grassPlane = instanceData[numPlantInstances];
        grassPlane.arrayLayer = static_cast<float>(numPlantImages + 1);

        // Create buffer for per-vertex data
        LLGL::BufferDescriptor desc;

        desc.type                   = LLGL::BufferType::Vertex;
        desc.size                   = sizeof(vertexData);
        desc.vertexBuffer.format    = vertexFormatPerVertex;

        vertexBuffers[0] = renderer->CreateBuffer(desc, vertexData);

        // Create buffer for per-instance data
        desc.size                   = sizeof(Instance) * instanceData.size();
        desc.vertexBuffer.format    = vertexFormatPerInstance;

        vertexBuffers[1] = renderer->CreateBuffer(desc, instanceData.data());

        // Create vertex buffer array
        vertexBufferArray = renderer->CreateBufferArray(2, vertexBuffers);

        // Create constant buffer
        constantBuffer = CreateConstantBuffer(settings);

        return vertexFormat;
    }

    void CreateTextures()
    {
        const std::string texturePath = "../Media/Textures/";
        std::string filename;

        std::vector<unsigned char> arrayImageBuffer;

        // Load all array images
        int width = 0, height = 0;

        auto numImages = (numPlantImages + 1);

        for (unsigned int i = 0; i < numImages; ++i)
        {
            // Setup filename for "Plants_N.png" where N is from 0 to 9
            if (i < numPlantImages)
                filename = texturePath + "Plants_" + std::to_string(i) + ".png";
            else
                filename = texturePath + "Grass.jpg";

            // Load all images from file (using STBI library, see https://github.com/nothings/stb)
            int w = 0, h = 0, c = 0;
            unsigned char* imageBuffer = stbi_load(filename.c_str(), &w, &h, &c, 4);
            if (!imageBuffer)
                throw std::runtime_error("failed to load texture from file: \"" + filename + "\"");

            // Copy image buffer into array image buffer
            if ( ( width != 0 && height != 0 ) && ( width != w || height != h ) )
                throw std::runtime_error("image size mismatch");

            width = w;
            height = h;

            auto imageBufferSize = w*h*4;
            auto imageBufferOffset = arrayImageBuffer.size();
            arrayImageBuffer.resize(imageBufferOffset + imageBufferSize);

            std::copy(imageBuffer, imageBuffer + imageBufferSize, arrayImageBuffer.data() + imageBufferOffset);

            // Release temporary image data
            stbi_image_free(imageBuffer);

            // Show info
            std::cout << "loaded texture: " << filename << std::endl;
        }

        // Create array texture object with 'numImages' layers
        LLGL::ImageDescriptor imageDesc(
            LLGL::ImageFormat::RGBA,
            LLGL::DataType::UInt8,
            arrayImageBuffer.data()
        );

        arrayTexture = renderer->CreateTexture(
            LLGL::Texture2DArrayDesc(LLGL::TextureFormat::RGBA, width, height, numImages),
            &imageDesc
        );

        // Generate MIP-maps
        renderer->GenerateMips(*arrayTexture);
    }

    void CreateSamplers()
    {
        // Create sampler state object for the grass plane
        LLGL::SamplerDescriptor samplerDesc;
        {
            samplerDesc.maxAnisotropy = 8;
        }
        samplers[1] = renderer->CreateSampler(samplerDesc);

        // Create sampler state object for the plants
        {
            samplerDesc.textureWrapU = LLGL::TextureWrap::Clamp;
            samplerDesc.textureWrapV = LLGL::TextureWrap::Clamp;
            samplerDesc.textureWrapW = LLGL::TextureWrap::Clamp;
        }
        samplers[0] = renderer->CreateSampler(samplerDesc);
    }

    void CreatePipelines()
    {
        // Create common graphics pipeline for scene rendering
        LLGL::GraphicsPipelineDescriptor pipelineDesc;
        {
            pipelineDesc.shaderProgram              = shaderProgram;
            pipelineDesc.primitiveTopology          = LLGL::PrimitiveTopology::TriangleStrip;
            pipelineDesc.depth.testEnabled          = true;
            pipelineDesc.depth.writeEnabled         = true;
            //pipelineDesc.rasterizer.multiSampling   = LLGL::MultiSamplingDescriptor(8);
        }
        pipeline = renderer->CreateGraphicsPipeline(pipelineDesc);
    }

    void UpdateAnimation()
    {
        // Update view rotation by user input
        if (input->KeyPressed(LLGL::Key::RButton) || input->KeyPressed(LLGL::Key::LButton))
            viewRotation += static_cast<float>(input->GetMouseMotion().x) * 0.005f;
        else
            viewRotation += 0.002f;

        // Set view-projection matrix
        Gs::Matrix4f vMatrix;

        Gs::RotateFree(vMatrix, { 0, 1, 0 }, viewRotation);
        Gs::RotateFree(vMatrix, { 1, 0, 0 }, Gs::Deg2Rad(-33.0f));
        Gs::Translate(vMatrix, { 0, 0, -18 });

        settings.vpMatrix = projection * vMatrix.Inverse();

        // Process wave animation
        static const float  animationRadius  = 0.1f;
        static const float  animationSpeed   = 0.01f;
        static float        animationTime;

        animationTime += animationSpeed;

        settings.animationVector.x = std::sin(animationTime) * animationRadius;
        settings.animationVector.y = std::cos(animationTime) * animationRadius;

        // Upload new data to the constant buffer on the GPU
        UpdateBuffer(constantBuffer, settings);

        // Allow dynamic shader reloading (for debugging)
        if (input->KeyDown(LLGL::Key::R))
        {
            if (ReloadShaderProgram(shaderProgram))
            {
                renderer->Release(*pipeline);
                CreatePipelines();
            }
        }
    }

    void OnDrawFrame() override
    {
        UpdateAnimation();

        // Clear color- and depth buffers
        commands->ClearBuffers(LLGL::ClearBuffersFlags::Color | LLGL::ClearBuffersFlags::Depth);

        // Set buffer array, texture, and sampler
        commands->SetVertexBufferArray(*vertexBufferArray);
        commands->SetTexture(*arrayTexture, 0, LLGL::ShaderStageFlags::FragmentStage);
        commands->SetConstantBuffer(*constantBuffer, 0, LLGL::ShaderStageFlags::VertexStage);

        // Set graphics pipeline state
        commands->SetGraphicsPipeline(*pipeline);

        // Draw all plant instances with 4 vertices each
        commands->SetSampler(*samplers[0], 0, LLGL::ShaderStageFlags::FragmentStage);
        commands->DrawInstanced(4, 0, numPlantInstances);

        // Draw grass plane (last instance)
        commands->SetSampler(*samplers[1], 0, LLGL::ShaderStageFlags::FragmentStage);
        commands->DrawInstanced(4, 4, 1, numPlantInstances);

        // Present result on the screen
        context->Present();
    }

};

LLGL_IMPLEMENT_TUTORIAL(Tutorial10);

<commit_msg>Minor update in Tutorial10.<commit_after>/*
 * main.cpp (Tutorial10_Instancing)
 *
 * This file is part of the "LLGL" project (Copyright (c) 2015 by Lukas Hermanns)
 * See "LICENSE.txt" for license information.
 */

#include "../tutorial.h"

class Tutorial10 : public Tutorial
{

    // Static configuration for this demo
    static const unsigned int   numPlantInstances   = 20000;
    static const unsigned int   numPlantImages      = 10;
    const float                 positionRange       = 40.0f;

    LLGL::ShaderProgram*        shaderProgram       = nullptr;

    LLGL::GraphicsPipeline*     pipeline            = nullptr;

    // Two vertex buffer, one for per-vertex data, one for per-instance data
    LLGL::Buffer*               vertexBuffers[2]    = { nullptr };
    LLGL::BufferArray*          vertexBufferArray   = nullptr;

    LLGL::Buffer*               constantBuffer      = nullptr;

    // 2D-array texture for all plant images
    LLGL::Texture*              arrayTexture        = nullptr;

    LLGL::Sampler*              samplers[2]         = { nullptr };

    float                       viewRotation        = 0.0f;

    struct Settings
    {
        Gs::Matrix4f    vpMatrix;          // View-projection matrix
        Gs::Vector2f    animationVector;   // Animation vector to make the plants wave in the wind
        float           _pad0[2];
    }
    settings;

public:

    Tutorial10() :
        Tutorial( L"LLGL Tutorial 10: Instancing" )
    {
        // Create all graphics objects
        auto vertexFormat = CreateBuffers();
        shaderProgram = LoadStandardShaderProgram(vertexFormat);
        CreateTextures();
        CreateSamplers();
        CreatePipelines();

        // Show info
        std::cout << "press LEFT/RIGHT MOUSE BUTTON to rotate the camera around the scene" << std::endl;
        std::cout << "press R KEY to reload the shader program" << std::endl;
    }

private:

    float Random(float a, float b) const
    {
        auto rnd = static_cast<float>(rand()) / RAND_MAX;
        return a + (b - a) * rnd;
    }

    LLGL::VertexFormat CreateBuffers()
    {
        // Specify vertex formats
        LLGL::VertexFormat vertexFormatPerVertex;
        vertexFormatPerVertex.AppendAttribute({ "position", LLGL::VectorType::Float3 });
        vertexFormatPerVertex.AppendAttribute({ "texCoord", LLGL::VectorType::Float2 });

        LLGL::VertexFormat vertexFormatPerInstance;
        vertexFormatPerInstance.AppendAttribute({ "color",      LLGL::VectorType::Float3, 1 });
        vertexFormatPerInstance.AppendAttribute({ "wMatrix", 0, LLGL::VectorType::Float4, 1 });
        vertexFormatPerInstance.AppendAttribute({ "wMatrix", 1, LLGL::VectorType::Float4, 1 });
        vertexFormatPerInstance.AppendAttribute({ "wMatrix", 2, LLGL::VectorType::Float4, 1 });
        vertexFormatPerInstance.AppendAttribute({ "wMatrix", 3, LLGL::VectorType::Float4, 1 });
        vertexFormatPerInstance.AppendAttribute({ "arrayLayer", LLGL::VectorType::Float,  1 });

        LLGL::VertexFormat vertexFormat;
        vertexFormat.AppendAttributes(vertexFormatPerVertex);
        vertexFormat.AppendAttributes(vertexFormatPerInstance);

        // Initialize per-vertex data (4 vertices for the plane of each plant)
        static const float grassSize    = 100.0f;
        static const float grassTexSize = 40.0f;

        struct Vertex
        {
            Gs::Vector3f position;
            Gs::Vector2f texCoord;
        }
        vertexData[] =
        {
            // Vertices for plants plane
            { { -1, 0, 0 }, { 0, 1 } },
            { { -1, 2, 0 }, { 0, 0 } },
            { {  1, 0, 0 }, { 1, 1 } },
            { {  1, 2, 0 }, { 1, 0 } },

            // Vertices for grass plane
            { { -grassSize, 0, -grassSize }, {            0, grassTexSize } },
            { { -grassSize, 0,  grassSize }, {            0,            0 } },
            { {  grassSize, 0, -grassSize }, { grassTexSize, grassTexSize } },
            { {  grassSize, 0,  grassSize }, { grassTexSize,            0 } },
        };

        // Initialize per-instance data (use dynamic container to avoid a stack overflow)
        struct Instance
        {
            LLGL::ColorRGBf color;      // Instance color
            Gs::Matrix4f    wMatrix;    // World matrix
            float           arrayLayer; // Array texture layer
        };

        std::vector<Instance> instanceData(numPlantInstances + 1);

        for (std::size_t i = 0; i < numPlantInstances; ++i)
        {
            auto& instance = instanceData[i];

            // Set random color variations
            instance.color.r = Random(0.6f, 1.0f);
            instance.color.g = Random(0.8f, 1.0f);
            instance.color.b = Random(0.6f, 1.0f);
            instance.color *= Random(0.8f, 1.0f);

            // Distribute instances randomly over the specified position range
            Gs::Translate(
                instance.wMatrix,
                {
                    Random(-positionRange, positionRange),
                    0.0f,
                    Random(-positionRange, positionRange)
                }
            );

            // Rotate plane randomly
            Gs::RotateFree(instance.wMatrix, { 0, 1, 0 }, Random(0.0f, Gs::pi*2.0f));

            // Scale size randomly
            Gs::Scale(instance.wMatrix, Gs::Vector3f(Random(0.7f, 1.5f)));

            // Set array texture layer randomly, too
            instance.arrayLayer = std::floor( Random(0.0f, static_cast<float>(numPlantImages) - Gs::Epsilon<float>()) );
        }

        // Initialize last instance (for grass plane)
        auto& grassPlane = instanceData[numPlantInstances];
        grassPlane.arrayLayer = static_cast<float>(numPlantImages + 1);

        // Create buffer for per-vertex data
        LLGL::BufferDescriptor desc;

        desc.type                   = LLGL::BufferType::Vertex;
        desc.size                   = sizeof(vertexData);
        desc.vertexBuffer.format    = vertexFormatPerVertex;

        vertexBuffers[0] = renderer->CreateBuffer(desc, vertexData);

        // Create buffer for per-instance data
        desc.size                   = sizeof(Instance) * instanceData.size();
        desc.vertexBuffer.format    = vertexFormatPerInstance;

        vertexBuffers[1] = renderer->CreateBuffer(desc, instanceData.data());

        // Create vertex buffer array
        vertexBufferArray = renderer->CreateBufferArray(2, vertexBuffers);

        // Create constant buffer
        constantBuffer = CreateConstantBuffer(settings);

        return vertexFormat;
    }

    void CreateTextures()
    {
        const std::string texturePath = "../Media/Textures/";
        std::string filename;

        std::vector<unsigned char> arrayImageBuffer;

        // Load all array images
        int width = 0, height = 0;

        auto numImages = (numPlantImages + 1);

        for (unsigned int i = 0; i < numImages; ++i)
        {
            // Setup filename for "Plants_N.png" where N is from 0 to 9
            if (i < numPlantImages)
                filename = texturePath + "Plants_" + std::to_string(i) + ".png";
            else
                filename = texturePath + "Grass.jpg";

            // Load all images from file (using STBI library, see https://github.com/nothings/stb)
            int w = 0, h = 0, c = 0;
            unsigned char* imageBuffer = stbi_load(filename.c_str(), &w, &h, &c, 4);
            if (!imageBuffer)
                throw std::runtime_error("failed to load texture from file: \"" + filename + "\"");

            // Copy image buffer into array image buffer
            if ( ( width != 0 && height != 0 ) && ( width != w || height != h ) )
                throw std::runtime_error("image size mismatch");

            width = w;
            height = h;

            auto imageBufferSize = w*h*4;
            auto imageBufferOffset = arrayImageBuffer.size();
            arrayImageBuffer.resize(imageBufferOffset + imageBufferSize);

            std::copy(imageBuffer, imageBuffer + imageBufferSize, arrayImageBuffer.data() + imageBufferOffset);

            // Release temporary image data
            stbi_image_free(imageBuffer);

            // Show info
            std::cout << "loaded texture: " << filename << std::endl;
        }

        // Create array texture object with 'numImages' layers
        LLGL::ImageDescriptor imageDesc(
            LLGL::ImageFormat::RGBA,
            LLGL::DataType::UInt8,
            arrayImageBuffer.data()
        );

        arrayTexture = renderer->CreateTexture(
            LLGL::Texture2DArrayDesc(LLGL::TextureFormat::RGBA, width, height, numImages),
            &imageDesc
        );

        // Generate MIP-maps
        renderer->GenerateMips(*arrayTexture);
    }

    void CreateSamplers()
    {
        // Create sampler state object for the grass plane
        LLGL::SamplerDescriptor samplerDesc;
        {
            samplerDesc.maxAnisotropy = 8;
        }
        samplers[1] = renderer->CreateSampler(samplerDesc);

        // Create sampler state object for the plants
        {
            samplerDesc.textureWrapU = LLGL::TextureWrap::Clamp;
            samplerDesc.textureWrapV = LLGL::TextureWrap::Clamp;
            samplerDesc.textureWrapW = LLGL::TextureWrap::Clamp;
        }
        samplers[0] = renderer->CreateSampler(samplerDesc);
    }

    void CreatePipelines()
    {
        // Create common graphics pipeline for scene rendering
        LLGL::GraphicsPipelineDescriptor pipelineDesc;
        {
            pipelineDesc.shaderProgram              = shaderProgram;
            pipelineDesc.primitiveTopology          = LLGL::PrimitiveTopology::TriangleStrip;
            pipelineDesc.depth.testEnabled          = true;
            pipelineDesc.depth.writeEnabled         = true;
            //pipelineDesc.rasterizer.multiSampling   = LLGL::MultiSamplingDescriptor(8);
        }
        pipeline = renderer->CreateGraphicsPipeline(pipelineDesc);
    }

    void UpdateAnimation()
    {
        // Update view rotation by user input
        if (input->KeyPressed(LLGL::Key::RButton) || input->KeyPressed(LLGL::Key::LButton))
            viewRotation += static_cast<float>(input->GetMouseMotion().x) * 0.005f;
        else
            viewRotation += 0.002f;

        // Set view-projection matrix
        Gs::Matrix4f vMatrix;

        Gs::RotateFree(vMatrix, { 0, 1, 0 }, viewRotation);
        Gs::RotateFree(vMatrix, { 1, 0, 0 }, Gs::Deg2Rad(-33.0f));
        Gs::Translate(vMatrix, { 0, 0, -18 });

        settings.vpMatrix = projection * vMatrix.Inverse();

        // Process wave animation
        static const float  animationRadius  = 0.1f;
        static const float  animationSpeed   = 0.01f;
        static float        animationTime;

        animationTime += animationSpeed;

        settings.animationVector.x = std::sin(animationTime) * animationRadius;
        settings.animationVector.y = std::cos(animationTime) * animationRadius;

        // Upload new data to the constant buffer on the GPU
        UpdateBuffer(constantBuffer, settings);

        // Allow dynamic shader reloading (for debugging)
        if (input->KeyDown(LLGL::Key::R))
        {
            if (ReloadShaderProgram(shaderProgram))
            {
                renderer->Release(*pipeline);
                CreatePipelines();
            }
        }
    }

    void OnDrawFrame() override
    {
        UpdateAnimation();

        // Clear color- and depth buffers
        commands->ClearBuffers(LLGL::ClearBuffersFlags::Color | LLGL::ClearBuffersFlags::Depth);

        // Set buffer array, texture, and sampler
        commands->SetVertexBufferArray(*vertexBufferArray);
        commands->SetTexture(*arrayTexture, 0, LLGL::ShaderStageFlags::FragmentStage);
        commands->SetConstantBuffer(*constantBuffer, 0, LLGL::ShaderStageFlags::VertexStage);

        // Set graphics pipeline state
        commands->SetGraphicsPipeline(*pipeline);

        // Draw all plant instances (vertices: 4, first vertex: 0, instances: numPlantInstances)
        commands->SetSampler(*samplers[0], 0, LLGL::ShaderStageFlags::FragmentStage);
        commands->DrawInstanced(4, 0, numPlantInstances);

        // Draw grass plane (vertices: 4, first vertex: 4, instances: 1, instance offset: numPlantInstances)
        commands->SetSampler(*samplers[1], 0, LLGL::ShaderStageFlags::FragmentStage);
        commands->DrawInstanced(4, 4, 1, numPlantInstances);

        // Present result on the screen
        context->Present();
    }

};

LLGL_IMPLEMENT_TUTORIAL(Tutorial10);

<|endoftext|>


<commit_before>//----------------------------  fe_collection_04.cc  ---------------------------
//    $Id$
//    Version: $Name$
//
//    Copyright (C) 2005, 2006, 2012 by the deal.II authors
//
//    This file is subject to QPL and may not be  distributed
//    without copyright and license information. Please refer
//    to the file deal.II/doc/license.html for the  text  and
//    further information on this license.
//
//----------------------------  fe_collection_04.cc  ---------------------------

// test the results of FECollection::n_blocks(). test the case where elements
// have the same number of components but different numbers of blocks. this
// needs to lead to an assertion.

#include "../tests.h"
#include <deal.II/base/logstream.h>
#include <deal.II/hp/fe_collection.h>
#include <deal.II/fe/fe_system.h>
#include <deal.II/fe/fe_q.h>
#include <deal.II/fe/fe_raviart_thomas.h>

#include <fstream>

template <int dim>
void test ()
{
				   // now the same with one of the elements
				   // being non-primitive. the other one can
				   // then not simply be a FESystem but must
				   // in fact be an FESystem of FESystem to
				   // hide multiple components in one
				   // block. this test tries to see what
				   // happens if one doesn't do this
  hp::FECollection<dim> fe_collection;
  fe_collection.push_back (FE_RaviartThomas<dim>(1));
  fe_collection.push_back (FESystem<dim>(FE_Q<dim>(2),dim));

				   // we will get an assertion failure in
				   // n_blocks here. what it returns instead
				   // is then meaningless but it will in fact
				   // be the number of blocks of the first
				   // element of the collection, which here is
				   // one
  Assert (fe_collection.n_blocks() == 1,
	  ExcInternalError());

  deallog << "OK" << std::endl;
}

int main ()
{
  deal_II_exceptions::disable_abort_on_exception();

  std::ofstream logfile("fe_collection_04/output");
  logfile.precision(2);

  deallog.attach(logfile);
  deallog.depth_console(0);
  deallog.threshold_double(1.e-10);

  test<2> ();
  test<3> ();

  deallog << "OK" << std::endl;
}
<commit_msg>fix tests (throw in assert change)<commit_after>//----------------------------  fe_collection_04.cc  ---------------------------
//    $Id$
//    Version: $Name$
//
//    Copyright (C) 2005, 2006, 2012 by the deal.II authors
//
//    This file is subject to QPL and may not be  distributed
//    without copyright and license information. Please refer
//    to the file deal.II/doc/license.html for the  text  and
//    further information on this license.
//
//----------------------------  fe_collection_04.cc  ---------------------------

// test the results of FECollection::n_blocks(). test the case where elements
// have the same number of components but different numbers of blocks. this
// needs to lead to an assertion.

#include "../tests.h"
#include <deal.II/base/logstream.h>
#include <deal.II/hp/fe_collection.h>
#include <deal.II/fe/fe_system.h>
#include <deal.II/fe/fe_q.h>
#include <deal.II/fe/fe_raviart_thomas.h>

#include <fstream>

template <int dim>
void test ()
{
				   // now the same with one of the elements
				   // being non-primitive. the other one can
				   // then not simply be a FESystem but must
				   // in fact be an FESystem of FESystem to
				   // hide multiple components in one
				   // block. this test tries to see what
				   // happens if one doesn't do this
  hp::FECollection<dim> fe_collection;
  fe_collection.push_back (FE_RaviartThomas<dim>(1));
  fe_collection.push_back (FESystem<dim>(FE_Q<dim>(2),dim));

				   // we will get an assertion failure in
				   // n_blocks here.
  try
    {
      fe_collection.n_blocks();
    }
  catch (...)
    {
    }

  deallog << "OK" << std::endl;
}

int main ()
{
  deal_II_exceptions::disable_abort_on_exception();

  std::ofstream logfile("fe_collection_04/output");
  logfile.precision(2);

  deallog.attach(logfile);
  deallog.depth_console(0);
  deallog.threshold_double(1.e-10);

  test<2> ();
  test<3> ();

  deallog << "OK" << std::endl;
}
<|endoftext|>


<commit_before>#include <boost/test/unit_test.hpp>

#include <string>
#include "internals/acceptor.hpp"
#include <iostream>

#include <thread>
#include "common.hpp"

BOOST_AUTO_TEST_SUITE(AcceptorTestSuite)

using namespace everest::internals;

const unsigned short TEST_PORT = 6000;

BOOST_AUTO_TEST_CASE(BadArgs)
{
	// Port 53 is most likely taken
	BOOST_CHECK_THROW(Acceptor a(53, EV_DEFAULT, [](int) {}), std::runtime_error);
	BOOST_CHECK_THROW(Acceptor a(TEST_PORT, nullptr, [](int) {}), std::runtime_error);
}

BOOST_AUTO_TEST_CASE(Accept)
{
	bool isCalled = false;

	struct ev_loop* loop = EV_DEFAULT;

	Acceptor a(TEST_PORT, loop, [&](int) {
		ev_break(loop, EVBREAK_ALL);
		isCalled = true;
	});

	a.start();

	runParallel("wget localhost:6000 -T 1 --tries=1 -q");

	ev_run(loop, 0);

	BOOST_CHECK(isCalled);
}

BOOST_AUTO_TEST_SUITE_END()
<commit_msg>Taking port from the variable<commit_after>#include <boost/test/unit_test.hpp>

#include <string>
#include "internals/acceptor.hpp"
#include <iostream>

#include <thread>
#include "common.hpp"

BOOST_AUTO_TEST_SUITE(AcceptorTestSuite)

using namespace everest::internals;

const unsigned short TEST_PORT = 6000;

BOOST_AUTO_TEST_CASE(BadArgs)
{
	// Port 53 is most likely taken
	BOOST_CHECK_THROW(Acceptor a(53, EV_DEFAULT, [](int) {}), std::runtime_error);
	BOOST_CHECK_THROW(Acceptor a(TEST_PORT, nullptr, [](int) {}), std::runtime_error);
}

BOOST_AUTO_TEST_CASE(Accept)
{
	bool isCalled = false;

	struct ev_loop* loop = EV_DEFAULT;

	Acceptor a(TEST_PORT, loop, [&](int) {
		ev_break(loop, EVBREAK_ALL);
		isCalled = true;
	});

	a.start();

	runParallel("wget -T 1 --tries=1 -q localhost:" + std::to_string(TEST_PORT));

	ev_run(loop, 0);

	BOOST_CHECK(isCalled);
}

BOOST_AUTO_TEST_SUITE_END()
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2015 ScyllaDB
 */

/*
 * This file is part of Scylla.
 *
 * Scylla 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.
 *
 * Scylla 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Scylla.  If not, see <http://www.gnu.org/licenses/>.
 */

#pragma once

#include "mutation.hh"

class mutation_partition_assertion {
    schema_ptr _schema;
    const mutation_partition& _m;
public:
    mutation_partition_assertion(schema_ptr s, const mutation_partition& m)
        : _schema(s)
        , _m(m)
    { }

    // If ck_ranges is passed, verifies only that information relevant for ck_ranges matches.
    mutation_partition_assertion& is_equal_to(const mutation_partition& other,
            const stdx::optional<query::clustering_row_ranges>& ck_ranges = {}) {
        return is_equal_to(*_schema, other, ck_ranges);
    }

    // If ck_ranges is passed, verifies only that information relevant for ck_ranges matches.
    mutation_partition_assertion& is_equal_to(const schema& s, const mutation_partition& other,
            const stdx::optional<query::clustering_row_ranges>& ck_ranges = {}) {
        if (ck_ranges) {
            mutation_partition_assertion(_schema, _m.sliced(*_schema, *ck_ranges))
                .is_equal_to(s, other.sliced(s, *ck_ranges));
            return *this;
        }
        if (!_m.equal(*_schema, other, s)) {
            BOOST_FAIL(sprint("Mutations differ, expected %s\n ...but got: %s", other, _m));
        }
        if (!other.equal(s, _m, *_schema)) {
            BOOST_FAIL(sprint("Mutation inequality is not symmetric for %s\n ...and: %s", other, _m));
        }
        return *this;
    }

    mutation_partition_assertion& is_not_equal_to(const mutation_partition& other) {
        return is_not_equal_to(*_schema, other);
    }

    mutation_partition_assertion& is_not_equal_to(const schema& s, const mutation_partition& other) {
        if (_m.equal(*_schema, other, s)) {
            BOOST_FAIL(sprint("Mutations equal but expected to differ: %s\n ...and: %s", other, _m));
        }
        return *this;
    }

    mutation_partition_assertion& has_same_continuity(const mutation_partition& other) {
        if (!_m.equal_continuity(*_schema, other)) {
            BOOST_FAIL(sprint("Continuity doesn't match: %s\n ...and: %s", other, _m));
        }
        return *this;
    }

    mutation_partition_assertion& is_continuous(const position_range& r, is_continuous cont = is_continuous::yes) {
        if (!_m.check_continuity(*_schema, r, cont)) {
            BOOST_FAIL(sprint("Expected range %s to be %s in %s", r, cont ? "continuous" : "discontinuous", _m));
        }
        return *this;
    }
};

static inline
mutation_partition_assertion assert_that(schema_ptr s, const mutation_partition& mp) {
    return {std::move(s), mp};
}

class mutation_assertion {
    mutation _m;
public:
    mutation_assertion(mutation m)
        : _m(std::move(m))
    { }

    // If ck_ranges is passed, verifies only that information relevant for ck_ranges matches.
    mutation_assertion& is_equal_to(const mutation& other, const stdx::optional<query::clustering_row_ranges>& ck_ranges = {}) {
        if (ck_ranges) {
            mutation_assertion(_m.sliced(*ck_ranges)).is_equal_to(other.sliced(*ck_ranges));
            return *this;
        }
        if (_m != other) {
            BOOST_FAIL(sprint("Mutations differ, expected %s\n ...but got: %s", other, _m));
        }
        if (other != _m) {
            BOOST_FAIL(sprint("Mutation inequality is not symmetric for %s\n ...and: %s", other, _m));
        }
        return *this;
    }

    mutation_assertion& is_not_equal_to(const mutation& other) {
        if (_m == other) {
            BOOST_FAIL(sprint("Mutations equal but expected to differ: %s\n ...and: %s", other, _m));
        }
        return *this;
    }

    mutation_assertion& has_schema(schema_ptr s) {
        if (_m.schema() != s) {
            BOOST_FAIL(sprint("Expected mutation of schema %s, but got %s", *s, *_m.schema()));
        }
        return *this;
    }

    mutation_assertion& has_same_continuity(const mutation& other) {
        assert_that(_m.schema(), _m.partition()).has_same_continuity(other.partition());
        return *this;
    }

    mutation_assertion& is_continuous(const position_range& r, is_continuous cont = is_continuous::yes) {
        assert_that(_m.schema(), _m.partition()).is_continuous(r, cont);
        return *this;
    }

    // Verifies that mutation data remains unchanged when upgraded to the new schema
    void is_upgrade_equivalent(schema_ptr new_schema) {
        mutation m2 = _m;
        m2.upgrade(new_schema);
        BOOST_REQUIRE(m2.schema() == new_schema);
        mutation_assertion(m2).is_equal_to(_m);

        mutation m3 = m2;
        m3.upgrade(_m.schema());
        BOOST_REQUIRE(m3.schema() == _m.schema());
        mutation_assertion(m3).is_equal_to(_m);
        mutation_assertion(m3).is_equal_to(m2);
    }
};

static inline
mutation_assertion assert_that(mutation m) {
    return { std::move(m) };
}

static inline
mutation_assertion assert_that(streamed_mutation sm) {
    auto mo = mutation_from_streamed_mutation(std::move(sm)).get0();
    return { std::move(*mo) };
}

class mutation_opt_assertions {
    mutation_opt _mo;
public:
    mutation_opt_assertions(mutation_opt mo) : _mo(std::move(mo)) {}

    mutation_assertion has_mutation() {
        if (!_mo) {
            BOOST_FAIL("Expected engaged mutation_opt, but found not");
        }
        return { *_mo };
    }

    void has_no_mutation() {
        if (_mo) {
            BOOST_FAIL("Expected disengaged mutation_opt");
        }
    }
};

static inline
mutation_opt_assertions assert_that(mutation_opt mo) {
    return { std::move(mo) };
}

static inline
mutation_opt_assertions assert_that(streamed_mutation_opt smo) {
    auto mo = mutation_from_streamed_mutation(std::move(smo)).get0();
    return { std::move(mo) };
}

class streamed_mutation_assertions {
    streamed_mutation _sm;
    clustering_key::equality _ck_eq;
public:
    streamed_mutation_assertions(streamed_mutation sm)
        : _sm(std::move(sm)), _ck_eq(*_sm.schema()) { }

    streamed_mutation_assertions& produces_static_row() {
        auto mfopt = _sm().get0();
        if (!mfopt) {
            BOOST_FAIL("Expected static row, got end of stream");
        }
        if (mfopt->mutation_fragment_kind() != mutation_fragment::kind::static_row) {
            BOOST_FAIL(sprint("Expected static row, got: %s", mfopt->mutation_fragment_kind()));
        }
        return *this;
    }

    streamed_mutation_assertions& produces(mutation_fragment::kind k, std::vector<int> ck_elements) {
        std::vector<bytes> ck_bytes;
        for (auto&& e : ck_elements) {
            ck_bytes.emplace_back(int32_type->decompose(e));
        }
        auto ck = clustering_key_prefix::from_exploded(*_sm.schema(), std::move(ck_bytes));

        auto mfopt = _sm().get0();
        if (!mfopt) {
            BOOST_FAIL(sprint("Expected mutation fragment %s, got end of stream", ck));
        }
        if (mfopt->mutation_fragment_kind() != k) {
            BOOST_FAIL(sprint("Expected mutation fragment kind %s, got: %s", k, mfopt->mutation_fragment_kind()));
        }
        if (!_ck_eq(mfopt->key(), ck)) {
            BOOST_FAIL(sprint("Expected key %s, got: %s", ck, mfopt->key()));
        }
        return *this;
    }

    streamed_mutation_assertions& produces(mutation_fragment mf) {
        auto mfopt = _sm().get0();
        if (!mfopt) {
            BOOST_FAIL(sprint("Expected mutation fragment %s, got end of stream", mf));
        }
        if (!mfopt->equal(*_sm.schema(), mf)) {
            BOOST_FAIL(sprint("Expected %s, but got %s", mf, *mfopt));
        }
        return *this;
    }

    streamed_mutation_assertions& produces(const mutation& m) {
        assert_that(mutation_from_streamed_mutation(_sm).get0()).is_equal_to(m);
        return *this;
    }

    streamed_mutation_assertions& produces_only(const std::deque<mutation_fragment>& fragments) {
        for (auto&& f : fragments) {
            produces(f);
        }
        produces_end_of_stream();
        return *this;
    }

    streamed_mutation_assertions& produces_row_with_key(const clustering_key& ck) {
        BOOST_TEST_MESSAGE(sprint("Expect %s", ck));
        auto mfo = _sm().get0();
        if (!mfo) {
            BOOST_FAIL(sprint("Expected row with key %s, but got end of stream", ck));
        }
        if (!mfo->is_clustering_row()) {
            BOOST_FAIL(sprint("Expected row with key %s, but got %s", ck, *mfo));
        }
        auto& actual = mfo->as_clustering_row().key();
        if (!actual.equal(*_sm.schema(), ck)) {
            BOOST_FAIL(sprint("Expected row with key %s, but key is %s", ck, actual));
        }
        return *this;
    }

    // If ck_ranges is passed, verifies only that information relevant for ck_ranges matches.
    streamed_mutation_assertions& produces_range_tombstone(const range_tombstone& rt, const query::clustering_row_ranges& ck_ranges = {}) {
        BOOST_TEST_MESSAGE(sprint("Expect %s", rt));
        auto mfo = _sm().get0();
        if (!mfo) {
            BOOST_FAIL(sprint("Expected range tombstone %s, but got end of stream", rt));
        }
        if (!mfo->is_range_tombstone()) {
            BOOST_FAIL(sprint("Expected range tombstone %s, but got %s", rt, *mfo));
        }
        auto& actual = mfo->as_range_tombstone();
        const schema& s = *_sm.schema();
        if (!ck_ranges.empty()) {
            range_tombstone_list actual_list(s);
            range_tombstone_list expected_list(s);
            actual_list.apply(s, actual);
            expected_list.apply(s, rt);
            actual_list.trim(s, ck_ranges);
            expected_list.trim(s, ck_ranges);
            if (!actual_list.equal(s, expected_list)) {
                BOOST_FAIL(sprint("Expected %s, but got %s", expected_list, actual_list));
            }
        } else if (!actual.equal(s, rt)) {
            BOOST_FAIL(sprint("Expected range tombstone %s, but got %s", rt, actual));
        }
        return *this;
    }

    streamed_mutation_assertions& fwd_to(const clustering_key& ck1, const clustering_key& ck2) {
        return fwd_to(position_range{
            position_in_partition(position_in_partition::clustering_row_tag_t(), ck1),
            position_in_partition(position_in_partition::clustering_row_tag_t(), ck2)
        });
    }

    streamed_mutation_assertions& fwd_to(position_range range) {
        BOOST_TEST_MESSAGE(sprint("Forwarding to %s", range));
        _sm.fast_forward_to(std::move(range)).get();
        return *this;
    }

    streamed_mutation_assertions& produces_end_of_stream() {
        auto mfopt = _sm().get0();
        if (mfopt) {
            BOOST_FAIL(sprint("Expected end of stream, got: %s", *mfopt));
        }
        return *this;
    }

    void has_monotonic_positions() {
        position_in_partition::less_compare less(*_sm.schema());
        mutation_fragment_opt prev;
        for (;;) {
            mutation_fragment_opt mfo = _sm().get0();
            if (!mfo) {
                break;
            }
            if (prev) {
                if (!less(prev->position(), mfo->position())) {
                    BOOST_FAIL(sprint("previous fragment has greater position: prev=%s, current=%s", *prev, *mfo));
                }
            }
            prev = std::move(mfo);
        }
    }
};

static inline streamed_mutation_assertions assert_that_stream(streamed_mutation sm)
{
    return streamed_mutation_assertions(std::move(sm));
}
<commit_msg>tests: streamed_mutation_assertions: Merge range_tombstones with the same position in produces_range_tombstone()<commit_after>/*
 * Copyright (C) 2015 ScyllaDB
 */

/*
 * This file is part of Scylla.
 *
 * Scylla 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.
 *
 * Scylla 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Scylla.  If not, see <http://www.gnu.org/licenses/>.
 */

#pragma once

#include "mutation.hh"

class mutation_partition_assertion {
    schema_ptr _schema;
    const mutation_partition& _m;
public:
    mutation_partition_assertion(schema_ptr s, const mutation_partition& m)
        : _schema(s)
        , _m(m)
    { }

    // If ck_ranges is passed, verifies only that information relevant for ck_ranges matches.
    mutation_partition_assertion& is_equal_to(const mutation_partition& other,
            const stdx::optional<query::clustering_row_ranges>& ck_ranges = {}) {
        return is_equal_to(*_schema, other, ck_ranges);
    }

    // If ck_ranges is passed, verifies only that information relevant for ck_ranges matches.
    mutation_partition_assertion& is_equal_to(const schema& s, const mutation_partition& other,
            const stdx::optional<query::clustering_row_ranges>& ck_ranges = {}) {
        if (ck_ranges) {
            mutation_partition_assertion(_schema, _m.sliced(*_schema, *ck_ranges))
                .is_equal_to(s, other.sliced(s, *ck_ranges));
            return *this;
        }
        if (!_m.equal(*_schema, other, s)) {
            BOOST_FAIL(sprint("Mutations differ, expected %s\n ...but got: %s", other, _m));
        }
        if (!other.equal(s, _m, *_schema)) {
            BOOST_FAIL(sprint("Mutation inequality is not symmetric for %s\n ...and: %s", other, _m));
        }
        return *this;
    }

    mutation_partition_assertion& is_not_equal_to(const mutation_partition& other) {
        return is_not_equal_to(*_schema, other);
    }

    mutation_partition_assertion& is_not_equal_to(const schema& s, const mutation_partition& other) {
        if (_m.equal(*_schema, other, s)) {
            BOOST_FAIL(sprint("Mutations equal but expected to differ: %s\n ...and: %s", other, _m));
        }
        return *this;
    }

    mutation_partition_assertion& has_same_continuity(const mutation_partition& other) {
        if (!_m.equal_continuity(*_schema, other)) {
            BOOST_FAIL(sprint("Continuity doesn't match: %s\n ...and: %s", other, _m));
        }
        return *this;
    }

    mutation_partition_assertion& is_continuous(const position_range& r, is_continuous cont = is_continuous::yes) {
        if (!_m.check_continuity(*_schema, r, cont)) {
            BOOST_FAIL(sprint("Expected range %s to be %s in %s", r, cont ? "continuous" : "discontinuous", _m));
        }
        return *this;
    }
};

static inline
mutation_partition_assertion assert_that(schema_ptr s, const mutation_partition& mp) {
    return {std::move(s), mp};
}

class mutation_assertion {
    mutation _m;
public:
    mutation_assertion(mutation m)
        : _m(std::move(m))
    { }

    // If ck_ranges is passed, verifies only that information relevant for ck_ranges matches.
    mutation_assertion& is_equal_to(const mutation& other, const stdx::optional<query::clustering_row_ranges>& ck_ranges = {}) {
        if (ck_ranges) {
            mutation_assertion(_m.sliced(*ck_ranges)).is_equal_to(other.sliced(*ck_ranges));
            return *this;
        }
        if (_m != other) {
            BOOST_FAIL(sprint("Mutations differ, expected %s\n ...but got: %s", other, _m));
        }
        if (other != _m) {
            BOOST_FAIL(sprint("Mutation inequality is not symmetric for %s\n ...and: %s", other, _m));
        }
        return *this;
    }

    mutation_assertion& is_not_equal_to(const mutation& other) {
        if (_m == other) {
            BOOST_FAIL(sprint("Mutations equal but expected to differ: %s\n ...and: %s", other, _m));
        }
        return *this;
    }

    mutation_assertion& has_schema(schema_ptr s) {
        if (_m.schema() != s) {
            BOOST_FAIL(sprint("Expected mutation of schema %s, but got %s", *s, *_m.schema()));
        }
        return *this;
    }

    mutation_assertion& has_same_continuity(const mutation& other) {
        assert_that(_m.schema(), _m.partition()).has_same_continuity(other.partition());
        return *this;
    }

    mutation_assertion& is_continuous(const position_range& r, is_continuous cont = is_continuous::yes) {
        assert_that(_m.schema(), _m.partition()).is_continuous(r, cont);
        return *this;
    }

    // Verifies that mutation data remains unchanged when upgraded to the new schema
    void is_upgrade_equivalent(schema_ptr new_schema) {
        mutation m2 = _m;
        m2.upgrade(new_schema);
        BOOST_REQUIRE(m2.schema() == new_schema);
        mutation_assertion(m2).is_equal_to(_m);

        mutation m3 = m2;
        m3.upgrade(_m.schema());
        BOOST_REQUIRE(m3.schema() == _m.schema());
        mutation_assertion(m3).is_equal_to(_m);
        mutation_assertion(m3).is_equal_to(m2);
    }
};

static inline
mutation_assertion assert_that(mutation m) {
    return { std::move(m) };
}

static inline
mutation_assertion assert_that(streamed_mutation sm) {
    auto mo = mutation_from_streamed_mutation(std::move(sm)).get0();
    return { std::move(*mo) };
}

class mutation_opt_assertions {
    mutation_opt _mo;
public:
    mutation_opt_assertions(mutation_opt mo) : _mo(std::move(mo)) {}

    mutation_assertion has_mutation() {
        if (!_mo) {
            BOOST_FAIL("Expected engaged mutation_opt, but found not");
        }
        return { *_mo };
    }

    void has_no_mutation() {
        if (_mo) {
            BOOST_FAIL("Expected disengaged mutation_opt");
        }
    }
};

static inline
mutation_opt_assertions assert_that(mutation_opt mo) {
    return { std::move(mo) };
}

static inline
mutation_opt_assertions assert_that(streamed_mutation_opt smo) {
    auto mo = mutation_from_streamed_mutation(std::move(smo)).get0();
    return { std::move(mo) };
}

class streamed_mutation_assertions {
    streamed_mutation _sm;
    clustering_key::equality _ck_eq;
public:
    streamed_mutation_assertions(streamed_mutation sm)
        : _sm(std::move(sm)), _ck_eq(*_sm.schema()) { }

    streamed_mutation_assertions& produces_static_row() {
        auto mfopt = _sm().get0();
        if (!mfopt) {
            BOOST_FAIL("Expected static row, got end of stream");
        }
        if (mfopt->mutation_fragment_kind() != mutation_fragment::kind::static_row) {
            BOOST_FAIL(sprint("Expected static row, got: %s", mfopt->mutation_fragment_kind()));
        }
        return *this;
    }

    streamed_mutation_assertions& produces(mutation_fragment::kind k, std::vector<int> ck_elements) {
        std::vector<bytes> ck_bytes;
        for (auto&& e : ck_elements) {
            ck_bytes.emplace_back(int32_type->decompose(e));
        }
        auto ck = clustering_key_prefix::from_exploded(*_sm.schema(), std::move(ck_bytes));

        auto mfopt = _sm().get0();
        if (!mfopt) {
            BOOST_FAIL(sprint("Expected mutation fragment %s, got end of stream", ck));
        }
        if (mfopt->mutation_fragment_kind() != k) {
            BOOST_FAIL(sprint("Expected mutation fragment kind %s, got: %s", k, mfopt->mutation_fragment_kind()));
        }
        if (!_ck_eq(mfopt->key(), ck)) {
            BOOST_FAIL(sprint("Expected key %s, got: %s", ck, mfopt->key()));
        }
        return *this;
    }

    streamed_mutation_assertions& produces(mutation_fragment mf) {
        auto mfopt = _sm().get0();
        if (!mfopt) {
            BOOST_FAIL(sprint("Expected mutation fragment %s, got end of stream", mf));
        }
        if (!mfopt->equal(*_sm.schema(), mf)) {
            BOOST_FAIL(sprint("Expected %s, but got %s", mf, *mfopt));
        }
        return *this;
    }

    streamed_mutation_assertions& produces(const mutation& m) {
        assert_that(mutation_from_streamed_mutation(_sm).get0()).is_equal_to(m);
        return *this;
    }

    streamed_mutation_assertions& produces_only(const std::deque<mutation_fragment>& fragments) {
        for (auto&& f : fragments) {
            produces(f);
        }
        produces_end_of_stream();
        return *this;
    }

    streamed_mutation_assertions& produces_row_with_key(const clustering_key& ck) {
        BOOST_TEST_MESSAGE(sprint("Expect %s", ck));
        auto mfo = _sm().get0();
        if (!mfo) {
            BOOST_FAIL(sprint("Expected row with key %s, but got end of stream", ck));
        }
        if (!mfo->is_clustering_row()) {
            BOOST_FAIL(sprint("Expected row with key %s, but got %s", ck, *mfo));
        }
        auto& actual = mfo->as_clustering_row().key();
        if (!actual.equal(*_sm.schema(), ck)) {
            BOOST_FAIL(sprint("Expected row with key %s, but key is %s", ck, actual));
        }
        return *this;
    }

    // If ck_ranges is passed, verifies only that information relevant for ck_ranges matches.
    streamed_mutation_assertions& produces_range_tombstone(const range_tombstone& rt, const query::clustering_row_ranges& ck_ranges = {}) {
        BOOST_TEST_MESSAGE(sprint("Expect %s", rt));
        auto mfo = _sm().get0();
        if (!mfo) {
            BOOST_FAIL(sprint("Expected range tombstone %s, but got end of stream", rt));
        }
        if (!mfo->is_range_tombstone()) {
            BOOST_FAIL(sprint("Expected range tombstone %s, but got %s", rt, *mfo));
        }
        const schema& s = *_sm.schema();
        range_tombstone_list actual_list(s);
        position_in_partition::equal_compare eq(s);
        while (mutation_fragment* next = _sm.peek().get0()) {
            if (!next->is_range_tombstone() || !eq(next->position(), mfo->position())) {
                break;
            }
            actual_list.apply(s, _sm().get0()->as_range_tombstone());
        }
        actual_list.apply(s, mfo->as_range_tombstone());
        {
            range_tombstone_list expected_list(s);
            expected_list.apply(s, rt);
            actual_list.trim(s, ck_ranges);
            expected_list.trim(s, ck_ranges);
            if (!actual_list.equal(s, expected_list)) {
                BOOST_FAIL(sprint("Expected %s, but got %s", expected_list, actual_list));
            }
        }
        return *this;
    }

    streamed_mutation_assertions& fwd_to(const clustering_key& ck1, const clustering_key& ck2) {
        return fwd_to(position_range{
            position_in_partition(position_in_partition::clustering_row_tag_t(), ck1),
            position_in_partition(position_in_partition::clustering_row_tag_t(), ck2)
        });
    }

    streamed_mutation_assertions& fwd_to(position_range range) {
        BOOST_TEST_MESSAGE(sprint("Forwarding to %s", range));
        _sm.fast_forward_to(std::move(range)).get();
        return *this;
    }

    streamed_mutation_assertions& produces_end_of_stream() {
        auto mfopt = _sm().get0();
        if (mfopt) {
            BOOST_FAIL(sprint("Expected end of stream, got: %s", *mfopt));
        }
        return *this;
    }

    void has_monotonic_positions() {
        position_in_partition::less_compare less(*_sm.schema());
        mutation_fragment_opt prev;
        for (;;) {
            mutation_fragment_opt mfo = _sm().get0();
            if (!mfo) {
                break;
            }
            if (prev) {
                if (!less(prev->position(), mfo->position())) {
                    BOOST_FAIL(sprint("previous fragment has greater position: prev=%s, current=%s", *prev, *mfo));
                }
            }
            prev = std::move(mfo);
        }
    }
};

static inline streamed_mutation_assertions assert_that_stream(streamed_mutation sm)
{
    return streamed_mutation_assertions(std::move(sm));
}
<|endoftext|>


<commit_before>/**
  * Touhou Community Reliant Automatic Patcher
  * Update plugin
  *
  * ----
  *
  * Update notifications for thcrap and the game itself.
  */

#include <thcrap.h>
#include "self.h"

static const char *update_url_message =
	"The new version can be found at\n"
	"\n"
	"\t";
static const char *mbox_copy_message =
	"\n"
	"\n"
	"(Press Ctrl+C to copy the text of this message box and the URL)";

/// Self-updating messages
/// ----------------------
static UINT self_msg_type[] = {
	MB_ICONINFORMATION, // SELF_OK
	MB_ICONINFORMATION, // SELF_NO_PUBLIC_KEY
	MB_ICONEXCLAMATION, // SELF_SERVER_ERROR
	MB_ICONEXCLAMATION, // SELF_DISK_ERROR
	MB_ICONERROR, // SELF_NO_SIG
	MB_ICONERROR, // SELF_SIG_FAIL
	MB_ICONINFORMATION, // SELF_REPLACE_ERROR
};

static const char *self_header_failure =
	"A new version (${build}) of the ${project} is available.\n"
	"\n";

static const char *self_body[] = {
	// SELF_OK
	"The ${project} has been successfully updated to version ${build}.\n"
	"\n"
	"For further information about this new release, visit\n"
	"\n"
	"\t${desc_url}",
	// SELF_NO_PUBLIC_KEY
	"Due to the lack of a digital signature on the currently running "
	"patcher build, an automatic update was not attempted.\n"
	"\n"
	"You can manually download the update archive from\n"
	"\n"
	"\t${desc_url}\n"
	"\n"
	"However, we can't prove its authenticity. Be careful!",
	// SELF_SERVER_ERROR
	"An automatic update was attempted, but none of the download servers "
	"could be reached.\n"
	"\n"
	"The new version can be found at\n"
	"\n"
	"\t${desc_url}\n"
	"\n"
	"However, instead of manually downloading the archive, we recommend "
	"to repeat this automatic update later, as this process also checks "
	"the digital signature on the archive for authenticity.",
	// SELF_DISK_ERROR
	"An automatic update was attempted, but the update archive could not "
	"be saved to disk, possibly due to a lack of writing permissions in "
	"the ${project_short} directory (${thcrap_dir}).\n"
	"\n"
	"You can manually download the update archive from\n"
	"\n"
	"\t${desc_url}\n"
	"\n"
	"Its digital signature has already been verified to be authentic.",
	// SELF_NO_SIG
	"An automatic update was attempted, but the server did not provide a "
	"digital signature to prove the authenticity of the update archive.\n"
	"\n"
	"Thus, it may have been maliciously altered.",
	// SELF_SIG_FAIL
	"An automatic update was attempted, but the digital signature of the "
	"update archive could not be verified against the public key on the "
	"currently running patcher build.\n"
	"\n"
	"This means that the update has been maliciously altered.",
	// SELF_REPLACE_ERROR
	"An automatic update was attempted, but the current build could not "
	"be replaced with the new one, possibly due to a lack of writing "
	"permissions.\n"
	"\n"
	"Please manually extract the new version from the update archive that "
	"has been saved to your ${project_short} directory "
	"(${thcrap_dir}${arc_fn}). "
	"Its digital signature has already been verified to be authentic.\n"
	"\n"
	"For further information about this new release, visit\n"
	"\n"
	"\t${desc_url}"
};

const char *self_sig_error =
	"\n"
	"We advise against downloading it from the originating website until "
	"this problem has been resolved.";
/// ----------------------

int IsLatestBuild(json_t *build, json_t **latest)
{
	json_t *json_latest = json_object_get(runconfig_get(), "latest");
	size_t i;
	if(!json_is_string(build) || !latest || !json_latest) {
		return -1;
	}
	json_flex_array_foreach(json_latest, i, *latest) {
		if(json_equal(build, *latest)) {
			return 1;
		}
	}
	return 0;
}

int update_notify_thcrap(void)
{
	const size_t SELF_MSG_SLOT = (size_t)self_body;
	self_result_t ret = SELF_OK;
	json_t *run_cfg = runconfig_get();
	DWORD min_build = json_object_get_hex(run_cfg, "thcrap_version_min");
	if(min_build > PROJECT_VERSION()) {
		const char *thcrap_dir = json_object_get_string(run_cfg, "thcrap_dir");
		const char *thcrap_url = json_object_get_string(run_cfg, "thcrap_url");
		char *arc_fn = NULL;
		const char *self_msg = NULL;
		char min_build_str[11];
		str_hexdate_format(min_build_str, min_build);
		ret = self_update(thcrap_dir, &arc_fn);
		strings_sprintf(SELF_MSG_SLOT,
			"%s%s",
			ret != SELF_OK ? self_header_failure : "",
			self_body[ret]
		);
		if(ret == SELF_NO_SIG || ret == SELF_SIG_FAIL) {
			strings_strcat(SELF_MSG_SLOT, self_sig_error);
		}
		strings_replace(SELF_MSG_SLOT, "${project}", PROJECT_NAME());
		strings_replace(SELF_MSG_SLOT, "${project_short}", PROJECT_NAME_SHORT());
		strings_replace(SELF_MSG_SLOT, "${build}", min_build_str);
		strings_replace(SELF_MSG_SLOT, "${thcrap_dir}", thcrap_dir);
		strings_replace(SELF_MSG_SLOT, "${desc_url}", thcrap_url);
		self_msg = strings_replace(SELF_MSG_SLOT, "${arc_fn}", arc_fn);
		log_mboxf(NULL, MB_OK | self_msg_type[ret], self_msg);
		SAFE_FREE(arc_fn);
	}
	return ret;
}

int update_notify_game(void)
{
	json_t *run_cfg = runconfig_get();
	const json_t *title = runconfig_title_get();
	json_t *build = json_object_get(run_cfg, "build");
	json_t *latest = NULL;
	int ret;

	if(!json_is_string(build) || !json_is_string(title)) {
		return -1;
	}
	ret = IsLatestBuild(build, &latest) == 0 && json_is_string(latest);
	if(ret) {
		const char *url_update = json_object_get_string(run_cfg, "url_update");
		log_mboxf("Old version detected", MB_OK | MB_ICONINFORMATION,
			"You are running an old version of %s (%s).\n"
			"\n"
			"While %s is confirmed to work with this version, we recommend updating "
			"your game to the latest official version (%s).%s%s%s%s",
			json_string_value(title), json_string_value(build), PROJECT_NAME_SHORT(),
			json_string_value(latest),
			url_update ? "\n\n": "",
			url_update ? update_url_message : "",
			url_update ? url_update : "",
			url_update ? mbox_copy_message : ""
		);
	}
	return ret;
}
<commit_msg>update: Reword the old version message.<commit_after>/**
  * Touhou Community Reliant Automatic Patcher
  * Update plugin
  *
  * ----
  *
  * Update notifications for thcrap and the game itself.
  */

#include <thcrap.h>
#include "self.h"

static const char *update_url_message =
	"The new version can be found at\n"
	"\n"
	"\t";
static const char *mbox_copy_message =
	"\n"
	"\n"
	"(Press Ctrl+C to copy the text of this message box and the URL)";

/// Self-updating messages
/// ----------------------
static UINT self_msg_type[] = {
	MB_ICONINFORMATION, // SELF_OK
	MB_ICONINFORMATION, // SELF_NO_PUBLIC_KEY
	MB_ICONEXCLAMATION, // SELF_SERVER_ERROR
	MB_ICONEXCLAMATION, // SELF_DISK_ERROR
	MB_ICONERROR, // SELF_NO_SIG
	MB_ICONERROR, // SELF_SIG_FAIL
	MB_ICONINFORMATION, // SELF_REPLACE_ERROR
};

static const char *self_header_failure =
	"A new version (${build}) of the ${project} is available.\n"
	"\n";

static const char *self_body[] = {
	// SELF_OK
	"The ${project} has been successfully updated to version ${build}.\n"
	"\n"
	"For further information about this new release, visit\n"
	"\n"
	"\t${desc_url}",
	// SELF_NO_PUBLIC_KEY
	"Due to the lack of a digital signature on the currently running "
	"patcher build, an automatic update was not attempted.\n"
	"\n"
	"You can manually download the update archive from\n"
	"\n"
	"\t${desc_url}\n"
	"\n"
	"However, we can't prove its authenticity. Be careful!",
	// SELF_SERVER_ERROR
	"An automatic update was attempted, but none of the download servers "
	"could be reached.\n"
	"\n"
	"The new version can be found at\n"
	"\n"
	"\t${desc_url}\n"
	"\n"
	"However, instead of manually downloading the archive, we recommend "
	"to repeat this automatic update later, as this process also checks "
	"the digital signature on the archive for authenticity.",
	// SELF_DISK_ERROR
	"An automatic update was attempted, but the update archive could not "
	"be saved to disk, possibly due to a lack of writing permissions in "
	"the ${project_short} directory (${thcrap_dir}).\n"
	"\n"
	"You can manually download the update archive from\n"
	"\n"
	"\t${desc_url}\n"
	"\n"
	"Its digital signature has already been verified to be authentic.",
	// SELF_NO_SIG
	"An automatic update was attempted, but the server did not provide a "
	"digital signature to prove the authenticity of the update archive.\n"
	"\n"
	"Thus, it may have been maliciously altered.",
	// SELF_SIG_FAIL
	"An automatic update was attempted, but the digital signature of the "
	"update archive could not be verified against the public key on the "
	"currently running patcher build.\n"
	"\n"
	"This means that the update has been maliciously altered.",
	// SELF_REPLACE_ERROR
	"An automatic update was attempted, but the current build could not "
	"be replaced with the new one, possibly due to a lack of writing "
	"permissions.\n"
	"\n"
	"Please manually extract the new version from the update archive that "
	"has been saved to your ${project_short} directory "
	"(${thcrap_dir}${arc_fn}). "
	"Its digital signature has already been verified to be authentic.\n"
	"\n"
	"For further information about this new release, visit\n"
	"\n"
	"\t${desc_url}"
};

const char *self_sig_error =
	"\n"
	"We advise against downloading it from the originating website until "
	"this problem has been resolved.";
/// ----------------------

int IsLatestBuild(json_t *build, json_t **latest)
{
	json_t *json_latest = json_object_get(runconfig_get(), "latest");
	size_t i;
	if(!json_is_string(build) || !latest || !json_latest) {
		return -1;
	}
	json_flex_array_foreach(json_latest, i, *latest) {
		if(json_equal(build, *latest)) {
			return 1;
		}
	}
	return 0;
}

int update_notify_thcrap(void)
{
	const size_t SELF_MSG_SLOT = (size_t)self_body;
	self_result_t ret = SELF_OK;
	json_t *run_cfg = runconfig_get();
	DWORD min_build = json_object_get_hex(run_cfg, "thcrap_version_min");
	if(min_build > PROJECT_VERSION()) {
		const char *thcrap_dir = json_object_get_string(run_cfg, "thcrap_dir");
		const char *thcrap_url = json_object_get_string(run_cfg, "thcrap_url");
		char *arc_fn = NULL;
		const char *self_msg = NULL;
		char min_build_str[11];
		str_hexdate_format(min_build_str, min_build);
		ret = self_update(thcrap_dir, &arc_fn);
		strings_sprintf(SELF_MSG_SLOT,
			"%s%s",
			ret != SELF_OK ? self_header_failure : "",
			self_body[ret]
		);
		if(ret == SELF_NO_SIG || ret == SELF_SIG_FAIL) {
			strings_strcat(SELF_MSG_SLOT, self_sig_error);
		}
		strings_replace(SELF_MSG_SLOT, "${project}", PROJECT_NAME());
		strings_replace(SELF_MSG_SLOT, "${project_short}", PROJECT_NAME_SHORT());
		strings_replace(SELF_MSG_SLOT, "${build}", min_build_str);
		strings_replace(SELF_MSG_SLOT, "${thcrap_dir}", thcrap_dir);
		strings_replace(SELF_MSG_SLOT, "${desc_url}", thcrap_url);
		self_msg = strings_replace(SELF_MSG_SLOT, "${arc_fn}", arc_fn);
		log_mboxf(NULL, MB_OK | self_msg_type[ret], self_msg);
		SAFE_FREE(arc_fn);
	}
	return ret;
}

int update_notify_game(void)
{
	json_t *run_cfg = runconfig_get();
	const json_t *title = runconfig_title_get();
	json_t *build = json_object_get(run_cfg, "build");
	json_t *latest = NULL;
	int ret;

	if(!json_is_string(build) || !json_is_string(title)) {
		return -1;
	}
	ret = IsLatestBuild(build, &latest) == 0 && json_is_string(latest);
	if(ret) {
		const char *url_update = json_object_get_string(run_cfg, "url_update");
		log_mboxf("Old version detected", MB_OK | MB_ICONINFORMATION,
			"You are running an old version of %s (%s).\n"
			"\n"
			"%s may or may not work with this version, so we recommend updating "
			"your game to the latest official version (%s).%s%s%s%s",
			json_string_value(title), json_string_value(build), PROJECT_NAME_SHORT(),
			json_string_value(latest),
			url_update ? "\n\n": "",
			url_update ? update_url_message : "",
			url_update ? url_update : "",
			url_update ? mbox_copy_message : ""
		);
	}
	return ret;
}
<|endoftext|>


<commit_before><commit_msg>some more build failures<commit_after><|endoftext|>


<commit_before><commit_msg>coverity#738591 Uninitialized pointer field<commit_after><|endoftext|>


<commit_before><commit_msg>again just refactoring ...<commit_after><|endoftext|>


<commit_before>#include "TMVA/BDTControlPlots.h"
#include <vector>
#include <string>

#include "TH1.h"
#include "TGraph.h"

// input: - Input file (result from TMVA),
//        - use of TMVA plotting TStyle

void TMVA::BDTControlPlots( TString fin , Bool_t useTMVAStyle  )
{
   // set style and remove existing canvas'
   TMVAGlob::Initialize( useTMVAStyle );

   // checks if file with name "fin" is already open, and if not opens one
   TFile* file;
   file = TMVAGlob::OpenFile( fin );

   // get all titles of the method BDT
   TList titles;
   TString methodName = "Method_BDT";
   UInt_t ninst = TMVAGlob::GetListOfTitles(methodName,titles);
   if (ninst==0) {
      cout << "Could not locate directory 'Method_BDT' in file " << fin << endl;
      return;
   }
   // loop over all titles
   TIter keyIter(&titles);
   TDirectory *bdtdir;
   TKey *key;
   while ((key = TMVAGlob::NextKey(keyIter,"TDirectory"))) {
      bdtdir = (TDirectory *)key->ReadObj();
      bdtcontrolplots( bdtdir );
   }
}

void TMVA::bdtcontrolplots( TDirectory *bdtdir ) {

   const Int_t nPlots = 6;

   Int_t width  = 900;
   Int_t height = 600;
   char cn[100], cn2[100];
   const TString titName = bdtdir->GetName();
   sprintf( cn, "cv_%s", titName.Data() );
   TCanvas *c = new TCanvas( cn,  Form( "%s Control Plots", titName.Data() ),
                             width, height );
   c->Divide(3,2);

   TString hname[nPlots]={"BoostMonitor","BoostWeight","BoostWeightVsTree","ErrFractHist","NodesBeforePruning",titName+"_FOMvsIterFrame"};

   Bool_t BoostMonitorIsDone=kFALSE;

   for (Int_t i=0; i<nPlots; i++){
      Int_t color = 4;
      c->cd(i+1);
      TH1 *h = (TH1*) bdtdir->Get(hname[i]);

      if (h){
         h->SetMaximum(h->GetMaximum()*1.3);
         h->SetMinimum( 0 );
         h->SetMarkerColor(color);
         h->SetMarkerSize( 0.7 );
         h->SetMarkerStyle( 24 );
         h->SetLineWidth(1);
         h->SetLineColor(color);
         if(hname[i]=="NodesBeforePruning")h->SetTitle("Nodes before/after pruning");
         h->Draw();
         if(hname[i]=="NodesBeforePruning"){
            TH1 *h2 = (TH1*) bdtdir->Get("NodesAfterPruning");
            h2->SetLineWidth(1);
            h2->SetLineColor(2);
            h2->Draw("same");
         }
         if(hname[i]=="BoostMonitor"){ // a plot only available in case DoBoostMontior option has bee set
            TGraph *g = (TGraph*) bdtdir->Get("BoostMonitorGraph");
            g->Draw("LP*");
            BoostMonitorIsDone = kTRUE;
         }
         if(hname[i]==titName+"_FOMvsIterFrame"){ // a plot only available in case DoBoostMontior option has bee set
            TGraph *g = (TGraph*) bdtdir->Get(titName+"_FOMvsIter");
            g->Draw();
         }
         c->Update();
      }
   }

   TCanvas *c2 = NULL;
   if (BoostMonitorIsDone){
      sprintf( cn2, "cv2_%s", titName.Data() );
      c2 = new TCanvas( cn2,  Form( "%s BoostWeights", titName.Data() ),
                                 1200, 1200 );
      c2->Divide(5,5);
      Int_t ipad=1;

      TIter keys( bdtdir->GetListOfKeys() );
      TKey *key;
      //      gDirectory->ls();
      while ( (key = (TKey*)keys.Next()) && ipad < 26) {
         TObject *obj=key->ReadObj();
         if (obj->IsA()->InheritsFrom(TH1::Class())){
            TH1F *hx = (TH1F*)obj;
            TString hhname_(Form("%s",obj->GetTitle()));
            if (hhname_.Contains("BoostWeightsInTreeB")){
               c2->cd(ipad++);
               hx->SetLineColor(4);
               hx->Draw();
               hhname_.ReplaceAll("TreeB","TreeS");
               bdtdir->GetObject(hhname_.Data(),hx);
               if (hx) {
                  hx->SetLineColor(2);
                  hx->Draw("same");
               }
            }
            c2->Update();
         }
      }

   }

   // write to file
   TString fname = Form( "plots/%s_ControlPlots", titName.Data() );
   TMVAGlob::imgconv( c, fname );

   if (c2){
      fname = Form( "plots/%s_ControlPlots2", titName.Data() );
      TMVAGlob::imgconv( c2, fname );
   }

   TCanvas *c3 = NULL;
   if (BoostMonitorIsDone){
      sprintf( cn2, "cv3_%s", titName.Data() );
      c3 = new TCanvas( cn2,  Form( "%s Variables", titName.Data() ),
                        1200, 1200 );
      c3->Divide(5,5);
      Int_t ipad=1;

      TIter keys( bdtdir->GetListOfKeys() );
      TKey *key;
      //      gDirectory->ls();
      while ( (key = (TKey*)keys.Next()) && ipad < 26) {
         TObject *obj=key->ReadObj();
         if (obj->IsA()->InheritsFrom(TH1::Class())){
            TH1F *hx = (TH1F*)obj;
            TString hname_(Form("%s",obj->GetTitle()));
            if (hname_.Contains("SigVar0AtTree")){
               c3->cd(ipad++);
               hx->SetLineColor(4);
               hx->Draw();
               hname_.ReplaceAll("Sig","Bkg");
               bdtdir->GetObject(hname_.Data(),hx);
               if (hx) {
                  hx->SetLineColor(2);
                  hx->Draw("same");
               }
            }
            c3->Update();
         }
      }

   }

}

<commit_msg>get rid of warning about set-but-unused variable<commit_after>#include "TMVA/BDTControlPlots.h"
#include <vector>
#include <string>

#include "TH1.h"
#include "TGraph.h"

// input: - Input file (result from TMVA),
//        - use of TMVA plotting TStyle

void TMVA::BDTControlPlots( TString fin , Bool_t useTMVAStyle  )
{
   // set style and remove existing canvas'
   TMVAGlob::Initialize( useTMVAStyle );

   // checks if file with name "fin" is already open, and if not opens one
   TFile* file;
   file = TMVAGlob::OpenFile( fin );
   if (file == NULL) {
      cout << "Problems with input file, tried to open " << fin << " but somehow did not succeed .." << endl;
      return;
   }

   // get all titles of the method BDT
   TList titles;
   TString methodName = "Method_BDT";
   UInt_t ninst = TMVAGlob::GetListOfTitles(methodName,titles);
   if (ninst==0) {
      cout << "Could not locate directory 'Method_BDT' in file " << fin << endl;
      return;
   }
   // loop over all titles
   TIter keyIter(&titles);
   TDirectory *bdtdir;
   TKey *key;
   while ((key = TMVAGlob::NextKey(keyIter,"TDirectory"))) {
      bdtdir = (TDirectory *)key->ReadObj();
      bdtcontrolplots( bdtdir );
   }
}

void TMVA::bdtcontrolplots( TDirectory *bdtdir ) {

   const Int_t nPlots = 6;

   Int_t width  = 900;
   Int_t height = 600;
   char cn[100], cn2[100];
   const TString titName = bdtdir->GetName();
   sprintf( cn, "cv_%s", titName.Data() );
   TCanvas *c = new TCanvas( cn,  Form( "%s Control Plots", titName.Data() ),
                             width, height );
   c->Divide(3,2);

   TString hname[nPlots]={"BoostMonitor","BoostWeight","BoostWeightVsTree","ErrFractHist","NodesBeforePruning",titName+"_FOMvsIterFrame"};

   Bool_t BoostMonitorIsDone=kFALSE;

   for (Int_t i=0; i<nPlots; i++){
      Int_t color = 4;
      c->cd(i+1);
      TH1 *h = (TH1*) bdtdir->Get(hname[i]);

      if (h){
         h->SetMaximum(h->GetMaximum()*1.3);
         h->SetMinimum( 0 );
         h->SetMarkerColor(color);
         h->SetMarkerSize( 0.7 );
         h->SetMarkerStyle( 24 );
         h->SetLineWidth(1);
         h->SetLineColor(color);
         if(hname[i]=="NodesBeforePruning")h->SetTitle("Nodes before/after pruning");
         h->Draw();
         if(hname[i]=="NodesBeforePruning"){
            TH1 *h2 = (TH1*) bdtdir->Get("NodesAfterPruning");
            h2->SetLineWidth(1);
            h2->SetLineColor(2);
            h2->Draw("same");
         }
         if(hname[i]=="BoostMonitor"){ // a plot only available in case DoBoostMontior option has bee set
            TGraph *g = (TGraph*) bdtdir->Get("BoostMonitorGraph");
            g->Draw("LP*");
            BoostMonitorIsDone = kTRUE;
         }
         if(hname[i]==titName+"_FOMvsIterFrame"){ // a plot only available in case DoBoostMontior option has bee set
            TGraph *g = (TGraph*) bdtdir->Get(titName+"_FOMvsIter");
            g->Draw();
         }
         c->Update();
      }
   }

   TCanvas *c2 = NULL;
   if (BoostMonitorIsDone){
      sprintf( cn2, "cv2_%s", titName.Data() );
      c2 = new TCanvas( cn2,  Form( "%s BoostWeights", titName.Data() ),
                                 1200, 1200 );
      c2->Divide(5,5);
      Int_t ipad=1;

      TIter keys( bdtdir->GetListOfKeys() );
      TKey *key;
      //      gDirectory->ls();
      while ( (key = (TKey*)keys.Next()) && ipad < 26) {
         TObject *obj=key->ReadObj();
         if (obj->IsA()->InheritsFrom(TH1::Class())){
            TH1F *hx = (TH1F*)obj;
            TString hhname_(Form("%s",obj->GetTitle()));
            if (hhname_.Contains("BoostWeightsInTreeB")){
               c2->cd(ipad++);
               hx->SetLineColor(4);
               hx->Draw();
               hhname_.ReplaceAll("TreeB","TreeS");
               bdtdir->GetObject(hhname_.Data(),hx);
               if (hx) {
                  hx->SetLineColor(2);
                  hx->Draw("same");
               }
            }
            c2->Update();
         }
      }

   }

   // write to file
   TString fname = Form( "plots/%s_ControlPlots", titName.Data() );
   TMVAGlob::imgconv( c, fname );

   if (c2){
      fname = Form( "plots/%s_ControlPlots2", titName.Data() );
      TMVAGlob::imgconv( c2, fname );
   }

   TCanvas *c3 = NULL;
   if (BoostMonitorIsDone){
      sprintf( cn2, "cv3_%s", titName.Data() );
      c3 = new TCanvas( cn2,  Form( "%s Variables", titName.Data() ),
                        1200, 1200 );
      c3->Divide(5,5);
      Int_t ipad=1;

      TIter keys( bdtdir->GetListOfKeys() );
      TKey *key;
      //      gDirectory->ls();
      while ( (key = (TKey*)keys.Next()) && ipad < 26) {
         TObject *obj=key->ReadObj();
         if (obj->IsA()->InheritsFrom(TH1::Class())){
            TH1F *hx = (TH1F*)obj;
            TString hname_(Form("%s",obj->GetTitle()));
            if (hname_.Contains("SigVar0AtTree")){
               c3->cd(ipad++);
               hx->SetLineColor(4);
               hx->Draw();
               hname_.ReplaceAll("Sig","Bkg");
               bdtdir->GetObject(hname_.Data(),hx);
               if (hx) {
                  hx->SetLineColor(2);
                  hx->Draw("same");
               }
            }
            c3->Update();
         }
      }

   }

}

<|endoftext|>